diff options
Diffstat (limited to 'indra')
203 files changed, 12205 insertions, 11523 deletions
diff --git a/indra/linux_crash_logger/CMakeLists.txt b/indra/linux_crash_logger/CMakeLists.txt index c0fc1b2be0..81d14c695b 100755 --- a/indra/linux_crash_logger/CMakeLists.txt +++ b/indra/linux_crash_logger/CMakeLists.txt @@ -4,6 +4,7 @@ project(linux_crash_logger) include(00-Common) include(GLH) +include(LLCoreHttp) include(LLCommon) include(LLCrashLogger) include(LLMath) @@ -13,8 +14,10 @@ include(LLXML) include(Linking) include(UI) include(FreeType) +include(Boost) include_directories( + ${LLCOREHTTP_INCLUDE_DIRS} ${LLCOMMON_INCLUDE_DIRS} ${LLCRASHLOGGER_INCLUDE_DIRS} ${LLMATH_INCLUDE_DIRS} @@ -53,6 +56,10 @@ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--as-needed") add_executable(linux-crash-logger ${linux_crash_logger_SOURCE_FILES}) +# llcommon uses `clock_gettime' which is provided by librt on linux. +set(LIBRT_LIBRARY rt) + + target_link_libraries(linux-crash-logger ${LLCRASHLOGGER_LIBRARIES} ${LLVFS_LIBRARIES} @@ -60,10 +67,14 @@ target_link_libraries(linux-crash-logger ${LLMESSAGE_LIBRARIES} ${LLVFS_LIBRARIES} ${LLMATH_LIBRARIES} + ${LLCOREHTTP_LIBRARIES} ${LLCOMMON_LIBRARIES} + ${BOOST_CONTEXT_LIBRARY} + ${BOOST_COROUTINE_LIBRARY} ${UI_LIBRARIES} ${DB_LIBRARIES} ${FREETYPE_LIBRARIES} + ${LIBRT_LIBRARY} ) add_custom_target(linux-crash-logger-target ALL diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 1459b9ada2..9757679ce1 100755 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -8,6 +8,7 @@ include(LLCommon) include(Linking) include(Boost) include(LLSharedLibs) +include(JsonCpp) include(GoogleBreakpad) include(GooglePerfTools) include(Copy3rdPartyLibs) @@ -17,6 +18,7 @@ include(URIPARSER) include_directories( ${EXPAT_INCLUDE_DIRS} ${LLCOMMON_INCLUDE_DIRS} + ${JSONCPP_INCLUDE_DIR} ${ZLIB_INCLUDE_DIRS} ${BREAKPAD_INCLUDE_DIRECTORIES} ${URIPARSER_INCLUDE_DIRS} @@ -85,6 +87,7 @@ set(llcommon_SOURCE_FILES llrefcount.cpp llrun.cpp llsd.cpp + llsdjson.cpp llsdparam.cpp llsdserialize.cpp llsdserialize_xml.cpp @@ -193,6 +196,7 @@ set(llcommon_HEADER_FILES llrefcount.h llsafehandle.h llsd.h + llsdjson.h llsdparam.h llsdserialize.h llsdserialize_xml.h @@ -260,6 +264,7 @@ target_link_libraries( ${APRUTIL_LIBRARIES} ${APR_LIBRARIES} ${EXPAT_LIBRARIES} + ${JSONCPP_LIBRARIES} ${ZLIB_LIBRARIES} ${WINDOWS_LIBRARIES} ${BOOST_PROGRAM_OPTIONS_LIBRARY} diff --git a/indra/llcommon/fix_macros.h b/indra/llcommon/fix_macros.h index ef959decff..43c09c54bc 100755 --- a/indra/llcommon/fix_macros.h +++ b/indra/llcommon/fix_macros.h @@ -16,10 +16,6 @@ // these macros all over again. // who injects MACROS with such generic names?! Grr. -#ifdef equivalent -#undef equivalent -#endif - #ifdef check #undef check #endif diff --git a/indra/llcommon/linden_common.h b/indra/llcommon/linden_common.h index 5cfcdab41c..e5a913a6a9 100755 --- a/indra/llcommon/linden_common.h +++ b/indra/llcommon/linden_common.h @@ -51,6 +51,7 @@ #include <cstdlib> #include <ctime> #include <iosfwd> +#include <memory> // Linden only libs in alpha-order other than stdtypes.h // *NOTE: Please keep includes here to a minimum, see above. diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp index baaddcaed1..d76401d01b 100755 --- a/indra/llcommon/llcoros.cpp +++ b/indra/llcommon/llcoros.cpp @@ -39,6 +39,44 @@ #include "llerror.h" #include "stringize.h" +// do nothing, when we need nothing done +void LLCoros::no_cleanup(CoroData*) {} + +// CoroData for the currently-running coroutine. Use a thread_specific_ptr +// because each thread potentially has its own distinct pool of coroutines. +// This thread_specific_ptr does NOT own the CoroData object! That's owned by +// LLCoros::mCoros. It merely identifies it. For this reason we instantiate +// it with a no-op cleanup function. +boost::thread_specific_ptr<LLCoros::CoroData> +LLCoros::sCurrentCoro(LLCoros::no_cleanup); + +//static +LLCoros::coro::self& LLCoros::get_self() +{ + CoroData* current = sCurrentCoro.get(); + if (! current) + { + LL_ERRS("LLCoros") << "Calling get_self() from non-coroutine context!" << LL_ENDL; + } + return *current->mSelf; +} + +llcoro::Suspending::Suspending(): + mSuspended(LLCoros::sCurrentCoro.get()) +{ + // Revert mCurrentCoro to the value it had at the moment we last switched + // into this coroutine. + LLCoros::sCurrentCoro.reset(mSuspended->mPrev); +} + +llcoro::Suspending::~Suspending() +{ + // Okay, we're back, update our mPrev + mSuspended->mPrev = LLCoros::sCurrentCoro.get(); + // and reinstate our sCurrentCoro. + LLCoros::sCurrentCoro.reset(mSuspended); +} + LLCoros::LLCoros(): // MAINT-2724: default coroutine stack size too small on Windows. // Previously we used @@ -58,9 +96,9 @@ bool LLCoros::cleanup(const LLSD&) { // Has this coroutine exited (normal return, exception, exit() call) // since last tick? - if (mi->second->exited()) + if (mi->second->mCoro.exited()) { - LL_INFOS("LLCoros") << "LLCoros: cleaning up coroutine " << mi->first << LL_ENDL; + LL_INFOS("LLCoros") << "LLCoros: cleaning up coroutine " << mi->first << LL_ENDL; // The erase() call will invalidate its passed iterator value -- // so increment mi FIRST -- but pass its original value to // erase(). This is what postincrement is all about. @@ -94,7 +132,7 @@ std::string LLCoros::generateDistinctName(const std::string& prefix) const { if (mCoros.find(name) == mCoros.end()) { - LL_INFOS("LLCoros") << "LLCoros: launching coroutine " << name << LL_ENDL; + LL_INFOS("LLCoros") << "LLCoros: launching coroutine " << name << LL_ENDL; return name; } } @@ -114,20 +152,15 @@ bool LLCoros::kill(const std::string& name) return true; } -std::string LLCoros::getNameByID(const void* self_id) const +std::string LLCoros::getName() const { - // Walk the existing coroutines, looking for one from which the 'self_id' - // passed to us comes. - for (CoroMap::const_iterator mi(mCoros.begin()), mend(mCoros.end()); mi != mend; ++mi) + CoroData* current = sCurrentCoro.get(); + if (! current) { - namespace coro_private = boost::dcoroutines::detail; - if (static_cast<void*>(coro_private::coroutine_accessor::get_impl(const_cast<coro&>(*mi->second)).get()) - == self_id) - { - return mi->first; - } + // not in a coroutine + return ""; } - return ""; + return current->mName; } void LLCoros::setStackSize(S32 stacksize) @@ -136,10 +169,24 @@ void LLCoros::setStackSize(S32 stacksize) mStackSize = stacksize; } +// Top-level wrapper around caller's coroutine callable. This function accepts +// the coroutine library's implicit coro::self& parameter and sets sCurrentSelf +// but does not pass it down to the caller's callable. +void LLCoros::toplevel(coro::self& self, CoroData* data, const callable_t& callable) +{ + // capture the 'self' param in CoroData + data->mSelf = &self; + // run the code the caller actually wants in the coroutine + callable(); + // This cleanup isn't perfectly symmetrical with the way we initially set + // data->mPrev, but this is our last chance to reset mCurrentCoro. + sCurrentCoro.reset(data->mPrev); +} + /***************************************************************************** * MUST BE LAST *****************************************************************************/ -// Turn off MSVC optimizations for just LLCoros::launchImpl() -- see +// Turn off MSVC optimizations for just LLCoros::launch() -- see // DEV-32777. But MSVC doesn't support push/pop for optimization flags as it // does for warning suppression, and we really don't want to force // optimization ON for other code even in Debug or RelWithDebInfo builds. @@ -147,15 +194,33 @@ void LLCoros::setStackSize(S32 stacksize) #if LL_MSVC // work around broken optimizations #pragma warning(disable: 4748) +#pragma warning(disable: 4355) // 'this' used in initializer list: yes, intentionally #pragma optimize("", off) #endif // LL_MSVC -std::string LLCoros::launchImpl(const std::string& prefix, coro* newCoro) +LLCoros::CoroData::CoroData(CoroData* prev, const std::string& name, + const callable_t& callable, S32 stacksize): + mPrev(prev), + mName(name), + // Wrap the caller's callable in our toplevel() function so we can manage + // sCurrentCoro appropriately at startup and shutdown of each coroutine. + mCoro(boost::bind(toplevel, _1, this, callable), stacksize), + mSelf(0) +{ +} + +std::string LLCoros::launch(const std::string& prefix, const callable_t& callable) { std::string name(generateDistinctName(prefix)); + // pass the current value of sCurrentCoro as previous context + CoroData* newCoro = new CoroData(sCurrentCoro.get(), name, + callable, mStackSize); + // Store it in our pointer map mCoros.insert(name, newCoro); + // also set it as current + sCurrentCoro.reset(newCoro); /* Run the coroutine until its first wait, then return here */ - (*newCoro)(std::nothrow); + (newCoro->mCoro)(std::nothrow); return name; } diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h index 01ee11da1a..56eed8cafe 100755 --- a/indra/llcommon/llcoros.h +++ b/indra/llcommon/llcoros.h @@ -32,12 +32,17 @@ #include <boost/dcoroutine/coroutine.hpp> #include "llsingleton.h" #include <boost/ptr_container/ptr_map.hpp> +#include <boost/function.hpp> +#include <boost/thread/tss.hpp> #include <string> -#include <boost/preprocessor/repetition/enum_params.hpp> -#include <boost/preprocessor/repetition/enum_binary_params.hpp> -#include <boost/preprocessor/iteration/local.hpp> #include <stdexcept> +// forward-declare helper class +namespace llcoro +{ +class Suspending; +} + /** * Registry of named Boost.Coroutine instances * @@ -80,8 +85,8 @@ class LL_COMMON_API LLCoros: public LLSingleton<LLCoros> public: /// Canonical boost::dcoroutines::coroutine signature we use typedef boost::dcoroutines::coroutine<void()> coro; - /// Canonical 'self' type - typedef coro::self self; + /// Canonical callable type + typedef boost::function<void()> callable_t; /** * Create and start running a new coroutine with specified name. The name @@ -94,39 +99,33 @@ public: * { * public: * ... - * // Do NOT NOT NOT accept reference params other than 'self'! + * // Do NOT NOT NOT accept reference params! * // Pass by value only! - * void myCoroutineMethod(LLCoros::self& self, std::string, LLSD); + * void myCoroutineMethod(std::string, LLSD); * ... * }; * ... * std::string name = LLCoros::instance().launch( - * "mycoro", boost::bind(&MyClass::myCoroutineMethod, this, _1, + * "mycoro", boost::bind(&MyClass::myCoroutineMethod, this, * "somestring", LLSD(17)); * @endcode * - * Your function/method must accept LLCoros::self& as its first parameter. - * It can accept any other parameters you want -- but ONLY BY VALUE! - * Other reference parameters are a BAD IDEA! You Have Been Warned. See + * Your function/method can accept any parameters you want -- but ONLY BY + * VALUE! Reference parameters are a BAD IDEA! You Have Been Warned. See * DEV-32777 comments for an explanation. * - * Pass a callable that accepts the single LLCoros::self& parameter. It - * may work to pass a free function whose only parameter is 'self'; for - * all other cases use boost::bind(). Of course, for a non-static class - * method, the first parameter must be the class instance. Use the - * placeholder _1 for the 'self' parameter. Any other parameters should be - * passed via the bind() expression. + * Pass a nullary callable. It works to directly pass a nullary free + * function (or static method); for all other cases use boost::bind(). Of + * course, for a non-static class method, the first parameter must be the + * class instance. Any other parameters should be passed via the bind() + * expression. * * launch() tweaks the suggested name so it won't collide with any * existing coroutine instance, creates the coroutine instance, registers * it with the tweaked name and runs it until its first wait. At that * point it returns the tweaked name. */ - template <typename CALLABLE> - std::string launch(const std::string& prefix, const CALLABLE& callable) - { - return launchImpl(prefix, new coro(callable, mStackSize)); - } + std::string launch(const std::string& prefix, const callable_t& callable); /** * Abort a running coroutine by name. Normally, when a coroutine either @@ -138,33 +137,85 @@ public: bool kill(const std::string& name); /** - * From within a coroutine, pass its @c self object to look up the - * (tweaked) name string by which this coroutine is registered. Returns - * the empty string if not found (e.g. if the coroutine was launched by - * hand rather than using LLCoros::launch()). + * From within a coroutine, look up the (tweaked) name string by which + * this coroutine is registered. Returns the empty string if not found + * (e.g. if the coroutine was launched by hand rather than using + * LLCoros::launch()). */ - template <typename COROUTINE_SELF> - std::string getName(const COROUTINE_SELF& self) const - { - return getNameByID(self.get_id()); - } - - /// getName() by self.get_id() - std::string getNameByID(const void* self_id) const; + std::string getName() const; /// for delayed initialization void setStackSize(S32 stacksize); + /// get the current coro::self& for those who really really care + static coro::self& get_self(); + private: - friend class LLSingleton<LLCoros>; LLCoros(); - std::string launchImpl(const std::string& prefix, coro* newCoro); + friend class LLSingleton<LLCoros>; + friend class llcoro::Suspending; std::string generateDistinctName(const std::string& prefix) const; bool cleanup(const LLSD&); + struct CoroData; + static void no_cleanup(CoroData*); + static void toplevel(coro::self& self, CoroData* data, const callable_t& callable); S32 mStackSize; - typedef boost::ptr_map<std::string, coro> CoroMap; + + // coroutine-local storage, as it were: one per coro we track + struct CoroData + { + CoroData(CoroData* prev, const std::string& name, + const callable_t& callable, S32 stacksize); + + // The boost::dcoroutines library supports asymmetric coroutines. Every + // time we context switch out of a coroutine, we pass control to the + // previously-active one (or to the non-coroutine stack owned by the + // thread). So our management of the "current" coroutine must be able to + // restore the previous value when we're about to switch away. + CoroData* mPrev; + // tweaked name of the current coroutine + const std::string mName; + // the actual coroutine instance + LLCoros::coro mCoro; + // When the dcoroutine library calls a top-level callable, it implicitly + // passes coro::self& as the first parameter. All our consumer code used + // to explicitly pass coro::self& down through all levels of call stack, + // because at the leaf level we need it for context-switching. But since + // coroutines are based on cooperative switching, we can cause the + // top-level entry point to stash a pointer to the currently-running + // coroutine, and manage it appropriately as we switch out and back in. + // That eliminates the need to pass it as an explicit parameter down + // through every level, which is unfortunately viral in nature. Finding it + // implicitly rather than explicitly allows minor maintenance in which a + // leaf-level function adds a new async I/O call that suspends the calling + // coroutine, WITHOUT having to propagate coro::self& through every + // function signature down to that point -- and of course through every + // other caller of every such function. + LLCoros::coro::self* mSelf; + }; + typedef boost::ptr_map<std::string, CoroData> CoroMap; CoroMap mCoros; + + // identify the current coroutine's CoroData + static boost::thread_specific_ptr<LLCoros::CoroData> sCurrentCoro; +}; + +namespace llcoro +{ + +/// Instantiate one of these in a block surrounding any leaf point when +/// control literally switches away from this coroutine. +class Suspending +{ +public: + Suspending(); + ~Suspending(); + +private: + LLCoros::CoroData* mSuspended; }; +} // namespace llcoro + #endif /* ! defined(LL_LLCOROS_H) */ diff --git a/indra/llcommon/llerror.h b/indra/llcommon/llerror.h index 63040e1772..b1b5e9be7d 100755 --- a/indra/llcommon/llerror.h +++ b/indra/llcommon/llerror.h @@ -354,6 +354,7 @@ typedef LLError::NoClassInfo _LL_CLASS_TO_LOG; #define LL_WARNS(...) lllog(LLError::LEVEL_WARN, false, ##__VA_ARGS__) #define LL_ERRS(...) lllog(LLError::LEVEL_ERROR, false, ##__VA_ARGS__) // alternative to llassert_always that prints explanatory message +#define LL_WARNS_IF(exp, ...) if (exp) LL_WARNS(##__VA_ARGS__) << "(" #exp ")" #define LL_ERRS_IF(exp, ...) if (exp) LL_ERRS(##__VA_ARGS__) << "(" #exp ")" // Only print the log message once (good for warnings or infos that would otherwise diff --git a/indra/llcommon/lleventcoro.cpp b/indra/llcommon/lleventcoro.cpp index 81cc33fbba..66cc7cada0 100755 --- a/indra/llcommon/lleventcoro.cpp +++ b/indra/llcommon/lleventcoro.cpp @@ -34,38 +34,63 @@ #include <map> // std headers // external library headers +#include <boost/dcoroutine/coroutine.hpp> +#include <boost/dcoroutine/future.hpp> // other Linden headers #include "llsdserialize.h" #include "llerror.h" #include "llcoros.h" -std::string LLEventDetail::listenerNameForCoroImpl(const void* self_id) +namespace { - // First, if this coroutine was launched by LLCoros::launch(), find that name. - std::string name(LLCoros::instance().getNameByID(self_id)); + +/** + * waitForEventOn() permits a coroutine to temporarily listen on an + * LLEventPump any number of times. We don't really want to have to ask + * the caller to label each such call with a distinct string; the whole + * point of waitForEventOn() is to present a nice sequential interface to + * the underlying LLEventPump-with-named-listeners machinery. So we'll use + * LLEventPump::inventName() to generate a distinct name for each + * temporary listener. On the other hand, because a given coroutine might + * call waitForEventOn() any number of times, we don't really want to + * consume an arbitrary number of generated inventName()s: that namespace, + * though large, is nonetheless finite. So we memoize an invented name for + * each distinct coroutine instance. + */ +std::string listenerNameForCoro() +{ + // If this coroutine was launched by LLCoros::launch(), find that name. + std::string name(LLCoros::instance().getName()); if (! name.empty()) { return name; } - // Apparently this coroutine wasn't launched by LLCoros::launch(). Check - // whether we have a memo for this self_id. - typedef std::map<const void*, std::string> MapType; - static MapType memo; - MapType::const_iterator found = memo.find(self_id); - if (found != memo.end()) - { - // this coroutine instance has called us before, reuse same name - return found->second; - } // this is the first time we've been called for this coroutine instance name = LLEventPump::inventName("coro"); - memo[self_id] = name; - LL_INFOS("LLEventCoro") << "listenerNameForCoroImpl(" << self_id << "): inventing coro name '" + LL_INFOS("LLEventCoro") << "listenerNameForCoro(): inventing coro name '" << name << "'" << LL_ENDL; return name; } -void LLEventDetail::storeToLLSDPath(LLSD& dest, const LLSD& rawPath, const LLSD& value) +/** + * Implement behavior described for postAndWait()'s @a replyPumpNamePath + * parameter: + * + * * If <tt>path.isUndefined()</tt>, do nothing. + * * If <tt>path.isString()</tt>, @a dest is an LLSD map: store @a value + * into <tt>dest[path.asString()]</tt>. + * * If <tt>path.isInteger()</tt>, @a dest is an LLSD array: store @a + * value into <tt>dest[path.asInteger()]</tt>. + * * If <tt>path.isArray()</tt>, iteratively apply the rules above to step + * down through the structure of @a dest. The last array entry in @a + * path specifies the entry in the lowest-level structure in @a dest + * into which to store @a value. + * + * @note + * In the degenerate case in which @a path is an empty array, @a dest will + * @em become @a value rather than @em containing it. + */ +void storeToLLSDPath(LLSD& dest, const LLSD& rawPath, const LLSD& value) { if (rawPath.isUndefined()) { @@ -118,6 +143,155 @@ void LLEventDetail::storeToLLSDPath(LLSD& dest, const LLSD& rawPath, const LLSD& *pdest = value; } +} // anonymous + +void llcoro::yield() +{ + // By viewer convention, we post an event on the "mainloop" LLEventPump + // each iteration of the main event-handling loop. So waiting for a single + // event on "mainloop" gives us a one-frame yield. + waitForEventOn("mainloop"); +} + +LLSD llcoro::postAndWait(const LLSD& event, const LLEventPumpOrPumpName& requestPump, + const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath) +{ + // declare the future + boost::dcoroutines::future<LLSD> future(LLCoros::get_self()); + // make a callback that will assign a value to the future, and listen on + // the specified LLEventPump with that callback + std::string listenerName(listenerNameForCoro()); + LLTempBoundListener connection( + replyPump.getPump().listen(listenerName, + voidlistener(boost::dcoroutines::make_callback(future)))); + // skip the "post" part if requestPump is default-constructed + if (requestPump) + { + // If replyPumpNamePath is non-empty, store the replyPump name in the + // request event. + LLSD modevent(event); + storeToLLSDPath(modevent, replyPumpNamePath, replyPump.getPump().getName()); + LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName + << " posting to " << requestPump.getPump().getName() + << LL_ENDL; + + // *NOTE:Mani - Removed because modevent could contain user's hashed passwd. + // << ": " << modevent << LL_ENDL; + requestPump.getPump().post(modevent); + } + LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName + << " about to wait on LLEventPump " << replyPump.getPump().getName() + << LL_ENDL; + // trying to dereference ("resolve") the future makes us wait for it + LLSD value; + { + // instantiate Suspending to manage the "current" coroutine + llcoro::Suspending suspended; + value = *future; + } // destroy Suspending as soon as we're back + LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName + << " resuming with " << value << LL_ENDL; + // returning should disconnect the connection + return value; +} + +namespace +{ + +/** + * This helper is specifically for the two-pump version of waitForEventOn(). + * We use a single future object, but we want to listen on two pumps with it. + * Since we must still adapt from (the callable constructed by) + * boost::dcoroutines::make_callback() (void return) to provide an event + * listener (bool return), we've adapted VoidListener for the purpose. The + * basic idea is that we construct a distinct instance of WaitForEventOnHelper + * -- binding different instance data -- for each of the pumps. Then, when a + * pump delivers an LLSD value to either WaitForEventOnHelper, it can combine + * that LLSD with its discriminator to feed the future object. + */ +template <typename LISTENER> +class WaitForEventOnHelper +{ +public: + WaitForEventOnHelper(const LISTENER& listener, int discriminator): + mListener(listener), + mDiscrim(discriminator) + {} + // this signature is required for an LLEventPump listener + bool operator()(const LLSD& event) + { + // our future object is defined to accept LLEventWithID + mListener(LLEventWithID(event, mDiscrim)); + // don't swallow the event, let other listeners see it + return false; + } +private: + LISTENER mListener; + const int mDiscrim; +}; + +/// WaitForEventOnHelper type-inference helper +template <typename LISTENER> +WaitForEventOnHelper<LISTENER> wfeoh(const LISTENER& listener, int discriminator) +{ + return WaitForEventOnHelper<LISTENER>(listener, discriminator); +} + +} // anonymous + +namespace llcoro +{ + +LLEventWithID postAndWait2(const LLSD& event, + const LLEventPumpOrPumpName& requestPump, + const LLEventPumpOrPumpName& replyPump0, + const LLEventPumpOrPumpName& replyPump1, + const LLSD& replyPump0NamePath, + const LLSD& replyPump1NamePath) +{ + // declare the future + boost::dcoroutines::future<LLEventWithID> future(LLCoros::get_self()); + // either callback will assign a value to this future; listen on + // each specified LLEventPump with a callback + std::string name(listenerNameForCoro()); + LLTempBoundListener connection0( + replyPump0.getPump().listen(name + "a", + wfeoh(boost::dcoroutines::make_callback(future), 0))); + LLTempBoundListener connection1( + replyPump1.getPump().listen(name + "b", + wfeoh(boost::dcoroutines::make_callback(future), 1))); + // skip the "post" part if requestPump is default-constructed + if (requestPump) + { + // If either replyPumpNamePath is non-empty, store the corresponding + // replyPump name in the request event. + LLSD modevent(event); + storeToLLSDPath(modevent, replyPump0NamePath, + replyPump0.getPump().getName()); + storeToLLSDPath(modevent, replyPump1NamePath, + replyPump1.getPump().getName()); + LL_DEBUGS("lleventcoro") << "postAndWait2(): coroutine " << name + << " posting to " << requestPump.getPump().getName() + << ": " << modevent << LL_ENDL; + requestPump.getPump().post(modevent); + } + LL_DEBUGS("lleventcoro") << "postAndWait2(): coroutine " << name + << " about to wait on LLEventPumps " << replyPump0.getPump().getName() + << ", " << replyPump1.getPump().getName() << LL_ENDL; + // trying to dereference ("resolve") the future makes us wait for it + LLEventWithID value; + { + // instantiate Suspending to manage "current" coroutine + llcoro::Suspending suspended; + value = *future; + } // destroy Suspending as soon as we're back + LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << name + << " resuming with (" << value.first << ", " << value.second << ")" + << LL_ENDL; + // returning should disconnect both connections + return value; +} + LLSD errorException(const LLEventWithID& result, const std::string& desc) { // If the result arrived on the error pump (pump 1), instead of @@ -144,3 +318,5 @@ LLSD errorLog(const LLEventWithID& result, const std::string& desc) // A simple return must therefore be from the reply pump (pump 0). return result.first; } + +} // namespace llcoro diff --git a/indra/llcommon/lleventcoro.h b/indra/llcommon/lleventcoro.h index abbeeaa373..e2ce4bb193 100755 --- a/indra/llcommon/lleventcoro.h +++ b/indra/llcommon/lleventcoro.h @@ -29,8 +29,6 @@ #if ! defined(LL_LLEVENTCORO_H) #define LL_LLEVENTCORO_H -#include <boost/dcoroutine/coroutine.hpp> -#include <boost/dcoroutine/future.hpp> #include <boost/optional.hpp> #include <string> #include <stdexcept> @@ -74,13 +72,16 @@ private: boost::optional<LLEventPump&> mPump; }; +namespace llcoro +{ + /// This is an adapter for a signature like void LISTENER(const LLSD&), which /// isn't a valid LLEventPump listener: such listeners should return bool. template <typename LISTENER> -class LLVoidListener +class VoidListener { public: - LLVoidListener(const LISTENER& listener): + VoidListener(const LISTENER& listener): mListener(listener) {} bool operator()(const LLSD& event) @@ -93,65 +94,19 @@ private: LISTENER mListener; }; -/// LLVoidListener helper function to infer the type of the LISTENER +/// VoidListener helper function to infer the type of the LISTENER template <typename LISTENER> -LLVoidListener<LISTENER> voidlistener(const LISTENER& listener) +VoidListener<LISTENER> voidlistener(const LISTENER& listener) { - return LLVoidListener<LISTENER>(listener); + return VoidListener<LISTENER>(listener); } -namespace LLEventDetail -{ - /// Implementation for listenerNameForCoro(), see below - LL_COMMON_API std::string listenerNameForCoroImpl(const void* self_id); - - /** - * waitForEventOn() permits a coroutine to temporarily listen on an - * LLEventPump any number of times. We don't really want to have to ask - * the caller to label each such call with a distinct string; the whole - * point of waitForEventOn() is to present a nice sequential interface to - * the underlying LLEventPump-with-named-listeners machinery. So we'll use - * LLEventPump::inventName() to generate a distinct name for each - * temporary listener. On the other hand, because a given coroutine might - * call waitForEventOn() any number of times, we don't really want to - * consume an arbitrary number of generated inventName()s: that namespace, - * though large, is nonetheless finite. So we memoize an invented name for - * each distinct coroutine instance (each different 'self' object). We - * can't know the type of 'self', because it depends on the coroutine - * body's signature. So we cast its address to void*, looking for distinct - * pointer values. Yes, that means that an early coroutine could cache a - * value here, then be destroyed, only to be supplanted by a later - * coroutine (of the same or different type), and we'll end up - * "recognizing" the second one and reusing the listener name -- but - * that's okay, since it won't collide with any listener name used by the - * earlier coroutine since that earlier coroutine no longer exists. - */ - template <typename COROUTINE_SELF> - std::string listenerNameForCoro(COROUTINE_SELF& self) - { - return listenerNameForCoroImpl(self.get_id()); - } - - /** - * Implement behavior described for postAndWait()'s @a replyPumpNamePath - * parameter: - * - * * If <tt>path.isUndefined()</tt>, do nothing. - * * If <tt>path.isString()</tt>, @a dest is an LLSD map: store @a value - * into <tt>dest[path.asString()]</tt>. - * * If <tt>path.isInteger()</tt>, @a dest is an LLSD array: store @a - * value into <tt>dest[path.asInteger()]</tt>. - * * If <tt>path.isArray()</tt>, iteratively apply the rules above to step - * down through the structure of @a dest. The last array entry in @a - * path specifies the entry in the lowest-level structure in @a dest - * into which to store @a value. - * - * @note - * In the degenerate case in which @a path is an empty array, @a dest will - * @em become @a value rather than @em containing it. - */ - LL_COMMON_API void storeToLLSDPath(LLSD& dest, const LLSD& path, const LLSD& value); -} // namespace LLEventDetail +/** + * Yield control from a coroutine for one "mainloop" tick. If your coroutine + * runs without suspending for nontrivial time, sprinkle in calls to this + * function to avoid stalling the rest of the viewer processing. + */ +void yield(); /** * Post specified LLSD event on the specified LLEventPump, then wait for a @@ -159,7 +114,7 @@ namespace LLEventDetail * convenience: the difference between this function and the sequence * @code * requestPump.post(myEvent); - * LLSD reply = waitForEventOn(self, replyPump); + * LLSD reply = waitForEventOn(replyPump); * @endcode * is that the sequence above fails if the reply is posted immediately on * @a replyPump, that is, before <tt>requestPump.post()</tt> returns. In the @@ -201,97 +156,25 @@ namespace LLEventDetail * @a replyPumpNamePath specifies the entry in the lowest-level structure in * @a event into which to store <tt>replyPump.getName()</tt>. */ -template <typename SELF> -LLSD postAndWait(SELF& self, const LLSD& event, const LLEventPumpOrPumpName& requestPump, - const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath=LLSD()) -{ - // declare the future - boost::dcoroutines::future<LLSD> future(self); - // make a callback that will assign a value to the future, and listen on - // the specified LLEventPump with that callback - std::string listenerName(LLEventDetail::listenerNameForCoro(self)); - LLTempBoundListener connection( - replyPump.getPump().listen(listenerName, - voidlistener(boost::dcoroutines::make_callback(future)))); - // skip the "post" part if requestPump is default-constructed - if (requestPump) - { - // If replyPumpNamePath is non-empty, store the replyPump name in the - // request event. - LLSD modevent(event); - LLEventDetail::storeToLLSDPath(modevent, replyPumpNamePath, replyPump.getPump().getName()); - LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName - << " posting to " << requestPump.getPump().getName() - << LL_ENDL; - - // *NOTE:Mani - Removed because modevent could contain user's hashed passwd. - // << ": " << modevent << LL_ENDL; - requestPump.getPump().post(modevent); - } - LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName - << " about to wait on LLEventPump " << replyPump.getPump().getName() - << LL_ENDL; - // trying to dereference ("resolve") the future makes us wait for it - LLSD value(*future); - LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName - << " resuming with " << value << LL_ENDL; - // returning should disconnect the connection - return value; -} +LLSD postAndWait(const LLSD& event, const LLEventPumpOrPumpName& requestPump, + const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath=LLSD()); /// Wait for the next event on the specified LLEventPump. Pass either the /// LLEventPump& or its string name. -template <typename SELF> -LLSD waitForEventOn(SELF& self, const LLEventPumpOrPumpName& pump) +inline +LLSD waitForEventOn(const LLEventPumpOrPumpName& pump) { // This is now a convenience wrapper for postAndWait(). - return postAndWait(self, LLSD(), LLEventPumpOrPumpName(), pump); + return postAndWait(LLSD(), LLEventPumpOrPumpName(), pump); } +} // namespace llcoro + /// return type for two-pump variant of waitForEventOn() typedef std::pair<LLSD, int> LLEventWithID; -namespace LLEventDetail +namespace llcoro { - /** - * This helper is specifically for the two-pump version of waitForEventOn(). - * We use a single future object, but we want to listen on two pumps with it. - * Since we must still adapt from (the callable constructed by) - * boost::dcoroutines::make_callback() (void return) to provide an event - * listener (bool return), we've adapted LLVoidListener for the purpose. The - * basic idea is that we construct a distinct instance of WaitForEventOnHelper - * -- binding different instance data -- for each of the pumps. Then, when a - * pump delivers an LLSD value to either WaitForEventOnHelper, it can combine - * that LLSD with its discriminator to feed the future object. - */ - template <typename LISTENER> - class WaitForEventOnHelper - { - public: - WaitForEventOnHelper(const LISTENER& listener, int discriminator): - mListener(listener), - mDiscrim(discriminator) - {} - // this signature is required for an LLEventPump listener - bool operator()(const LLSD& event) - { - // our future object is defined to accept LLEventWithID - mListener(LLEventWithID(event, mDiscrim)); - // don't swallow the event, let other listeners see it - return false; - } - private: - LISTENER mListener; - const int mDiscrim; - }; - - /// WaitForEventOnHelper type-inference helper - template <typename LISTENER> - WaitForEventOnHelper<LISTENER> wfeoh(const LISTENER& listener, int discriminator) - { - return WaitForEventOnHelper<LISTENER>(listener, discriminator); - } -} // namespace LLEventDetail /** * This function waits for a reply on either of two specified LLEventPumps. @@ -313,7 +196,7 @@ namespace LLEventDetail * I'd have preferred to overload the name postAndWait() for both signatures. * But consider the following ambiguous call: * @code - * postAndWait(self, LLSD(), requestPump, replyPump, "someString"); + * postAndWait(LLSD(), requestPump, replyPump, "someString"); * @endcode * "someString" could be converted to either LLSD (@a replyPumpNamePath for * the single-pump function) or LLEventOrPumpName (@a replyPump1 for two-pump @@ -322,69 +205,29 @@ namespace LLEventDetail * It seems less burdensome to write postAndWait2() than to write either * LLSD("someString") or LLEventOrPumpName("someString"). */ -template <typename SELF> -LLEventWithID postAndWait2(SELF& self, const LLSD& event, +LLEventWithID postAndWait2(const LLSD& event, const LLEventPumpOrPumpName& requestPump, const LLEventPumpOrPumpName& replyPump0, const LLEventPumpOrPumpName& replyPump1, const LLSD& replyPump0NamePath=LLSD(), - const LLSD& replyPump1NamePath=LLSD()) -{ - // declare the future - boost::dcoroutines::future<LLEventWithID> future(self); - // either callback will assign a value to this future; listen on - // each specified LLEventPump with a callback - std::string name(LLEventDetail::listenerNameForCoro(self)); - LLTempBoundListener connection0( - replyPump0.getPump().listen(name + "a", - LLEventDetail::wfeoh(boost::dcoroutines::make_callback(future), 0))); - LLTempBoundListener connection1( - replyPump1.getPump().listen(name + "b", - LLEventDetail::wfeoh(boost::dcoroutines::make_callback(future), 1))); - // skip the "post" part if requestPump is default-constructed - if (requestPump) - { - // If either replyPumpNamePath is non-empty, store the corresponding - // replyPump name in the request event. - LLSD modevent(event); - LLEventDetail::storeToLLSDPath(modevent, replyPump0NamePath, - replyPump0.getPump().getName()); - LLEventDetail::storeToLLSDPath(modevent, replyPump1NamePath, - replyPump1.getPump().getName()); - LL_DEBUGS("lleventcoro") << "postAndWait2(): coroutine " << name - << " posting to " << requestPump.getPump().getName() - << ": " << modevent << LL_ENDL; - requestPump.getPump().post(modevent); - } - LL_DEBUGS("lleventcoro") << "postAndWait2(): coroutine " << name - << " about to wait on LLEventPumps " << replyPump0.getPump().getName() - << ", " << replyPump1.getPump().getName() << LL_ENDL; - // trying to dereference ("resolve") the future makes us wait for it - LLEventWithID value(*future); - LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << name - << " resuming with (" << value.first << ", " << value.second << ")" - << LL_ENDL; - // returning should disconnect both connections - return value; -} + const LLSD& replyPump1NamePath=LLSD()); /** * Wait for the next event on either of two specified LLEventPumps. */ -template <typename SELF> +inline LLEventWithID -waitForEventOn(SELF& self, - const LLEventPumpOrPumpName& pump0, const LLEventPumpOrPumpName& pump1) +waitForEventOn(const LLEventPumpOrPumpName& pump0, const LLEventPumpOrPumpName& pump1) { // This is now a convenience wrapper for postAndWait2(). - return postAndWait2(self, LLSD(), LLEventPumpOrPumpName(), pump0, pump1); + return postAndWait2(LLSD(), LLEventPumpOrPumpName(), pump0, pump1); } /** * Helper for the two-pump variant of waitForEventOn(), e.g.: * * @code - * LLSD reply = errorException(waitForEventOn(self, replyPump, errorPump), + * LLSD reply = errorException(waitForEventOn(replyPump, errorPump), * "error response from login.cgi"); * @endcode * @@ -400,6 +243,8 @@ waitForEventOn(SELF& self, */ LLSD errorException(const LLEventWithID& result, const std::string& desc); +} // namespace llcoro + /** * Exception thrown by errorException(). We don't call this LLEventError * because it's not an error in event processing: rather, this exception @@ -420,12 +265,17 @@ private: LLSD mData; }; +namespace llcoro +{ + /** * Like errorException(), save that this trips a fatal error using LL_ERRS * rather than throwing an exception. */ LL_COMMON_API LLSD errorLog(const LLEventWithID& result, const std::string& desc); +} // namespace llcoro + /** * Certain event APIs require the name of an LLEventPump on which they should * post results. While it works to invent a distinct name and let @@ -454,26 +304,16 @@ public: /** * Wait for an event on this LLEventPump. - * - * @note - * The other major usage pattern we considered was to bind @c self at - * LLCoroEventPump construction time, which would avoid passing the - * parameter to each wait() call. But if we were going to bind @c self as - * a class member, we'd need to specify a class template parameter - * indicating its type. The big advantage of passing it to the wait() call - * is that the type can be implicit. */ - template <typename SELF> - LLSD wait(SELF& self) + LLSD wait() { - return waitForEventOn(self, mPump); + return llcoro::waitForEventOn(mPump); } - template <typename SELF> - LLSD postAndWait(SELF& self, const LLSD& event, const LLEventPumpOrPumpName& requestPump, + LLSD postAndWait(const LLSD& event, const LLEventPumpOrPumpName& requestPump, const LLSD& replyPumpNamePath=LLSD()) { - return ::postAndWait(self, event, requestPump, mPump, replyPumpNamePath); + return llcoro::postAndWait(event, requestPump, mPump, replyPumpNamePath); } private: @@ -509,57 +349,51 @@ public: /// request pump 1 LLEventPump& getPump1() { return mPump1; } - /// waitForEventOn(self, either of our two LLEventPumps) - template <typename SELF> - LLEventWithID wait(SELF& self) + /// waitForEventOn(either of our two LLEventPumps) + LLEventWithID wait() { - return waitForEventOn(self, mPump0, mPump1); + return llcoro::waitForEventOn(mPump0, mPump1); } - /// errorException(wait(self)) - template <typename SELF> - LLSD waitWithException(SELF& self) + /// errorException(wait()) + LLSD waitWithException() { - return errorException(wait(self), std::string("Error event on ") + getName1()); + return llcoro::errorException(wait(), std::string("Error event on ") + getName1()); } - /// errorLog(wait(self)) - template <typename SELF> - LLSD waitWithLog(SELF& self) + /// errorLog(wait()) + LLSD waitWithLog() { - return errorLog(wait(self), std::string("Error event on ") + getName1()); + return llcoro::errorLog(wait(), std::string("Error event on ") + getName1()); } - template <typename SELF> - LLEventWithID postAndWait(SELF& self, const LLSD& event, + LLEventWithID postAndWait(const LLSD& event, const LLEventPumpOrPumpName& requestPump, const LLSD& replyPump0NamePath=LLSD(), const LLSD& replyPump1NamePath=LLSD()) { - return postAndWait2(self, event, requestPump, mPump0, mPump1, - replyPump0NamePath, replyPump1NamePath); + return llcoro::postAndWait2(event, requestPump, mPump0, mPump1, + replyPump0NamePath, replyPump1NamePath); } - template <typename SELF> - LLSD postAndWaitWithException(SELF& self, const LLSD& event, + LLSD postAndWaitWithException(const LLSD& event, const LLEventPumpOrPumpName& requestPump, const LLSD& replyPump0NamePath=LLSD(), const LLSD& replyPump1NamePath=LLSD()) { - return errorException(postAndWait(self, event, requestPump, - replyPump0NamePath, replyPump1NamePath), - std::string("Error event on ") + getName1()); + return llcoro::errorException(postAndWait(event, requestPump, + replyPump0NamePath, replyPump1NamePath), + std::string("Error event on ") + getName1()); } - template <typename SELF> - LLSD postAndWaitWithLog(SELF& self, const LLSD& event, + LLSD postAndWaitWithLog(const LLSD& event, const LLEventPumpOrPumpName& requestPump, const LLSD& replyPump0NamePath=LLSD(), const LLSD& replyPump1NamePath=LLSD()) { - return errorLog(postAndWait(self, event, requestPump, - replyPump0NamePath, replyPump1NamePath), - std::string("Error event on ") + getName1()); + return llcoro::errorLog(postAndWait(event, requestPump, + replyPump0NamePath, replyPump1NamePath), + std::string("Error event on ") + getName1()); } private: diff --git a/indra/llcommon/llrefcount.h b/indra/llcommon/llrefcount.h index 3836a9b5fb..1107973569 100755 --- a/indra/llcommon/llrefcount.h +++ b/indra/llcommon/llrefcount.h @@ -144,14 +144,9 @@ private: }; /** - * intrusive pointer support - * this allows you to use boost::intrusive_ptr with any LLRefCount-derived type - */ -/** * intrusive pointer support for LLThreadSafeRefCount * this allows you to use boost::intrusive_ptr with any LLThreadSafeRefCount-derived type */ - inline void intrusive_ptr_add_ref(LLThreadSafeRefCount* p) { p->ref(); @@ -162,6 +157,10 @@ inline void intrusive_ptr_release(LLThreadSafeRefCount* p) p->unref(); } +/** + * intrusive pointer support + * this allows you to use boost::intrusive_ptr with any LLRefCount-derived type + */ inline void intrusive_ptr_add_ref(LLRefCount* p) { p->ref(); diff --git a/indra/llcommon/llsdjson.cpp b/indra/llcommon/llsdjson.cpp new file mode 100644 index 0000000000..2afdba388a --- /dev/null +++ b/indra/llcommon/llsdjson.cpp @@ -0,0 +1,78 @@ +/** + * @file llsdjson.cpp + * @brief LLSD flexible data system + * + * $LicenseInfo:firstyear=2015&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2015, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +// Must turn on conditional declarations in header file so definitions end up +// with proper linkage. +#define LLSD_DEBUG_INFO +#include "linden_common.h" + +#include "llsdjson.h" + +#include "llerror.h" +#include "../llmath/llmath.h" + +//========================================================================= +LLSD LlsdFromJson(const Json::Value &val) +{ + LLSD result; + + switch (val.type()) + { + default: + case Json::nullValue: + break; + case Json::intValue: + result = LLSD(static_cast<LLSD::Integer>(val.asInt())); + break; + case Json::uintValue: + result = LLSD(static_cast<LLSD::Integer>(val.asUInt())); + break; + case Json::realValue: + result = LLSD(static_cast<LLSD::Real>(val.asDouble())); + break; + case Json::stringValue: + result = LLSD(static_cast<LLSD::String>(val.asString())); + break; + case Json::booleanValue: + result = LLSD(static_cast<LLSD::Boolean>(val.asBool())); + break; + case Json::arrayValue: + result = LLSD::emptyArray(); + for (Json::ValueConstIterator it = val.begin(); it != val.end(); ++it) + { + result.append(LlsdFromJson((*it))); + } + break; + case Json::objectValue: + result = LLSD::emptyMap(); + for (Json::ValueConstIterator it = val.begin(); it != val.end(); ++it) + { + result[it.memberName()] = LlsdFromJson((*it)); + } + break; + } + return result; +} diff --git a/indra/llcommon/llsdjson.h b/indra/llcommon/llsdjson.h new file mode 100644 index 0000000000..cdf9fed500 --- /dev/null +++ b/indra/llcommon/llsdjson.h @@ -0,0 +1,59 @@ +/** + * @file llsdjson.cpp + * @brief LLSD flexible data system + * + * $LicenseInfo:firstyear=2015&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2015, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLSDJSON_H +#define LL_LLSDJSON_H + +#include <map> +#include <string> +#include <vector> + +#include "stdtypes.h" + +#include "llsd.h" +#include "value.h" + +/// Convert a parsed JSON structure into LLSD maintaining member names and +/// array indexes. +/// JSON/JavaScript types are converted as follows: +/// +/// JSON Type | LLSD Type +/// --------------+-------------- +/// null | undefined +/// integer | LLSD::Integer +/// unsigned | LLSD::Integer +/// real/numeric | LLSD::Real +/// string | LLSD::String +/// boolean | LLSD::Boolean +/// array | LLSD::Array +/// object | LLSD::Map +/// +/// For maps and arrays child entries will be converted and added to the structure. +/// Order is preserved for an array but not for objects. +LLSD LlsdFromJson(const Json::Value &val); + + +#endif // LL_LLSDJSON_H diff --git a/indra/llcommon/llstring.h b/indra/llcommon/llstring.h index 0177f48bf5..393f6d7a8c 100755 --- a/indra/llcommon/llstring.h +++ b/indra/llcommon/llstring.h @@ -1394,6 +1394,7 @@ BOOL LLStringUtilBase<T>::containsNonprintable(const string_type& string) return rv; } +// *TODO: reimplement in terms of algorithm //static template<class T> void LLStringUtilBase<T>::stripNonprintable(string_type& string) @@ -1427,6 +1428,7 @@ void LLStringUtilBase<T>::stripNonprintable(string_type& string) delete []c_string; } +// *TODO: reimplement in terms of algorithm template<class T> std::basic_string<T> LLStringUtilBase<T>::quote(const string_type& str, const string_type& triggers, diff --git a/indra/llcommon/tests/lleventcoro_test.cpp b/indra/llcommon/tests/lleventcoro_test.cpp index 2096807e53..00be5035f2 100755 --- a/indra/llcommon/tests/lleventcoro_test.cpp +++ b/indra/llcommon/tests/lleventcoro_test.cpp @@ -59,17 +59,20 @@ // http://www.boost.org/LICENSE_1_0.txt) /*****************************************************************************/ +#define BOOST_RESULT_OF_USE_TR1 1 // On some platforms, Boost.Coroutine must #define magic symbols before // #including platform-API headers. Naturally, that's ineffective unless the // Boost.Coroutine #include is the *first* #include of the platform header. // That means that client code must generally #include Boost.Coroutine headers // before anything else. +#define BOOST_RESULT_OF_USE_TR1 1 #include <boost/dcoroutine/coroutine.hpp> // Normally, lleventcoro.h obviates future.hpp. We only include this because // we implement a "by hand" test of future functionality. #include <boost/dcoroutine/future.hpp> #include <boost/bind.hpp> #include <boost/range.hpp> +#include <boost/utility.hpp> #include "linden_common.h" @@ -82,9 +85,12 @@ #include "llevents.h" #include "tests/wrapllerrs.h" #include "stringize.h" +#include "llcoros.h" #include "lleventcoro.h" #include "../test/debug.h" +using namespace llcoro; + /***************************************************************************** * from the banana.cpp example program borrowed for test<1>() *****************************************************************************/ @@ -121,9 +127,6 @@ typedef coroutine<std::string::iterator(void)> match_coroutine_type; /***************************************************************************** * Test helpers *****************************************************************************/ -// I suspect this will be typical of coroutines used in Linden software -typedef boost::dcoroutines::coroutine<void()> coroutine_type; - /// Simulate an event API whose response is immediate: sent on receipt of the /// initial request, rather than after some delay. This is the case that /// distinguishes postAndWait() from calling post(), then calling @@ -162,306 +165,7 @@ private: *****************************************************************************/ namespace tut { - struct coroutine_data - { - // Define coroutine bodies as methods here so they can use ensure*() - - void explicit_wait(coroutine_type::self& self) - { - BEGIN - { - // ... do whatever preliminary stuff must happen ... - - // declare the future - boost::dcoroutines::future<LLSD> future(self); - // tell the future what to wait for - LLTempBoundListener connection( - LLEventPumps::instance().obtain("source").listen("coro", voidlistener(boost::dcoroutines::make_callback(future)))); - ensure("Not yet", ! future); - // attempting to dereference ("resolve") the future causes the calling - // coroutine to wait for it - debug("about to wait"); - result = *future; - ensure("Got it", future); - } - END - } - - void waitForEventOn1(coroutine_type::self& self) - { - BEGIN - { - result = waitForEventOn(self, "source"); - } - END - } - - void waitForEventOn2(coroutine_type::self& self) - { - BEGIN - { - LLEventWithID pair = waitForEventOn(self, "reply", "error"); - result = pair.first; - which = pair.second; - debug(STRINGIZE("result = " << result << ", which = " << which)); - } - END - } - - void postAndWait1(coroutine_type::self& self) - { - BEGIN - { - result = postAndWait(self, - LLSDMap("value", 17), // request event - immediateAPI.getPump(), // requestPump - "reply1", // replyPump - "reply"); // request["reply"] = name - } - END - } - - void postAndWait2(coroutine_type::self& self) - { - BEGIN - { - LLEventWithID pair = ::postAndWait2(self, - LLSDMap("value", 18), - immediateAPI.getPump(), - "reply2", - "error2", - "reply", - "error"); - result = pair.first; - which = pair.second; - debug(STRINGIZE("result = " << result << ", which = " << which)); - } - END - } - - void postAndWait2_1(coroutine_type::self& self) - { - BEGIN - { - LLEventWithID pair = ::postAndWait2(self, - LLSDMap("value", 18)("fail", LLSD()), - immediateAPI.getPump(), - "reply2", - "error2", - "reply", - "error"); - result = pair.first; - which = pair.second; - debug(STRINGIZE("result = " << result << ", which = " << which)); - } - END - } - - void coroPump(coroutine_type::self& self) - { - BEGIN - { - LLCoroEventPump waiter; - replyName = waiter.getName(); - result = waiter.wait(self); - } - END - } - - void coroPumpPost(coroutine_type::self& self) - { - BEGIN - { - LLCoroEventPump waiter; - result = waiter.postAndWait(self, LLSDMap("value", 17), - immediateAPI.getPump(), "reply"); - } - END - } - - void coroPumps(coroutine_type::self& self) - { - BEGIN - { - LLCoroEventPumps waiter; - replyName = waiter.getName0(); - errorName = waiter.getName1(); - LLEventWithID pair(waiter.wait(self)); - result = pair.first; - which = pair.second; - } - END - } - - void coroPumpsNoEx(coroutine_type::self& self) - { - BEGIN - { - LLCoroEventPumps waiter; - replyName = waiter.getName0(); - errorName = waiter.getName1(); - result = waiter.waitWithException(self); - } - END - } - - void coroPumpsEx(coroutine_type::self& self) - { - BEGIN - { - LLCoroEventPumps waiter; - replyName = waiter.getName0(); - errorName = waiter.getName1(); - try - { - result = waiter.waitWithException(self); - debug("no exception"); - } - catch (const LLErrorEvent& e) - { - debug(STRINGIZE("exception " << e.what())); - errordata = e.getData(); - } - } - END - } - - void coroPumpsNoLog(coroutine_type::self& self) - { - BEGIN - { - LLCoroEventPumps waiter; - replyName = waiter.getName0(); - errorName = waiter.getName1(); - result = waiter.waitWithLog(self); - } - END - } - - void coroPumpsLog(coroutine_type::self& self) - { - BEGIN - { - LLCoroEventPumps waiter; - replyName = waiter.getName0(); - errorName = waiter.getName1(); - WrapLLErrs capture; - try - { - result = waiter.waitWithLog(self); - debug("no exception"); - } - catch (const WrapLLErrs::FatalException& e) - { - debug(STRINGIZE("exception " << e.what())); - threw = e.what(); - } - } - END - } - - void coroPumpsPost(coroutine_type::self& self) - { - BEGIN - { - LLCoroEventPumps waiter; - LLEventWithID pair(waiter.postAndWait(self, LLSDMap("value", 23), - immediateAPI.getPump(), "reply", "error")); - result = pair.first; - which = pair.second; - } - END - } - - void coroPumpsPost_1(coroutine_type::self& self) - { - BEGIN - { - LLCoroEventPumps waiter; - LLEventWithID pair( - waiter.postAndWait(self, LLSDMap("value", 23)("fail", LLSD()), - immediateAPI.getPump(), "reply", "error")); - result = pair.first; - which = pair.second; - } - END - } - - void coroPumpsPostNoEx(coroutine_type::self& self) - { - BEGIN - { - LLCoroEventPumps waiter; - result = waiter.postAndWaitWithException(self, LLSDMap("value", 8), - immediateAPI.getPump(), "reply", "error"); - } - END - } - - void coroPumpsPostEx(coroutine_type::self& self) - { - BEGIN - { - LLCoroEventPumps waiter; - try - { - result = waiter.postAndWaitWithException(self, - LLSDMap("value", 9)("fail", LLSD()), - immediateAPI.getPump(), "reply", "error"); - debug("no exception"); - } - catch (const LLErrorEvent& e) - { - debug(STRINGIZE("exception " << e.what())); - errordata = e.getData(); - } - } - END - } - - void coroPumpsPostNoLog(coroutine_type::self& self) - { - BEGIN - { - LLCoroEventPumps waiter; - result = waiter.postAndWaitWithLog(self, LLSDMap("value", 30), - immediateAPI.getPump(), "reply", "error"); - } - END - } - - void coroPumpsPostLog(coroutine_type::self& self) - { - BEGIN - { - LLCoroEventPumps waiter; - WrapLLErrs capture; - try - { - result = waiter.postAndWaitWithLog(self, - LLSDMap("value", 31)("fail", LLSD()), - immediateAPI.getPump(), "reply", "error"); - debug("no exception"); - } - catch (const WrapLLErrs::FatalException& e) - { - debug(STRINGIZE("exception " << e.what())); - threw = e.what(); - } - } - END - } - - void ensure_done(coroutine_type& coro) - { - ensure("coroutine complete", ! coro); - } - - ImmediateAPI immediateAPI; - std::string replyName, errorName, threw; - LLSD result, errordata; - int which; - }; + struct coroutine_data {}; typedef test_group<coroutine_data> coroutine_group; typedef coroutine_group::object object; coroutine_group coroutinegrp("coroutine"); @@ -511,16 +215,56 @@ namespace tut ensure("done", ! matcher); } + // use static data so we can intersperse coroutine functions with the + // tests that engage them + ImmediateAPI immediateAPI; + std::string replyName, errorName, threw; + LLSD result, errordata; + int which; + + // reinit vars at the start of each test + void clear() + { + replyName.clear(); + errorName.clear(); + threw.clear(); + result = LLSD(); + errordata = LLSD(); + which = 0; + } + + void explicit_wait(boost::dcoroutines::coroutine<void()>::self& self) + { + BEGIN + { + // ... do whatever preliminary stuff must happen ... + + // declare the future + boost::dcoroutines::future<LLSD> future(self); + // tell the future what to wait for + LLTempBoundListener connection( + LLEventPumps::instance().obtain("source").listen("coro", voidlistener(boost::dcoroutines::make_callback(future)))); + ensure("Not yet", ! future); + // attempting to dereference ("resolve") the future causes the calling + // coroutine to wait for it + debug("about to wait"); + result = *future; + ensure("Got it", future); + } + END + } + template<> template<> void object::test<2>() { + clear(); set_test_name("explicit_wait"); DEBUG; // Construct the coroutine instance that will run explicit_wait. // Pass the ctor a callable that accepts the coroutine_type::self // param passed by the library. - coroutine_type coro(boost::bind(&coroutine_data::explicit_wait, this, _1)); + boost::dcoroutines::coroutine<void()> coro(explicit_wait); // Start the coroutine coro(std::nothrow); // When the coroutine waits for the event pump, it returns here. @@ -528,37 +272,56 @@ namespace tut // Satisfy the wait. LLEventPumps::instance().obtain("source").post("received"); // Now wait for the coroutine to complete. - ensure_done(coro); + ensure("coroutine complete", ! coro); // ensure the coroutine ran and woke up again with the intended result ensure_equals(result.asString(), "received"); } + void waitForEventOn1() + { + BEGIN + { + result = waitForEventOn("source"); + } + END + } + template<> template<> void object::test<3>() { + clear(); set_test_name("waitForEventOn1"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::waitForEventOn1, this, _1)); - coro(std::nothrow); + LLCoros::instance().launch("test<3>", waitForEventOn1); debug("about to send"); LLEventPumps::instance().obtain("source").post("received"); debug("back from send"); - ensure_done(coro); ensure_equals(result.asString(), "received"); } + void waitForEventOn2() + { + BEGIN + { + LLEventWithID pair = waitForEventOn("reply", "error"); + result = pair.first; + which = pair.second; + debug(STRINGIZE("result = " << result << ", which = " << which)); + } + END + } + template<> template<> void object::test<4>() { + clear(); set_test_name("waitForEventOn2 reply"); { DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::waitForEventOn2, this, _1)); - coro(std::nothrow); + LLCoros::instance().launch("test<4>", waitForEventOn2); debug("about to send"); LLEventPumps::instance().obtain("reply").post("received"); debug("back from send"); - ensure_done(coro); } ensure_equals(result.asString(), "received"); ensure_equals("which pump", which, 0); @@ -567,43 +330,65 @@ namespace tut template<> template<> void object::test<5>() { + clear(); set_test_name("waitForEventOn2 error"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::waitForEventOn2, this, _1)); - coro(std::nothrow); + LLCoros::instance().launch("test<5>", waitForEventOn2); debug("about to send"); LLEventPumps::instance().obtain("error").post("badness"); debug("back from send"); - ensure_done(coro); ensure_equals(result.asString(), "badness"); ensure_equals("which pump", which, 1); } + void coroPump() + { + BEGIN + { + LLCoroEventPump waiter; + replyName = waiter.getName(); + result = waiter.wait(); + } + END + } + template<> template<> void object::test<6>() { + clear(); set_test_name("coroPump"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::coroPump, this, _1)); - coro(std::nothrow); + LLCoros::instance().launch("test<6>", coroPump); debug("about to send"); LLEventPumps::instance().obtain(replyName).post("received"); debug("back from send"); - ensure_done(coro); ensure_equals(result.asString(), "received"); } + void coroPumps() + { + BEGIN + { + LLCoroEventPumps waiter; + replyName = waiter.getName0(); + errorName = waiter.getName1(); + LLEventWithID pair(waiter.wait()); + result = pair.first; + which = pair.second; + } + END + } + template<> template<> void object::test<7>() { + clear(); set_test_name("coroPumps reply"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::coroPumps, this, _1)); - coro(std::nothrow); + LLCoros::instance().launch("test<7>", coroPumps); debug("about to send"); LLEventPumps::instance().obtain(replyName).post("received"); debug("back from send"); - ensure_done(coro); ensure_equals(result.asString(), "received"); ensure_equals("which pump", which, 0); } @@ -611,188 +396,389 @@ namespace tut template<> template<> void object::test<8>() { + clear(); set_test_name("coroPumps error"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::coroPumps, this, _1)); - coro(std::nothrow); + LLCoros::instance().launch("test<8>", coroPumps); debug("about to send"); LLEventPumps::instance().obtain(errorName).post("badness"); debug("back from send"); - ensure_done(coro); ensure_equals(result.asString(), "badness"); ensure_equals("which pump", which, 1); } + void coroPumpsNoEx() + { + BEGIN + { + LLCoroEventPumps waiter; + replyName = waiter.getName0(); + errorName = waiter.getName1(); + result = waiter.waitWithException(); + } + END + } + template<> template<> void object::test<9>() { + clear(); set_test_name("coroPumpsNoEx"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::coroPumpsNoEx, this, _1)); - coro(std::nothrow); + LLCoros::instance().launch("test<9>", coroPumpsNoEx); debug("about to send"); LLEventPumps::instance().obtain(replyName).post("received"); debug("back from send"); - ensure_done(coro); ensure_equals(result.asString(), "received"); } + void coroPumpsEx() + { + BEGIN + { + LLCoroEventPumps waiter; + replyName = waiter.getName0(); + errorName = waiter.getName1(); + try + { + result = waiter.waitWithException(); + debug("no exception"); + } + catch (const LLErrorEvent& e) + { + debug(STRINGIZE("exception " << e.what())); + errordata = e.getData(); + } + } + END + } + template<> template<> void object::test<10>() { + clear(); set_test_name("coroPumpsEx"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::coroPumpsEx, this, _1)); - coro(std::nothrow); + LLCoros::instance().launch("test<10>", coroPumpsEx); debug("about to send"); LLEventPumps::instance().obtain(errorName).post("badness"); debug("back from send"); - ensure_done(coro); ensure("no result", result.isUndefined()); ensure_equals("got error", errordata.asString(), "badness"); } + void coroPumpsNoLog() + { + BEGIN + { + LLCoroEventPumps waiter; + replyName = waiter.getName0(); + errorName = waiter.getName1(); + result = waiter.waitWithLog(); + } + END + } + template<> template<> void object::test<11>() { + clear(); set_test_name("coroPumpsNoLog"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::coroPumpsNoLog, this, _1)); - coro(std::nothrow); + LLCoros::instance().launch("test<11>", coroPumpsNoLog); debug("about to send"); LLEventPumps::instance().obtain(replyName).post("received"); debug("back from send"); - ensure_done(coro); ensure_equals(result.asString(), "received"); } + void coroPumpsLog() + { + BEGIN + { + LLCoroEventPumps waiter; + replyName = waiter.getName0(); + errorName = waiter.getName1(); + WrapLLErrs capture; + try + { + result = waiter.waitWithLog(); + debug("no exception"); + } + catch (const WrapLLErrs::FatalException& e) + { + debug(STRINGIZE("exception " << e.what())); + threw = e.what(); + } + } + END + } + template<> template<> void object::test<12>() { + clear(); set_test_name("coroPumpsLog"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::coroPumpsLog, this, _1)); - coro(std::nothrow); + LLCoros::instance().launch("test<12>", coroPumpsLog); debug("about to send"); LLEventPumps::instance().obtain(errorName).post("badness"); debug("back from send"); - ensure_done(coro); ensure("no result", result.isUndefined()); ensure_contains("got error", threw, "badness"); } + void postAndWait1() + { + BEGIN + { + result = postAndWait(LLSDMap("value", 17), // request event + immediateAPI.getPump(), // requestPump + "reply1", // replyPump + "reply"); // request["reply"] = name + } + END + } + template<> template<> void object::test<13>() { + clear(); set_test_name("postAndWait1"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::postAndWait1, this, _1)); - coro(std::nothrow); - ensure_done(coro); + LLCoros::instance().launch("test<13>", postAndWait1); ensure_equals(result.asInteger(), 18); } + void postAndWait2() + { + BEGIN + { + LLEventWithID pair = ::postAndWait2(LLSDMap("value", 18), + immediateAPI.getPump(), + "reply2", + "error2", + "reply", + "error"); + result = pair.first; + which = pair.second; + debug(STRINGIZE("result = " << result << ", which = " << which)); + } + END + } + template<> template<> void object::test<14>() { + clear(); set_test_name("postAndWait2"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::postAndWait2, this, _1)); - coro(std::nothrow); - ensure_done(coro); + LLCoros::instance().launch("test<14>", postAndWait2); ensure_equals(result.asInteger(), 19); ensure_equals(which, 0); } + void postAndWait2_1() + { + BEGIN + { + LLEventWithID pair = ::postAndWait2(LLSDMap("value", 18)("fail", LLSD()), + immediateAPI.getPump(), + "reply2", + "error2", + "reply", + "error"); + result = pair.first; + which = pair.second; + debug(STRINGIZE("result = " << result << ", which = " << which)); + } + END + } + template<> template<> void object::test<15>() { + clear(); set_test_name("postAndWait2_1"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::postAndWait2_1, this, _1)); - coro(std::nothrow); - ensure_done(coro); + LLCoros::instance().launch("test<15>", postAndWait2_1); ensure_equals(result.asInteger(), 19); ensure_equals(which, 1); } + void coroPumpPost() + { + BEGIN + { + LLCoroEventPump waiter; + result = waiter.postAndWait(LLSDMap("value", 17), + immediateAPI.getPump(), "reply"); + } + END + } + template<> template<> void object::test<16>() { + clear(); set_test_name("coroPumpPost"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::coroPumpPost, this, _1)); - coro(std::nothrow); - ensure_done(coro); + LLCoros::instance().launch("test<16>", coroPumpPost); ensure_equals(result.asInteger(), 18); } + void coroPumpsPost() + { + BEGIN + { + LLCoroEventPumps waiter; + LLEventWithID pair(waiter.postAndWait(LLSDMap("value", 23), + immediateAPI.getPump(), "reply", "error")); + result = pair.first; + which = pair.second; + } + END + } + template<> template<> void object::test<17>() { + clear(); set_test_name("coroPumpsPost reply"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPost, this, _1)); - coro(std::nothrow); - ensure_done(coro); + LLCoros::instance().launch("test<17>", coroPumpsPost); ensure_equals(result.asInteger(), 24); ensure_equals("which pump", which, 0); } + void coroPumpsPost_1() + { + BEGIN + { + LLCoroEventPumps waiter; + LLEventWithID pair( + waiter.postAndWait(LLSDMap("value", 23)("fail", LLSD()), + immediateAPI.getPump(), "reply", "error")); + result = pair.first; + which = pair.second; + } + END + } + template<> template<> void object::test<18>() { + clear(); set_test_name("coroPumpsPost error"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPost_1, this, _1)); - coro(std::nothrow); - ensure_done(coro); + LLCoros::instance().launch("test<18>", coroPumpsPost_1); ensure_equals(result.asInteger(), 24); ensure_equals("which pump", which, 1); } + void coroPumpsPostNoEx() + { + BEGIN + { + LLCoroEventPumps waiter; + result = waiter.postAndWaitWithException(LLSDMap("value", 8), + immediateAPI.getPump(), "reply", "error"); + } + END + } + template<> template<> void object::test<19>() { + clear(); set_test_name("coroPumpsPostNoEx"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPostNoEx, this, _1)); - coro(std::nothrow); - ensure_done(coro); + LLCoros::instance().launch("test<19>", coroPumpsPostNoEx); ensure_equals(result.asInteger(), 9); } + void coroPumpsPostEx() + { + BEGIN + { + LLCoroEventPumps waiter; + try + { + result = waiter.postAndWaitWithException( + LLSDMap("value", 9)("fail", LLSD()), + immediateAPI.getPump(), "reply", "error"); + debug("no exception"); + } + catch (const LLErrorEvent& e) + { + debug(STRINGIZE("exception " << e.what())); + errordata = e.getData(); + } + } + END + } + template<> template<> void object::test<20>() { + clear(); set_test_name("coroPumpsPostEx"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPostEx, this, _1)); - coro(std::nothrow); - ensure_done(coro); + LLCoros::instance().launch("test<20>", coroPumpsPostEx); ensure("no result", result.isUndefined()); ensure_equals("got error", errordata.asInteger(), 10); } + void coroPumpsPostNoLog() + { + BEGIN + { + LLCoroEventPumps waiter; + result = waiter.postAndWaitWithLog(LLSDMap("value", 30), + immediateAPI.getPump(), "reply", "error"); + } + END + } + template<> template<> void object::test<21>() { + clear(); set_test_name("coroPumpsPostNoLog"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPostNoLog, this, _1)); - coro(std::nothrow); - ensure_done(coro); + LLCoros::instance().launch("test<21>", coroPumpsPostNoLog); ensure_equals(result.asInteger(), 31); } + void coroPumpsPostLog() + { + BEGIN + { + LLCoroEventPumps waiter; + WrapLLErrs capture; + try + { + result = waiter.postAndWaitWithLog( + LLSDMap("value", 31)("fail", LLSD()), + immediateAPI.getPump(), "reply", "error"); + debug("no exception"); + } + catch (const WrapLLErrs::FatalException& e) + { + debug(STRINGIZE("exception " << e.what())); + threw = e.what(); + } + } + END + } + template<> template<> void object::test<22>() { + clear(); set_test_name("coroPumpsPostLog"); DEBUG; - coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPostLog, this, _1)); - coro(std::nothrow); - ensure_done(coro); + LLCoros::instance().launch("test<22>", coroPumpsPostLog); ensure("no result", result.isUndefined()); ensure_contains("got error", threw, "32"); } diff --git a/indra/llcorehttp/CMakeLists.txt b/indra/llcorehttp/CMakeLists.txt index a0b1ea13b1..9631e960c5 100755 --- a/indra/llcorehttp/CMakeLists.txt +++ b/indra/llcorehttp/CMakeLists.txt @@ -134,9 +134,13 @@ if (LL_TESTS) ${BOOST_THREAD_LIBRARY} ) + # If http_proxy is in the current environment (e.g. to fetch s3-proxy + # autobuild packages), suppress it for this integration test: it screws up + # the tests. LL_ADD_INTEGRATION_TEST(llcorehttp "${llcorehttp_TEST_SOURCE_FILES}" "${test_libs}" + "-Dhttp_proxy" ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_llcorehttp_peer.py" ) diff --git a/indra/llcorehttp/_httpinternal.h b/indra/llcorehttp/_httpinternal.h index a2a60ca056..79c89d6c92 100755 --- a/indra/llcorehttp/_httpinternal.h +++ b/indra/llcorehttp/_httpinternal.h @@ -104,8 +104,9 @@ namespace LLCore { // Maxium number of policy classes that can be defined. -// *TODO: Currently limited to the default class + 1, extend. -const int HTTP_POLICY_CLASS_LIMIT = 8; +// *TODO: Currently limited to the default class + 1, extend. +// (TSN: should this be more dynamically sized. Is there a reason to hard limit the number of policies?) +const int HTTP_POLICY_CLASS_LIMIT = 32; // Debug/informational tracing. Used both // as a global option and in per-request traces. diff --git a/indra/llcorehttp/_httplibcurl.cpp b/indra/llcorehttp/_httplibcurl.cpp index 81b44ab90b..17e997688f 100755 --- a/indra/llcorehttp/_httplibcurl.cpp +++ b/indra/llcorehttp/_httplibcurl.cpp @@ -554,7 +554,7 @@ void HttpLibcurl::HandleCache::freeHandle(CURL * handle) // --------------------------------------- -struct curl_slist * append_headers_to_slist(const HttpHeaders * headers, struct curl_slist * slist) +struct curl_slist * append_headers_to_slist(const HttpHeaders::ptr_t &headers, struct curl_slist * slist) { const HttpHeaders::const_iterator end(headers->end()); for (HttpHeaders::const_iterator it(headers->begin()); end != it; ++it) diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp index b9632a7921..e588ed8a9b 100755 --- a/indra/llcorehttp/_httpoprequest.cpp +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -122,8 +122,8 @@ HttpOpRequest::HttpOpRequest() mReqBody(NULL), mReqOffset(0), mReqLength(0), - mReqHeaders(NULL), - mReqOptions(NULL), + mReqHeaders(), + mReqOptions(), mCurlActive(false), mCurlHandle(NULL), mCurlService(NULL), @@ -135,11 +135,12 @@ HttpOpRequest::HttpOpRequest() mReplyOffset(0), mReplyLength(0), mReplyFullLength(0), - mReplyHeaders(NULL), + mReplyHeaders(), mPolicyRetries(0), mPolicy503Retries(0), mPolicyRetryAt(HttpTime(0)), - mPolicyRetryLimit(HTTP_RETRY_COUNT_DEFAULT) + mPolicyRetryLimit(HTTP_RETRY_COUNT_DEFAULT), + mCallbackSSLVerify(NULL) { // *NOTE: As members are added, retry initialization/cleanup // may need to be extended in @see prepareRequest(). @@ -155,18 +156,6 @@ HttpOpRequest::~HttpOpRequest() mReqBody = NULL; } - if (mReqOptions) - { - mReqOptions->release(); - mReqOptions = NULL; - } - - if (mReqHeaders) - { - mReqHeaders->release(); - mReqHeaders = NULL; - } - if (mCurlHandle) { // Uncertain of thread context so free using @@ -193,11 +182,6 @@ HttpOpRequest::~HttpOpRequest() mReplyBody = NULL; } - if (mReplyHeaders) - { - mReplyHeaders->release(); - mReplyHeaders = NULL; - } } @@ -259,7 +243,9 @@ void HttpOpRequest::visitNotifier(HttpRequest * request) response->setStatus(mStatus); response->setBody(mReplyBody); response->setHeaders(mReplyHeaders); - if (mReplyOffset || mReplyLength) + response->setRequestURL(mReqURL); + + if (mReplyOffset || mReplyLength) { // Got an explicit offset/length in response response->setRange(mReplyOffset, mReplyLength, mReplyFullLength); @@ -267,6 +253,14 @@ void HttpOpRequest::visitNotifier(HttpRequest * request) response->setContentType(mReplyConType); response->setRetries(mPolicyRetries, mPolicy503Retries); + HttpResponse::TransferStats::ptr_t stats = HttpResponse::TransferStats::ptr_t(new HttpResponse::TransferStats); + + curl_easy_getinfo(mCurlHandle, CURLINFO_SIZE_DOWNLOAD, &stats->mSizeDownload); + curl_easy_getinfo(mCurlHandle, CURLINFO_TOTAL_TIME, &stats->mTotalTime); + curl_easy_getinfo(mCurlHandle, CURLINFO_SPEED_DOWNLOAD, &stats->mSpeedDownload); + + response->setTransferStats(stats); + mUserHandler->onCompleted(static_cast<HttpHandle>(this), response); response->release(); @@ -287,8 +281,8 @@ HttpStatus HttpOpRequest::cancel() HttpStatus HttpOpRequest::setupGet(HttpRequest::policy_t policy_id, HttpRequest::priority_t priority, const std::string & url, - HttpOptions * options, - HttpHeaders * headers) + const HttpOptions::ptr_t & options, + const HttpHeaders::ptr_t & headers) { setupCommon(policy_id, priority, url, NULL, options, headers); mReqMethod = HOR_GET; @@ -302,8 +296,8 @@ HttpStatus HttpOpRequest::setupGetByteRange(HttpRequest::policy_t policy_id, const std::string & url, size_t offset, size_t len, - HttpOptions * options, - HttpHeaders * headers) + const HttpOptions::ptr_t & options, + const HttpHeaders::ptr_t & headers) { setupCommon(policy_id, priority, url, NULL, options, headers); mReqMethod = HOR_GET; @@ -322,8 +316,8 @@ HttpStatus HttpOpRequest::setupPost(HttpRequest::policy_t policy_id, HttpRequest::priority_t priority, const std::string & url, BufferArray * body, - HttpOptions * options, - HttpHeaders * headers) + const HttpOptions::ptr_t & options, + const HttpHeaders::ptr_t & headers) { setupCommon(policy_id, priority, url, body, options, headers); mReqMethod = HOR_POST; @@ -336,8 +330,8 @@ HttpStatus HttpOpRequest::setupPut(HttpRequest::policy_t policy_id, HttpRequest::priority_t priority, const std::string & url, BufferArray * body, - HttpOptions * options, - HttpHeaders * headers) + const HttpOptions::ptr_t & options, + const HttpHeaders::ptr_t & headers) { setupCommon(policy_id, priority, url, body, options, headers); mReqMethod = HOR_PUT; @@ -346,12 +340,52 @@ HttpStatus HttpOpRequest::setupPut(HttpRequest::policy_t policy_id, } +HttpStatus HttpOpRequest::setupDelete(HttpRequest::policy_t policy_id, + HttpRequest::priority_t priority, + const std::string & url, + const HttpOptions::ptr_t & options, + const HttpHeaders::ptr_t & headers) +{ + setupCommon(policy_id, priority, url, NULL, options, headers); + mReqMethod = HOR_DELETE; + + return HttpStatus(); +} + + +HttpStatus HttpOpRequest::setupPatch(HttpRequest::policy_t policy_id, + HttpRequest::priority_t priority, + const std::string & url, + BufferArray * body, + const HttpOptions::ptr_t & options, + const HttpHeaders::ptr_t & headers) +{ + setupCommon(policy_id, priority, url, body, options, headers); + mReqMethod = HOR_PATCH; + + return HttpStatus(); +} + + +HttpStatus HttpOpRequest::setupCopy(HttpRequest::policy_t policy_id, + HttpRequest::priority_t priority, + const std::string & url, + const HttpOptions::ptr_t & options, + const HttpHeaders::ptr_t &headers) +{ + setupCommon(policy_id, priority, url, NULL, options, headers); + mReqMethod = HOR_COPY; + + return HttpStatus(); +} + + void HttpOpRequest::setupCommon(HttpRequest::policy_t policy_id, HttpRequest::priority_t priority, const std::string & url, BufferArray * body, - HttpOptions * options, - HttpHeaders * headers) + const HttpOptions::ptr_t & options, + const HttpHeaders::ptr_t & headers) { mProcFlags = 0U; mReqPolicy = policy_id; @@ -364,12 +398,10 @@ void HttpOpRequest::setupCommon(HttpRequest::policy_t policy_id, } if (headers && ! mReqHeaders) { - headers->addRef(); mReqHeaders = headers; } - if (options && ! mReqOptions) + if (options && !mReqOptions) { - options->addRef(); mReqOptions = options; if (options->getWantHeaders()) { @@ -416,11 +448,7 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) mReplyOffset = 0; mReplyLength = 0; mReplyFullLength = 0; - if (mReplyHeaders) - { - mReplyHeaders->release(); - mReplyHeaders = NULL; - } + mReplyHeaders.reset(); mReplyConType.clear(); // *FIXME: better error handling later @@ -452,18 +480,8 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) code = curl_easy_setopt(mCurlHandle, CURLOPT_ENCODING, ""); check_curl_easy_code(code, CURLOPT_ENCODING); - // The Linksys WRT54G V5 router has an issue with frequent - // DNS lookups from LAN machines. If they happen too often, - // like for every HTTP request, the router gets annoyed after - // about 700 or so requests and starts issuing TCP RSTs to - // new connections. Reuse the DNS lookups for even a few - // seconds and no RSTs. - code = curl_easy_setopt(mCurlHandle, CURLOPT_DNS_CACHE_TIMEOUT, 15); - check_curl_easy_code(code, CURLOPT_DNS_CACHE_TIMEOUT); code = curl_easy_setopt(mCurlHandle, CURLOPT_AUTOREFERER, 1); check_curl_easy_code(code, CURLOPT_AUTOREFERER); - code = curl_easy_setopt(mCurlHandle, CURLOPT_FOLLOWLOCATION, 1); - check_curl_easy_code(code, CURLOPT_FOLLOWLOCATION); code = curl_easy_setopt(mCurlHandle, CURLOPT_MAXREDIRS, HTTP_REDIRECTS_DEFAULT); check_curl_easy_code(code, CURLOPT_MAXREDIRS); code = curl_easy_setopt(mCurlHandle, CURLOPT_WRITEFUNCTION, writeCallback); @@ -474,11 +492,57 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) check_curl_easy_code(code, CURLOPT_READFUNCTION); code = curl_easy_setopt(mCurlHandle, CURLOPT_READDATA, this); check_curl_easy_code(code, CURLOPT_READDATA); - code = curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYPEER, 1); + code = curl_easy_setopt(mCurlHandle, CURLOPT_SEEKFUNCTION, seekCallback); + check_curl_easy_code(code, CURLOPT_SEEKFUNCTION); + code = curl_easy_setopt(mCurlHandle, CURLOPT_SEEKDATA, this); + check_curl_easy_code(code, CURLOPT_SEEKDATA); + + code = curl_easy_setopt(mCurlHandle, CURLOPT_COOKIEFILE, ""); + check_curl_easy_code(code, CURLOPT_COOKIEFILE); + + if (gpolicy.mSslCtxCallback) + { + code = curl_easy_setopt(mCurlHandle, CURLOPT_SSL_CTX_FUNCTION, curlSslCtxCallback); + check_curl_easy_code(code, CURLOPT_SSL_CTX_FUNCTION); + code = curl_easy_setopt(mCurlHandle, CURLOPT_SSL_CTX_DATA, this); + check_curl_easy_code(code, CURLOPT_SSL_CTX_DATA); + mCallbackSSLVerify = gpolicy.mSslCtxCallback; + } + + long follow_redirect(1L); + long sslPeerV(0L); + long sslHostV(0L); + long dnsCacheTimeout(-1L); + long nobody(0L); + + if (mReqOptions) + { + follow_redirect = mReqOptions->getFollowRedirects() ? 1L : 0L; + sslPeerV = mReqOptions->getSSLVerifyPeer() ? 1L : 0L; + sslHostV = mReqOptions->getSSLVerifyHost() ? 2L : 0L; + dnsCacheTimeout = mReqOptions->getDNSCacheTimeout(); + nobody = mReqOptions->getHeadersOnly() ? 1L : 0L; + } + code = curl_easy_setopt(mCurlHandle, CURLOPT_FOLLOWLOCATION, follow_redirect); + check_curl_easy_code(code, CURLOPT_FOLLOWLOCATION); + + code = curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYPEER, sslPeerV); check_curl_easy_code(code, CURLOPT_SSL_VERIFYPEER); - code = curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYHOST, 0); + code = curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYHOST, sslHostV); check_curl_easy_code(code, CURLOPT_SSL_VERIFYHOST); + code = curl_easy_setopt(mCurlHandle, CURLOPT_NOBODY, nobody); + check_curl_easy_code(code, CURLOPT_NOBODY); + + // The Linksys WRT54G V5 router has an issue with frequent + // DNS lookups from LAN machines. If they happen too often, + // like for every HTTP request, the router gets annoyed after + // about 700 or so requests and starts issuing TCP RSTs to + // new connections. Reuse the DNS lookups for even a few + // seconds and no RSTs. + code = curl_easy_setopt(mCurlHandle, CURLOPT_DNS_CACHE_TIMEOUT, dnsCacheTimeout); + check_curl_easy_code(code, CURLOPT_DNS_CACHE_TIMEOUT); + if (gpolicy.mUseLLProxy) { // Use the viewer-based thread-safe API which has a @@ -509,10 +573,9 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) switch (mReqMethod) { case HOR_GET: - code = curl_easy_setopt(mCurlHandle, CURLOPT_HTTPGET, 1); + if (nobody == 0) + code = curl_easy_setopt(mCurlHandle, CURLOPT_HTTPGET, 1); check_curl_easy_code(code, CURLOPT_HTTPGET); - mCurlHeaders = curl_slist_append(mCurlHeaders, "Connection: keep-alive"); - mCurlHeaders = curl_slist_append(mCurlHeaders, "Keep-alive: 300"); break; case HOR_POST: @@ -531,12 +594,14 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) code = curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDSIZE, data_size); check_curl_easy_code(code, CURLOPT_POSTFIELDSIZE); mCurlHeaders = curl_slist_append(mCurlHeaders, "Expect:"); - mCurlHeaders = curl_slist_append(mCurlHeaders, "Connection: keep-alive"); - mCurlHeaders = curl_slist_append(mCurlHeaders, "Keep-alive: 300"); } break; - case HOR_PUT: + case HOR_PATCH: + code = curl_easy_setopt(mCurlHandle, CURLOPT_CUSTOMREQUEST, "PATCH"); + check_curl_easy_code(code, CURLOPT_CUSTOMREQUEST); + // fall through. The rest is the same as PUT + case HOR_PUT: { code = curl_easy_setopt(mCurlHandle, CURLOPT_UPLOAD, 1); check_curl_easy_code(code, CURLOPT_UPLOAD); @@ -547,15 +612,20 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) } code = curl_easy_setopt(mCurlHandle, CURLOPT_INFILESIZE, data_size); check_curl_easy_code(code, CURLOPT_INFILESIZE); - code = curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDS, (void *) NULL); - check_curl_easy_code(code, CURLOPT_POSTFIELDS); mCurlHeaders = curl_slist_append(mCurlHeaders, "Expect:"); - // *TODO: Should this be 'Keep-Alive' ? - mCurlHeaders = curl_slist_append(mCurlHeaders, "Connection: keep-alive"); - mCurlHeaders = curl_slist_append(mCurlHeaders, "Keep-alive: 300"); } break; + case HOR_DELETE: + code = curl_easy_setopt(mCurlHandle, CURLOPT_CUSTOMREQUEST, "DELETE"); + check_curl_easy_code(code, CURLOPT_CUSTOMREQUEST); + break; + + case HOR_COPY: + code = curl_easy_setopt(mCurlHandle, CURLOPT_CUSTOMREQUEST, "COPY"); + check_curl_easy_code(code, CURLOPT_CUSTOMREQUEST); + break; + default: LL_ERRS(LOG_CORE) << "Invalid HTTP method in request: " << int(mReqMethod) << ". Can't recover." @@ -563,6 +633,11 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) break; } + + // *TODO: Should this be 'Keep-Alive' ? + mCurlHeaders = curl_slist_append(mCurlHeaders, "Connection: keep-alive"); + mCurlHeaders = curl_slist_append(mCurlHeaders, "Keep-alive: 300"); + // Tracing if (mTracing >= HTTP_TRACE_CURL_HEADERS) { @@ -723,6 +798,37 @@ size_t HttpOpRequest::readCallback(void * data, size_t size, size_t nmemb, void return read_size; } + +int HttpOpRequest::seekCallback(void *userdata, curl_off_t offset, int origin) +{ + HttpOpRequest * op(static_cast<HttpOpRequest *>(userdata)); + + if (!op->mReqBody) + { + return 0; + } + + size_t newPos = 0; + if (origin == SEEK_SET) + newPos = offset; + else if (origin == SEEK_END) + newPos = static_cast<curl_off_t>(op->mReqBody->size()) + offset; + else if (origin == SEEK_CUR) + newPos = static_cast<curl_off_t>(op->mCurlBodyPos) + offset; + else + return 2; + + if (newPos >= op->mReqBody->size()) + { + LL_WARNS(LOG_CORE) << "Attempt to seek to position outside post body." << LL_ENDL; + return 2; + } + + op->mCurlBodyPos = (size_t)newPos; + + return 0; +} + size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, void * userdata) { @@ -817,7 +923,7 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi // Save headers in response if (! op->mReplyHeaders) { - op->mReplyHeaders = new HttpHeaders; + op->mReplyHeaders = HttpHeaders::ptr_t(new HttpHeaders); } op->mReplyHeaders->append(name, value ? value : ""); } @@ -873,6 +979,35 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi } +CURLcode HttpOpRequest::curlSslCtxCallback(CURL *curl, void *sslctx, void *userdata) +{ + HttpOpRequest * op(static_cast<HttpOpRequest *>(userdata)); + + if (op->mCallbackSSLVerify) + { + SSL_CTX * ctx = (SSL_CTX *)sslctx; + // disable any default verification for server certs + SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL); + // set the verification callback. + SSL_CTX_set_cert_verify_callback(ctx, sslCertVerifyCallback, userdata); + // the calls are void + } + + return CURLE_OK; +} + +int HttpOpRequest::sslCertVerifyCallback(X509_STORE_CTX *ctx, void *param) +{ + HttpOpRequest * op(static_cast<HttpOpRequest *>(param)); + + if (op->mCallbackSSLVerify) + { + op->mStatus = op->mCallbackSSLVerify(op->mReqURL, op->mUserHandler, ctx); + } + + return (op->mStatus) ? 1 : 0; +} + int HttpOpRequest::debugCallback(CURL * handle, curl_infotype info, char * buffer, size_t len, void * userdata) { HttpOpRequest * op(static_cast<HttpOpRequest *>(userdata)); diff --git a/indra/llcorehttp/_httpoprequest.h b/indra/llcorehttp/_httpoprequest.h index 2f628b5aba..a9083be02b 100755 --- a/indra/llcorehttp/_httpoprequest.h +++ b/indra/llcorehttp/_httpoprequest.h @@ -33,19 +33,22 @@ #include <string> #include <curl/curl.h> +#include <openssl/x509_vfy.h> +#include <openssl/ssl.h> + #include "httpcommon.h" #include "httprequest.h" #include "_httpoperation.h" #include "_refcounted.h" +#include "httpheaders.h" +#include "httpoptions.h" namespace LLCore { class BufferArray; -class HttpHeaders; -class HttpOptions; /// HttpOpRequest requests a supported HTTP method invocation with @@ -77,7 +80,10 @@ public: { HOR_GET, HOR_POST, - HOR_PUT + HOR_PUT, + HOR_DELETE, + HOR_PATCH, + HOR_COPY }; virtual void stageFromRequest(HttpService *); @@ -98,32 +104,51 @@ public: HttpStatus setupGet(HttpRequest::policy_t policy_id, HttpRequest::priority_t priority, const std::string & url, - HttpOptions * options, - HttpHeaders * headers); + const HttpOptions::ptr_t & options, + const HttpHeaders::ptr_t & headers); HttpStatus setupGetByteRange(HttpRequest::policy_t policy_id, HttpRequest::priority_t priority, const std::string & url, size_t offset, size_t len, - HttpOptions * options, - HttpHeaders * headers); + const HttpOptions::ptr_t & options, + const HttpHeaders::ptr_t & headers); HttpStatus setupPost(HttpRequest::policy_t policy_id, HttpRequest::priority_t priority, const std::string & url, BufferArray * body, - HttpOptions * options, - HttpHeaders * headers); + const HttpOptions::ptr_t & options, + const HttpHeaders::ptr_t & headers); HttpStatus setupPut(HttpRequest::policy_t policy_id, HttpRequest::priority_t priority, const std::string & url, BufferArray * body, - HttpOptions * options, - HttpHeaders * headers); - - // Internal method used to setup the libcurl options for a request. + const HttpOptions::ptr_t & options, + const HttpHeaders::ptr_t & headers); + + HttpStatus setupDelete(HttpRequest::policy_t policy_id, + HttpRequest::priority_t priority, + const std::string & url, + const HttpOptions::ptr_t & options, + const HttpHeaders::ptr_t & headers); + + HttpStatus setupPatch(HttpRequest::policy_t policy_id, + HttpRequest::priority_t priority, + const std::string & url, + BufferArray * body, + const HttpOptions::ptr_t & options, + const HttpHeaders::ptr_t & headers); + + HttpStatus setupCopy(HttpRequest::policy_t policy_id, + HttpRequest::priority_t priority, + const std::string & url, + const HttpOptions::ptr_t & options, + const HttpHeaders::ptr_t & headers); + + // Internal method used to setup the libcurl options for a request. // Does all the libcurl handle setup in one place. // // Threading: called by worker thread @@ -141,8 +166,8 @@ protected: HttpRequest::priority_t priority, const std::string & url, BufferArray * body, - HttpOptions * options, - HttpHeaders * headers); + const HttpOptions::ptr_t & options, + const HttpHeaders::ptr_t & headers); // libcurl operational callbacks // @@ -150,7 +175,11 @@ protected: // static size_t writeCallback(void * data, size_t size, size_t nmemb, void * userdata); static size_t readCallback(void * data, size_t size, size_t nmemb, void * userdata); + static int seekCallback(void *data, curl_off_t offset, int origin); static size_t headerCallback(void * data, size_t size, size_t nmemb, void * userdata); + static CURLcode curlSslCtxCallback(CURL *curl, void *ssl_ctx, void *userptr); + static int sslCertVerifyCallback(X509_STORE_CTX *ctx, void *param); + static int debugCallback(CURL *, curl_infotype info, char * buffer, size_t len, void * userdata); protected: @@ -159,6 +188,8 @@ protected: static const unsigned int PF_SAVE_HEADERS = 0x00000002U; static const unsigned int PF_USE_RETRY_AFTER = 0x00000004U; + HttpRequest::policyCallback_t mCallbackSSLVerify; + public: // Request data EMethod mReqMethod; @@ -166,8 +197,8 @@ public: BufferArray * mReqBody; off_t mReqOffset; size_t mReqLength; - HttpHeaders * mReqHeaders; - HttpOptions * mReqOptions; + HttpHeaders::ptr_t mReqHeaders; + HttpOptions::ptr_t mReqOptions; // Transport data bool mCurlActive; @@ -184,7 +215,7 @@ public: off_t mReplyOffset; size_t mReplyLength; size_t mReplyFullLength; - HttpHeaders * mReplyHeaders; + HttpHeaders::ptr_t mReplyHeaders; std::string mReplyConType; int mReplyRetryAfter; @@ -215,7 +246,7 @@ public: // Internal function to append the contents of an HttpHeaders // instance to a curl_slist object. -curl_slist * append_headers_to_slist(const HttpHeaders *, curl_slist * slist); +curl_slist * append_headers_to_slist(const HttpHeaders::ptr_t &, curl_slist * slist); } // end namespace LLCore diff --git a/indra/llcorehttp/_httppolicyglobal.cpp b/indra/llcorehttp/_httppolicyglobal.cpp index 1dc95f3dce..3d0df96ade 100755 --- a/indra/llcorehttp/_httppolicyglobal.cpp +++ b/indra/llcorehttp/_httppolicyglobal.cpp @@ -106,6 +106,20 @@ HttpStatus HttpPolicyGlobal::set(HttpRequest::EPolicyOption opt, const std::stri return HttpStatus(); } +HttpStatus HttpPolicyGlobal::set(HttpRequest::EPolicyOption opt, HttpRequest::policyCallback_t value) +{ + switch (opt) + { + case HttpRequest::PO_SSL_VERIFY_CALLBACK: + mSslCtxCallback = value; + break; + + default: + return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG); + } + + return HttpStatus(); +} HttpStatus HttpPolicyGlobal::get(HttpRequest::EPolicyOption opt, long * value) const { @@ -154,4 +168,20 @@ HttpStatus HttpPolicyGlobal::get(HttpRequest::EPolicyOption opt, std::string * v return HttpStatus(); } + +HttpStatus HttpPolicyGlobal::get(HttpRequest::EPolicyOption opt, HttpRequest::policyCallback_t * value) const +{ + switch (opt) + { + case HttpRequest::PO_SSL_VERIFY_CALLBACK: + *value = mSslCtxCallback; + break; + + default: + return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG); + } + + return HttpStatus(); +} + } // end namespace LLCore diff --git a/indra/llcorehttp/_httppolicyglobal.h b/indra/llcorehttp/_httppolicyglobal.h index 67c4ba9481..e02da4386a 100755 --- a/indra/llcorehttp/_httppolicyglobal.h +++ b/indra/llcorehttp/_httppolicyglobal.h @@ -60,8 +60,10 @@ private: public: HttpStatus set(HttpRequest::EPolicyOption opt, long value); HttpStatus set(HttpRequest::EPolicyOption opt, const std::string & value); + HttpStatus set(HttpRequest::EPolicyOption opt, HttpRequest::policyCallback_t value); HttpStatus get(HttpRequest::EPolicyOption opt, long * value) const; HttpStatus get(HttpRequest::EPolicyOption opt, std::string * value) const; + HttpStatus get(HttpRequest::EPolicyOption opt, HttpRequest::policyCallback_t * value) const; public: long mConnectionLimit; @@ -70,6 +72,7 @@ public: std::string mHttpProxy; long mTrace; long mUseLLProxy; + HttpRequest::policyCallback_t mSslCtxCallback; }; // end class HttpPolicyGlobal } // end namespace LLCore diff --git a/indra/llcorehttp/_httpservice.cpp b/indra/llcorehttp/_httpservice.cpp index c673e1be1d..252db78c89 100755 --- a/indra/llcorehttp/_httpservice.cpp +++ b/indra/llcorehttp/_httpservice.cpp @@ -53,15 +53,16 @@ namespace LLCore const HttpService::OptionDescriptor HttpService::sOptionDesc[] = { // isLong isDynamic isGlobal isClass - { true, true, true, true }, // PO_CONNECTION_LIMIT - { true, true, false, true }, // PO_PER_HOST_CONNECTION_LIMIT - { false, false, true, false }, // PO_CA_PATH - { false, false, true, false }, // PO_CA_FILE - { false, true, true, false }, // PO_HTTP_PROXY - { true, true, true, false }, // PO_LLPROXY - { true, true, true, false }, // PO_TRACE - { true, true, false, true }, // PO_ENABLE_PIPELINING - { true, true, false, true } // PO_THROTTLE_RATE + { true, true, true, true, false }, // PO_CONNECTION_LIMIT + { true, true, false, true, false }, // PO_PER_HOST_CONNECTION_LIMIT + { false, false, true, false, false }, // PO_CA_PATH + { false, false, true, false, false }, // PO_CA_FILE + { false, true, true, false, false }, // PO_HTTP_PROXY + { true, true, true, false, false }, // PO_LLPROXY + { true, true, true, false, false }, // PO_TRACE + { true, true, false, true, false }, // PO_ENABLE_PIPELINING + { true, true, false, true, false }, // PO_THROTTLE_RATE + { false, false, true, false, true } // PO_SSL_VERIFY_CALLBACK }; HttpService * HttpService::sInstance(NULL); volatile HttpService::EState HttpService::sState(NOT_INITIALIZED); @@ -413,6 +414,34 @@ HttpStatus HttpService::getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequ return status; } +HttpStatus HttpService::getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass, + HttpRequest::policyCallback_t * ret_value) +{ + HttpStatus status(HttpStatus::LLCORE, LLCore::HE_INVALID_ARG); + + if (opt < HttpRequest::PO_CONNECTION_LIMIT // option must be in range + || opt >= HttpRequest::PO_LAST // ditto + || (sOptionDesc[opt].mIsLong) // datatype is string + || (pclass != HttpRequest::GLOBAL_POLICY_ID && pclass > mLastPolicy) // pclass in valid range + || (pclass == HttpRequest::GLOBAL_POLICY_ID && !sOptionDesc[opt].mIsGlobal) // global setting permitted + || (pclass != HttpRequest::GLOBAL_POLICY_ID && !sOptionDesc[opt].mIsClass)) // class setting permitted + // can always get, no dynamic check + { + return status; + } + + // Only global has callback values + if (pclass == HttpRequest::GLOBAL_POLICY_ID) + { + HttpPolicyGlobal & opts(mPolicy->getGlobalOptions()); + + status = opts.get(opt, ret_value); + } + + return status; +} + + HttpStatus HttpService::setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass, long value, long * ret_value) @@ -489,6 +518,37 @@ HttpStatus HttpService::setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequ return status; } - + +HttpStatus HttpService::setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass, + HttpRequest::policyCallback_t value, HttpRequest::policyCallback_t * ret_value) +{ + HttpStatus status(HttpStatus::LLCORE, LLCore::HE_INVALID_ARG); + + if (opt < HttpRequest::PO_CONNECTION_LIMIT // option must be in range + || opt >= HttpRequest::PO_LAST // ditto + || (sOptionDesc[opt].mIsLong) // datatype is string + || (pclass != HttpRequest::GLOBAL_POLICY_ID && pclass > mLastPolicy) // pclass in valid range + || (pclass == HttpRequest::GLOBAL_POLICY_ID && !sOptionDesc[opt].mIsGlobal) // global setting permitted + || (pclass != HttpRequest::GLOBAL_POLICY_ID && !sOptionDesc[opt].mIsClass) // class setting permitted + || (RUNNING == sState && !sOptionDesc[opt].mIsDynamic)) // dynamic setting permitted + { + return status; + } + + // Callbacks values are always global (at this time). + if (pclass == HttpRequest::GLOBAL_POLICY_ID) + { + HttpPolicyGlobal & opts(mPolicy->getGlobalOptions()); + + status = opts.set(opt, value); + if (status && ret_value) + { + status = opts.get(opt, ret_value); + } + } + + return status; +} + } // end namespace LLCore diff --git a/indra/llcorehttp/_httpservice.h b/indra/llcorehttp/_httpservice.h index cf23f3ab61..ac518a5de7 100755 --- a/indra/llcorehttp/_httpservice.h +++ b/indra/llcorehttp/_httpservice.h @@ -201,17 +201,24 @@ protected: bool mIsDynamic; bool mIsGlobal; bool mIsClass; + bool mIsCallback; }; HttpStatus getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t, long * ret_value); HttpStatus getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t, std::string * ret_value); + HttpStatus getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t, + HttpRequest::policyCallback_t * ret_value); + HttpStatus setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t, long value, long * ret_value); HttpStatus setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t, const std::string & value, std::string * ret_value); - + HttpStatus setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t, + HttpRequest::policyCallback_t value, + HttpRequest::policyCallback_t * ret_value); + protected: static const OptionDescriptor sOptionDesc[HttpRequest::PO_LAST]; static HttpService * sInstance; diff --git a/indra/llcorehttp/_refcounted.h b/indra/llcorehttp/_refcounted.h index 402e725152..7f713f2298 100755 --- a/indra/llcorehttp/_refcounted.h +++ b/indra/llcorehttp/_refcounted.h @@ -32,6 +32,7 @@ #include "fix_macros.h" #include <boost/thread.hpp> +#include <boost/intrusive_ptr.hpp> #include "llapr.h" @@ -120,7 +121,36 @@ inline void RefCounted::destroySelf() delete this; } +/** + * boost::intrusive_ptr may be used to manage RefCounted classes. + * Unfortunately RefCounted and boost::intrusive_ptr use different conventions + * for the initial refcount value. To avoid leaky (immortal) objects, you + * should really construct boost::intrusive_ptr<RefCounted*>(rawptr, false). + * IntrusivePtr<T> encapsulates that for you. + */ +template <typename T> +struct IntrusivePtr: public boost::intrusive_ptr<T> +{ + IntrusivePtr(): + boost::intrusive_ptr<T>() + {} + IntrusivePtr(T* p): + boost::intrusive_ptr<T>(p, false) + {} +}; + +inline void intrusive_ptr_add_ref(RefCounted* p) +{ + p->addRef(); +} + +inline void intrusive_ptr_release(RefCounted* p) +{ + p->release(); +} + } // end namespace LLCoreInt + #endif // LLCOREINT__REFCOUNTED_H_ diff --git a/indra/llcorehttp/bufferarray.h b/indra/llcorehttp/bufferarray.h index 1094a435b4..320adf2b8b 100755 --- a/indra/llcorehttp/bufferarray.h +++ b/indra/llcorehttp/bufferarray.h @@ -30,6 +30,7 @@ #include <cstdlib> #include <vector> +#include "boost/intrusive_ptr.hpp" #include "_refcounted.h" @@ -73,6 +74,8 @@ public: BufferArray(); + typedef LLCoreInt::IntrusivePtr<BufferArray> ptr_t; + protected: virtual ~BufferArray(); // Use release() @@ -129,6 +132,7 @@ protected: container_t mBlocks; size_t mLen; + }; // end class BufferArray diff --git a/indra/llcorehttp/examples/http_texture_load.cpp b/indra/llcorehttp/examples/http_texture_load.cpp index 9d9631b980..737282c7df 100755 --- a/indra/llcorehttp/examples/http_texture_load.cpp +++ b/indra/llcorehttp/examples/http_texture_load.cpp @@ -83,7 +83,7 @@ public: WorkingSet(); ~WorkingSet(); - bool reload(LLCore::HttpRequest *, LLCore::HttpOptions *); + bool reload(LLCore::HttpRequest *, LLCore::HttpOptions::ptr_t &); virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response); @@ -121,7 +121,7 @@ public: int mRetriesHttp503; int mSuccesses; long mByteCount; - LLCore::HttpHeaders * mHeaders; + LLCore::HttpHeaders::ptr_t mHeaders; }; @@ -304,7 +304,7 @@ int main(int argc, char** argv) LLCore::HttpRequest * hr = new LLCore::HttpRequest(); // Get request options - LLCore::HttpOptions * opt = new LLCore::HttpOptions(); + LLCore::HttpOptions::ptr_t opt = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()); opt->setRetries(12); opt->setUseRetryAfter(true); @@ -363,8 +363,7 @@ int main(int argc, char** argv) // Clean up hr->requestStopThread(NULL); ms_sleep(1000); - opt->release(); - opt = NULL; + opt.reset(); delete hr; LLCore::HttpRequest::destroyService(); term_curl(); @@ -427,22 +426,17 @@ WorkingSet::WorkingSet() { mAssets.reserve(30000); - mHeaders = new LLCore::HttpHeaders; + mHeaders = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders); mHeaders->append("Accept", "image/x-j2c"); } WorkingSet::~WorkingSet() { - if (mHeaders) - { - mHeaders->release(); - mHeaders = NULL; - } } -bool WorkingSet::reload(LLCore::HttpRequest * hr, LLCore::HttpOptions * opt) +bool WorkingSet::reload(LLCore::HttpRequest * hr, LLCore::HttpOptions::ptr_t & opt) { if (mRequestLowWater <= mHandles.size()) { diff --git a/indra/llcorehttp/httpcommon.cpp b/indra/llcorehttp/httpcommon.cpp index 7907e958a4..99238ea920 100755 --- a/indra/llcorehttp/httpcommon.cpp +++ b/indra/llcorehttp/httpcommon.cpp @@ -42,7 +42,7 @@ HttpStatus::operator unsigned long() const { static const int shift(sizeof(unsigned long) * 4); - unsigned long result(((unsigned long) mType) << shift | (unsigned long) (int) mStatus); + unsigned long result(((unsigned long)mDetails->mType) << shift | (unsigned long)(int)mDetails->mStatus); return result; } @@ -131,30 +131,34 @@ std::string HttpStatus::toString() const { return std::string(""); } - switch (mType) + switch (getType()) { case EXT_CURL_EASY: - return std::string(curl_easy_strerror(CURLcode(mStatus))); + return std::string(curl_easy_strerror(CURLcode(getStatus()))); case EXT_CURL_MULTI: - return std::string(curl_multi_strerror(CURLMcode(mStatus))); + return std::string(curl_multi_strerror(CURLMcode(getStatus()))); case LLCORE: - if (mStatus >= 0 && mStatus < llcore_errors_count) + if (getStatus() >= 0 && getStatus() < llcore_errors_count) { - return std::string(llcore_errors[mStatus]); + return std::string(llcore_errors[getStatus()]); } break; default: if (isHttpStatus()) { + // special handling for status 499 "Linden Catchall" + if ((getType() == 499) && (!getMessage().empty())) + return getMessage(); + // Binary search for the error code and string int bottom(0), top(http_errors_count); while (true) { int at((bottom + top) / 2); - if (mType == http_errors[at].mCode) + if (getType() == http_errors[at].mCode) { return std::string(http_errors[at].mText); } @@ -162,7 +166,7 @@ std::string HttpStatus::toString() const { break; } - else if (mType < http_errors[at].mCode) + else if (getType() < http_errors[at].mCode) { top = at; } @@ -182,9 +186,9 @@ std::string HttpStatus::toTerseString() const { std::ostringstream result; - unsigned int error_value((unsigned short) mStatus); + unsigned int error_value((unsigned short)getStatus()); - switch (mType) + switch (getType()) { case EXT_CURL_EASY: result << "Easy_"; @@ -202,7 +206,7 @@ std::string HttpStatus::toTerseString() const if (isHttpStatus()) { result << "Http_"; - error_value = mType; + error_value = getType(); } else { @@ -244,7 +248,7 @@ bool HttpStatus::isRetryable() const // Disable the '*this == inv_status' test and look for 'Core_9' // failures in log files. - return ((isHttpStatus() && mType >= 499 && mType <= 599) || // Include special 499 in retryables + return ((isHttpStatus() && getType() >= 499 && getType() <= 599) || // Include special 499 in retryables *this == cant_connect || // Connection reset/endpoint problems *this == cant_res_proxy || // DNS problems *this == cant_res_host || // DNS problems diff --git a/indra/llcorehttp/httpcommon.h b/indra/llcorehttp/httpcommon.h index 9601f94125..898d3d47fa 100755 --- a/indra/llcorehttp/httpcommon.h +++ b/indra/llcorehttp/httpcommon.h @@ -188,10 +188,12 @@ /// #include "linden_common.h" // Modifies curl/curl.h interfaces - +#include "boost/intrusive_ptr.hpp" +#include "boost/shared_ptr.hpp" +#include "boost/weak_ptr.hpp" +#include "boost/function.hpp" #include <string> - namespace LLCore { @@ -286,52 +288,63 @@ enum HttpError /// 5. Construct an HTTP 301 status code to be treated as success: /// HttpStatus(301, HE_SUCCESS); /// +/// 6. Construct a failed status of HTTP Status 499 with a custom error message +/// HttpStatus(499, "Failed LLSD Response"); struct HttpStatus { typedef unsigned short type_enum_t; HttpStatus() - : mType(LLCORE), - mStatus(HE_SUCCESS) - {} + { + mDetails = boost::shared_ptr<Details>(new Details(LLCORE, HE_SUCCESS)); + } HttpStatus(type_enum_t type, short status) - : mType(type), - mStatus(status) - {} + { + mDetails = boost::shared_ptr<Details>(new Details(type, status)); + } HttpStatus(int http_status) - : mType(http_status), - mStatus(http_status >= 200 && http_status <= 299 - ? HE_SUCCESS - : HE_REPLY_ERROR) - { - llassert(http_status >= 100 && http_status <= 999); - } + { + mDetails = boost::shared_ptr<Details>(new Details(http_status, + (http_status >= 200 && http_status <= 299) ? HE_SUCCESS : HE_REPLY_ERROR)); + llassert(http_status >= 100 && http_status <= 999); + } + + HttpStatus(int http_status, const std::string &message) + { + mDetails = boost::shared_ptr<Details>(new Details(http_status, + (http_status >= 200 && http_status <= 299) ? HE_SUCCESS : HE_REPLY_ERROR)); + llassert(http_status >= 100 && http_status <= 999); + mDetails->mMessage = message; + } HttpStatus(const HttpStatus & rhs) - : mType(rhs.mType), - mStatus(rhs.mStatus) - {} + { + mDetails = rhs.mDetails; + } + + ~HttpStatus() + { + } HttpStatus & operator=(const HttpStatus & rhs) - { - // Don't care if lhs & rhs are the same object + { + mDetails = rhs.mDetails; + return *this; + } - mType = rhs.mType; - mStatus = rhs.mStatus; - return *this; - } + HttpStatus & clone(const HttpStatus &rhs) + { + mDetails = boost::shared_ptr<Details>(new Details(*rhs.mDetails)); + return *this; + } static const type_enum_t EXT_CURL_EASY = 0; ///< mStatus is an error from a curl_easy_*() call static const type_enum_t EXT_CURL_MULTI = 1; ///< mStatus is an error from a curl_multi_*() call static const type_enum_t LLCORE = 2; ///< mStatus is an HE_* error code ///< 100-999 directly represent HTTP status codes - - type_enum_t mType; - short mStatus; - /// Test for successful status in the code regardless /// of error source (internal, libcurl). /// @@ -339,7 +352,7 @@ struct HttpStatus /// operator bool() const { - return 0 == mStatus; + return 0 == mDetails->mStatus; } /// Inverse of previous operator. @@ -347,14 +360,14 @@ struct HttpStatus /// @return 'true' on any error condition bool operator !() const { - return 0 != mStatus; + return 0 != mDetails->mStatus; } /// Equality and inequality tests to bypass bool conversion /// which will do the wrong thing in conditional expressions. bool operator==(const HttpStatus & rhs) const { - return mType == rhs.mType && mStatus == rhs.mStatus; + return (*mDetails == *rhs.mDetails); } bool operator!=(const HttpStatus & rhs) const @@ -395,7 +408,7 @@ struct HttpStatus /// HTTP response status (100 - 999). bool isHttpStatus() const { - return mType >= type_enum_t(100) && mType <= type_enum_t(999); + return mDetails->mType >= type_enum_t(100) && mDetails->mType <= type_enum_t(999); } /// Returns true if the status is one that will be retried @@ -403,7 +416,78 @@ struct HttpStatus /// where that logic needs to be replicated. Only applies /// to failed statuses, successful statuses will return false. bool isRetryable() const; - + + /// Returns the currently set status code as a raw number + /// + short getStatus() const + { + return mDetails->mStatus; + } + + /// Returns the currently set status type + /// + type_enum_t getType() const + { + return mDetails->mType; + } + + /// Returns an optional error message if one has been set. + /// + std::string getMessage() const + { + return mDetails->mMessage; + } + + /// Sets an optional error message + /// + void setMessage(const std::string &message) + { + mDetails->mMessage = message; + } + + /// Retrieves an optionally recorded SSL certificate. + void * getErrorData() const + { + return mDetails->mErrorData; + } + + /// Optionally sets an SSL certificate on this status. + void setErrorData(void *data) + { + mDetails->mErrorData = data; + } + +private: + + struct Details + { + Details(type_enum_t type, short status): + mType(type), + mStatus(status), + mMessage(), + mErrorData(NULL) + {} + + Details(const Details &rhs) : + mType(rhs.mType), + mStatus(rhs.mStatus), + mMessage(rhs.mMessage), + mErrorData(rhs.mErrorData) + {} + + bool operator == (const Details &rhs) const + { + return (mType == rhs.mType) && (mStatus == rhs.mStatus); + } + + type_enum_t mType; + short mStatus; + std::string mMessage; + void * mErrorData; + }; + + boost::shared_ptr<Details> mDetails; + }; // end struct HttpStatus } // end namespace LLCore diff --git a/indra/llcorehttp/httphandler.h b/indra/llcorehttp/httphandler.h index 9171e4e7b9..7bc9096703 100755 --- a/indra/llcorehttp/httphandler.h +++ b/indra/llcorehttp/httphandler.h @@ -45,7 +45,7 @@ class HttpResponse; /// be shared by any number of requests and across instances /// of HttpRequest running in the same thread. /// -/// Threading: HttpHandler itself is pure interface and is +/// Threading: HttpHandler itself is interface and is /// tread-compatible. Most derivations, however, will have /// different constraints. /// @@ -53,12 +53,13 @@ class HttpResponse; /// that is rarely a good idea. Queued requests and replies keep /// a naked pointer to the handler and this can result in a /// dangling pointer if lifetimes aren't managed correctly. - -class HttpHandler +/// +/// *TODO: public std::enable_shared_from_this<HttpHandler> +class HttpHandler { public: virtual ~HttpHandler() - {} + { } /// Method invoked during calls to @see update(). Each invocation /// represents the completion of some requested operation. Caller diff --git a/indra/llcorehttp/httpheaders.cpp b/indra/llcorehttp/httpheaders.cpp index 23ebea361c..f586191a7c 100755 --- a/indra/llcorehttp/httpheaders.cpp +++ b/indra/llcorehttp/httpheaders.cpp @@ -34,7 +34,6 @@ namespace LLCore HttpHeaders::HttpHeaders() - : RefCounted(true) {} @@ -105,7 +104,7 @@ void HttpHeaders::appendNormal(const char * header, size_t size) // Find from end to simulate a tradition of using single-valued // std::map for this in the past. -const std::string * HttpHeaders::find(const char * name) const +const std::string * HttpHeaders::find(const std::string &name) const { const_reverse_iterator iend(rend()); for (const_reverse_iterator iter(rbegin()); iend != iter; ++iter) @@ -118,6 +117,24 @@ const std::string * HttpHeaders::find(const char * name) const return NULL; } +void HttpHeaders::remove(const char *name) +{ + remove(std::string(name)); +} + +void HttpHeaders::remove(const std::string &name) +{ + iterator iend(end()); + for (iterator iter(begin()); iend != iter; ++iter) + { + if ((*iter).first == name) + { + mHeaders.erase(iter); + return; + } + } +} + // Standard Iterators HttpHeaders::iterator HttpHeaders::begin() diff --git a/indra/llcorehttp/httpheaders.h b/indra/llcorehttp/httpheaders.h index f70cd898f3..b9168cb6ec 100755 --- a/indra/llcorehttp/httpheaders.h +++ b/indra/llcorehttp/httpheaders.h @@ -28,8 +28,8 @@ #define _LLCORE_HTTP_HEADERS_H_ +#include "httpcommon.h" #include <string> - #include "_refcounted.h" @@ -74,7 +74,7 @@ namespace LLCore /// constructor is given a refcount. /// -class HttpHeaders : public LLCoreInt::RefCounted +class HttpHeaders: private boost::noncopyable { public: typedef std::pair<std::string, std::string> header_t; @@ -85,15 +85,17 @@ public: typedef container_t::const_reverse_iterator const_reverse_iterator; typedef container_t::value_type value_type; typedef container_t::size_type size_type; + typedef boost::shared_ptr<HttpHeaders> ptr_t; public: /// @post In addition to the instance, caller has a refcount /// to the instance. A call to @see release() will destroy /// the instance. HttpHeaders(); + virtual ~HttpHeaders(); // Use release() + //typedef LLCoreInt::IntrusivePtr<HttpHeaders> ptr_t; protected: - virtual ~HttpHeaders(); // Use release() HttpHeaders(const HttpHeaders &); // Not defined void operator=(const HttpHeaders &); // Not defined @@ -145,8 +147,16 @@ public: // a pointer to a std::string in the container. // Pointer is valid only for the lifetime of // the container or until container is modifed. - // - const std::string * find(const char * name) const; + const std::string * find(const std::string &name) const; + const std::string * find(const char * name) const + { + return find(std::string(name)); + } + + // Remove the header from the list if found. + // + void remove(const std::string &name); + void remove(const char *name); // Count of headers currently in the list. size_type size() const diff --git a/indra/llcorehttp/httpoptions.cpp b/indra/llcorehttp/httpoptions.cpp index 5bf1ecb4a5..aab447f2dd 100755 --- a/indra/llcorehttp/httpoptions.cpp +++ b/indra/llcorehttp/httpoptions.cpp @@ -25,7 +25,7 @@ */ #include "httpoptions.h" - +#include "lldefs.h" #include "_httpinternal.h" @@ -33,14 +33,18 @@ namespace LLCore { -HttpOptions::HttpOptions() - : RefCounted(true), - mWantHeaders(false), - mTracing(HTTP_TRACE_OFF), - mTimeout(HTTP_REQUEST_TIMEOUT_DEFAULT), - mTransferTimeout(HTTP_REQUEST_XFER_TIMEOUT_DEFAULT), - mRetries(HTTP_RETRY_COUNT_DEFAULT), - mUseRetryAfter(HTTP_USE_RETRY_AFTER_DEFAULT) +HttpOptions::HttpOptions() : + mWantHeaders(false), + mTracing(HTTP_TRACE_OFF), + mTimeout(HTTP_REQUEST_TIMEOUT_DEFAULT), + mTransferTimeout(HTTP_REQUEST_XFER_TIMEOUT_DEFAULT), + mRetries(HTTP_RETRY_COUNT_DEFAULT), + mUseRetryAfter(HTTP_USE_RETRY_AFTER_DEFAULT), + mFollowRedirects(true), + mVerifyPeer(false), + mVerifyHost(false), + mDNSCacheTimeout(-1L), + mNoBody(false) {} @@ -82,5 +86,31 @@ void HttpOptions::setUseRetryAfter(bool use_retry) mUseRetryAfter = use_retry; } +void HttpOptions::setFollowRedirects(bool follow_redirect) +{ + mFollowRedirects = follow_redirect; +} + +void HttpOptions::setSSLVerifyPeer(bool verify) +{ + mVerifyPeer = verify; +} + +void HttpOptions::setSSLVerifyHost(bool verify) +{ + mVerifyHost = verify; +} + +void HttpOptions::setDNSCacheTimeout(int timeout) +{ + mDNSCacheTimeout = timeout; +} + +void HttpOptions::setHeadersOnly(bool nobody) +{ + mNoBody = nobody; + if (mNoBody) + setWantHeaders(true); +} } // end namespace LLCore diff --git a/indra/llcorehttp/httpoptions.h b/indra/llcorehttp/httpoptions.h index 4ab5ff18c4..510eaa45bb 100755 --- a/indra/llcorehttp/httpoptions.h +++ b/indra/llcorehttp/httpoptions.h @@ -29,7 +29,6 @@ #include "httpcommon.h" - #include "_refcounted.h" @@ -56,59 +55,110 @@ namespace LLCore /// Allocation: Refcounted, heap only. Caller of the constructor /// is given a refcount. /// -class HttpOptions : public LLCoreInt::RefCounted +class HttpOptions : private boost::noncopyable { public: HttpOptions(); + typedef boost::shared_ptr<HttpOptions> ptr_t; + + virtual ~HttpOptions(); // Use release() + protected: - virtual ~HttpOptions(); // Use release() HttpOptions(const HttpOptions &); // Not defined void operator=(const HttpOptions &); // Not defined public: + // Default: false void setWantHeaders(bool wanted); bool getWantHeaders() const - { - return mWantHeaders; - } + { + return mWantHeaders; + } // Default: 0 void setTrace(int long); int getTrace() const - { - return mTracing; - } + { + return mTracing; + } // Default: 30 void setTimeout(unsigned int timeout); unsigned int getTimeout() const - { - return mTimeout; - } + { + return mTimeout; + } // Default: 0 void setTransferTimeout(unsigned int timeout); unsigned int getTransferTimeout() const - { - return mTransferTimeout; - } + { + return mTransferTimeout; + } + /// Sets the number of retries on an LLCore::HTTPRequest before the + /// request fails. // Default: 8 void setRetries(unsigned int retries); unsigned int getRetries() const - { - return mRetries; - } + { + return mRetries; + } // Default: true void setUseRetryAfter(bool use_retry); bool getUseRetryAfter() const - { - return mUseRetryAfter; - } + { + return mUseRetryAfter; + } + + /// Instructs the LLCore::HTTPRequest to follow redirects + /// Default: false + void setFollowRedirects(bool follow_redirect); + bool getFollowRedirects() const + { + return mFollowRedirects; + } + + /// Instructs the LLCore::HTTPRequest to verify that the exchanged security + /// certificate is authentic. + /// Default: false + void setSSLVerifyPeer(bool verify); + bool getSSLVerifyPeer() const + { + return mVerifyPeer; + } + + /// Instructs the LLCore::HTTPRequest to verify that the name in the + /// security certificate matches the name of the host contacted. + /// Default: false + void setSSLVerifyHost(bool verify); + bool getSSLVerifyHost() const + { + return mVerifyHost; + } + + /// Sets the time for DNS name caching in seconds. Setting this value + /// to 0 will disable name caching. Setting this value to -1 causes the + /// name cache to never time out. + /// Default: -1 + void setDNSCacheTimeout(int timeout); + int getDNSCacheTimeout() const + { + return mDNSCacheTimeout; + } + + /// Retrieve only the headers and status from the request. Setting this + /// to true implies setWantHeaders(true) as well. + /// Default: false + void setHeadersOnly(bool nobody); + bool getHeadersOnly() const + { + return mNoBody; + } protected: bool mWantHeaders; @@ -117,6 +167,11 @@ protected: unsigned int mTransferTimeout; unsigned int mRetries; bool mUseRetryAfter; + bool mFollowRedirects; + bool mVerifyPeer; + bool mVerifyHost; + int mDNSCacheTimeout; + bool mNoBody; }; // end class HttpOptions diff --git a/indra/llcorehttp/httprequest.cpp b/indra/llcorehttp/httprequest.cpp index 7b1888e3eb..f0dfde6153 100755 --- a/indra/llcorehttp/httprequest.cpp +++ b/indra/llcorehttp/httprequest.cpp @@ -117,6 +117,15 @@ HttpStatus HttpRequest::setStaticPolicyOption(EPolicyOption opt, policy_t pclass return HttpService::instanceOf()->setPolicyOption(opt, pclass, value, ret_value); } +HttpStatus HttpRequest::setStaticPolicyOption(EPolicyOption opt, policy_t pclass, policyCallback_t value, policyCallback_t * ret_value) +{ + if (HttpService::RUNNING == HttpService::instanceOf()->getState()) + { + return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC); + } + + return HttpService::instanceOf()->setPolicyOption(opt, pclass, value, ret_value); +} HttpHandle HttpRequest::setPolicyOption(EPolicyOption opt, policy_t pclass, long value, HttpHandler * handler) @@ -188,8 +197,8 @@ HttpStatus HttpRequest::getStatus() const HttpHandle HttpRequest::requestGet(policy_t policy_id, priority_t priority, const std::string & url, - HttpOptions * options, - HttpHeaders * headers, + const HttpOptions::ptr_t & options, + const HttpHeaders::ptr_t & headers, HttpHandler * user_handler) { HttpStatus status; @@ -222,8 +231,8 @@ HttpHandle HttpRequest::requestGetByteRange(policy_t policy_id, const std::string & url, size_t offset, size_t len, - HttpOptions * options, - HttpHeaders * headers, + const HttpOptions::ptr_t & options, + const HttpHeaders::ptr_t & headers, HttpHandler * user_handler) { HttpStatus status; @@ -255,8 +264,8 @@ HttpHandle HttpRequest::requestPost(policy_t policy_id, priority_t priority, const std::string & url, BufferArray * body, - HttpOptions * options, - HttpHeaders * headers, + const HttpOptions::ptr_t & options, + const HttpHeaders::ptr_t & headers, HttpHandler * user_handler) { HttpStatus status; @@ -288,8 +297,8 @@ HttpHandle HttpRequest::requestPut(policy_t policy_id, priority_t priority, const std::string & url, BufferArray * body, - HttpOptions * options, - HttpHeaders * headers, + const HttpOptions::ptr_t & options, + const HttpHeaders::ptr_t & headers, HttpHandler * user_handler) { HttpStatus status; @@ -316,6 +325,100 @@ HttpHandle HttpRequest::requestPut(policy_t policy_id, return handle; } +HttpHandle HttpRequest::requestDelete(policy_t policy_id, + priority_t priority, + const std::string & url, + const HttpOptions::ptr_t & options, + const HttpHeaders::ptr_t & headers, + HttpHandler * user_handler) +{ + HttpStatus status; + HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); + + HttpOpRequest * op = new HttpOpRequest(); + if (!(status = op->setupDelete(policy_id, priority, url, options, headers))) + { + op->release(); + mLastReqStatus = status; + return handle; + } + op->setReplyPath(mReplyQueue, user_handler); + if (!(status = mRequestQueue->addOp(op))) // transfers refcount + { + op->release(); + mLastReqStatus = status; + return handle; + } + + mLastReqStatus = status; + handle = static_cast<HttpHandle>(op); + + return handle; +} + +HttpHandle HttpRequest::requestPatch(policy_t policy_id, + priority_t priority, + const std::string & url, + BufferArray * body, + const HttpOptions::ptr_t & options, + const HttpHeaders::ptr_t & headers, + HttpHandler * user_handler) +{ + HttpStatus status; + HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); + + HttpOpRequest * op = new HttpOpRequest(); + if (!(status = op->setupPatch(policy_id, priority, url, body, options, headers))) + { + op->release(); + mLastReqStatus = status; + return handle; + } + op->setReplyPath(mReplyQueue, user_handler); + if (!(status = mRequestQueue->addOp(op))) // transfers refcount + { + op->release(); + mLastReqStatus = status; + return handle; + } + + mLastReqStatus = status; + handle = static_cast<HttpHandle>(op); + + return handle; +} + +HttpHandle HttpRequest::requestCopy(policy_t policy_id, + priority_t priority, + const std::string & url, + const HttpOptions::ptr_t & options, + const HttpHeaders::ptr_t & headers, + HttpHandler * user_handler) +{ + HttpStatus status; + HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); + + HttpOpRequest * op = new HttpOpRequest(); + if (!(status = op->setupCopy(policy_id, priority, url, options, headers))) + { + op->release(); + mLastReqStatus = status; + return handle; + } + op->setReplyPath(mReplyQueue, user_handler); + if (!(status = mRequestQueue->addOp(op))) // transfers refcount + { + op->release(); + mLastReqStatus = status; + return handle; + } + + mLastReqStatus = status; + handle = static_cast<HttpHandle>(op); + + return handle; +} + HttpHandle HttpRequest::requestNoOp(HttpHandler * user_handler) { diff --git a/indra/llcorehttp/httprequest.h b/indra/llcorehttp/httprequest.h index 7f23723b0b..20a223c482 100755 --- a/indra/llcorehttp/httprequest.h +++ b/indra/llcorehttp/httprequest.h @@ -31,6 +31,8 @@ #include "httpcommon.h" #include "httphandler.h" +#include "httpheaders.h" +#include "httpoptions.h" namespace LLCore { @@ -38,8 +40,6 @@ namespace LLCore class HttpRequestQueue; class HttpReplyQueue; class HttpService; -class HttpOptions; -class HttpHeaders; class HttpOperation; class BufferArray; @@ -97,6 +97,8 @@ public: typedef unsigned int policy_t; typedef unsigned int priority_t; + typedef boost::shared_ptr<HttpRequest> ptr_t; + typedef boost::weak_ptr<HttpRequest> wptr_t; public: /// @name PolicyMethods /// @{ @@ -163,7 +165,7 @@ public: /// Long value that if non-zero enables the use of the /// traditional LLProxy code for http/socks5 support. If - // enabled, has priority over GP_HTTP_PROXY. + /// enabled, has priority over GP_HTTP_PROXY. /// /// Global only PO_LLPROXY, @@ -219,15 +221,25 @@ public: /// Controls whether client-side throttling should be /// performed on this policy class. Positive values /// enable throttling and specify the request rate - /// (requests per second) that should be targetted. + /// (requests per second) that should be targeted. /// A value of zero, the default, specifies no throttling. /// /// Per-class only PO_THROTTLE_RATE, + /// Controls the callback function used to control SSL CTX + /// certificate verification. + /// + /// Global only + PO_SSL_VERIFY_CALLBACK, + PO_LAST // Always at end }; + /// Prototype for policy based callbacks. The callback methods will be executed + /// on the worker thread so no modifications should be made to the HttpHandler object. + typedef boost::function<HttpStatus(const std::string &, HttpHandler const * const, void *)> policyCallback_t; + /// Set a policy option for a global or class parameter at /// startup time (prior to thread start). /// @@ -243,6 +255,8 @@ public: long value, long * ret_value); static HttpStatus setStaticPolicyOption(EPolicyOption opt, policy_t pclass, const std::string & value, std::string * ret_value); + static HttpStatus setStaticPolicyOption(EPolicyOption opt, policy_t pclass, + policyCallback_t value, policyCallback_t * ret_value);; /// Set a parameter on a class-based policy option. Calls /// made after the start of the servicing thread are @@ -334,8 +348,8 @@ public: HttpHandle requestGet(policy_t policy_id, priority_t priority, const std::string & url, - HttpOptions * options, - HttpHeaders * headers, + const HttpOptions::ptr_t & options, + const HttpHeaders::ptr_t & headers, HttpHandler * handler); @@ -377,8 +391,8 @@ public: const std::string & url, size_t offset, size_t len, - HttpOptions * options, - HttpHeaders * headers, + const HttpOptions::ptr_t & options, + const HttpHeaders::ptr_t & headers, HttpHandler * handler); @@ -418,8 +432,8 @@ public: priority_t priority, const std::string & url, BufferArray * body, - HttpOptions * options, - HttpHeaders * headers, + const HttpOptions::ptr_t & options, + const HttpHeaders::ptr_t & headers, HttpHandler * handler); @@ -459,12 +473,73 @@ public: priority_t priority, const std::string & url, BufferArray * body, - HttpOptions * options, - HttpHeaders * headers, + const HttpOptions::ptr_t & options, + const HttpHeaders::ptr_t & headers, HttpHandler * handler); - /// Queue a NoOp request. + /// Queue a full HTTP PUT. Query arguments and body may + /// be provided. Caller is responsible for escaping and + /// encoding and communicating the content types. + /// + /// @param policy_id @see requestGet() + /// @param priority " + /// @param url " + /// @param options @see requestGet()K(optional) + /// @param headers " + /// @param handler " + /// @return " + /// + HttpHandle requestDelete(policy_t policy_id, + priority_t priority, + const std::string & url, + const HttpOptions::ptr_t & options, + const HttpHeaders::ptr_t & headers, + HttpHandler * user_handler); + + /// Queue a full HTTP PUT. Query arguments and body may + /// be provided. Caller is responsible for escaping and + /// encoding and communicating the content types. + /// + /// @param policy_id @see requestGet() + /// @param priority " + /// @param url " + /// @param body Byte stream to be sent as the body. No + /// further encoding or escaping will be done + /// to the content. + /// @param options @see requestGet()K(optional) + /// @param headers " + /// @param handler " + /// @return " + /// + HttpHandle requestPatch(policy_t policy_id, + priority_t priority, + const std::string & url, + BufferArray * body, + const HttpOptions::ptr_t & options, + const HttpHeaders::ptr_t & headers, + HttpHandler * user_handler); + + /// Queue a full HTTP PUT. Query arguments and body may + /// be provided. Caller is responsible for escaping and + /// encoding and communicating the content types. + /// + /// @param policy_id @see requestGet() + /// @param priority " + /// @param url " + /// @param options @see requestGet()K(optional) + /// @param headers " + /// @param handler " + /// @return " + /// + HttpHandle requestCopy(policy_t policy_id, + priority_t priority, + const std::string & url, + const HttpOptions::ptr_t & options, + const HttpHeaders::ptr_t & headers, + HttpHandler * user_handler); + + /// Queue a NoOp request. /// The request is queued and serviced by the working thread which /// immediately processes it and returns the request to the reply /// queue. diff --git a/indra/llcorehttp/httpresponse.cpp b/indra/llcorehttp/httpresponse.cpp index c974395b0a..f5ad2ebd47 100755 --- a/indra/llcorehttp/httpresponse.cpp +++ b/indra/llcorehttp/httpresponse.cpp @@ -39,16 +39,17 @@ HttpResponse::HttpResponse() mReplyLength(0U), mReplyFullLength(0U), mBufferArray(NULL), - mHeaders(NULL), + mHeaders(), mRetries(0U), - m503Retries(0U) + m503Retries(0U), + mRequestUrl() {} HttpResponse::~HttpResponse() { setBody(NULL); - setHeaders(NULL); + //setHeaders(); } @@ -71,23 +72,14 @@ void HttpResponse::setBody(BufferArray * ba) } -void HttpResponse::setHeaders(HttpHeaders * headers) +void HttpResponse::setHeaders(HttpHeaders::ptr_t &headers) { - if (mHeaders == headers) - return; - - if (mHeaders) - { - mHeaders->release(); - } - - if (headers) - { - headers->addRef(); - } - - mHeaders = headers; + mHeaders = headers; } +size_t HttpResponse::getBodySize() const +{ + return (mBufferArray) ? mBufferArray->size() : 0; +} } // end namespace LLCore diff --git a/indra/llcorehttp/httpresponse.h b/indra/llcorehttp/httpresponse.h index aee64e2878..0bfa4585c7 100755 --- a/indra/llcorehttp/httpresponse.h +++ b/indra/llcorehttp/httpresponse.h @@ -31,7 +31,7 @@ #include <string> #include "httpcommon.h" - +#include "httpheaders.h" #include "_refcounted.h" @@ -69,6 +69,18 @@ protected: void operator=(const HttpResponse &); // Not defined public: + /// Statistics for the HTTP + struct TransferStats + { + typedef boost::shared_ptr<TransferStats> ptr_t; + + TransferStats() : mSizeDownload(0.0), mTotalTime(0.0), mSpeedDownload(0.0) {} + F64 mSizeDownload; + F64 mTotalTime; + F64 mSpeedDownload; + }; + + /// Returns the final status of the requested operation. /// HttpStatus getStatus() const @@ -92,6 +104,10 @@ public: return mBufferArray; } + /// Safely get the size of the body buffer. If the body buffer is missing + /// return 0 as the size. + size_t getBodySize() const; + /// Set the response data in the instance. Will drop the reference /// count to any existing data and increment the count of that passed /// in. It is legal to set the data to NULL. @@ -104,13 +120,13 @@ public: /// /// Caller can hold onto the headers by incrementing the reference /// count of the returned object. - HttpHeaders * getHeaders() const - { + HttpHeaders::ptr_t getHeaders() const + { return mHeaders; - } + } /// Behaves like @see setResponse() but for header data. - void setHeaders(HttpHeaders * headers); + void setHeaders(HttpHeaders::ptr_t &headers); /// If a 'Range:' header was used, these methods are involved /// in setting and returning data about the actual response. @@ -168,6 +184,27 @@ public: m503Retries = retries_503; } + void setTransferStats(TransferStats::ptr_t &stats) + { + mStats = stats; + } + + TransferStats::ptr_t getTransferStats() + { + return mStats; + } + + void setRequestURL(const std::string &url) + { + mRequestUrl = url; + } + + const std::string &getRequestURL() const + { + return mRequestUrl; + } + + protected: // Response data here HttpStatus mStatus; @@ -175,10 +212,13 @@ protected: unsigned int mReplyLength; unsigned int mReplyFullLength; BufferArray * mBufferArray; - HttpHeaders * mHeaders; + HttpHeaders::ptr_t mHeaders; std::string mContentType; unsigned int mRetries; unsigned int m503Retries; + std::string mRequestUrl; + + TransferStats::ptr_t mStats; }; diff --git a/indra/llcorehttp/tests/test_httpheaders.hpp b/indra/llcorehttp/tests/test_httpheaders.hpp index 668c36dc66..c05f1d9429 100755 --- a/indra/llcorehttp/tests/test_httpheaders.hpp +++ b/indra/llcorehttp/tests/test_httpheaders.hpp @@ -59,13 +59,12 @@ void HttpHeadersTestObjectType::test<1>() mMemTotal = GetMemTotal(); // create a new ref counted object with an implicit reference - HttpHeaders * headers = new HttpHeaders(); - ensure("One ref on construction of HttpHeaders", headers->getRefCount() == 1); + HttpHeaders::ptr_t headers = HttpHeaders::ptr_t(new HttpHeaders()); ensure("Memory being used", mMemTotal < GetMemTotal()); ensure("Nothing in headers", 0 == headers->size()); // release the implicit reference, causing the object to be released - headers->release(); + headers.reset(); // make sure we didn't leak any memory ensure(mMemTotal == GetMemTotal()); @@ -80,7 +79,7 @@ void HttpHeadersTestObjectType::test<2>() mMemTotal = GetMemTotal(); // create a new ref counted object with an implicit reference - HttpHeaders * headers = new HttpHeaders(); + HttpHeaders::ptr_t headers = HttpHeaders::ptr_t(new HttpHeaders()); { // Append a few strings @@ -101,7 +100,7 @@ void HttpHeadersTestObjectType::test<2>() } // release the implicit reference, causing the object to be released - headers->release(); + headers.reset(); // make sure we didn't leak any memory ensure(mMemTotal == GetMemTotal()); @@ -116,7 +115,7 @@ void HttpHeadersTestObjectType::test<3>() mMemTotal = GetMemTotal(); // create a new ref counted object with an implicit reference - HttpHeaders * headers = new HttpHeaders(); + HttpHeaders::ptr_t headers = HttpHeaders::ptr_t(new HttpHeaders()); { // Append a few strings @@ -151,7 +150,7 @@ void HttpHeadersTestObjectType::test<3>() } // release the implicit reference, causing the object to be released - headers->release(); + headers.reset(); // make sure we didn't leak any memory ensure(mMemTotal == GetMemTotal()); @@ -166,8 +165,8 @@ void HttpHeadersTestObjectType::test<4>() mMemTotal = GetMemTotal(); // create a new ref counted object with an implicit reference - HttpHeaders * headers = new HttpHeaders(); - + HttpHeaders::ptr_t headers = HttpHeaders::ptr_t(new HttpHeaders()); + { static char line1[] = " AcCePT : image/yourfacehere"; static char line1v[] = "image/yourfacehere"; @@ -251,7 +250,7 @@ void HttpHeadersTestObjectType::test<4>() } // release the implicit reference, causing the object to be released - headers->release(); + headers.reset(); // make sure we didn't leak any memory ensure(mMemTotal == GetMemTotal()); @@ -267,7 +266,7 @@ void HttpHeadersTestObjectType::test<5>() mMemTotal = GetMemTotal(); // create a new ref counted object with an implicit reference - HttpHeaders * headers = new HttpHeaders(); + HttpHeaders::ptr_t headers = HttpHeaders::ptr_t(new HttpHeaders()); HttpHeaders::iterator end(headers->end()), begin(headers->begin()); ensure("Empty container has equal begin/end const iterators", end == begin); @@ -337,7 +336,7 @@ void HttpHeadersTestObjectType::test<5>() } // release the implicit reference, causing the object to be released - headers->release(); + headers.reset(); // make sure we didn't leak any memory ensure(mMemTotal == GetMemTotal()); @@ -353,7 +352,7 @@ void HttpHeadersTestObjectType::test<6>() mMemTotal = GetMemTotal(); // create a new ref counted object with an implicit reference - HttpHeaders * headers = new HttpHeaders(); + HttpHeaders::ptr_t headers = HttpHeaders::ptr_t(new HttpHeaders()); HttpHeaders::reverse_iterator rend(headers->rend()), rbegin(headers->rbegin()); ensure("Empty container has equal rbegin/rend const iterators", rend == rbegin); @@ -421,7 +420,7 @@ void HttpHeadersTestObjectType::test<6>() } // release the implicit reference, causing the object to be released - headers->release(); + headers.reset(); // make sure we didn't leak any memory ensure(mMemTotal == GetMemTotal()); diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp index 43f7e36da5..1f606bd0c1 100755 --- a/indra/llcorehttp/tests/test_httprequest.hpp +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -112,7 +112,7 @@ public: if (! mHeadersRequired.empty() || ! mHeadersDisallowed.empty()) { ensure("Response required with header check", response != NULL); - HttpHeaders * header(response->getHeaders()); // Will not hold onto this + HttpHeaders::ptr_t header(response->getHeaders()); // Will not hold onto this ensure("Some quantity of headers returned", header != NULL); if (! mHeadersRequired.empty()) @@ -638,7 +638,7 @@ void HttpRequestTestObjectType::test<7>() mHandlerCalls = 0; HttpRequest * req = NULL; - HttpOptions * opts = NULL; + HttpOptions::ptr_t opts; try { @@ -653,7 +653,7 @@ void HttpRequestTestObjectType::test<7>() req = new HttpRequest(); ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); - opts = new HttpOptions(); + opts = HttpOptions::ptr_t(new HttpOptions()); opts->setRetries(1); // Don't try for too long - default retries take about 18S // Issue a GET that can't connect @@ -664,7 +664,7 @@ void HttpRequestTestObjectType::test<7>() 0, 0, opts, - NULL, + HttpHeaders::ptr_t(), &handler); ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); @@ -705,8 +705,7 @@ void HttpRequestTestObjectType::test<7>() ensure("Thread actually stopped running", HttpService::isStopped()); // release options - opts->release(); - opts = NULL; + opts.reset(); // release the request object delete req; @@ -728,11 +727,7 @@ void HttpRequestTestObjectType::test<7>() catch (...) { stop_thread(req); - if (opts) - { - opts->release(); - opts = NULL; - } + opts.reset(); delete req; HttpRequest::destroyService(); throw; @@ -779,8 +774,8 @@ void HttpRequestTestObjectType::test<8>() HttpHandle handle = req->requestGet(HttpRequest::DEFAULT_POLICY_ID, 0U, url_base, - NULL, - NULL, + HttpOptions::ptr_t(), + HttpHeaders::ptr_t(), &handler); ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); @@ -889,8 +884,8 @@ void HttpRequestTestObjectType::test<9>() url_base, 0, 0, - NULL, - NULL, + HttpOptions::ptr_t(), + HttpHeaders::ptr_t(), &handler); ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); @@ -1001,9 +996,9 @@ void HttpRequestTestObjectType::test<10>() 0U, url_base, body, - NULL, - NULL, - &handler); + HttpOptions::ptr_t(), + HttpHeaders::ptr_t(), + &handler); ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); // Run the notification pump. @@ -1119,9 +1114,9 @@ void HttpRequestTestObjectType::test<11>() 0U, url_base, body, - NULL, - NULL, - &handler); + HttpOptions::ptr_t(), + HttpHeaders::ptr_t(), + &handler); ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); // Run the notification pump. @@ -1239,9 +1234,9 @@ void HttpRequestTestObjectType::test<12>() url_base, 0, 0, - NULL, - NULL, - &handler); + HttpOptions::ptr_t(), + HttpHeaders::ptr_t(), + &handler); ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); // Run the notification pump. @@ -1332,7 +1327,7 @@ void HttpRequestTestObjectType::test<13>() mHandlerCalls = 0; HttpRequest * req = NULL; - HttpOptions * opts = NULL; + HttpOptions::ptr_t opts; try { @@ -1350,7 +1345,7 @@ void HttpRequestTestObjectType::test<13>() req = new HttpRequest(); ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); - opts = new HttpOptions(); + opts = HttpOptions::ptr_t(new HttpOptions()); opts->setWantHeaders(true); // Issue a GET that succeeds @@ -1364,13 +1359,12 @@ void HttpRequestTestObjectType::test<13>() 0, 0, opts, - NULL, - &handler); + HttpHeaders::ptr_t(), + &handler); ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); // release options - opts->release(); - opts = NULL; + opts.reset(); // Run the notification pump. int count(0); @@ -1430,11 +1424,7 @@ void HttpRequestTestObjectType::test<13>() catch (...) { stop_thread(req); - if (opts) - { - opts->release(); - opts = NULL; - } + opts.reset(); delete req; HttpRequest::destroyService(); throw; @@ -1460,7 +1450,7 @@ void HttpRequestTestObjectType::test<14>() mHandlerCalls = 0; HttpRequest * req = NULL; - HttpOptions * opts = NULL; + HttpOptions::ptr_t opts; try { @@ -1475,7 +1465,7 @@ void HttpRequestTestObjectType::test<14>() req = new HttpRequest(); ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); - opts = new HttpOptions(); + opts = HttpOptions::ptr_t(new HttpOptions); opts->setRetries(0); // Don't retry opts->setTimeout(2); @@ -1487,8 +1477,8 @@ void HttpRequestTestObjectType::test<14>() 0, 0, opts, - NULL, - &handler); + HttpHeaders::ptr_t(), + &handler); ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); // Run the notification pump. @@ -1528,8 +1518,7 @@ void HttpRequestTestObjectType::test<14>() ensure("Thread actually stopped running", HttpService::isStopped()); // release options - opts->release(); - opts = NULL; + opts.reset(); // release the request object delete req; @@ -1552,11 +1541,7 @@ void HttpRequestTestObjectType::test<14>() catch (...) { stop_thread(req); - if (opts) - { - opts->release(); - opts = NULL; - } + opts.reset(); delete req; HttpRequest::destroyService(); throw; @@ -1609,9 +1594,9 @@ void HttpRequestTestObjectType::test<15>() HttpHandle handle = req->requestGet(HttpRequest::DEFAULT_POLICY_ID, 0U, url_base, - NULL, - NULL, - &handler); + HttpOptions::ptr_t(), + HttpHeaders::ptr_t(), + &handler); ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); // Run the notification pump. @@ -1703,8 +1688,8 @@ void HttpRequestTestObjectType::test<16>() mHandlerCalls = 0; HttpRequest * req = NULL; - HttpOptions * options = NULL; - HttpHeaders * headers = NULL; + HttpOptions::ptr_t options; + HttpHeaders::ptr_t headers; try { @@ -1719,7 +1704,7 @@ void HttpRequestTestObjectType::test<16>() req = new HttpRequest(); // options set - options = new HttpOptions(); + options = HttpOptions::ptr_t(new HttpOptions()); options->setWantHeaders(true); // Issue a GET that *can* connect @@ -1776,7 +1761,7 @@ void HttpRequestTestObjectType::test<16>() 0U, url_base + "reflect/", options, - NULL, + HttpHeaders::ptr_t(), &handler); ensure("Valid handle returned for get request", handle != LLCORE_HTTP_HANDLE_INVALID); @@ -1792,7 +1777,7 @@ void HttpRequestTestObjectType::test<16>() ensure("One handler invocation for request", mHandlerCalls == 1); // Do a texture-style fetch - headers = new HttpHeaders; + headers = HttpHeaders::ptr_t(new HttpHeaders); headers->append("Accept", "image/x-j2c"); mStatus = HttpStatus(200); @@ -1897,17 +1882,8 @@ void HttpRequestTestObjectType::test<16>() ensure("Thread actually stopped running", HttpService::isStopped()); // release options & headers - if (options) - { - options->release(); - } - options = NULL; - - if (headers) - { - headers->release(); - } - headers = NULL; + options.reset(); + headers.reset(); // release the request object delete req; @@ -1919,16 +1895,9 @@ void HttpRequestTestObjectType::test<16>() catch (...) { stop_thread(req); - if (options) - { - options->release(); - options = NULL; - } - if (headers) - { - headers->release(); - headers = NULL; - } + options.reset(); + headers.reset(); + delete req; HttpRequest::destroyService(); throw; @@ -1960,8 +1929,8 @@ void HttpRequestTestObjectType::test<17>() mHandlerCalls = 0; HttpRequest * req = NULL; - HttpOptions * options = NULL; - HttpHeaders * headers = NULL; + HttpOptions::ptr_t options; + HttpHeaders::ptr_t headers; BufferArray * ba = NULL; try @@ -1977,7 +1946,7 @@ void HttpRequestTestObjectType::test<17>() req = new HttpRequest(); // options set - options = new HttpOptions(); + options = HttpOptions::ptr_t(new HttpOptions()); options->setWantHeaders(true); // And a buffer array @@ -2049,7 +2018,7 @@ void HttpRequestTestObjectType::test<17>() url_base + "reflect/", ba, options, - NULL, + HttpHeaders::ptr_t(), &handler); ensure("Valid handle returned for get request", handle != LLCORE_HTTP_HANDLE_INVALID); ba->release(); @@ -2095,17 +2064,8 @@ void HttpRequestTestObjectType::test<17>() ensure("Thread actually stopped running", HttpService::isStopped()); // release options & headers - if (options) - { - options->release(); - } - options = NULL; - - if (headers) - { - headers->release(); - } - headers = NULL; + options.reset(); + headers.reset(); // release the request object delete req; @@ -2122,17 +2082,10 @@ void HttpRequestTestObjectType::test<17>() ba->release(); ba = NULL; } - if (options) - { - options->release(); - options = NULL; - } - if (headers) - { - headers->release(); - headers = NULL; - } - delete req; + options.reset(); + headers.reset(); + + delete req; HttpRequest::destroyService(); throw; } @@ -2163,8 +2116,8 @@ void HttpRequestTestObjectType::test<18>() mHandlerCalls = 0; HttpRequest * req = NULL; - HttpOptions * options = NULL; - HttpHeaders * headers = NULL; + HttpOptions::ptr_t options; + HttpHeaders::ptr_t headers; BufferArray * ba = NULL; try @@ -2180,7 +2133,7 @@ void HttpRequestTestObjectType::test<18>() req = new HttpRequest(); // options set - options = new HttpOptions(); + options = HttpOptions::ptr_t(new HttpOptions()); options->setWantHeaders(true); // And a buffer array @@ -2253,7 +2206,7 @@ void HttpRequestTestObjectType::test<18>() url_base + "reflect/", ba, options, - NULL, + HttpHeaders::ptr_t(), &handler); ensure("Valid handle returned for get request", handle != LLCORE_HTTP_HANDLE_INVALID); ba->release(); @@ -2299,17 +2252,8 @@ void HttpRequestTestObjectType::test<18>() ensure("Thread actually stopped running", HttpService::isStopped()); // release options & headers - if (options) - { - options->release(); - } - options = NULL; - - if (headers) - { - headers->release(); - } - headers = NULL; + options.reset(); + headers.reset(); // release the request object delete req; @@ -2326,17 +2270,10 @@ void HttpRequestTestObjectType::test<18>() ba->release(); ba = NULL; } - if (options) - { - options->release(); - options = NULL; - } - if (headers) - { - headers->release(); - headers = NULL; - } - delete req; + options.reset(); + headers.reset(); + + delete req; HttpRequest::destroyService(); throw; } @@ -2367,8 +2304,8 @@ void HttpRequestTestObjectType::test<19>() mHandlerCalls = 0; HttpRequest * req = NULL; - HttpOptions * options = NULL; - HttpHeaders * headers = NULL; + HttpOptions::ptr_t options; + HttpHeaders::ptr_t headers; try { @@ -2383,11 +2320,11 @@ void HttpRequestTestObjectType::test<19>() req = new HttpRequest(); // options set - options = new HttpOptions(); + options = HttpOptions::ptr_t(new HttpOptions()); options->setWantHeaders(true); // headers - headers = new HttpHeaders; + headers = HttpHeaders::ptr_t(new HttpHeaders); headers->append("Keep-Alive", "120"); headers->append("Accept-encoding", "deflate"); headers->append("Accept", "text/plain"); @@ -2502,17 +2439,8 @@ void HttpRequestTestObjectType::test<19>() ensure("Thread actually stopped running", HttpService::isStopped()); // release options & headers - if (options) - { - options->release(); - } - options = NULL; - - if (headers) - { - headers->release(); - } - headers = NULL; + options.reset(); + headers.reset(); // release the request object delete req; @@ -2524,16 +2452,9 @@ void HttpRequestTestObjectType::test<19>() catch (...) { stop_thread(req); - if (options) - { - options->release(); - options = NULL; - } - if (headers) - { - headers->release(); - headers = NULL; - } + options.reset(); + headers.reset(); + delete req; HttpRequest::destroyService(); throw; @@ -2565,8 +2486,8 @@ void HttpRequestTestObjectType::test<20>() mHandlerCalls = 0; HttpRequest * req = NULL; - HttpOptions * options = NULL; - HttpHeaders * headers = NULL; + HttpOptions::ptr_t options; + HttpHeaders::ptr_t headers; BufferArray * ba = NULL; try @@ -2582,11 +2503,11 @@ void HttpRequestTestObjectType::test<20>() req = new HttpRequest(); // options set - options = new HttpOptions(); + options = HttpOptions::ptr_t(new HttpOptions()); options->setWantHeaders(true); // headers - headers = new HttpHeaders(); + headers = HttpHeaders::ptr_t(new HttpHeaders()); headers->append("keep-Alive", "120"); headers->append("Accept", "text/html"); headers->append("content-type", "application/llsd+xml"); @@ -2720,17 +2641,8 @@ void HttpRequestTestObjectType::test<20>() ensure("Thread actually stopped running", HttpService::isStopped()); // release options & headers - if (options) - { - options->release(); - } - options = NULL; - - if (headers) - { - headers->release(); - } - headers = NULL; + options.reset(); + headers.reset(); // release the request object delete req; @@ -2747,16 +2659,8 @@ void HttpRequestTestObjectType::test<20>() ba->release(); ba = NULL; } - if (options) - { - options->release(); - options = NULL; - } - if (headers) - { - headers->release(); - headers = NULL; - } + options.reset(); + headers.reset(); delete req; HttpRequest::destroyService(); throw; @@ -2788,8 +2692,8 @@ void HttpRequestTestObjectType::test<21>() mHandlerCalls = 0; HttpRequest * req = NULL; - HttpOptions * options = NULL; - HttpHeaders * headers = NULL; + HttpOptions::ptr_t options; + HttpHeaders::ptr_t headers; BufferArray * ba = NULL; try @@ -2805,11 +2709,11 @@ void HttpRequestTestObjectType::test<21>() req = new HttpRequest(); // options set - options = new HttpOptions(); + options = HttpOptions::ptr_t(new HttpOptions()); options->setWantHeaders(true); // headers - headers = new HttpHeaders; + headers = HttpHeaders::ptr_t(new HttpHeaders); headers->append("content-type", "text/plain"); headers->append("content-type", "text/html"); headers->append("content-type", "application/llsd+xml"); @@ -2937,17 +2841,8 @@ void HttpRequestTestObjectType::test<21>() ensure("Thread actually stopped running", HttpService::isStopped()); // release options & headers - if (options) - { - options->release(); - } - options = NULL; - - if (headers) - { - headers->release(); - } - headers = NULL; + options.reset(); + headers.reset(); // release the request object delete req; @@ -2964,16 +2859,8 @@ void HttpRequestTestObjectType::test<21>() ba->release(); ba = NULL; } - if (options) - { - options->release(); - options = NULL; - } - if (headers) - { - headers->release(); - headers = NULL; - } + options.reset(); + headers.reset(); delete req; HttpRequest::destroyService(); throw; @@ -3000,13 +2887,13 @@ void HttpRequestTestObjectType::test<22>() mMemTotal = GetMemTotal(); mHandlerCalls = 0; - HttpOptions * options = NULL; + HttpOptions::ptr_t options; HttpRequest * req = NULL; try { // options set - options = new HttpOptions(); + options = HttpOptions::ptr_t(new HttpOptions()); options->setRetries(1); // Partial_File is retryable and can timeout in here // Get singletons created @@ -3035,8 +2922,8 @@ void HttpRequestTestObjectType::test<22>() 0, 25, options, - NULL, - &handler); + HttpHeaders::ptr_t(), + &handler); ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); } @@ -3067,7 +2954,7 @@ void HttpRequestTestObjectType::test<22>() 0, 25, options, - NULL, + HttpHeaders::ptr_t(), &handler); ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); } @@ -3099,8 +2986,8 @@ void HttpRequestTestObjectType::test<22>() 0, 25, options, - NULL, - &handler); + HttpHeaders::ptr_t(), + &handler); ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); } @@ -3144,11 +3031,7 @@ void HttpRequestTestObjectType::test<22>() ensure("Thread actually stopped running", HttpService::isStopped()); // release options - if (options) - { - options->release(); - options = NULL; - } + options.reset(); // release the request object delete req; @@ -3198,7 +3081,7 @@ void HttpRequestTestObjectType::test<23>() mHandlerCalls = 0; HttpRequest * req = NULL; - HttpOptions * opts = NULL; + HttpOptions::ptr_t opts; try { @@ -3213,7 +3096,7 @@ void HttpRequestTestObjectType::test<23>() req = new HttpRequest(); ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); - opts = new HttpOptions(); + opts = HttpOptions::ptr_t(new HttpOptions()); opts->setRetries(1); // Retry once only opts->setUseRetryAfter(true); // Try to parse the retry-after header @@ -3230,8 +3113,8 @@ void HttpRequestTestObjectType::test<23>() 0, 0, opts, - NULL, - &handler); + HttpHeaders::ptr_t(), + &handler); std::ostringstream testtag; testtag << "Valid handle returned for 503 request #" << i; @@ -3277,8 +3160,7 @@ void HttpRequestTestObjectType::test<23>() ensure("Thread actually stopped running", HttpService::isStopped()); // release options - opts->release(); - opts = NULL; + opts.reset(); // release the request object delete req; @@ -3299,11 +3181,7 @@ void HttpRequestTestObjectType::test<23>() catch (...) { stop_thread(req); - if (opts) - { - opts->release(); - opts = NULL; - } + opts.reset(); delete req; HttpRequest::destroyService(); throw; diff --git a/indra/llcorehttp/tests/test_httpstatus.hpp b/indra/llcorehttp/tests/test_httpstatus.hpp index 0b379836c9..4502d32fe1 100755 --- a/indra/llcorehttp/tests/test_httpstatus.hpp +++ b/indra/llcorehttp/tests/test_httpstatus.hpp @@ -55,77 +55,68 @@ void HttpStatusTestObjectType::test<1>() // auto allocation fine for this HttpStatus status; - status.mType = HttpStatus::EXT_CURL_EASY; - status.mStatus = 0; + + status = HttpStatus(HttpStatus::EXT_CURL_EASY, 0); ensure(bool(status)); ensure(false == !(status)); - status.mType = HttpStatus::EXT_CURL_MULTI; - status.mStatus = 0; + status = HttpStatus(HttpStatus::EXT_CURL_MULTI, 0); ensure(bool(status)); ensure(false == !(status)); - - status.mType = HttpStatus::LLCORE; - status.mStatus = HE_SUCCESS; + + status = HttpStatus(HttpStatus::LLCORE, HE_SUCCESS); ensure(bool(status)); ensure(false == !(status)); - status.mType = HttpStatus::EXT_CURL_MULTI; - status.mStatus = -1; + status = HttpStatus(HttpStatus::EXT_CURL_MULTI, -1); ensure(false == bool(status)); ensure(!(status)); - status.mType = HttpStatus::EXT_CURL_EASY; - status.mStatus = CURLE_BAD_DOWNLOAD_RESUME; + status = HttpStatus(HttpStatus::EXT_CURL_EASY, CURLE_BAD_DOWNLOAD_RESUME); ensure(false == bool(status)); ensure(!(status)); } -template <> template <> -void HttpStatusTestObjectType::test<2>() -{ - set_test_name("HttpStatus memory structure"); - - // Require that an HttpStatus object can be trivially - // returned as a function return value in registers. - // One should fit in an int on all platforms. - - ensure(sizeof(HttpStatus) <= sizeof(int)); -} +// template <> template <> +// void HttpStatusTestObjectType::test<2>() +// { +// set_test_name("HttpStatus memory structure"); +// +// // Require that an HttpStatus object can be trivially +// // returned as a function return value in registers. +// // One should fit in an int on all platforms. +// +// //ensure(sizeof(HttpStatus) <= sizeof(int)); +// } template <> template <> -void HttpStatusTestObjectType::test<3>() +void HttpStatusTestObjectType::test<2>() { set_test_name("HttpStatus valid status string conversion"); - HttpStatus status; - status.mType = HttpStatus::EXT_CURL_EASY; - status.mStatus = 0; + HttpStatus status = HttpStatus(HttpStatus::EXT_CURL_EASY, 0); std::string msg = status.toString(); // std::cout << "Result: " << msg << std::endl; ensure(msg.empty()); - - status.mType = HttpStatus::EXT_CURL_EASY; - status.mStatus = CURLE_BAD_FUNCTION_ARGUMENT; + + status = HttpStatus(HttpStatus::EXT_CURL_EASY, CURLE_BAD_FUNCTION_ARGUMENT); msg = status.toString(); // std::cout << "Result: " << msg << std::endl; ensure(! msg.empty()); - status.mType = HttpStatus::EXT_CURL_MULTI; - status.mStatus = CURLM_OUT_OF_MEMORY; + status = HttpStatus(HttpStatus::EXT_CURL_MULTI, CURLM_OUT_OF_MEMORY); msg = status.toString(); // std::cout << "Result: " << msg << std::endl; ensure(! msg.empty()); - status.mType = HttpStatus::LLCORE; - status.mStatus = HE_SHUTTING_DOWN; + status = HttpStatus(HttpStatus::LLCORE, HE_SHUTTING_DOWN); msg = status.toString(); // std::cout << "Result: " << msg << std::endl; ensure(! msg.empty()); @@ -133,32 +124,28 @@ void HttpStatusTestObjectType::test<3>() template <> template <> -void HttpStatusTestObjectType::test<4>() +void HttpStatusTestObjectType::test<3>() { set_test_name("HttpStatus invalid status string conversion"); - HttpStatus status; - status.mType = HttpStatus::EXT_CURL_EASY; - status.mStatus = 32726; + HttpStatus status = HttpStatus(HttpStatus::EXT_CURL_EASY, 32726); std::string msg = status.toString(); // std::cout << "Result: " << msg << std::endl; ensure(! msg.empty()); - - status.mType = HttpStatus::EXT_CURL_MULTI; - status.mStatus = -470; + + status = HttpStatus(HttpStatus::EXT_CURL_MULTI, -470); msg = status.toString(); // std::cout << "Result: " << msg << std::endl; ensure(! msg.empty()); - status.mType = HttpStatus::LLCORE; - status.mStatus = 923; + status = HttpStatus(HttpStatus::LLCORE, 923); msg = status.toString(); // std::cout << "Result: " << msg << std::endl; ensure(! msg.empty()); } template <> template <> -void HttpStatusTestObjectType::test<5>() +void HttpStatusTestObjectType::test<4>() { set_test_name("HttpStatus equality/inequality testing"); @@ -170,62 +157,55 @@ void HttpStatusTestObjectType::test<5>() HttpStatus status2(HttpStatus::EXT_CURL_EASY, HE_SUCCESS); ensure(status1 != status2); - status1.mType = HttpStatus::LLCORE; - status1.mStatus = HE_REPLY_ERROR; - status2.mType = HttpStatus::LLCORE; - status2.mStatus= HE_SHUTTING_DOWN; + status1 = HttpStatus(HttpStatus::LLCORE, HE_REPLY_ERROR); + status1 = HttpStatus(HttpStatus::LLCORE, HE_SHUTTING_DOWN); + ensure(status1 != status2); } template <> template <> -void HttpStatusTestObjectType::test<6>() +void HttpStatusTestObjectType::test<5>() { set_test_name("HttpStatus basic HTTP status encoding"); HttpStatus status; - status.mType = 200; - status.mStatus = HE_SUCCESS; + + status = HttpStatus(200, HE_SUCCESS); std::string msg = status.toString(); ensure(msg.empty()); ensure(bool(status)); // Normally a success but application says error - status.mStatus = HE_REPLY_ERROR; + status = HttpStatus(200, HE_REPLY_ERROR); msg = status.toString(); ensure(! msg.empty()); ensure(! bool(status)); ensure(status.toULong() > 1UL); // Biggish number, not a bool-to-ulong // Same statuses with distinct success/fail are distinct - status.mType = 200; - status.mStatus = HE_SUCCESS; + status = HttpStatus(200, HE_SUCCESS); HttpStatus status2(200, HE_REPLY_ERROR); ensure(status != status2); // Normally an error but application says okay - status.mType = 406; - status.mStatus = HE_SUCCESS; + status = HttpStatus(406, HE_SUCCESS); msg = status.toString(); ensure(msg.empty()); ensure(bool(status)); // Different statuses but both successful are distinct - status.mType = 200; - status.mStatus = HE_SUCCESS; - status2.mType = 201; - status2.mStatus = HE_SUCCESS; + status = HttpStatus(200, HE_SUCCESS); + status2 = HttpStatus(201, HE_SUCCESS); ensure(status != status2); // Different statuses but both failed are distinct - status.mType = 200; - status.mStatus = HE_REPLY_ERROR; - status2.mType = 201; - status2.mStatus = HE_REPLY_ERROR; + status = HttpStatus(200, HE_REPLY_ERROR); + status2 = HttpStatus(201, HE_REPLY_ERROR); ensure(status != status2); } template <> template <> -void HttpStatusTestObjectType::test<7>() +void HttpStatusTestObjectType::test<6>() { set_test_name("HttpStatus HTTP status text strings"); @@ -234,34 +214,30 @@ void HttpStatusTestObjectType::test<7>() ensure(! msg.empty()); // Should be something ensure(msg == "Continue"); - status.mStatus = HE_SUCCESS; + status = HttpStatus(200, HE_SUCCESS); msg = status.toString(); ensure(msg.empty()); // Success is empty - status.mType = 199; - status.mStatus = HE_REPLY_ERROR; + status = HttpStatus(199, HE_REPLY_ERROR); msg = status.toString(); ensure(msg == "Unknown error"); - status.mType = 505; // Last defined string - status.mStatus = HE_REPLY_ERROR; + status = HttpStatus(505, HE_REPLY_ERROR); msg = status.toString(); ensure(msg == "HTTP Version not supported"); - status.mType = 506; // One beyond - status.mStatus = HE_REPLY_ERROR; + status = HttpStatus(506, HE_REPLY_ERROR); msg = status.toString(); ensure(msg == "Unknown error"); - status.mType = 999; // Last HTTP status - status.mStatus = HE_REPLY_ERROR; + status = HttpStatus(999, HE_REPLY_ERROR); msg = status.toString(); ensure(msg == "Unknown error"); } template <> template <> -void HttpStatusTestObjectType::test<8>() +void HttpStatusTestObjectType::test<7>() { set_test_name("HttpStatus toHex() nominal function"); @@ -273,7 +249,7 @@ void HttpStatusTestObjectType::test<8>() template <> template <> -void HttpStatusTestObjectType::test<9>() +void HttpStatusTestObjectType::test<8>() { set_test_name("HttpStatus toTerseString() nominal function"); diff --git a/indra/llcrashlogger/CMakeLists.txt b/indra/llcrashlogger/CMakeLists.txt index ba4e34d92b..da23b46b7b 100755 --- a/indra/llcrashlogger/CMakeLists.txt +++ b/indra/llcrashlogger/CMakeLists.txt @@ -3,6 +3,7 @@ project(llcrashlogger) include(00-Common) +include(LLCoreHttp) include(LLCommon) include(LLMath) include(LLMessage) @@ -10,6 +11,7 @@ include(LLVFS) include(LLXML) include_directories( + ${LLCOREHTTP_INCLUDE_DIRS} ${LLCOMMON_INCLUDE_DIRS} ${LLMATH_INCLUDE_DIRS} ${LLMESSAGE_INCLUDE_DIRS} diff --git a/indra/llcrashlogger/llcrashlogger.cpp b/indra/llcrashlogger/llcrashlogger.cpp index 7a97c16ea7..4caf6dcd05 100755 --- a/indra/llcrashlogger/llcrashlogger.cpp +++ b/indra/llcrashlogger/llcrashlogger.cpp @@ -40,38 +40,45 @@ #include "lldir.h" #include "llfile.h" #include "llsdserialize.h" -#include "lliopipe.h" -#include "llpumpio.h" -#include "llhttpclient.h" #include "llsdserialize.h" #include "llproxy.h" - -LLPumpIO* gServicePump = NULL; +#include "llcorehttputil.h" +#include "llhttpsdhandler.h" +#include "httpcommon.h" +#include "httpresponse.h" + +#include <curl/curl.h> +#include <openssl/crypto.h> + BOOL gBreak = false; BOOL gSent = false; -class LLCrashLoggerResponder : public LLHTTPClient::Responder +int LLCrashLogger::ssl_mutex_count = 0; +LLCoreInt::HttpMutex ** LLCrashLogger::ssl_mutex_list = NULL; + +class LLCrashLoggerHandler : public LLHttpSDHandler { - LOG_CLASS(LLCrashLoggerResponder); + LOG_CLASS(LLCrashLoggerHandler); public: - LLCrashLoggerResponder() - { - } + LLCrashLoggerHandler() {} protected: - virtual void httpFailure() - { - LL_WARNS() << dumpResponse() << LL_ENDL; - gBreak = true; - } + virtual void onSuccess(LLCore::HttpResponse * response, const LLSD &content); + virtual void onFailure(LLCore::HttpResponse * response, LLCore::HttpStatus status); - virtual void httpSuccess() - { - gBreak = true; - gSent = true; - } }; +void LLCrashLoggerHandler::onSuccess(LLCore::HttpResponse * response, const LLSD &content) +{ + gBreak = true; + gSent = true; +} + +void LLCrashLoggerHandler::onFailure(LLCore::HttpResponse * response, LLCore::HttpStatus status) +{ + gBreak = true; +} + LLCrashLogger::LLCrashLogger() : mCrashBehavior(CRASH_BEHAVIOR_ALWAYS_SEND), mCrashInPreviousExec(false), @@ -389,14 +396,23 @@ bool LLCrashLogger::saveCrashBehaviorSetting(S32 crash_behavior) bool LLCrashLogger::runCrashLogPost(std::string host, LLSD data, std::string msg, int retries, int timeout) { + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); + gBreak = false; + httpOpts->setTimeout(timeout); + for(int i = 0; i < retries; ++i) { updateApplication(llformat("%s, try %d...", msg.c_str(), i+1)); - LLHTTPClient::post(host, data, new LLCrashLoggerResponder(), timeout); - while(!gBreak) + + LLCoreHttpUtil::requestPostWithLLSD(httpRequest.get(), LLCore::HttpRequest::DEFAULT_POLICY_ID, 0, + host, data, httpOpts, LLCore::HttpHeaders::ptr_t(), new LLCrashLoggerHandler); + + while(!gBreak) { updateApplication(); // No new message, just pump the IO + httpRequest->update(0L); } if(gSent) { @@ -510,8 +526,6 @@ bool LLCrashLogger::sendCrashLogs() void LLCrashLogger::updateApplication(const std::string& message) { - gServicePump->pump(); - gServicePump->callback(); if (!message.empty()) LL_INFOS() << message << LL_ENDL; } @@ -576,16 +590,74 @@ bool LLCrashLogger::init() return false; } - gServicePump = new LLPumpIO(gAPRPoolp); - gServicePump->prime(gAPRPoolp); - LLHTTPClient::setPump(*gServicePump); - + init_curl(); + LLCore::HttpRequest::createService(); + LLCore::HttpRequest::startThread(); + return true; } // For cleanup code common to all platforms. void LLCrashLogger::commonCleanup() { + term_curl(); LLError::logToFile(""); //close crashreport.log LLProxy::cleanupClass(); } + +void LLCrashLogger::init_curl() +{ + curl_global_init(CURL_GLOBAL_ALL); + + ssl_mutex_count = CRYPTO_num_locks(); + if (ssl_mutex_count > 0) + { + ssl_mutex_list = new LLCoreInt::HttpMutex *[ssl_mutex_count]; + + for (int i(0); i < ssl_mutex_count; ++i) + { + ssl_mutex_list[i] = new LLCoreInt::HttpMutex; + } + + CRYPTO_set_locking_callback(ssl_locking_callback); + CRYPTO_set_id_callback(ssl_thread_id_callback); + } +} + + +void LLCrashLogger::term_curl() +{ + CRYPTO_set_locking_callback(NULL); + for (int i(0); i < ssl_mutex_count; ++i) + { + delete ssl_mutex_list[i]; + } + delete[] ssl_mutex_list; +} + + +unsigned long LLCrashLogger::ssl_thread_id_callback(void) +{ +#if LL_WINDOWS + return (unsigned long)GetCurrentThread(); +#else + return (unsigned long)pthread_self(); +#endif +} + + +void LLCrashLogger::ssl_locking_callback(int mode, int type, const char * /* file */, int /* line */) +{ + if (type >= 0 && type < ssl_mutex_count) + { + if (mode & CRYPTO_LOCK) + { + ssl_mutex_list[type]->lock(); + } + else + { + ssl_mutex_list[type]->unlock(); + } + } +} + diff --git a/indra/llcrashlogger/llcrashlogger.h b/indra/llcrashlogger/llcrashlogger.h index a06bf1d6ac..f5383daefc 100755 --- a/indra/llcrashlogger/llcrashlogger.h +++ b/indra/llcrashlogger/llcrashlogger.h @@ -34,6 +34,7 @@ #include "llsd.h" #include "llcontrol.h" #include "llcrashlock.h" +#include "_mutex.h" // Crash reporter behavior const S32 CRASH_BEHAVIOR_ASK = 0; @@ -66,6 +67,11 @@ public: bool readMinidump(std::string minidump_path); protected: + static void init_curl(); + static void term_curl(); + static unsigned long ssl_thread_id_callback(void); + static void ssl_locking_callback(int mode, int type, const char * file, int line); + S32 mCrashBehavior; BOOL mCrashInPreviousExec; std::map<std::string, std::string> mFileMap; @@ -78,6 +84,10 @@ protected: LLSD mDebugLog; bool mSentCrashLogs; LLCrashLock mKeyMaster; + + static int ssl_mutex_count; + static LLCoreInt::HttpMutex ** ssl_mutex_list; + }; #endif //LLCRASHLOGGER_H diff --git a/indra/llmessage/CMakeLists.txt b/indra/llmessage/CMakeLists.txt index 0a308fbf10..fc51d147a6 100755 --- a/indra/llmessage/CMakeLists.txt +++ b/indra/llmessage/CMakeLists.txt @@ -14,6 +14,7 @@ include(LLAddBuildTest) include(Python) include(Tut) include(Python) +include(JsonCpp) include_directories (${CMAKE_CURRENT_SOURCE_DIR}) @@ -23,6 +24,7 @@ include_directories( ${LLMATH_INCLUDE_DIRS} ${LLMESSAGE_INCLUDE_DIRS} ${LLVFS_INCLUDE_DIRS} + ${JSONCPP_INCLUDE_DIR} ) set(llmessage_SOURCE_FILES @@ -47,9 +49,9 @@ set(llmessage_SOURCE_FILES llhost.cpp llhttpassetstorage.cpp llhttpclient.cpp - llhttpclientadapter.cpp llhttpconstants.cpp llhttpnode.cpp + llhttpsdhandler.cpp llhttpsender.cpp llinstantmessage.cpp lliobuffer.cpp @@ -74,7 +76,6 @@ set(llmessage_SOURCE_FILES llpumpio.cpp llsdappservices.cpp llsdhttpserver.cpp - llsdmessage.cpp llsdmessagebuilder.cpp llsdmessagereader.cpp llsdrpcclient.cpp @@ -142,10 +143,10 @@ set(llmessage_HEADER_FILES llhttpassetstorage.h llhttpclient.h llhttpclientinterface.h - llhttpclientadapter.h llhttpconstants.h llhttpnode.h llhttpnodeadapter.h + llhttpsdhandler.h llhttpsender.h llinstantmessage.h llinvite.h @@ -176,7 +177,6 @@ set(llmessage_HEADER_FILES llregionhandle.h llsdappservices.h llsdhttpserver.h - llsdmessage.h llsdmessagebuilder.h llsdmessagereader.h llsdrpcclient.h @@ -226,9 +226,10 @@ target_link_libraries( llmessage ${CURL_LIBRARIES} ${LLCOMMON_LIBRARIES} - ${LLVFS_LIBRARES} + ${LLVFS_LIBRARIES} ${LLMATH_LIBRARIES} ${CARES_LIBRARIES} + ${JSONCPP_LIBRARIES} ${OPENSSL_LIBRARIES} ${CRYPTO_LIBRARIES} ${XMLRPCEPI_LIBRARIES} @@ -243,36 +244,25 @@ if (LL_TESTS) ) LL_ADD_PROJECT_UNIT_TESTS(llmessage "${llmessage_TEST_SOURCE_FILES}") + # set(TEST_DEBUG on) + set(test_libs - ${CURL_LIBRARIES} - ${LLMESSAGE_LIBRARIES} ${WINDOWS_LIBRARIES} ${LLVFS_LIBRARIES} ${LLMATH_LIBRARIES} + ${CURL_LIBRARIES} ${LLCOMMON_LIBRARIES} - ${GOOGLEMOCK_LIBRARIES} - ) - - LL_ADD_INTEGRATION_TEST( - llsdmessage - "llsdmessage.cpp" - "${test_libs}" - ${PYTHON_EXECUTABLE} - "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_llsdmessage_peer.py" - ) - - LL_ADD_INTEGRATION_TEST( - llhttpclient - "llhttpclient.cpp" - "${test_libs}" - ${PYTHON_EXECUTABLE} - "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_llsdmessage_peer.py" + ${LLMESSAGE_LIBRARIES} + ${LLCOREHTTP_LIBRARIES} + ${JSONCPP_LIBRARIES} + ${BOOST_COROUTINE_LIBRARY} + ${BOOST_CONTEXT_LIBRARY} + ${GOOGLEMOCK_LIBRARIES} ) - LL_ADD_INTEGRATION_TEST(llavatarnamecache "" "${test_libs}") + #LL_ADD_INTEGRATION_TEST(llavatarnamecache "" "${test_libs}") LL_ADD_INTEGRATION_TEST(llhost "" "${test_libs}") - LL_ADD_INTEGRATION_TEST(llhttpclientadapter "" "${test_libs}") LL_ADD_INTEGRATION_TEST(llpartdata "" "${test_libs}") LL_ADD_INTEGRATION_TEST(llxfer_file "" "${test_libs}") endif (LL_TESTS) diff --git a/indra/llmessage/llavatarnamecache.cpp b/indra/llmessage/llavatarnamecache.cpp index 549708097a..d262862c80 100755 --- a/indra/llmessage/llavatarnamecache.cpp +++ b/indra/llmessage/llavatarnamecache.cpp @@ -30,12 +30,20 @@ #include "llcachename.h" // we wrap this system #include "llframetimer.h" -#include "llhttpclient.h" #include "llsd.h" #include "llsdserialize.h" - +#include "httpresponse.h" +#include "llhttpsdhandler.h" #include <boost/tokenizer.hpp> +#include "httpcommon.h" +#include "httprequest.h" +#include "httpheaders.h" +#include "httpoptions.h" +#include "llcoros.h" +#include "lleventcoro.h" +#include "llcorehttputil.h" + #include <map> #include <set> @@ -90,6 +98,12 @@ namespace LLAvatarNameCache // Time-to-live for a temp cache entry. const F64 TEMP_CACHE_ENTRY_LIFETIME = 60.0; + LLCore::HttpRequest::ptr_t sHttpRequest; + LLCore::HttpHeaders::ptr_t sHttpHeaders; + LLCore::HttpOptions::ptr_t sHttpOptions; + LLCore::HttpRequest::policy_t sHttpPolicy; + LLCore::HttpRequest::priority_t sHttpPriority; + //----------------------------------------------------------------------- // Internal methods //----------------------------------------------------------------------- @@ -121,7 +135,12 @@ namespace LLAvatarNameCache // Erase expired names from cache void eraseUnrefreshed(); - bool expirationFromCacheControl(const LLSD& headers, F64 *expires); + bool expirationFromCacheControl(const LLSD& headers, F64 *expires); + + // This is a coroutine. + void requestAvatarNameCache_(std::string url, std::vector<LLUUID> agentIds); + + void handleAvNameCacheSuccess(const LLSD &data, const LLSD &httpResult); } /* Sample response: @@ -163,94 +182,117 @@ namespace LLAvatarNameCache </llsd> */ -class LLAvatarNameResponder : public LLHTTPClient::Responder +// Coroutine for sending and processing avatar name cache requests. +// Do not call directly. See documentation in lleventcoro.h and llcoro.h for +// further explanation. +void LLAvatarNameCache::requestAvatarNameCache_(std::string url, std::vector<LLUUID> agentIds) { - LOG_CLASS(LLAvatarNameResponder); -private: - // need to store agent ids that are part of this request in case of - // an error, so we can flag them as unavailable - std::vector<LLUUID> mAgentIDs; - -public: - LLAvatarNameResponder(const std::vector<LLUUID>& agent_ids) - : mAgentIDs(agent_ids) - { } - -protected: - /*virtual*/ void httpSuccess() - { - const LLSD& content = getContent(); - if (!content.isMap()) - { - failureResult(HTTP_INTERNAL_ERROR, "Malformed response contents", content); - return; - } - // Pull expiration out of headers if available - F64 expires = LLAvatarNameCache::nameExpirationFromHeaders(getResponseHeaders()); - F64 now = LLFrameTimer::getTotalSeconds(); + LL_DEBUGS("AvNameCache") << "Entering coroutine " << LLCoros::instance().getName() + << " with url '" << url << "', requesting " << agentIds.size() << " Agent Ids" << LL_ENDL; - const LLSD& agents = content["agents"]; - LLSD::array_const_iterator it = agents.beginArray(); - for ( ; it != agents.endArray(); ++it) - { - const LLSD& row = *it; - LLUUID agent_id = row["id"].asUUID(); + try + { + bool success = true; - LLAvatarName av_name; - av_name.fromLLSD(row); + LLCoreHttpUtil::HttpCoroutineAdapter httpAdapter("NameCache", LLAvatarNameCache::sHttpPolicy); + LLSD results = httpAdapter.getAndYield(sHttpRequest, url); + LLSD httpResults; - // Use expiration time from header - av_name.mExpires = expires; + LL_DEBUGS() << results << LL_ENDL; - LL_DEBUGS("AvNameCache") << "LLAvatarNameResponder::result for " << agent_id << LL_ENDL; - av_name.dump(); - - // cache it and fire signals - LLAvatarNameCache::processName(agent_id, av_name); - } + if (!results.isMap()) + { + LL_WARNS("AvNameCache") << " Invalid result returned from LLCoreHttpUtil::HttpCoroHandler." << LL_ENDL; + success = false; + } + else + { + httpResults = results["http_result"]; + success = httpResults["success"].asBoolean(); + if (!success) + { + LL_WARNS("AvNameCache") << "Error result from LLCoreHttpUtil::HttpCoroHandler. Code " + << httpResults["status"] << ": '" << httpResults["message"] << "'" << LL_ENDL; + } + } - // Same logic as error response case - const LLSD& unresolved_agents = content["bad_ids"]; - S32 num_unresolved = unresolved_agents.size(); - if (num_unresolved > 0) - { - LL_WARNS("AvNameCache") << "LLAvatarNameResponder::result " << num_unresolved << " unresolved ids; " - << "expires in " << expires - now << " seconds" - << LL_ENDL; - it = unresolved_agents.beginArray(); - for ( ; it != unresolved_agents.endArray(); ++it) - { - const LLUUID& agent_id = *it; + if (!success) + { // on any sort of failure add dummy records for any agent IDs + // in this request that we do not have cached already + std::vector<LLUUID>::const_iterator it = agentIds.begin(); + for ( ; it != agentIds.end(); ++it) + { + const LLUUID& agent_id = *it; + LLAvatarNameCache::handleAgentError(agent_id); + } + return; + } - LL_WARNS("AvNameCache") << "LLAvatarNameResponder::result " - << "failed id " << agent_id - << LL_ENDL; + LLAvatarNameCache::handleAvNameCacheSuccess(results, httpResults); - LLAvatarNameCache::handleAgentError(agent_id); - } - } - LL_DEBUGS("AvNameCache") << "LLAvatarNameResponder::result " - << LLAvatarNameCache::sCache.size() << " cached names" - << LL_ENDL; } + catch (std::exception e) + { + LL_WARNS() << "Caught exception '" << e.what() << "'" << LL_ENDL; + } + catch (...) + { + LL_WARNS() << "Caught unknown exception." << LL_ENDL; + } +} - /*virtual*/ void httpFailure() - { - // If there's an error, it might be caused by PeopleApi, - // or when loading textures on startup and using a very slow - // network, this query may time out. - // What we should do depends on whether or not we have a cached name - LL_WARNS("AvNameCache") << dumpResponse() << LL_ENDL; - - // Add dummy records for any agent IDs in this request that we do not have cached already - std::vector<LLUUID>::const_iterator it = mAgentIDs.begin(); - for ( ; it != mAgentIDs.end(); ++it) - { - const LLUUID& agent_id = *it; - LLAvatarNameCache::handleAgentError(agent_id); - } - } -}; +void LLAvatarNameCache::handleAvNameCacheSuccess(const LLSD &data, const LLSD &httpResult) +{ + + LLSD headers = httpResult["headers"]; + // Pull expiration out of headers if available + F64 expires = LLAvatarNameCache::nameExpirationFromHeaders(headers); + F64 now = LLFrameTimer::getTotalSeconds(); + + const LLSD& agents = data["agents"]; + LLSD::array_const_iterator it = agents.beginArray(); + for (; it != agents.endArray(); ++it) + { + const LLSD& row = *it; + LLUUID agent_id = row["id"].asUUID(); + + LLAvatarName av_name; + av_name.fromLLSD(row); + + // Use expiration time from header + av_name.mExpires = expires; + + LL_DEBUGS("AvNameCache") << "LLAvatarNameResponder::result for " << agent_id << LL_ENDL; + av_name.dump(); + + // cache it and fire signals + LLAvatarNameCache::processName(agent_id, av_name); + } + + // Same logic as error response case + const LLSD& unresolved_agents = data["bad_ids"]; + S32 num_unresolved = unresolved_agents.size(); + if (num_unresolved > 0) + { + LL_WARNS("AvNameCache") << "LLAvatarNameResponder::result " << num_unresolved << " unresolved ids; " + << "expires in " << expires - now << " seconds" + << LL_ENDL; + it = unresolved_agents.beginArray(); + for (; it != unresolved_agents.endArray(); ++it) + { + const LLUUID& agent_id = *it; + + LL_WARNS("AvNameCache") << "LLAvatarNameResponder::result " + << "failed id " << agent_id + << LL_ENDL; + + LLAvatarNameCache::handleAgentError(agent_id); + } + } + LL_DEBUGS("AvNameCache") << "LLAvatarNameResponder::result " + << LLAvatarNameCache::sCache.size() << " cached names" + << LL_ENDL; +} // Provide some fallback for agents that return errors void LLAvatarNameCache::handleAgentError(const LLUUID& agent_id) @@ -353,10 +395,15 @@ void LLAvatarNameCache::requestNamesViaCapability() } } - if (!url.empty()) - { - LL_INFOS("AvNameCache") << "LLAvatarNameCache::requestNamesViaCapability getting " << ids << " ids" << LL_ENDL; - LLHTTPClient::get(url, new LLAvatarNameResponder(agent_ids)); + if (!url.empty()) + { + LL_DEBUGS("AvNameCache") << "LLAvatarNameCache::requestNamesViaCapability requested " << ids << " ids" << LL_ENDL; + + std::string coroname = + LLCoros::instance().launch("LLAvatarNameCache::requestAvatarNameCache_", + boost::bind(&LLAvatarNameCache::requestAvatarNameCache_, url, agent_ids)); + LL_DEBUGS("AvNameCache") << coroname << " with url '" << url << "', agent_ids.size()=" << agent_ids.size() << LL_ENDL; + } } @@ -419,11 +466,20 @@ void LLAvatarNameCache::initClass(bool running, bool usePeopleAPI) { sRunning = running; sUsePeopleAPI = usePeopleAPI; + + sHttpRequest = LLCore::HttpRequest::ptr_t(new LLCore::HttpRequest()); + sHttpHeaders = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders()); + sHttpOptions = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()); + sHttpPolicy = LLCore::HttpRequest::DEFAULT_POLICY_ID; + sHttpPriority = 0; } void LLAvatarNameCache::cleanupClass() { - sCache.clear(); + sHttpRequest.reset(); + sHttpHeaders.reset(); + sHttpOptions.reset(); + sCache.clear(); } bool LLAvatarNameCache::importFile(std::istream& istr) @@ -698,6 +754,50 @@ void LLAvatarNameCache::insert(const LLUUID& agent_id, const LLAvatarName& av_na sCache[agent_id] = av_name; } +#if 0 +F64 LLAvatarNameCache::nameExpirationFromHeaders(LLCore::HttpHeaders *headers) +{ + F64 expires = 0.0; + if (expirationFromCacheControl(headers, &expires)) + { + return expires; + } + else + { + // With no expiration info, default to an hour + const F64 DEFAULT_EXPIRES = 60.0 * 60.0; + F64 now = LLFrameTimer::getTotalSeconds(); + return now + DEFAULT_EXPIRES; + } +} + +bool LLAvatarNameCache::expirationFromCacheControl(LLCore::HttpHeaders *headers, F64 *expires) +{ + bool fromCacheControl = false; + F64 now = LLFrameTimer::getTotalSeconds(); + + // Allow the header to override the default + const std::string *cache_control; + + cache_control = headers->find(HTTP_IN_HEADER_CACHE_CONTROL); + + if (cache_control && !cache_control->empty()) + { + S32 max_age = 0; + if (max_age_from_cache_control(*cache_control, &max_age)) + { + *expires = now + (F64)max_age; + fromCacheControl = true; + } + } + LL_DEBUGS("AvNameCache") + << ( fromCacheControl ? "expires based on cache control " : "default expiration " ) + << "in " << *expires - now << " seconds" + << LL_ENDL; + + return fromCacheControl; +} +#else F64 LLAvatarNameCache::nameExpirationFromHeaders(const LLSD& headers) { F64 expires = 0.0; @@ -742,7 +842,7 @@ bool LLAvatarNameCache::expirationFromCacheControl(const LLSD& headers, F64 *exp return fromCacheControl; } - +#endif void LLAvatarNameCache::addUseDisplayNamesCallback(const use_display_name_signal_t::slot_type& cb) { diff --git a/indra/llmessage/llavatarnamecache.h b/indra/llmessage/llavatarnamecache.h index 5a10053a69..bd2715e956 100755 --- a/indra/llmessage/llavatarnamecache.h +++ b/indra/llmessage/llavatarnamecache.h @@ -29,7 +29,6 @@ #define LLAVATARNAMECACHE_H #include "llavatarname.h" // for convenience - #include <boost/signals2.hpp> class LLSD; @@ -49,7 +48,7 @@ namespace LLAvatarNameCache bool importFile(std::istream& istr); void exportFile(std::ostream& ostr); - // On the viewer, usually a simulator capabilitity. + // On the viewer, usually a simulator capabilities. // If empty, name cache will fall back to using legacy name lookup system. void setNameLookupURL(const std::string& name_lookup_url); @@ -90,7 +89,7 @@ namespace LLAvatarNameCache // Compute name expiration time from HTTP Cache-Control header, // or return default value, in seconds from epoch. - F64 nameExpirationFromHeaders(const LLSD& headers); + F64 nameExpirationFromHeaders(const LLSD& headers); void addUseDisplayNamesCallback(const use_display_name_signal_t::slot_type& cb); } diff --git a/indra/llmessage/llcorehttputil.cpp b/indra/llmessage/llcorehttputil.cpp index ee80b0fd94..a6ed287aeb 100644 --- a/indra/llmessage/llcorehttputil.cpp +++ b/indra/llmessage/llcorehttputil.cpp @@ -28,112 +28,985 @@ #include "linden_common.h" #include <sstream> - +#include <algorithm> +#include <iterator> #include "llcorehttputil.h" +#include "llhttpconstants.h" +#include "llsd.h" +#include "llsdjson.h" #include "llsdserialize.h" +#include "reader.h" +#include "llvfile.h" +#include "message.h" // for getting the port using namespace LLCore; namespace LLCoreHttpUtil { +void logMessageSuccess(std::string logAuth, std::string url, std::string message) +{ + LL_INFOS() << logAuth << " Success '" << message << "' for " << url << LL_ENDL; +} + +void logMessageFail(std::string logAuth, std::string url, std::string message) +{ + LL_WARNS() << logAuth << " Failure '" << message << "' for " << url << LL_ENDL; +} + +//========================================================================= +/// The HttpRequestPumper is a utility class. When constructed it will poll the +/// supplied HttpRequest once per frame until it is destroyed. +/// +class HttpRequestPumper +{ +public: + HttpRequestPumper(const LLCore::HttpRequest::ptr_t &request); + ~HttpRequestPumper(); + +private: + bool pollRequest(const LLSD&); + LLTempBoundListener mBoundListener; + LLCore::HttpRequest::ptr_t mHttpRequest; +}; + + +//========================================================================= // *TODO: Currently converts only from XML content. A mode // to convert using fromBinary() might be useful as well. Mesh // headers could use it. bool responseToLLSD(HttpResponse * response, bool log, LLSD & out_llsd) { - // Convert response to LLSD - BufferArray * body(response->getBody()); - if (! body || ! body->size()) - { - return false; - } + // Convert response to LLSD + BufferArray * body(response->getBody()); + if (!body || !body->size()) + { + return false; + } - LLCore::BufferArrayStream bas(body); - LLSD body_llsd; - S32 parse_status(LLSDSerialize::fromXML(body_llsd, bas, log)); - if (LLSDParser::PARSE_FAILURE == parse_status){ - return false; - } - out_llsd = body_llsd; - return true; + LLCore::BufferArrayStream bas(body); + LLSD body_llsd; + S32 parse_status(LLSDSerialize::fromXML(body_llsd, bas, log)); + if (LLSDParser::PARSE_FAILURE == parse_status){ + return false; + } + out_llsd = body_llsd; + return true; } HttpHandle requestPostWithLLSD(HttpRequest * request, - HttpRequest::policy_t policy_id, - HttpRequest::priority_t priority, - const std::string & url, - const LLSD & body, - HttpOptions * options, - HttpHeaders * headers, - HttpHandler * handler) + HttpRequest::policy_t policy_id, + HttpRequest::priority_t priority, + const std::string & url, + const LLSD & body, + const HttpOptions::ptr_t &options, + const HttpHeaders::ptr_t &headers, + HttpHandler * handler) { - HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); + HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); - BufferArray * ba = new BufferArray(); - BufferArrayStream bas(ba); - LLSDSerialize::toXML(body, bas); + BufferArray * ba = new BufferArray(); + BufferArrayStream bas(ba); + LLSDSerialize::toXML(body, bas); - handle = request->requestPost(policy_id, - priority, - url, - ba, - options, - headers, - handler); - ba->release(); - return handle; + handle = request->requestPost(policy_id, + priority, + url, + ba, + options, + headers, + handler); + ba->release(); + return handle; +} + + +HttpHandle requestPutWithLLSD(HttpRequest * request, + HttpRequest::policy_t policy_id, + HttpRequest::priority_t priority, + const std::string & url, + const LLSD & body, + const HttpOptions::ptr_t &options, + const HttpHeaders::ptr_t &headers, + HttpHandler * handler) +{ + HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); + + BufferArray * ba = new BufferArray(); + BufferArrayStream bas(ba); + LLSDSerialize::toXML(body, bas); + + handle = request->requestPut(policy_id, + priority, + url, + ba, + options, + headers, + handler); + ba->release(); + return handle; +} + +HttpHandle requestPatchWithLLSD(HttpRequest * request, + HttpRequest::policy_t policy_id, + HttpRequest::priority_t priority, + const std::string & url, + const LLSD & body, + const HttpOptions::ptr_t &options, + const HttpHeaders::ptr_t &headers, + HttpHandler * handler) +{ + HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); + + BufferArray * ba = new BufferArray(); + BufferArrayStream bas(ba); + LLSDSerialize::toXML(body, bas); + + handle = request->requestPatch(policy_id, + priority, + url, + ba, + options, + headers, + handler); + ba->release(); + return handle; } std::string responseToString(LLCore::HttpResponse * response) { - static const std::string empty("[Empty]"); - - if (! response) - { - return empty; - } - - BufferArray * body(response->getBody()); - if (! body || ! body->size()) - { - return empty; - } - - // Attempt to parse as LLSD regardless of content-type - LLSD body_llsd; - if (responseToLLSD(response, false, body_llsd)) - { - std::ostringstream tmp; - - LLSDSerialize::toPrettyNotation(body_llsd, tmp); - std::size_t temp_len(tmp.tellp()); - - if (temp_len) - { - return tmp.str().substr(0, std::min(temp_len, std::size_t(1024))); - } - } - else - { - // *TODO: More elaborate forms based on Content-Type as needed. - char content[1024]; - - size_t len(body->read(0, content, sizeof(content))); - if (len) - { - return std::string(content, 0, len); - } - } - - // Default - return empty; + static const std::string empty("[Empty]"); + + if (!response) + { + return empty; + } + + BufferArray * body(response->getBody()); + if (!body || !body->size()) + { + return empty; + } + + // Attempt to parse as LLSD regardless of content-type + LLSD body_llsd; + if (responseToLLSD(response, false, body_llsd)) + { + std::ostringstream tmp; + + LLSDSerialize::toPrettyNotation(body_llsd, tmp); + std::size_t temp_len(tmp.tellp()); + + if (temp_len) + { + return tmp.str().substr(0, std::min(temp_len, std::size_t(1024))); + } + } + else + { + // *TODO: More elaborate forms based on Content-Type as needed. + char content[1024]; + + size_t len(body->read(0, content, sizeof(content))); + if (len) + { + return std::string(content, 0, len); + } + } + + // Default + return empty; +} + +//======================================================================== + +HttpCoroHandler::HttpCoroHandler(LLEventStream &reply) : + mReplyPump(reply) +{ +} + +void HttpCoroHandler::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response) +{ + LLSD result; + + LLCore::HttpStatus status = response->getStatus(); + + if (status == LLCore::HttpStatus(LLCore::HttpStatus::LLCORE, LLCore::HE_HANDLE_NOT_FOUND)) + { // A response came in for a canceled request and we have not processed the + // cancel yet. Patience! + return; + } + + if (!status) + { + result = LLSD::emptyMap(); + LL_WARNS() + << "\n--------------------------------------------------------------------------\n" + << " Error[" << status.getType() << "] cannot access url '" << response->getRequestURL() + << "' because " << status.toString() + << "\n--------------------------------------------------------------------------" + << LL_ENDL; + } + else + { + result = this->handleSuccess(response, status); + } + + buildStatusEntry(response, status, result); + +#if 1 + // commenting out, but keeping since this can be useful for debugging + if (!status) + { + LLSD &httpStatus = result[HttpCoroutineAdapter::HTTP_RESULTS]; + + LLCore::BufferArray *body = response->getBody(); + LLCore::BufferArrayStream bas(body); + LLSD::String bodyData; + bodyData.reserve(response->getBodySize()); + bas >> std::noskipws; + bodyData.assign(std::istream_iterator<U8>(bas), std::istream_iterator<U8>()); + httpStatus["error_body"] = LLSD(bodyData); + + LL_WARNS() << "Returned body=" << std::endl << httpStatus["error_body"].asString() << LL_ENDL; + } +#endif + + mReplyPump.post(result); +} + +void HttpCoroHandler::buildStatusEntry(LLCore::HttpResponse *response, LLCore::HttpStatus status, LLSD &result) +{ + LLSD httpresults = LLSD::emptyMap(); + + writeStatusCodes(status, response->getRequestURL(), httpresults); + + LLSD httpHeaders = LLSD::emptyMap(); + LLCore::HttpHeaders::ptr_t hdrs = response->getHeaders(); + + if (hdrs) + { + for (LLCore::HttpHeaders::iterator it = hdrs->begin(); it != hdrs->end(); ++it) + { + if (!(*it).second.empty()) + { + httpHeaders[(*it).first] = (*it).second; + } + else + { + httpHeaders[(*it).first] = static_cast<LLSD::Boolean>(true); + } + } + } + + httpresults[HttpCoroutineAdapter::HTTP_RESULTS_HEADERS] = httpHeaders; + result[HttpCoroutineAdapter::HTTP_RESULTS] = httpresults; +} + +void HttpCoroHandler::writeStatusCodes(LLCore::HttpStatus status, const std::string &url, LLSD &result) +{ + result[HttpCoroutineAdapter::HTTP_RESULTS_SUCCESS] = static_cast<LLSD::Boolean>(status); + result[HttpCoroutineAdapter::HTTP_RESULTS_TYPE] = static_cast<LLSD::Integer>(status.getType()); + result[HttpCoroutineAdapter::HTTP_RESULTS_STATUS] = static_cast<LLSD::Integer>(status.getStatus()); + result[HttpCoroutineAdapter::HTTP_RESULTS_MESSAGE] = static_cast<LLSD::String>(status.getMessage()); + result[HttpCoroutineAdapter::HTTP_RESULTS_URL] = static_cast<LLSD::String>(url); + +} + +//========================================================================= +/// The HttpCoroLLSDHandler is a specialization of the LLCore::HttpHandler for +/// interacting with coroutines. When the request is completed the response +/// will be posted onto the supplied Event Pump. +/// +/// If the LLSD retrieved from through the HTTP connection is not in the form +/// of a LLSD::map it will be returned as in an llsd["content"] element. +/// +/// The LLSD posted back to the coroutine will have the following additions: +/// llsd["http_result"] -+- ["message"] - An error message returned from the HTTP status +/// +- ["status"] - The status code associated with the HTTP call +/// +- ["success"] - Success of failure of the HTTP call and LLSD parsing. +/// +- ["type"] - The LLCore::HttpStatus type associted with the HTTP call +/// +- ["url"] - The URL used to make the call. +/// +- ["headers"] - A map of name name value pairs with the HTTP headers. +/// +class HttpCoroLLSDHandler : public HttpCoroHandler +{ +public: + HttpCoroLLSDHandler(LLEventStream &reply); + +protected: + virtual LLSD handleSuccess(LLCore::HttpResponse * response, LLCore::HttpStatus &status); +}; + +//------------------------------------------------------------------------- +HttpCoroLLSDHandler::HttpCoroLLSDHandler(LLEventStream &reply): + HttpCoroHandler(reply) +{ +} + + +LLSD HttpCoroLLSDHandler::handleSuccess(LLCore::HttpResponse * response, LLCore::HttpStatus &status) +{ + LLSD result; + + const bool emit_parse_errors = false; + + bool parsed = !((response->getBodySize() == 0) || + !LLCoreHttpUtil::responseToLLSD(response, emit_parse_errors, result)); + + if (!parsed) + { + // Only emit a warning if we failed to parse when 'content-type' == 'application/llsd+xml' + LLCore::HttpHeaders::ptr_t headers(response->getHeaders()); + const std::string *contentType = (headers) ? headers->find(HTTP_IN_HEADER_CONTENT_TYPE) : NULL; + + if (contentType && (HTTP_CONTENT_LLSD_XML == *contentType)) + { + std::string thebody = LLCoreHttpUtil::responseToString(response); + LL_WARNS() << "Failed to deserialize . " << response->getRequestURL() << " [status:" << response->getStatus().toString() << "] " + << " body: " << thebody << LL_ENDL; + + // Replace the status with a new one indicating the failure. + status = LLCore::HttpStatus(499, "Failed to deserialize LLSD."); + } + } + + if (result.isUndefined()) + { // If we've gotten to this point and the result LLSD is still undefined + // either there was an issue deserializing the body or the response was + // blank. Create an empty map to hold the result either way. + result = LLSD::emptyMap(); + } + else if (!result.isMap()) + { // The results are not themselves a map. Move them down so that + // this method can return a map to the caller. + // *TODO: Should it always do this? + LLSD newResult = LLSD::emptyMap(); + newResult[HttpCoroutineAdapter::HTTP_RESULTS_CONTENT] = result; + result = newResult; + } + + return result; +} + +//======================================================================== +/// The HttpCoroRawHandler is a specialization of the LLCore::HttpHandler for +/// interacting with coroutines. +/// +/// In addition to the normal "http_results" the returned LLSD will contain +/// an entry keyed with "raw" containing the unprocessed results of the HTTP +/// call. +/// +class HttpCoroRawHandler : public HttpCoroHandler +{ +public: + HttpCoroRawHandler(LLEventStream &reply); + + virtual LLSD handleSuccess(LLCore::HttpResponse * response, LLCore::HttpStatus &status); +}; + +//------------------------------------------------------------------------- +HttpCoroRawHandler::HttpCoroRawHandler(LLEventStream &reply): + HttpCoroHandler(reply) +{ } +LLSD HttpCoroRawHandler::handleSuccess(LLCore::HttpResponse * response, LLCore::HttpStatus &status) +{ + LLSD result = LLSD::emptyMap(); + + BufferArray * body(response->getBody()); + if (!body || !body->size()) + { + return result; + } + + size_t size = body->size(); + + LLCore::BufferArrayStream bas(body); + +#if 1 + // This is the slower implementation. It is safe vis-a-vi the const_cast<> and modification + // of a LLSD managed array but contains an extra (potentially large) copy. + // + // *TODO: https://jira.secondlife.com/browse/MAINT-5221 + + LLSD::Binary data; + data.reserve(size); + bas >> std::noskipws; + data.assign(std::istream_iterator<U8>(bas), std::istream_iterator<U8>()); + + result[HttpCoroutineAdapter::HTTP_RESULTS_RAW] = data; + +#else + // This is disabled because it's dangerous. See the other case for an + // alternate implementation. + // We create a new LLSD::Binary object and assign it to the result map. + // The LLSD has created it's own copy so we retrieve it asBinary and const cast + // the reference so that we can modify it. + // *TODO: This is potentially dangerous... but I am trying to avoid a potentially + // large copy. + result[HttpCoroutineAdapter::HTTP_RESULTS_RAW] = LLSD::Binary(); + LLSD::Binary &data = const_cast<LLSD::Binary &>( result[HttpCoroutineAdapter::HTTP_RESULTS_RAW].asBinary() ); + + data.reserve(size); + bas >> std::noskipws; + data.assign(std::istream_iterator<U8>(bas), std::istream_iterator<U8>()); +#endif + + return result; +} + +//======================================================================== +/// The HttpCoroJSONHandler is a specialization of the LLCore::HttpHandler for +/// interacting with coroutines. +/// +/// In addition to the normal "http_results" the returned LLSD will contain +/// JSON entries will be converted into an LLSD map. All results are considered +/// strings +/// +class HttpCoroJSONHandler : public HttpCoroHandler +{ +public: + HttpCoroJSONHandler(LLEventStream &reply); + + virtual LLSD handleSuccess(LLCore::HttpResponse * response, LLCore::HttpStatus &status); +}; + +//------------------------------------------------------------------------- +HttpCoroJSONHandler::HttpCoroJSONHandler(LLEventStream &reply) : + HttpCoroHandler(reply) +{ +} + +LLSD HttpCoroJSONHandler::handleSuccess(LLCore::HttpResponse * response, LLCore::HttpStatus &status) +{ + LLSD result = LLSD::emptyMap(); + + BufferArray * body(response->getBody()); + if (!body || !body->size()) + { + return result; + } + + LLCore::BufferArrayStream bas(body); + Json::Value jsonRoot; + + try + { + bas >> jsonRoot; + } + catch (std::runtime_error e) + { // deserialization failed. Record the reason and pass back an empty map for markup. + status = LLCore::HttpStatus(499, std::string(e.what())); + return result; + } + + // Convert the JSON structure to LLSD + result = LlsdFromJson(jsonRoot); + + return result; +} + + +//======================================================================== +HttpRequestPumper::HttpRequestPumper(const LLCore::HttpRequest::ptr_t &request) : + mHttpRequest(request) +{ + mBoundListener = LLEventPumps::instance().obtain("mainloop"). + listen(LLEventPump::inventName(), boost::bind(&HttpRequestPumper::pollRequest, this, _1)); +} + +HttpRequestPumper::~HttpRequestPumper() +{ + if (mBoundListener.connected()) + { + mBoundListener.disconnect(); + } +} + +bool HttpRequestPumper::pollRequest(const LLSD&) +{ + if (mHttpRequest->getStatus() != HttpStatus(HttpStatus::LLCORE, HE_OP_CANCELED)) + { + mHttpRequest->update(0L); + } + return false; +} + +//======================================================================== +const std::string HttpCoroutineAdapter::HTTP_RESULTS("http_result"); +const std::string HttpCoroutineAdapter::HTTP_RESULTS_SUCCESS("success"); +const std::string HttpCoroutineAdapter::HTTP_RESULTS_TYPE("type"); +const std::string HttpCoroutineAdapter::HTTP_RESULTS_STATUS("status"); +const std::string HttpCoroutineAdapter::HTTP_RESULTS_MESSAGE("message"); +const std::string HttpCoroutineAdapter::HTTP_RESULTS_URL("url"); +const std::string HttpCoroutineAdapter::HTTP_RESULTS_HEADERS("headers"); +const std::string HttpCoroutineAdapter::HTTP_RESULTS_CONTENT("content"); +const std::string HttpCoroutineAdapter::HTTP_RESULTS_RAW("raw"); + +HttpCoroutineAdapter::HttpCoroutineAdapter(const std::string &name, + LLCore::HttpRequest::policy_t policyId, LLCore::HttpRequest::priority_t priority) : + mAdapterName(name), + mPolicyId(policyId), + mPriority(priority), + mYieldingHandle(LLCORE_HTTP_HANDLE_INVALID), + mWeakRequest(), + mWeakHandler() +{ +} + +HttpCoroutineAdapter::~HttpCoroutineAdapter() +{ + cancelYieldingOperation(); +} + +LLSD HttpCoroutineAdapter::postAndYield(LLCore::HttpRequest::ptr_t request, + const std::string & url, const LLSD & body, + LLCore::HttpOptions::ptr_t options, LLCore::HttpHeaders::ptr_t headers) +{ + LLEventStream replyPump(mAdapterName, true); + HttpCoroHandler::ptr_t httpHandler = HttpCoroHandler::ptr_t(new HttpCoroLLSDHandler(replyPump)); + + return postAndYield_(request, url, body, options, headers, httpHandler); +} + +LLSD HttpCoroutineAdapter::postAndYield_(LLCore::HttpRequest::ptr_t &request, + const std::string & url, const LLSD & body, + LLCore::HttpOptions::ptr_t &options, LLCore::HttpHeaders::ptr_t &headers, + HttpCoroHandler::ptr_t &handler) +{ + HttpRequestPumper pumper(request); + + checkDefaultHeaders(headers); + + // The HTTPCoroHandler does not self delete, so retrieval of a the contained + // pointer from the smart pointer is safe in this case. + LLCore::HttpHandle hhandle = requestPostWithLLSD(request, + mPolicyId, mPriority, url, body, options, headers, + handler.get()); + + if (hhandle == LLCORE_HTTP_HANDLE_INVALID) + { + return HttpCoroutineAdapter::buildImmediateErrorResult(request, url); + } + + saveState(hhandle, request, handler); + LLSD results = llcoro::waitForEventOn(handler->getReplyPump()); + cleanState(); + + return results; +} + +LLSD HttpCoroutineAdapter::postAndYield(LLCore::HttpRequest::ptr_t request, + const std::string & url, LLCore::BufferArray::ptr_t rawbody, + LLCore::HttpOptions::ptr_t options, LLCore::HttpHeaders::ptr_t headers) +{ + LLEventStream replyPump(mAdapterName, true); + HttpCoroHandler::ptr_t httpHandler = HttpCoroHandler::ptr_t(new HttpCoroLLSDHandler(replyPump)); + + return postAndYield_(request, url, rawbody, options, headers, httpHandler); +} + +LLSD HttpCoroutineAdapter::postRawAndYield(LLCore::HttpRequest::ptr_t request, + const std::string & url, LLCore::BufferArray::ptr_t rawbody, + LLCore::HttpOptions::ptr_t options, LLCore::HttpHeaders::ptr_t headers) +{ + LLEventStream replyPump(mAdapterName, true); + HttpCoroHandler::ptr_t httpHandler = HttpCoroHandler::ptr_t(new HttpCoroRawHandler(replyPump)); + + return postAndYield_(request, url, rawbody, options, headers, httpHandler); +} + +// *TODO: This functionality could be moved into the LLCore::Http library itself +// by having the CURL layer read the file directly. +LLSD HttpCoroutineAdapter::postFileAndYield(LLCore::HttpRequest::ptr_t request, + const std::string & url, std::string fileName, + LLCore::HttpOptions::ptr_t options, LLCore::HttpHeaders::ptr_t headers) +{ + LLCore::BufferArray::ptr_t fileData(new LLCore::BufferArray); + + // scoping for our streams so that they go away when we no longer need them. + { + LLCore::BufferArrayStream outs(fileData.get()); + llifstream ins(fileName.c_str(), std::iostream::binary | std::iostream::out); + + if (ins.is_open()) + { + + ins.seekg(0, std::ios::beg); + ins >> std::noskipws; + + std::copy(std::istream_iterator<U8>(ins), std::istream_iterator<U8>(), + std::ostream_iterator<U8>(outs)); + + ins.close(); + } + } + + return postAndYield(request, url, fileData, options, headers); +} + +// *TODO: This functionality could be moved into the LLCore::Http library itself +// by having the CURL layer read the file directly. +LLSD HttpCoroutineAdapter::postFileAndYield(LLCore::HttpRequest::ptr_t request, + const std::string & url, LLUUID assetId, LLAssetType::EType assetType, + LLCore::HttpOptions::ptr_t options, LLCore::HttpHeaders::ptr_t headers) +{ + LLCore::BufferArray::ptr_t fileData(new LLCore::BufferArray); + + // scoping for our streams so that they go away when we no longer need them. + { + LLCore::BufferArrayStream outs(fileData.get()); + LLVFile vfile(gVFS, assetId, assetType, LLVFile::READ); + + S32 fileSize = vfile.getSize(); + U8* fileBuffer; + fileBuffer = new U8[fileSize]; + vfile.read(fileBuffer, fileSize); + + outs.write((char*)fileBuffer, fileSize); + delete[] fileBuffer; + } + + return postAndYield(request, url, fileData, options, headers); +} + + +LLSD HttpCoroutineAdapter::postAndYield_(LLCore::HttpRequest::ptr_t &request, + const std::string & url, LLCore::BufferArray::ptr_t &rawbody, + LLCore::HttpOptions::ptr_t &options, LLCore::HttpHeaders::ptr_t &headers, + HttpCoroHandler::ptr_t &handler) +{ + HttpRequestPumper pumper(request); + + checkDefaultHeaders(headers); + + // The HTTPCoroHandler does not self delete, so retrieval of a the contained + // pointer from the smart pointer is safe in this case. + LLCore::HttpHandle hhandle = request->requestPost(mPolicyId, mPriority, url, rawbody.get(), + options, headers, handler.get()); + + if (hhandle == LLCORE_HTTP_HANDLE_INVALID) + { + return HttpCoroutineAdapter::buildImmediateErrorResult(request, url); + } + + saveState(hhandle, request, handler); + LLSD results = llcoro::waitForEventOn(handler->getReplyPump()); + cleanState(); + + //LL_INFOS() << "Results for transaction " << transactionId << LL_ENDL; + return results; +} + +LLSD HttpCoroutineAdapter::putAndYield(LLCore::HttpRequest::ptr_t request, + const std::string & url, const LLSD & body, + LLCore::HttpOptions::ptr_t options, LLCore::HttpHeaders::ptr_t headers) +{ + LLEventStream replyPump(mAdapterName + "Reply", true); + HttpCoroHandler::ptr_t httpHandler = HttpCoroHandler::ptr_t(new HttpCoroLLSDHandler(replyPump)); + + return putAndYield_(request, url, body, options, headers, httpHandler); +} + +LLSD HttpCoroutineAdapter::putAndYield_(LLCore::HttpRequest::ptr_t &request, + const std::string & url, const LLSD & body, + LLCore::HttpOptions::ptr_t &options, LLCore::HttpHeaders::ptr_t &headers, + HttpCoroHandler::ptr_t &handler) +{ + HttpRequestPumper pumper(request); + + checkDefaultHeaders(headers); + + // The HTTPCoroHandler does not self delete, so retrieval of a the contained + // pointer from the smart pointer is safe in this case. + LLCore::HttpHandle hhandle = requestPutWithLLSD(request, + mPolicyId, mPriority, url, body, options, headers, + handler.get()); + + if (hhandle == LLCORE_HTTP_HANDLE_INVALID) + { + return HttpCoroutineAdapter::buildImmediateErrorResult(request, url); + } + + saveState(hhandle, request, handler); + LLSD results = llcoro::waitForEventOn(handler->getReplyPump()); + cleanState(); + //LL_INFOS() << "Results for transaction " << transactionId << LL_ENDL; + return results; +} + +LLSD HttpCoroutineAdapter::getAndYield(LLCore::HttpRequest::ptr_t request, + const std::string & url, + LLCore::HttpOptions::ptr_t options, LLCore::HttpHeaders::ptr_t headers) +{ + LLEventStream replyPump(mAdapterName + "Reply", true); + HttpCoroHandler::ptr_t httpHandler = HttpCoroHandler::ptr_t(new HttpCoroLLSDHandler(replyPump)); + + return getAndYield_(request, url, options, headers, httpHandler); +} + +LLSD HttpCoroutineAdapter::getRawAndYield(LLCore::HttpRequest::ptr_t request, + const std::string & url, + LLCore::HttpOptions::ptr_t options, LLCore::HttpHeaders::ptr_t headers) +{ + LLEventStream replyPump(mAdapterName + "Reply", true); + HttpCoroHandler::ptr_t httpHandler = HttpCoroHandler::ptr_t(new HttpCoroRawHandler(replyPump)); + + return getAndYield_(request, url, options, headers, httpHandler); +} + +LLSD HttpCoroutineAdapter::getJsonAndYield(LLCore::HttpRequest::ptr_t request, + const std::string & url, LLCore::HttpOptions::ptr_t options, LLCore::HttpHeaders::ptr_t headers) +{ + LLEventStream replyPump(mAdapterName + "Reply", true); + HttpCoroHandler::ptr_t httpHandler = HttpCoroHandler::ptr_t(new HttpCoroJSONHandler(replyPump)); + + return getAndYield_(request, url, options, headers, httpHandler); +} + + +LLSD HttpCoroutineAdapter::getAndYield_(LLCore::HttpRequest::ptr_t &request, + const std::string & url, + LLCore::HttpOptions::ptr_t &options, LLCore::HttpHeaders::ptr_t &headers, + HttpCoroHandler::ptr_t &handler) +{ + HttpRequestPumper pumper(request); + checkDefaultHeaders(headers); + + // The HTTPCoroHandler does not self delete, so retrieval of a the contained + // pointer from the smart pointer is safe in this case. + LLCore::HttpHandle hhandle = request->requestGet(mPolicyId, mPriority, + url, options, headers, handler.get()); + + if (hhandle == LLCORE_HTTP_HANDLE_INVALID) + { + return HttpCoroutineAdapter::buildImmediateErrorResult(request, url); + } + + saveState(hhandle, request, handler); + LLSD results = llcoro::waitForEventOn(handler->getReplyPump()); + cleanState(); + //LL_INFOS() << "Results for transaction " << transactionId << LL_ENDL; + return results; +} + + +LLSD HttpCoroutineAdapter::deleteAndYield(LLCore::HttpRequest::ptr_t request, + const std::string & url, + LLCore::HttpOptions::ptr_t options, LLCore::HttpHeaders::ptr_t headers) +{ + LLEventStream replyPump(mAdapterName + "Reply", true); + HttpCoroHandler::ptr_t httpHandler = HttpCoroHandler::ptr_t(new HttpCoroLLSDHandler(replyPump)); + + return deleteAndYield_(request, url, options, headers, httpHandler); +} + +LLSD HttpCoroutineAdapter::deleteAndYield_(LLCore::HttpRequest::ptr_t &request, + const std::string & url, LLCore::HttpOptions::ptr_t &options, + LLCore::HttpHeaders::ptr_t &headers, HttpCoroHandler::ptr_t &handler) +{ + HttpRequestPumper pumper(request); + + checkDefaultHeaders(headers); + // The HTTPCoroHandler does not self delete, so retrieval of a the contained + // pointer from the smart pointer is safe in this case. + LLCore::HttpHandle hhandle = request->requestDelete(mPolicyId, mPriority, + url, options, headers, handler.get()); + + if (hhandle == LLCORE_HTTP_HANDLE_INVALID) + { + return HttpCoroutineAdapter::buildImmediateErrorResult(request, url); + } + + saveState(hhandle, request, handler); + LLSD results = llcoro::waitForEventOn(handler->getReplyPump()); + cleanState(); + //LL_INFOS() << "Results for transaction " << transactionId << LL_ENDL; + return results; +} + +void HttpCoroutineAdapter::checkDefaultHeaders(LLCore::HttpHeaders::ptr_t &headers) +{ + if (!headers) + headers.reset(new LLCore::HttpHeaders); + if (!headers->find(HTTP_OUT_HEADER_ACCEPT)) + { + headers->append(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_LLSD_XML); + } + + if (!headers->find("X-SecondLife-UDP-Listen-Port") && gMessageSystem) + { + headers->append("X-SecondLife-UDP-Listen-Port", llformat("%d", gMessageSystem->mPort)); + } +} + + +void HttpCoroutineAdapter::cancelYieldingOperation() +{ + LLCore::HttpRequest::ptr_t request = mWeakRequest.lock(); + HttpCoroHandler::ptr_t handler = mWeakHandler.lock(); + if ((request) && (handler) && (mYieldingHandle != LLCORE_HTTP_HANDLE_INVALID)) + { + cleanState(); + LL_INFOS() << "Canceling yielding request!" << LL_ENDL; + request->requestCancel(mYieldingHandle, handler.get()); + } +} + +void HttpCoroutineAdapter::saveState(LLCore::HttpHandle yieldingHandle, + LLCore::HttpRequest::ptr_t &request, HttpCoroHandler::ptr_t &handler) +{ + mWeakRequest = request; + mWeakHandler = handler; + mYieldingHandle = yieldingHandle; +} + +void HttpCoroutineAdapter::cleanState() +{ + mWeakRequest.reset(); + mWeakHandler.reset(); + mYieldingHandle = LLCORE_HTTP_HANDLE_INVALID; +} + +/*static*/ +LLSD HttpCoroutineAdapter::buildImmediateErrorResult(const LLCore::HttpRequest::ptr_t &request, + const std::string &url) +{ + LLCore::HttpStatus status = request->getStatus(); + LL_WARNS() << "Error posting to " << url << " Status=" << status.getStatus() << + " message = " << status.getMessage() << LL_ENDL; + + // Mimic the status results returned from an http error that we had + // to wait on + LLSD httpresults = LLSD::emptyMap(); + + HttpCoroHandler::writeStatusCodes(status, url, httpresults); + + LLSD errorres = LLSD::emptyMap(); + errorres["http_result"] = httpresults; + + return errorres; +} + +/*static*/ +LLCore::HttpStatus HttpCoroutineAdapter::getStatusFromLLSD(const LLSD &httpResults) +{ + LLCore::HttpStatus::type_enum_t type = static_cast<LLCore::HttpStatus::type_enum_t>(httpResults[HttpCoroutineAdapter::HTTP_RESULTS_TYPE].asInteger()); + short code = static_cast<short>(httpResults[HttpCoroutineAdapter::HTTP_RESULTS_STATUS].asInteger()); + + return LLCore::HttpStatus(type, code); +} + +/*static*/ +void HttpCoroutineAdapter::callbackHttpGet(const std::string &url, LLCore::HttpRequest::policy_t policyId, completionCallback_t success, completionCallback_t failure) +{ + LLCoros::instance().launch("HttpCoroutineAdapter::genericGetCoro", + boost::bind(&HttpCoroutineAdapter::trivialGetCoro, url, policyId, success, failure)); +} + +/*static*/ +void HttpCoroutineAdapter::messageHttpGet(const std::string &url, const std::string &success, const std::string &failure) +{ + completionCallback_t cbSuccess = (success.empty()) ? NULL : + static_cast<completionCallback_t>(boost::bind(&logMessageSuccess, "HttpCoroutineAdapter", url, success)); + completionCallback_t cbFailure = (failure.empty()) ? NULL : + static_cast<completionCallback_t>(boost::bind(&logMessageFail, "HttpCoroutineAdapter", url, failure)); + callbackHttpGet(url, cbSuccess, cbFailure); +} + +/*static*/ +void HttpCoroutineAdapter::trivialGetCoro(std::string url, LLCore::HttpRequest::policy_t policyId, completionCallback_t success, completionCallback_t failure) +{ + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("genericGetCoro", policyId)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); + + httpOpts->setWantHeaders(true); + + LL_INFOS("HttpCoroutineAdapter", "genericGetCoro") << "Generic GET for " << url << LL_ENDL; + + LLSD result = httpAdapter->getAndYield(httpRequest, url, httpOpts); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) + { + if (failure) + { + failure(httpResults); + } + } + else + { + if (success) + { + success(result); + } + } +} + +/*static*/ +void HttpCoroutineAdapter::callbackHttpPost(const std::string &url, LLCore::HttpRequest::policy_t policyId, const LLSD &postData, completionCallback_t success, completionCallback_t failure) +{ + LLCoros::instance().launch("HttpCoroutineAdapter::genericPostCoro", + boost::bind(&HttpCoroutineAdapter::trivialPostCoro, url, policyId, postData, success, failure)); +} + +/*static*/ +void HttpCoroutineAdapter::messageHttpPost(const std::string &url, const LLSD &postData, const std::string &success, const std::string &failure) +{ + completionCallback_t cbSuccess = (success.empty()) ? NULL : + static_cast<completionCallback_t>(boost::bind(&logMessageSuccess, "HttpCoroutineAdapter", url, success)); + completionCallback_t cbFailure = (failure.empty()) ? NULL : + static_cast<completionCallback_t>(boost::bind(&logMessageFail, "HttpCoroutineAdapter", url, failure)); + + callbackHttpPost(url, postData, cbSuccess, cbFailure); +} + +/*static*/ +void HttpCoroutineAdapter::trivialPostCoro(std::string url, LLCore::HttpRequest::policy_t policyId, LLSD postData, completionCallback_t success, completionCallback_t failure) +{ + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("genericPostCoro", policyId)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); + + httpOpts->setWantHeaders(true); + + LL_INFOS("HttpCoroutineAdapter", "genericPostCoro") << "Generic POST for " << url << LL_ENDL; + + LLSD result = httpAdapter->postAndYield(httpRequest, url, postData, httpOpts); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) + { + // If a failure routine is provided do it. + if (failure) + { + failure(httpResults); + } + } + else + { + // If a success routine is provided do it. + if (success) + { + success(result); + } + } +} + + } // end namespace LLCoreHttpUtil diff --git a/indra/llmessage/llcorehttputil.h b/indra/llmessage/llcorehttputil.h index d40172bc7a..999fd5f90b 100644 --- a/indra/llmessage/llcorehttputil.h +++ b/indra/llmessage/llcorehttputil.h @@ -36,9 +36,15 @@ #include "httpheaders.h" #include "httpoptions.h" #include "httphandler.h" +#include "llhttpconstants.h" // *TODO: move to llcorehttp #include "bufferarray.h" #include "bufferstream.h" #include "llsd.h" +#include "llevents.h" +#include "llcoros.h" +#include "lleventcoro.h" +#include "llassettype.h" +#include "lluuid.h" /// /// The base llcorehttp library implements many HTTP idioms @@ -101,15 +107,432 @@ std::string responseToString(LLCore::HttpResponse * response); /// a now-useless HttpHandler object. /// LLCore::HttpHandle requestPostWithLLSD(LLCore::HttpRequest * request, - LLCore::HttpRequest::policy_t policy_id, - LLCore::HttpRequest::priority_t priority, - const std::string & url, - const LLSD & body, - LLCore::HttpOptions * options, - LLCore::HttpHeaders * headers, - LLCore::HttpHandler * handler); + LLCore::HttpRequest::policy_t policy_id, + LLCore::HttpRequest::priority_t priority, + const std::string & url, + const LLSD & body, + const LLCore::HttpOptions::ptr_t &options, + const LLCore::HttpHeaders::ptr_t &headers, + LLCore::HttpHandler * handler); -} // end namespace LLCoreHttpUtil +inline LLCore::HttpHandle requestPostWithLLSD(LLCore::HttpRequest::ptr_t & request, + LLCore::HttpRequest::policy_t policy_id, + LLCore::HttpRequest::priority_t priority, + const std::string & url, + const LLSD & body, + const LLCore::HttpOptions::ptr_t & options, + const LLCore::HttpHeaders::ptr_t & headers, + LLCore::HttpHandler * handler) +{ + return requestPostWithLLSD(request.get(), policy_id, priority, + url, body, options, headers, handler); +} + +inline LLCore::HttpHandle requestPostWithLLSD(LLCore::HttpRequest::ptr_t & request, + LLCore::HttpRequest::policy_t policy_id, + LLCore::HttpRequest::priority_t priority, + const std::string & url, + const LLSD & body, + LLCore::HttpHandler * handler) +{ + LLCore::HttpOptions::ptr_t options; + LLCore::HttpHeaders::ptr_t headers; + + return requestPostWithLLSD(request.get(), policy_id, priority, + url, body, options, headers, handler); +} + + +/// Issue a standard HttpRequest::requestPut() call but using +/// and LLSD object as the request body. Conventions are the +/// same as with that method. Caller is expected to provide +/// an HttpHeaders object with a correct 'Content-Type:' header. +/// One will not be provided by this call. +/// +/// @return If request is successfully issued, the +/// HttpHandle representing the request. +/// On error, LLCORE_HTTP_HANDLE_INVALID +/// is returned and caller can fetch detailed +/// status with the getStatus() method on the +/// request object. In case of error, no +/// request is queued and caller may need to +/// perform additional cleanup such as freeing +/// a now-useless HttpHandler object. +/// +LLCore::HttpHandle requestPutWithLLSD(LLCore::HttpRequest * request, + LLCore::HttpRequest::policy_t policy_id, + LLCore::HttpRequest::priority_t priority, + const std::string & url, + const LLSD & body, + const LLCore::HttpOptions::ptr_t &options, + const LLCore::HttpHeaders::ptr_t &headers, + LLCore::HttpHandler * handler); + +inline LLCore::HttpHandle requestPutWithLLSD(LLCore::HttpRequest::ptr_t & request, + LLCore::HttpRequest::policy_t policy_id, + LLCore::HttpRequest::priority_t priority, + const std::string & url, + const LLSD & body, + const LLCore::HttpOptions::ptr_t & options, + const LLCore::HttpHeaders::ptr_t & headers, + LLCore::HttpHandler * handler) +{ + return requestPutWithLLSD(request.get(), policy_id, priority, + url, body, options, headers, handler); +} + +inline LLCore::HttpHandle requestPutWithLLSD(LLCore::HttpRequest::ptr_t & request, + LLCore::HttpRequest::policy_t policy_id, + LLCore::HttpRequest::priority_t priority, + const std::string & url, + const LLSD & body, + LLCore::HttpHandler * handler) +{ + LLCore::HttpOptions::ptr_t options; + LLCore::HttpHeaders::ptr_t headers; + + return requestPutWithLLSD(request.get(), policy_id, priority, + url, body, options, headers, handler); +} + +/// Issue a standard HttpRequest::requestPatch() call but using +/// and LLSD object as the request body. Conventions are the +/// same as with that method. Caller is expected to provide +/// an HttpHeaders object with a correct 'Content-Type:' header. +/// One will not be provided by this call. +/// +/// @return If request is successfully issued, the +/// HttpHandle representing the request. +/// On error, LLCORE_HTTP_HANDLE_INVALID +/// is returned and caller can fetch detailed +/// status with the getStatus() method on the +/// request object. In case of error, no +/// request is queued and caller may need to +/// perform additional cleanup such as freeing +/// a now-useless HttpHandler object. +/// +LLCore::HttpHandle requestPatchWithLLSD(LLCore::HttpRequest * request, + LLCore::HttpRequest::policy_t policy_id, + LLCore::HttpRequest::priority_t priority, + const std::string & url, + const LLSD & body, + const LLCore::HttpOptions::ptr_t &options, + const LLCore::HttpHeaders::ptr_t &headers, + LLCore::HttpHandler * handler); + +inline LLCore::HttpHandle requestPatchWithLLSD(LLCore::HttpRequest::ptr_t & request, + LLCore::HttpRequest::policy_t policy_id, + LLCore::HttpRequest::priority_t priority, + const std::string & url, + const LLSD & body, + const LLCore::HttpOptions::ptr_t & options, + const LLCore::HttpHeaders::ptr_t & headers, + LLCore::HttpHandler * handler) +{ + return requestPatchWithLLSD(request.get(), policy_id, priority, + url, body, options, headers, handler); +} + +inline LLCore::HttpHandle requestPatchWithLLSD(LLCore::HttpRequest::ptr_t & request, + LLCore::HttpRequest::policy_t policy_id, + LLCore::HttpRequest::priority_t priority, + const std::string & url, + const LLSD & body, + LLCore::HttpHandler * handler) +{ + LLCore::HttpOptions::ptr_t options; + LLCore::HttpHeaders::ptr_t headers; + + return requestPatchWithLLSD(request.get(), policy_id, priority, + url, body, options, headers, handler); +} + +//========================================================================= +/// The HttpCoroHandler is a specialization of the LLCore::HttpHandler for +/// interacting with coroutines. When the request is completed the response +/// will be posted onto the supplied Event Pump. +/// +/// The LLSD posted back to the coroutine will have the following additions: +/// llsd["http_result"] -+- ["message"] - An error message returned from the HTTP status +/// +- ["status"] - The status code associated with the HTTP call +/// +- ["success"] - Success of failure of the HTTP call and LLSD parsing. +/// +- ["type"] - The LLCore::HttpStatus type associted with the HTTP call +/// +- ["url"] - The URL used to make the call. +/// +- ["headers"] - A map of name name value pairs with the HTTP headers. +/// +class HttpCoroHandler : public LLCore::HttpHandler +{ +public: + typedef boost::shared_ptr<HttpCoroHandler> ptr_t; + typedef boost::weak_ptr<HttpCoroHandler> wptr_t; + + HttpCoroHandler(LLEventStream &reply); + + static void writeStatusCodes(LLCore::HttpStatus status, const std::string &url, LLSD &result); + + virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response); + + inline LLEventStream &getReplyPump() + { + return mReplyPump; + } + +protected: + /// this method may modify the status value + virtual LLSD handleSuccess(LLCore::HttpResponse * response, LLCore::HttpStatus &status) = 0; + +private: + void buildStatusEntry(LLCore::HttpResponse *response, LLCore::HttpStatus status, LLSD &result); + + LLEventStream &mReplyPump; +}; + +//========================================================================= +/// An adapter to handle some of the boilerplate code surrounding HTTP and coroutine +/// interaction. +/// +/// Construct an HttpCoroutineAdapter giving it a name and policy Id. After +/// any application specific setup call the post, put or get method. The request +/// will be automatically pumped and the method will return with an LLSD describing +/// the result of the operation. See HttpCoroHandler for a description of the +/// decoration done to the returned LLSD. +/// +/// Posting through the adapter will automatically add the following headers to +/// the request if they have not been previously specified in a supplied +/// HttpHeaders object: +/// "Accept=application/llsd+xml" +/// "X-SecondLife-UDP-Listen-Port=###" +/// +class HttpCoroutineAdapter +{ +public: + static const std::string HTTP_RESULTS; + static const std::string HTTP_RESULTS_SUCCESS; + static const std::string HTTP_RESULTS_TYPE; + static const std::string HTTP_RESULTS_STATUS; + static const std::string HTTP_RESULTS_MESSAGE; + static const std::string HTTP_RESULTS_URL; + static const std::string HTTP_RESULTS_HEADERS; + static const std::string HTTP_RESULTS_CONTENT; + static const std::string HTTP_RESULTS_RAW; + + typedef boost::shared_ptr<HttpCoroutineAdapter> ptr_t; + typedef boost::weak_ptr<HttpCoroutineAdapter> wptr_t; + + HttpCoroutineAdapter(const std::string &name, LLCore::HttpRequest::policy_t policyId, + LLCore::HttpRequest::priority_t priority = 0L); + ~HttpCoroutineAdapter(); + + /// Execute a Post transaction on the supplied URL and yield execution of + /// the coroutine until a result is available. + /// + /// @Note: the request's smart pointer is passed by value so that it will + /// not be deallocated during the yield. + LLSD postAndYield(LLCore::HttpRequest::ptr_t request, + const std::string & url, const LLSD & body, + LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), + LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders())); + LLSD postAndYield(LLCore::HttpRequest::ptr_t request, + const std::string & url, LLCore::BufferArray::ptr_t rawbody, + LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), + LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders())); + LLSD postAndYield(LLCore::HttpRequest::ptr_t &request, + const std::string & url, const LLSD & body, + LLCore::HttpHeaders::ptr_t &headers) + { + return postAndYield(request, url, body, + LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), headers); + } + + LLSD postAndYield(LLCore::HttpRequest::ptr_t &request, + const std::string & url, LLCore::BufferArray::ptr_t &rawbody, + LLCore::HttpHeaders::ptr_t &headers) + { + return postAndYield(request, url, rawbody, + LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), headers); + } + + LLSD postRawAndYield(LLCore::HttpRequest::ptr_t request, + const std::string & url, LLCore::BufferArray::ptr_t rawbody, + LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), + LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders())); + + LLSD postRawAndYield(LLCore::HttpRequest::ptr_t &request, + const std::string & url, LLCore::BufferArray::ptr_t &rawbody, + LLCore::HttpHeaders::ptr_t &headers) + { + return postRawAndYield(request, url, rawbody, + LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), headers); + } + + LLSD postFileAndYield(LLCore::HttpRequest::ptr_t request, + const std::string & url, std::string fileName, + LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), + LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders())); + + LLSD postFileAndYield(LLCore::HttpRequest::ptr_t &request, + const std::string & url, std::string fileName, + LLCore::HttpHeaders::ptr_t &headers) + { + return postFileAndYield(request, url, fileName, + LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), headers); + } + + + LLSD postFileAndYield(LLCore::HttpRequest::ptr_t request, + const std::string & url, LLUUID assetId, LLAssetType::EType assetType, + LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), + LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders())); + + LLSD postFileAndYield(LLCore::HttpRequest::ptr_t request, + const std::string & url, LLUUID assetId, LLAssetType::EType assetType, + LLCore::HttpHeaders::ptr_t &headers) + { + return postFileAndYield(request, url, assetId, assetType, + LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), headers); + } + + + /// Execute a Put transaction on the supplied URL and yield execution of + /// the coroutine until a result is available. + /// + /// @Note: the request's smart pointer is passed by value so that it will + /// not be deallocated during the yield. + LLSD putAndYield(LLCore::HttpRequest::ptr_t request, + const std::string & url, const LLSD & body, + LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), + LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders())); + + /// Execute a Get transaction on the supplied URL and yield execution of + /// the coroutine until a result is available. + /// + /// @Note: the request's smart pointer is passed by value so that it will + /// not be deallocated during the yield. + LLSD getAndYield(LLCore::HttpRequest::ptr_t request, + const std::string & url, + LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), + LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders())); + LLSD getAndYield(LLCore::HttpRequest::ptr_t &request, + const std::string & url, LLCore::HttpHeaders::ptr_t &headers) + { + return getAndYield(request, url, + LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), + headers); + } + + LLSD getRawAndYield(LLCore::HttpRequest::ptr_t request, + const std::string & url, + LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), + LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders())); + LLSD getRawAndYield(LLCore::HttpRequest::ptr_t &request, + const std::string & url, LLCore::HttpHeaders::ptr_t &headers) + { + return getRawAndYield(request, url, + LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), + headers); + } + + LLSD getJsonAndYield(LLCore::HttpRequest::ptr_t request, + const std::string & url, + LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), + LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders())); + LLSD getJsonndYield(LLCore::HttpRequest::ptr_t &request, + const std::string & url, LLCore::HttpHeaders::ptr_t &headers) + { + return getJsonAndYield(request, url, + LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), + headers); + } + + + /// Execute a DELETE transaction on the supplied URL and yield execution of + /// the coroutine until a result is available. + /// + /// @Note: the request's smart pointer is passed by value so that it will + /// not be deallocated during the yield. + LLSD deleteAndYield(LLCore::HttpRequest::ptr_t request, + const std::string & url, + LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), + LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders())); + + /// + void cancelYieldingOperation(); + + static LLCore::HttpStatus getStatusFromLLSD(const LLSD &httpResults); + + /// The convenience routines below can be provided with callback functors + /// which will be invoked in the case of success or failure. These callbacks + /// should match this form. + /// @sa callbackHttpGet + /// @sa callbackHttpPost + typedef boost::function<void(const LLSD &)> completionCallback_t; + + static void callbackHttpGet(const std::string &url, LLCore::HttpRequest::policy_t policyId, completionCallback_t success = NULL, completionCallback_t failure = NULL); + static void callbackHttpGet(const std::string &url, completionCallback_t success = NULL, completionCallback_t failure = NULL) + { + callbackHttpGet(url, LLCore::HttpRequest::DEFAULT_POLICY_ID, success, failure); + } + static void callbackHttpPost(const std::string &url, LLCore::HttpRequest::policy_t policyId, const LLSD &postData, completionCallback_t success = NULL, completionCallback_t failure = NULL); + static void callbackHttpPost(const std::string &url, const LLSD &postData, completionCallback_t success = NULL, completionCallback_t failure = NULL) + { + callbackHttpPost(url, LLCore::HttpRequest::DEFAULT_POLICY_ID, postData, success, failure); + } + + /// Generic Get and post routines for HTTP via coroutines. + /// These static methods do all required setup for the GET or POST operation. + /// When the operation completes successfully they will put the success message in the log at INFO level, + /// If the operation fails the failure message is written to the log at WARN level. + /// + static void messageHttpGet(const std::string &url, const std::string &success = std::string(), const std::string &failure = std::string()); + static void messageHttpPost(const std::string &url, const LLSD &postData, const std::string &success, const std::string &failure); + + +private: + static LLSD buildImmediateErrorResult(const LLCore::HttpRequest::ptr_t &request, const std::string &url); + + void saveState(LLCore::HttpHandle yieldingHandle, LLCore::HttpRequest::ptr_t &request, + HttpCoroHandler::ptr_t &handler); + void cleanState(); + + LLSD postAndYield_(LLCore::HttpRequest::ptr_t &request, + const std::string & url, const LLSD & body, + LLCore::HttpOptions::ptr_t &options, LLCore::HttpHeaders::ptr_t &headers, + HttpCoroHandler::ptr_t &handler); + + LLSD postAndYield_(LLCore::HttpRequest::ptr_t &request, + const std::string & url, LLCore::BufferArray::ptr_t &rawbody, + LLCore::HttpOptions::ptr_t &options, LLCore::HttpHeaders::ptr_t &headers, + HttpCoroHandler::ptr_t &handler); + + LLSD putAndYield_(LLCore::HttpRequest::ptr_t &request, + const std::string & url, const LLSD & body, + LLCore::HttpOptions::ptr_t &options, LLCore::HttpHeaders::ptr_t &headers, + HttpCoroHandler::ptr_t &handler); + + LLSD getAndYield_(LLCore::HttpRequest::ptr_t &request, + const std::string & url, LLCore::HttpOptions::ptr_t &options, + LLCore::HttpHeaders::ptr_t &headers, HttpCoroHandler::ptr_t &handler); + + LLSD deleteAndYield_(LLCore::HttpRequest::ptr_t &request, + const std::string & url, LLCore::HttpOptions::ptr_t &options, + LLCore::HttpHeaders::ptr_t &headers, HttpCoroHandler::ptr_t &handler); + + static void trivialGetCoro(std::string url, LLCore::HttpRequest::policy_t policyId, completionCallback_t success, completionCallback_t failure); + static void trivialPostCoro(std::string url, LLCore::HttpRequest::policy_t policyId, LLSD postData, completionCallback_t success, completionCallback_t failure); + + void checkDefaultHeaders(LLCore::HttpHeaders::ptr_t &headers); + + std::string mAdapterName; + LLCore::HttpRequest::priority_t mPriority; + LLCore::HttpRequest::policy_t mPolicyId; + + LLCore::HttpHandle mYieldingHandle; + LLCore::HttpRequest::wptr_t mWeakRequest; + HttpCoroHandler::wptr_t mWeakHandler; +}; + + +} // end namespace LLCoreHttpUtil #endif // LL_LLCOREHTTPUTIL_H diff --git a/indra/llmessage/llcurl.cpp b/indra/llmessage/llcurl.cpp index 73df47b933..ef28a4d211 100755 --- a/indra/llmessage/llcurl.cpp +++ b/indra/llmessage/llcurl.cpp @@ -69,13 +69,15 @@ do this. */ +// *TODO: TSN remove the commented code from this file ////////////////////////////////////////////////////////////////////////////// static const U32 EASY_HANDLE_POOL_SIZE = 5; static const S32 MULTI_PERFORM_CALL_REPEAT = 5; static const S32 CURL_REQUEST_TIMEOUT = 120; // seconds per operation static const S32 CURL_CONNECT_TIMEOUT = 30; //seconds to wait for a connection -static const S32 MAX_ACTIVE_REQUEST_COUNT = 100; + +//static const S32 MAX_ACTIVE_REQUEST_COUNT = 100; // DEBUG // S32 gCurlEasyCount = 0; @@ -676,6 +678,7 @@ void LLCurl::Easy::prepRequest(const std::string& url, } //////////////////////////////////////////////////////////////////////////// +#if 1 LLCurl::Multi::Multi(F32 idle_time_out) : mQueued(0), mErrorCount(0), @@ -1056,6 +1059,7 @@ void LLCurl::Multi::removeEasy(Easy* easy) easyFree(easy); } +#endif //------------------------------------------------------------ //LLCurlThread LLCurlThread::CurlRequest::CurlRequest(handle_t handle, LLCurl::Multi* multi, LLCurlThread* curl_thread) : @@ -1176,428 +1180,428 @@ std::string LLCurl::strerror(CURLcode errorcode) // For generating a simple request for data // using one multi and one easy per request -LLCurlRequest::LLCurlRequest() : - mActiveMulti(NULL), - mActiveRequestCount(0) -{ - mProcessing = FALSE; -} - -LLCurlRequest::~LLCurlRequest() -{ - //stop all Multi handle background threads - for (curlmulti_set_t::iterator iter = mMultiSet.begin(); iter != mMultiSet.end(); ++iter) - { - LLCurl::getCurlThread()->killMulti(*iter) ; - } - mMultiSet.clear() ; -} - -void LLCurlRequest::addMulti() -{ - LLCurl::Multi* multi = new LLCurl::Multi(); - if(!multi->isValid()) - { - LLCurl::getCurlThread()->killMulti(multi) ; - mActiveMulti = NULL ; - mActiveRequestCount = 0 ; - return; - } - - mMultiSet.insert(multi); - mActiveMulti = multi; - mActiveRequestCount = 0; -} - -LLCurl::Easy* LLCurlRequest::allocEasy() -{ - if (!mActiveMulti || - mActiveRequestCount >= MAX_ACTIVE_REQUEST_COUNT || - mActiveMulti->mErrorCount > 0) - { - addMulti(); - } - if(!mActiveMulti) - { - return NULL ; - } - - //llassert_always(mActiveMulti); - ++mActiveRequestCount; - LLCurl::Easy* easy = mActiveMulti->allocEasy(); - return easy; -} - -bool LLCurlRequest::addEasy(LLCurl::Easy* easy) -{ - llassert_always(mActiveMulti); - - if (mProcessing) - { - LL_ERRS() << "Posting to a LLCurlRequest instance from within a responder is not allowed (causes DNS timeouts)." << LL_ENDL; - } - bool res = mActiveMulti->addEasy(easy); - return res; -} - -void LLCurlRequest::get(const std::string& url, LLCurl::ResponderPtr responder) -{ - getByteRange(url, headers_t(), 0, -1, responder); -} - -// Note: (length==0) is interpreted as "the rest of the file", i.e. the whole file if (offset==0) or -// the remainder of the file if not. -bool LLCurlRequest::getByteRange(const std::string& url, - const headers_t& headers, - S32 offset, S32 length, - LLCurl::ResponderPtr responder) -{ - llassert(LLCurl::sNotQuitting); - LLCurl::Easy* easy = allocEasy(); - if (!easy) - { - return false; - } - easy->prepRequest(url, headers, responder); - easy->setopt(CURLOPT_HTTPGET, 1); - if (length > 0) - { - std::string range = llformat("bytes=%d-%d", offset,offset+length-1); - easy->slist_append(HTTP_OUT_HEADER_RANGE, range); - } - else if (offset > 0) - { - std::string range = llformat("bytes=%d-", offset); - easy->slist_append(HTTP_OUT_HEADER_RANGE, range); - } - easy->setHeaders(); - bool res = addEasy(easy); - return res; -} - -bool LLCurlRequest::post(const std::string& url, - const headers_t& headers, - const LLSD& data, - LLCurl::ResponderPtr responder, S32 time_out) -{ - llassert(LLCurl::sNotQuitting); - LLCurl::Easy* easy = allocEasy(); - if (!easy) - { - return false; - } - easy->prepRequest(url, headers, responder, time_out); - - LLSDSerialize::toXML(data, easy->getInput()); - S32 bytes = easy->getInput().str().length(); - - easy->setopt(CURLOPT_POST, 1); - easy->setopt(CURLOPT_POSTFIELDS, (void*)NULL); - easy->setopt(CURLOPT_POSTFIELDSIZE, bytes); - - easy->slist_append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_LLSD_XML); - easy->setHeaders(); - - LL_DEBUGS() << "POSTING: " << bytes << " bytes." << LL_ENDL; - bool res = addEasy(easy); - return res; -} - -bool LLCurlRequest::post(const std::string& url, - const headers_t& headers, - const std::string& data, - LLCurl::ResponderPtr responder, S32 time_out) -{ - llassert(LLCurl::sNotQuitting); - LLCurl::Easy* easy = allocEasy(); - if (!easy) - { - return false; - } - easy->prepRequest(url, headers, responder, time_out); - - easy->getInput().write(data.data(), data.size()); - S32 bytes = easy->getInput().str().length(); - - easy->setopt(CURLOPT_POST, 1); - easy->setopt(CURLOPT_POSTFIELDS, (void*)NULL); - easy->setopt(CURLOPT_POSTFIELDSIZE, bytes); - - easy->slist_append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_OCTET_STREAM); - easy->setHeaders(); - - LL_DEBUGS() << "POSTING: " << bytes << " bytes." << LL_ENDL; - bool res = addEasy(easy); - return res; -} - -// Note: call once per frame -S32 LLCurlRequest::process() -{ - S32 res = 0; - - mProcessing = TRUE; - for (curlmulti_set_t::iterator iter = mMultiSet.begin(); - iter != mMultiSet.end(); ) - { - curlmulti_set_t::iterator curiter = iter++; - LLCurl::Multi* multi = *curiter; - - if(!multi->isValid()) - { - if(multi == mActiveMulti) - { - mActiveMulti = NULL ; - mActiveRequestCount = 0 ; - } - mMultiSet.erase(curiter) ; - LLCurl::getCurlThread()->killMulti(multi) ; - continue ; - } - - S32 tres = multi->process(); - res += tres; - if (multi != mActiveMulti && tres == 0 && multi->mQueued == 0) - { - mMultiSet.erase(curiter); - LLCurl::getCurlThread()->killMulti(multi); - } - } - mProcessing = FALSE; - return res; -} - -S32 LLCurlRequest::getQueued() -{ - S32 queued = 0; - for (curlmulti_set_t::iterator iter = mMultiSet.begin(); - iter != mMultiSet.end(); ) - { - curlmulti_set_t::iterator curiter = iter++; - LLCurl::Multi* multi = *curiter; - - if(!multi->isValid()) - { - if(multi == mActiveMulti) - { - mActiveMulti = NULL ; - mActiveRequestCount = 0 ; - } - LLCurl::getCurlThread()->killMulti(multi); - mMultiSet.erase(curiter) ; - continue ; - } - - queued += multi->mQueued; - if (multi->getState() != LLCurl::Multi::STATE_READY) - { - ++queued; - } - } - return queued; -} - -LLCurlTextureRequest::LLCurlTextureRequest(S32 concurrency) : - LLCurlRequest(), - mConcurrency(concurrency), - mInQueue(0), - mMutex(NULL), - mHandleCounter(1), - mTotalIssuedRequests(0), - mTotalReceivedBits(0) -{ - mGlobalTimer.reset(); -} - -LLCurlTextureRequest::~LLCurlTextureRequest() -{ - mRequestMap.clear(); - - for(req_queue_t::iterator iter = mCachedRequests.begin(); iter != mCachedRequests.end(); ++iter) - { - delete *iter; - } - mCachedRequests.clear(); -} - -//return 0: success -// > 0: cached handle -U32 LLCurlTextureRequest::getByteRange(const std::string& url, - const headers_t& headers, - S32 offset, S32 length, U32 pri, - LLCurl::ResponderPtr responder, F32 delay_time) -{ - U32 ret_val = 0; - bool success = false; - - if(mInQueue < mConcurrency && delay_time < 0.f) - { - success = LLCurlRequest::getByteRange(url, headers, offset, length, responder); - } - - LLMutexLock lock(&mMutex); - - if(success) - { - mInQueue++; - mTotalIssuedRequests++; - } - else - { - request_t* request = new request_t(mHandleCounter, url, headers, offset, length, pri, responder); - if(delay_time > 0.f) - { - request->mStartTime = mGlobalTimer.getElapsedTimeF32() + delay_time; - } - - mCachedRequests.insert(request); - mRequestMap[mHandleCounter] = request; - ret_val = mHandleCounter; - mHandleCounter++; - - if(!mHandleCounter) - { - mHandleCounter = 1; - } - } - - return ret_val; -} - -void LLCurlTextureRequest::completeRequest(S32 received_bytes) -{ - LLMutexLock lock(&mMutex); - - llassert_always(mInQueue > 0); - - mInQueue--; - mTotalReceivedBits += received_bytes * 8; -} - -void LLCurlTextureRequest::nextRequests() -{ - if(mCachedRequests.empty() || mInQueue >= mConcurrency) - { - return; - } - - F32 cur_time = mGlobalTimer.getElapsedTimeF32(); - - req_queue_t::iterator iter; - { - LLMutexLock lock(&mMutex); - iter = mCachedRequests.begin(); - } - while(1) - { - request_t* request = *iter; - if(request->mStartTime < cur_time) - { - if(!LLCurlRequest::getByteRange(request->mUrl, request->mHeaders, request->mOffset, request->mLength, request->mResponder)) - { - break; - } - - LLMutexLock lock(&mMutex); - ++iter; - mInQueue++; - mTotalIssuedRequests++; - mCachedRequests.erase(request); - mRequestMap.erase(request->mHandle); - delete request; - - if(iter == mCachedRequests.end() || mInQueue >= mConcurrency) - { - break; - } - } - else - { - LLMutexLock lock(&mMutex); - ++iter; - if(iter == mCachedRequests.end() || mInQueue >= mConcurrency) - { - break; - } - } - } - - return; -} - -void LLCurlTextureRequest::updatePriority(U32 handle, U32 pri) -{ - if(!handle) - { - return; - } - - LLMutexLock lock(&mMutex); - - std::map<S32, request_t*>::iterator iter = mRequestMap.find(handle); - if(iter != mRequestMap.end()) - { - request_t* req = iter->second; - - if(req->mPriority != pri) - { - mCachedRequests.erase(req); - req->mPriority = pri; - mCachedRequests.insert(req); - } - } -} - -void LLCurlTextureRequest::removeRequest(U32 handle) -{ - if(!handle) - { - return; - } - - LLMutexLock lock(&mMutex); - - std::map<S32, request_t*>::iterator iter = mRequestMap.find(handle); - if(iter != mRequestMap.end()) - { - request_t* req = iter->second; - mRequestMap.erase(iter); - mCachedRequests.erase(req); - delete req; - } -} - -bool LLCurlTextureRequest::isWaiting(U32 handle) -{ - if(!handle) - { - return false; - } - - LLMutexLock lock(&mMutex); - return mRequestMap.find(handle) != mRequestMap.end(); -} - -U32 LLCurlTextureRequest::getTotalReceivedBits() -{ - LLMutexLock lock(&mMutex); - - U32 bits = mTotalReceivedBits; - mTotalReceivedBits = 0; - return bits; -} - -U32 LLCurlTextureRequest::getTotalIssuedRequests() -{ - LLMutexLock lock(&mMutex); - return mTotalIssuedRequests; -} - -S32 LLCurlTextureRequest::getNumRequests() -{ - LLMutexLock lock(&mMutex); - return mInQueue; -} +// LLCurlRequest::LLCurlRequest() : +// mActiveMulti(NULL), +// mActiveRequestCount(0) +// { +// mProcessing = FALSE; +// } +// +// LLCurlRequest::~LLCurlRequest() +// { +// //stop all Multi handle background threads +// for (curlmulti_set_t::iterator iter = mMultiSet.begin(); iter != mMultiSet.end(); ++iter) +// { +// LLCurl::getCurlThread()->killMulti(*iter) ; +// } +// mMultiSet.clear() ; +// } +// +// void LLCurlRequest::addMulti() +// { +// LLCurl::Multi* multi = new LLCurl::Multi(); +// if(!multi->isValid()) +// { +// LLCurl::getCurlThread()->killMulti(multi) ; +// mActiveMulti = NULL ; +// mActiveRequestCount = 0 ; +// return; +// } +// +// mMultiSet.insert(multi); +// mActiveMulti = multi; +// mActiveRequestCount = 0; +// } +// +// LLCurl::Easy* LLCurlRequest::allocEasy() +// { +// if (!mActiveMulti || +// mActiveRequestCount >= MAX_ACTIVE_REQUEST_COUNT || +// mActiveMulti->mErrorCount > 0) +// { +// addMulti(); +// } +// if(!mActiveMulti) +// { +// return NULL ; +// } +// +// //llassert_always(mActiveMulti); +// ++mActiveRequestCount; +// LLCurl::Easy* easy = mActiveMulti->allocEasy(); +// return easy; +// } +// +// bool LLCurlRequest::addEasy(LLCurl::Easy* easy) +// { +// llassert_always(mActiveMulti); +// +// if (mProcessing) +// { +// LL_ERRS() << "Posting to a LLCurlRequest instance from within a responder is not allowed (causes DNS timeouts)." << LL_ENDL; +// } +// bool res = mActiveMulti->addEasy(easy); +// return res; +// } +// +// void LLCurlRequest::get(const std::string& url, LLCurl::ResponderPtr responder) +// { +// getByteRange(url, headers_t(), 0, -1, responder); +// } +// +// // Note: (length==0) is interpreted as "the rest of the file", i.e. the whole file if (offset==0) or +// // the remainder of the file if not. +// bool LLCurlRequest::getByteRange(const std::string& url, +// const headers_t& headers, +// S32 offset, S32 length, +// LLCurl::ResponderPtr responder) +// { +// llassert(LLCurl::sNotQuitting); +// LLCurl::Easy* easy = allocEasy(); +// if (!easy) +// { +// return false; +// } +// easy->prepRequest(url, headers, responder); +// easy->setopt(CURLOPT_HTTPGET, 1); +// if (length > 0) +// { +// std::string range = llformat("bytes=%d-%d", offset,offset+length-1); +// easy->slist_append(HTTP_OUT_HEADER_RANGE, range); +// } +// else if (offset > 0) +// { +// std::string range = llformat("bytes=%d-", offset); +// easy->slist_append(HTTP_OUT_HEADER_RANGE, range); +// } +// easy->setHeaders(); +// bool res = addEasy(easy); +// return res; +// } +// +// bool LLCurlRequest::post(const std::string& url, +// const headers_t& headers, +// const LLSD& data, +// LLCurl::ResponderPtr responder, S32 time_out) +// { +// llassert(LLCurl::sNotQuitting); +// LLCurl::Easy* easy = allocEasy(); +// if (!easy) +// { +// return false; +// } +// easy->prepRequest(url, headers, responder, time_out); +// +// LLSDSerialize::toXML(data, easy->getInput()); +// S32 bytes = easy->getInput().str().length(); +// +// easy->setopt(CURLOPT_POST, 1); +// easy->setopt(CURLOPT_POSTFIELDS, (void*)NULL); +// easy->setopt(CURLOPT_POSTFIELDSIZE, bytes); +// +// easy->slist_append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_LLSD_XML); +// easy->setHeaders(); +// +// LL_DEBUGS() << "POSTING: " << bytes << " bytes." << LL_ENDL; +// bool res = addEasy(easy); +// return res; +// } +// +// bool LLCurlRequest::post(const std::string& url, +// const headers_t& headers, +// const std::string& data, +// LLCurl::ResponderPtr responder, S32 time_out) +// { +// llassert(LLCurl::sNotQuitting); +// LLCurl::Easy* easy = allocEasy(); +// if (!easy) +// { +// return false; +// } +// easy->prepRequest(url, headers, responder, time_out); +// +// easy->getInput().write(data.data(), data.size()); +// S32 bytes = easy->getInput().str().length(); +// +// easy->setopt(CURLOPT_POST, 1); +// easy->setopt(CURLOPT_POSTFIELDS, (void*)NULL); +// easy->setopt(CURLOPT_POSTFIELDSIZE, bytes); +// +// easy->slist_append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_OCTET_STREAM); +// easy->setHeaders(); +// +// LL_DEBUGS() << "POSTING: " << bytes << " bytes." << LL_ENDL; +// bool res = addEasy(easy); +// return res; +// } +// +// // Note: call once per frame +// S32 LLCurlRequest::process() +// { +// S32 res = 0; +// +// mProcessing = TRUE; +// for (curlmulti_set_t::iterator iter = mMultiSet.begin(); +// iter != mMultiSet.end(); ) +// { +// curlmulti_set_t::iterator curiter = iter++; +// LLCurl::Multi* multi = *curiter; +// +// if(!multi->isValid()) +// { +// if(multi == mActiveMulti) +// { +// mActiveMulti = NULL ; +// mActiveRequestCount = 0 ; +// } +// mMultiSet.erase(curiter) ; +// LLCurl::getCurlThread()->killMulti(multi) ; +// continue ; +// } +// +// S32 tres = multi->process(); +// res += tres; +// if (multi != mActiveMulti && tres == 0 && multi->mQueued == 0) +// { +// mMultiSet.erase(curiter); +// LLCurl::getCurlThread()->killMulti(multi); +// } +// } +// mProcessing = FALSE; +// return res; +// } +// +// S32 LLCurlRequest::getQueued() +// { +// S32 queued = 0; +// for (curlmulti_set_t::iterator iter = mMultiSet.begin(); +// iter != mMultiSet.end(); ) +// { +// curlmulti_set_t::iterator curiter = iter++; +// LLCurl::Multi* multi = *curiter; +// +// if(!multi->isValid()) +// { +// if(multi == mActiveMulti) +// { +// mActiveMulti = NULL ; +// mActiveRequestCount = 0 ; +// } +// LLCurl::getCurlThread()->killMulti(multi); +// mMultiSet.erase(curiter) ; +// continue ; +// } +// +// queued += multi->mQueued; +// if (multi->getState() != LLCurl::Multi::STATE_READY) +// { +// ++queued; +// } +// } +// return queued; +// } + +// LLCurlTextureRequest::LLCurlTextureRequest(S32 concurrency) : +// LLCurlRequest(), +// mConcurrency(concurrency), +// mInQueue(0), +// mMutex(NULL), +// mHandleCounter(1), +// mTotalIssuedRequests(0), +// mTotalReceivedBits(0) +// { +// mGlobalTimer.reset(); +// } +// +// LLCurlTextureRequest::~LLCurlTextureRequest() +// { +// mRequestMap.clear(); +// +// for(req_queue_t::iterator iter = mCachedRequests.begin(); iter != mCachedRequests.end(); ++iter) +// { +// delete *iter; +// } +// mCachedRequests.clear(); +// } +// +// //return 0: success +// // > 0: cached handle +// U32 LLCurlTextureRequest::getByteRange(const std::string& url, +// const headers_t& headers, +// S32 offset, S32 length, U32 pri, +// LLCurl::ResponderPtr responder, F32 delay_time) +// { +// U32 ret_val = 0; +// bool success = false; +// +// if(mInQueue < mConcurrency && delay_time < 0.f) +// { +// success = LLCurlRequest::getByteRange(url, headers, offset, length, responder); +// } +// +// LLMutexLock lock(&mMutex); +// +// if(success) +// { +// mInQueue++; +// mTotalIssuedRequests++; +// } +// else +// { +// request_t* request = new request_t(mHandleCounter, url, headers, offset, length, pri, responder); +// if(delay_time > 0.f) +// { +// request->mStartTime = mGlobalTimer.getElapsedTimeF32() + delay_time; +// } +// +// mCachedRequests.insert(request); +// mRequestMap[mHandleCounter] = request; +// ret_val = mHandleCounter; +// mHandleCounter++; +// +// if(!mHandleCounter) +// { +// mHandleCounter = 1; +// } +// } +// +// return ret_val; +// } +// +// void LLCurlTextureRequest::completeRequest(S32 received_bytes) +// { +// LLMutexLock lock(&mMutex); +// +// llassert_always(mInQueue > 0); +// +// mInQueue--; +// mTotalReceivedBits += received_bytes * 8; +// } +// +// void LLCurlTextureRequest::nextRequests() +// { +// if(mCachedRequests.empty() || mInQueue >= mConcurrency) +// { +// return; +// } +// +// F32 cur_time = mGlobalTimer.getElapsedTimeF32(); +// +// req_queue_t::iterator iter; +// { +// LLMutexLock lock(&mMutex); +// iter = mCachedRequests.begin(); +// } +// while(1) +// { +// request_t* request = *iter; +// if(request->mStartTime < cur_time) +// { +// if(!LLCurlRequest::getByteRange(request->mUrl, request->mHeaders, request->mOffset, request->mLength, request->mResponder)) +// { +// break; +// } +// +// LLMutexLock lock(&mMutex); +// ++iter; +// mInQueue++; +// mTotalIssuedRequests++; +// mCachedRequests.erase(request); +// mRequestMap.erase(request->mHandle); +// delete request; +// +// if(iter == mCachedRequests.end() || mInQueue >= mConcurrency) +// { +// break; +// } +// } +// else +// { +// LLMutexLock lock(&mMutex); +// ++iter; +// if(iter == mCachedRequests.end() || mInQueue >= mConcurrency) +// { +// break; +// } +// } +// } +// +// return; +// } +// +// void LLCurlTextureRequest::updatePriority(U32 handle, U32 pri) +// { +// if(!handle) +// { +// return; +// } +// +// LLMutexLock lock(&mMutex); +// +// std::map<S32, request_t*>::iterator iter = mRequestMap.find(handle); +// if(iter != mRequestMap.end()) +// { +// request_t* req = iter->second; +// +// if(req->mPriority != pri) +// { +// mCachedRequests.erase(req); +// req->mPriority = pri; +// mCachedRequests.insert(req); +// } +// } +// } +// +// void LLCurlTextureRequest::removeRequest(U32 handle) +// { +// if(!handle) +// { +// return; +// } +// +// LLMutexLock lock(&mMutex); +// +// std::map<S32, request_t*>::iterator iter = mRequestMap.find(handle); +// if(iter != mRequestMap.end()) +// { +// request_t* req = iter->second; +// mRequestMap.erase(iter); +// mCachedRequests.erase(req); +// delete req; +// } +// } +// +// bool LLCurlTextureRequest::isWaiting(U32 handle) +// { +// if(!handle) +// { +// return false; +// } +// +// LLMutexLock lock(&mMutex); +// return mRequestMap.find(handle) != mRequestMap.end(); +// } +// +// U32 LLCurlTextureRequest::getTotalReceivedBits() +// { +// LLMutexLock lock(&mMutex); +// +// U32 bits = mTotalReceivedBits; +// mTotalReceivedBits = 0; +// return bits; +// } +// +// U32 LLCurlTextureRequest::getTotalIssuedRequests() +// { +// LLMutexLock lock(&mMutex); +// return mTotalIssuedRequests; +// } +// +// S32 LLCurlTextureRequest::getNumRequests() +// { +// LLMutexLock lock(&mMutex); +// return mInQueue; +// } //////////////////////////////////////////////////////////////////////////// // For generating one easy request @@ -1988,10 +1992,10 @@ void LLCurlFF::check_easy_code(CURLcode code) { check_curl_code(code); } -void LLCurlFF::check_multi_code(CURLMcode code) -{ - check_curl_multi_code(code); -} +// void LLCurlFF::check_multi_code(CURLMcode code) +// { +// check_curl_multi_code(code); +// } // Static diff --git a/indra/llmessage/llcurl.h b/indra/llmessage/llcurl.h index 385d9fffa8..06b3ce45e1 100755 --- a/indra/llmessage/llcurl.h +++ b/indra/llmessage/llcurl.h @@ -418,100 +418,6 @@ private: } ; -class LLCurlRequest -{ -public: - typedef std::vector<std::string> headers_t; - - LLCurlRequest(); - ~LLCurlRequest(); - - void get(const std::string& url, LLCurl::ResponderPtr responder); - bool getByteRange(const std::string& url, const headers_t& headers, S32 offset, S32 length, LLCurl::ResponderPtr responder); - bool post(const std::string& url, const headers_t& headers, const LLSD& data, LLCurl::ResponderPtr responder, S32 time_out = 0); - bool post(const std::string& url, const headers_t& headers, const std::string& data, LLCurl::ResponderPtr responder, S32 time_out = 0); - - S32 process(); - S32 getQueued(); - -private: - void addMulti(); - LLCurl::Easy* allocEasy(); - bool addEasy(LLCurl::Easy* easy); - -private: - typedef std::set<LLCurl::Multi*> curlmulti_set_t; - curlmulti_set_t mMultiSet; - LLCurl::Multi* mActiveMulti; - S32 mActiveRequestCount; - BOOL mProcessing; -}; - -//for texture fetch only -class LLCurlTextureRequest : public LLCurlRequest -{ -public: - LLCurlTextureRequest(S32 concurrency); - ~LLCurlTextureRequest(); - - U32 getByteRange(const std::string& url, const headers_t& headers, S32 offset, S32 length, U32 pri, LLCurl::ResponderPtr responder, F32 delay_time = -1.f); - void nextRequests(); - void completeRequest(S32 received_bytes); - - void updatePriority(U32 handle, U32 pri); - void removeRequest(U32 handle); - - U32 getTotalReceivedBits(); - U32 getTotalIssuedRequests(); - S32 getNumRequests(); - bool isWaiting(U32 handle); - -private: - LLMutex mMutex; - S32 mConcurrency; - S32 mInQueue; //request currently in queue. - U32 mHandleCounter; - U32 mTotalIssuedRequests; - U32 mTotalReceivedBits; - - typedef struct _request_t - { - _request_t(U32 handle, const std::string& url, const headers_t& headers, S32 offset, S32 length, U32 pri, LLCurl::ResponderPtr responder) : - mHandle(handle), mUrl(url), mHeaders(headers), mOffset(offset), mLength(length), mPriority(pri), mResponder(responder), mStartTime(0.f) - {} - - U32 mHandle; - std::string mUrl; - LLCurlRequest::headers_t mHeaders; - S32 mOffset; - S32 mLength; - LLCurl::ResponderPtr mResponder; - U32 mPriority; - F32 mStartTime; //start time to issue this request - } request_t; - - struct request_compare - { - bool operator()(const request_t* lhs, const request_t* rhs) const - { - if(lhs->mPriority != rhs->mPriority) - { - return lhs->mPriority > rhs->mPriority; // higher priority in front of queue (set) - } - else - { - return (U32)lhs < (U32)rhs; - } - } - }; - - typedef std::set<request_t*, request_compare> req_queue_t; - req_queue_t mCachedRequests; - std::map<S32, request_t*> mRequestMap; - - LLFrameTimer mGlobalTimer; -}; - class LLCurlEasyRequest { public: @@ -550,7 +456,7 @@ private: namespace LLCurlFF { void check_easy_code(CURLcode code); - void check_multi_code(CURLMcode code); + //void check_multi_code(CURLMcode code); } #endif // LL_LLCURL_H diff --git a/indra/llmessage/llhttpclient.cpp b/indra/llmessage/llhttpclient.cpp index f8db3dded2..7f70e6a3f5 100755 --- a/indra/llmessage/llhttpclient.cpp +++ b/indra/llmessage/llhttpclient.cpp @@ -38,6 +38,10 @@ #include "lluri.h" #include "message.h" +#include "httpcommon.h" +#include "httprequest.h" +#include "httpoptions.h" + #include <curl/curl.h> diff --git a/indra/llmessage/llhttpclientadapter.cpp b/indra/llmessage/llhttpclientadapter.cpp deleted file mode 100755 index b56a804f94..0000000000 --- a/indra/llmessage/llhttpclientadapter.cpp +++ /dev/null @@ -1,73 +0,0 @@ -/** - * @file llhttpclientadapter.cpp - * @brief - * - * $LicenseInfo:firstyear=2009&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "llhttpclientadapter.h" -#include "llhttpclient.h" - -LLHTTPClientAdapter::~LLHTTPClientAdapter() -{ -} - -void LLHTTPClientAdapter::get(const std::string& url, LLCurl::ResponderPtr responder) -{ - LLSD empty_pragma_header; - // Pragma is required to stop curl adding "no-cache" - // Space is required to stop llurlrequest from turning off proxying - empty_pragma_header[HTTP_OUT_HEADER_PRAGMA] = " "; - LLHTTPClient::get(url, responder, empty_pragma_header); -} - -void LLHTTPClientAdapter::get(const std::string& url, LLCurl::ResponderPtr responder, const LLSD& headers) -{ - LLSD empty_pragma_header = headers; - if (!empty_pragma_header.has(HTTP_OUT_HEADER_PRAGMA)) - { - // as above - empty_pragma_header[HTTP_OUT_HEADER_PRAGMA] = " "; - } - LLHTTPClient::get(url, responder, empty_pragma_header); -} - -void LLHTTPClientAdapter::put(const std::string& url, const LLSD& body, LLCurl::ResponderPtr responder) -{ - LLHTTPClient::put(url, body, responder); -} - -void LLHTTPClientAdapter::put( - const std::string& url, - const LLSD& body, - LLCurl::ResponderPtr responder, - const LLSD& headers) -{ - LLHTTPClient::put(url, body, responder, headers); -} - -void LLHTTPClientAdapter::del( - const std::string& url, - LLCurl::ResponderPtr responder) -{ - LLHTTPClient::del(url, responder); -} diff --git a/indra/llmessage/llhttpclientadapter.h b/indra/llmessage/llhttpclientadapter.h deleted file mode 100755 index 270282c66f..0000000000 --- a/indra/llmessage/llhttpclientadapter.h +++ /dev/null @@ -1,51 +0,0 @@ -/** - * @file llhttpclientadepter.h - * @brief - * - * $LicenseInfo:firstyear=2008&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_HTTPCLIENTADAPTER_H -#define LL_HTTPCLIENTADAPTER_H - -#include "llhttpclientinterface.h" -#include "llsingleton.h" // LLSingleton<> - -class LLHTTPClientAdapter : public LLHTTPClientInterface, public LLSingleton<LLHTTPClientAdapter> -{ -public: - virtual ~LLHTTPClientAdapter(); - virtual void get(const std::string& url, LLCurl::ResponderPtr responder); - virtual void get(const std::string& url, LLCurl::ResponderPtr responder, const LLSD& headers); - virtual void put(const std::string& url, const LLSD& body, LLCurl::ResponderPtr responder); - virtual void put( - const std::string& url, - const LLSD& body, - LLCurl::ResponderPtr responder, - const LLSD& headers); - virtual void del( - const std::string& url, - LLCurl::ResponderPtr responder); -}; - -#endif - diff --git a/indra/llmessage/llhttpclientinterface.h b/indra/llmessage/llhttpclientinterface.h index 12a3857a61..9c1c8e7c11 100755 --- a/indra/llmessage/llhttpclientinterface.h +++ b/indra/llmessage/llhttpclientinterface.h @@ -32,14 +32,14 @@ #include <string> -class LLHTTPClientInterface -{ -public: - virtual ~LLHTTPClientInterface() {} - virtual void get(const std::string& url, LLCurl::ResponderPtr responder) = 0; - virtual void get(const std::string& url, LLCurl::ResponderPtr responder, const LLSD& headers) = 0; - virtual void put(const std::string& url, const LLSD& body, LLCurl::ResponderPtr responder) = 0; -}; +// class LLHTTPClientInterface +// { +// public: +// virtual ~LLHTTPClientInterface() {} +// virtual void get(const std::string& url, LLCurl::ResponderPtr responder) = 0; +// virtual void get(const std::string& url, LLCurl::ResponderPtr responder, const LLSD& headers) = 0; +// virtual void put(const std::string& url, const LLSD& body, LLCurl::ResponderPtr responder) = 0; +// }; #endif // LL_LLHTTPCLIENTINTERFACE_H diff --git a/indra/llmessage/llhttpsdhandler.cpp b/indra/llmessage/llhttpsdhandler.cpp new file mode 100644 index 0000000000..d99bdd3f66 --- /dev/null +++ b/indra/llmessage/llhttpsdhandler.cpp @@ -0,0 +1,105 @@ +/** +* @file llhttpsdhandler.h +* @brief Public-facing declarations for the HttpHandler class +* +* $LicenseInfo:firstyear=2012&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2012, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA +* $/LicenseInfo$ +*/ + +#include "linden_common.h" +#include "llhttpconstants.h" + +#include "llhttpsdhandler.h" +#include "httpresponse.h" +#include "httpheaders.h" +#include "llsd.h" +#include "llsdserialize.h" +#include "bufferstream.h" +#include "llcorehttputil.h" + +//======================================================================== +LLHttpSDHandler::LLHttpSDHandler(bool selfDelete): + mSelfDelete(selfDelete) +{ +} + +void LLHttpSDHandler::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response) +{ + LLCore::HttpStatus status = response->getStatus(); + + if (!status) + { + this->onFailure(response, status); + } + else + { + LLSD resplsd; + const bool emit_parse_errors = false; + + bool parsed = !((response->getBodySize() == 0) || + !LLCoreHttpUtil::responseToLLSD(response, emit_parse_errors, resplsd)); + + if (!parsed) + { + // Only emit a warning if we failed to parse when 'content-type' == 'application/llsd+xml' + LLCore::HttpHeaders::ptr_t headers(response->getHeaders()); + const std::string *contentType = (headers) ? headers->find(HTTP_IN_HEADER_CONTENT_TYPE) : NULL; + + if (contentType && (HTTP_CONTENT_LLSD_XML == *contentType)) + { + std::string thebody = LLCoreHttpUtil::responseToString(response); + + LL_WARNS() << "Failed to deserialize . " << response->getRequestURL() << " [status:" << response->getStatus().toString() << "] " + << " body: " << thebody << LL_ENDL; + } + } + + this->onSuccess(response, resplsd); + } + + // The handler must destroy itself when it is done. + // *TODO: I'm not fond of this pattern. A class shooting itself in the head + // outside of a smart pointer always makes me nervous. + if (mSelfDelete) + delete this; +} + +//======================================================================== +LLHttpSDGenericHandler::LLHttpSDGenericHandler(const std::string &name, bool selfDelete): + LLHttpSDHandler(selfDelete), + mName(name) +{ +} + +void LLHttpSDGenericHandler::onSuccess(LLCore::HttpResponse * response, const LLSD &content) +{ + LL_DEBUGS() << mName << " Success." << LL_ENDL; +} + +void LLHttpSDGenericHandler::onFailure(LLCore::HttpResponse * response, LLCore::HttpStatus status) +{ + LL_WARNS() + << "\n--------------------------------------------------------------------------\n" + << mName << " Error[" << status.toULong() << "] cannot access cap with url '" + << response->getRequestURL() << "' because " << status.toString() + << "\n--------------------------------------------------------------------------" + << LL_ENDL; +} diff --git a/indra/llmessage/llhttpsdhandler.h b/indra/llmessage/llhttpsdhandler.h new file mode 100644 index 0000000000..3b81dc66b9 --- /dev/null +++ b/indra/llmessage/llhttpsdhandler.h @@ -0,0 +1,72 @@ +/** +* @file llhttpsdhandler.h +* @brief Public-facing declarations for the HttpHandler class +* +* $LicenseInfo:firstyear=2012&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2012, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA +* $/LicenseInfo$ +*/ + +#ifndef _LLHTTPSDHANDLER_H_ +#define _LLHTTPSDHANDLER_H_ +#include "httpcommon.h" +#include "httphandler.h" +#include "lluri.h" + +/// Handler class LLCore's HTTP library. Splitting with separate success and +/// failure routines and parsing the result body into LLSD on success. It +/// is intended to be subclassed for specific capability handling. +/// +// *TODO: This class self deletes at the end of onCompleted method. This is +// less than ideal and should be revisited. +class LLHttpSDHandler : public LLCore::HttpHandler //, +// public std::enable_shared_from_this<LLHttpSDHandler> +{ +public: + + virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response); + +protected: + LLHttpSDHandler(bool selfDelete = true); + + virtual void onSuccess(LLCore::HttpResponse * response, const LLSD &content) = 0; + virtual void onFailure(LLCore::HttpResponse * response, LLCore::HttpStatus status) = 0; + +private: + bool mSelfDelete; + +}; + +/// A trivial implementation of LLHttpSDHandler. This success and failure +/// methods log the action taken, the URI accessed and the status code returned +/// in the response. +class LLHttpSDGenericHandler : public LLHttpSDHandler +{ +public: + LLHttpSDGenericHandler(const std::string &name, bool selfDelete = true); + +protected: + virtual void onSuccess(LLCore::HttpResponse * response, const LLSD &content); + virtual void onFailure(LLCore::HttpResponse * response, LLCore::HttpStatus status); + +private: + std::string mName; +}; +#endif diff --git a/indra/llmessage/llsdmessage.cpp b/indra/llmessage/llsdmessage.cpp deleted file mode 100755 index 61fcc5dd2f..0000000000 --- a/indra/llmessage/llsdmessage.cpp +++ /dev/null @@ -1,168 +0,0 @@ -/** - * @file llsdmessage.cpp - * @author Nat Goodspeed - * @date 2008-10-31 - * @brief Implementation for llsdmessage. - * - * $LicenseInfo:firstyear=2008&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#if LL_WINDOWS -#pragma warning (disable : 4675) // "resolved by ADL" -- just as I want! -#endif - -// Precompiled header -#include "linden_common.h" -// associated header -#include "llsdmessage.h" -// STL headers -// std headers -// external library headers -// other Linden headers -#include "llevents.h" -#include "llsdserialize.h" -#include "llhttpclient.h" -#include "llmessageconfig.h" -#include "llhost.h" -#include "message.h" -#include "llsdutil.h" - -// Declare a static LLSDMessage instance to ensure that we have a listener as -// soon as someone tries to post on our canonical LLEventPump name. -static LLSDMessage httpListener; - -LLSDMessage::LLSDMessage(): - // Instantiating our own local LLEventPump with a string name the - // constructor is NOT allowed to tweak is a way of ensuring Singleton - // semantics: attempting to instantiate a second LLSDMessage object would - // throw LLEventPump::DupPumpName. - mEventPump("LLHTTPClient") -{ - mEventPump.listen("self", boost::bind(&LLSDMessage::httpListener, this, _1)); -} - -bool LLSDMessage::httpListener(const LLSD& request) -{ - // Extract what we want from the request object. We do it all up front - // partly to document what we expect. - LLSD::String url(request["url"]); - LLSD payload(request["payload"]); - LLSD::String reply(request["reply"]); - LLSD::String error(request["error"]); - LLSD::Real timeout(request["timeout"]); - // If the LLSD doesn't even have a "url" key, we doubt it was intended for - // this listener. - if (url.empty()) - { - std::ostringstream out; - out << "request event without 'url' key to '" << mEventPump.getName() << "'"; - throw ArgError(out.str()); - } - // Establish default timeout. This test relies on LLSD::asReal() returning - // exactly 0.0 for an undef value. - if (! timeout) - { - timeout = HTTP_REQUEST_EXPIRY_SECS; - } - LLHTTPClient::post(url, payload, - new LLSDMessage::EventResponder(LLEventPumps::instance(), - request, - url, "POST", reply, error), - LLSD(), // headers - (F32)timeout); - return false; -} - -void LLSDMessage::EventResponder::httpSuccess() -{ - // If our caller passed an empty replyPump name, they're not - // listening: this is a fire-and-forget message. Don't bother posting - // to the pump whose name is "". - if (! mReplyPump.empty()) - { - LLSD response(getContent()); - mReqID.stamp(response); - mPumps.obtain(mReplyPump).post(response); - } - else // default success handling - { - LL_INFOS("LLSDMessage::EventResponder") - << "'" << mMessage << "' to '" << mTarget << "' succeeded" - << LL_ENDL; - } -} - -void LLSDMessage::EventResponder::httpFailure() -{ - // If our caller passed an empty errorPump name, they're not - // listening: "default error handling is acceptable." Only post to an - // explicit pump name. - if (! mErrorPump.empty()) - { - LLSD info(mReqID.makeResponse()); - info["target"] = mTarget; - info["message"] = mMessage; - info["status"] = getStatus(); - info["reason"] = getReason(); - info["content"] = getContent(); - mPumps.obtain(mErrorPump).post(info); - } - else // default error handling - { - // convention seems to be to use LL_INFOS(), but that seems a bit casual? - LL_WARNS("LLSDMessage::EventResponder") - << "'" << mMessage << "' to '" << mTarget - << "' failed " << dumpResponse() << LL_ENDL; - } -} - -LLSDMessage::ResponderAdapter::ResponderAdapter(LLHTTPClient::ResponderPtr responder, - const std::string& name): - mResponder(responder), - mReplyPump(name + ".reply", true), // tweak name for uniqueness - mErrorPump(name + ".error", true) -{ - mReplyPump.listen("self", boost::bind(&ResponderAdapter::listener, this, _1, true)); - mErrorPump.listen("self", boost::bind(&ResponderAdapter::listener, this, _1, false)); -} - -bool LLSDMessage::ResponderAdapter::listener(const LLSD& payload, bool success) -{ - if (success) - { - mResponder->successResult(payload); - } - else - { - mResponder->failureResult(payload["status"].asInteger(), payload["reason"], payload["content"]); - } - - /*---------------- MUST BE LAST STATEMENT BEFORE RETURN ----------------*/ - delete this; - // Destruction of mResponder will usually implicitly free its referent as well - /*------------------------- NOTHING AFTER THIS -------------------------*/ - return false; -} - -void LLSDMessage::link() -{ -} diff --git a/indra/llmessage/llsdmessage.h b/indra/llmessage/llsdmessage.h deleted file mode 100755 index e5d532d6a4..0000000000 --- a/indra/llmessage/llsdmessage.h +++ /dev/null @@ -1,168 +0,0 @@ -/** - * @file llsdmessage.h - * @author Nat Goodspeed - * @date 2008-10-30 - * @brief API intended to unify sending capability, UDP and TCP messages: - * https://wiki.lindenlab.com/wiki/Viewer:Messaging/Messaging_Notes - * - * $LicenseInfo:firstyear=2008&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#if ! defined(LL_LLSDMESSAGE_H) -#define LL_LLSDMESSAGE_H - -#include "llerror.h" // LOG_CLASS() -#include "llevents.h" // LLEventPumps -#include "llhttpclient.h" -#include <string> -#include <stdexcept> - -class LLSD; - -/** - * Class managing the messaging API described in - * https://wiki.lindenlab.com/wiki/Viewer:Messaging/Messaging_Notes - */ -class LLSDMessage -{ - LOG_CLASS(LLSDMessage); - -public: - LLSDMessage(); - - /// Exception if you specify arguments badly - struct ArgError: public std::runtime_error - { - ArgError(const std::string& what): - std::runtime_error(std::string("ArgError: ") + what) {} - }; - - /** - * The response idiom used by LLSDMessage -- LLEventPump names on which to - * post reply or error -- is designed for the case in which your - * reply/error handlers are methods on the same class as the method - * sending the message. Any state available to the sending method that - * must be visible to the reply/error methods can conveniently be stored - * on that class itself, if it's not already. - * - * The LLHTTPClient::Responder idiom requires a separate instance of a - * separate class so that it can dispatch to the code of interest by - * calling canonical virtual methods. Interesting state must be copied - * into that new object. - * - * With some trepidation, because existing response code is packaged in - * LLHTTPClient::Responder subclasses, we provide this adapter class - * <i>for transitional purposes only.</i> Instantiate a new heap - * ResponderAdapter with your new LLHTTPClient::ResponderPtr. Pass - * ResponderAdapter::getReplyName() and/or getErrorName() in your - * LLSDMessage (or LLViewerRegion::getCapAPI()) request event. The - * ResponderAdapter will call the appropriate Responder method, then - * @c delete itself. - */ - class ResponderAdapter - { - public: - /** - * Bind the new LLHTTPClient::Responder subclass instance. - * - * Passing the constructor a name other than the default is only - * interesting if you suspect some usage will lead to an exception or - * log message. - */ - ResponderAdapter(LLHTTPClient::ResponderPtr responder, - const std::string& name="ResponderAdapter"); - - /// EventPump name on which LLSDMessage should post reply event - std::string getReplyName() const { return mReplyPump.getName(); } - /// EventPump name on which LLSDMessage should post error event - std::string getErrorName() const { return mErrorPump.getName(); } - - private: - // We have two different LLEventStreams, though we route them both to - // the same listener, so that we can bind an extra flag identifying - // which case (reply or error) reached that listener. - bool listener(const LLSD&, bool success); - - LLHTTPClient::ResponderPtr mResponder; - LLEventStream mReplyPump, mErrorPump; - }; - - /** - * Force our implementation file to be linked with caller. The .cpp file - * contains a static instance of this class, which must be linked into the - * executable to support the canonical listener. But since the primary - * interface to that static instance is via a named LLEventPump rather - * than by direct reference, the linker doesn't necessarily perceive the - * necessity to bring in the translation unit. Referencing this dummy - * method forces the issue. - */ - static void link(); - -private: - friend class LLCapabilityListener; - /// Responder used for internal purposes by LLSDMessage and - /// LLCapabilityListener. Others should use higher-level APIs. - class EventResponder: public LLHTTPClient::Responder - { - LOG_CLASS(EventResponder); - public: - /** - * LLHTTPClient::Responder that dispatches via named LLEventPump instances. - * We bind LLEventPumps, even though it's an LLSingleton, for testability. - * We bind the string names of the desired LLEventPump instances rather - * than actually obtain()ing them so we only obtain() the one we're going - * to use. If the caller doesn't bother to listen() on it, the other pump - * may never materialize at all. - * @a target and @a message are only to clarify error processing. - * For a capability message, @a target should be the region description, - * @a message should be the capability name. - * For a service with a visible URL, pass the URL as @a target and the HTTP verb - * (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), - mErrorPump(errorPump) - {} - - protected: - virtual void httpSuccess(); - virtual void httpFailure(); - - private: - LLEventPumps& mPumps; - LLReqID mReqID; - const std::string mTarget, mMessage, mReplyPump, mErrorPump; - }; - -private: - bool httpListener(const LLSD&); - LLEventStream mEventPump; -}; - -#endif /* ! defined(LL_LLSDMESSAGE_H) */ diff --git a/indra/llmessage/llsdrpcclient.h b/indra/llmessage/llsdrpcclient.h index c4e0333ca3..d097ecdff7 100755 --- a/indra/llmessage/llsdrpcclient.h +++ b/indra/llmessage/llsdrpcclient.h @@ -37,7 +37,9 @@ #include "llchainio.h" #include "llfiltersd2xmlrpc.h" #include "lliopipe.h" -#include "llurlrequest.h" +#if 0 +//#include "llurlrequest.h" +#endif /** * @class LLSDRPCClientResponse @@ -218,6 +220,7 @@ protected: LLIOPipe::ptr_t mResponse; }; +#if 0 /** * @class LLSDRPCClientFactory * @brief Basic implementation for making an SD RPC client factory @@ -267,7 +270,9 @@ public: protected: std::string mURL; }; +#endif +#if 0 /** * @class LLXMLSDRPCClientFactory * @brief Basic implementation for making an XMLRPC to SD RPC client factory @@ -319,5 +324,6 @@ public: protected: std::string mURL; }; +#endif #endif // LL_LLSDRPCCLIENT_H diff --git a/indra/llmessage/tests/llhttpclientadapter_test.cpp b/indra/llmessage/tests/llhttpclientadapter_test.cpp deleted file mode 100755 index e9ce116bb3..0000000000 --- a/indra/llmessage/tests/llhttpclientadapter_test.cpp +++ /dev/null @@ -1,221 +0,0 @@ -/** - * @file llhttpclientadapter_test.cpp - * @brief Tests for LLHTTPClientAdapter - * - * $LicenseInfo:firstyear=2008&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "llhttpclientadapter.h" - -#include "../test/lltut.h" -#include "llhttpclient.h" -#include "llcurl_stub.cpp" - -float const HTTP_REQUEST_EXPIRY_SECS = 1.0F; - -std::vector<std::string> get_urls; -std::vector< LLCurl::ResponderPtr > get_responders; -void LLHTTPClient::get(const std::string& url, LLCurl::ResponderPtr responder, const LLSD& headers, const F32 timeout, bool follow_redirects) -{ - get_urls.push_back(url); - get_responders.push_back(responder); -} - -std::vector<std::string> put_urls; -std::vector<LLSD> put_body; -std::vector<LLSD> put_headers; -std::vector<LLCurl::ResponderPtr> put_responders; - -void LLHTTPClient::put(const std::string& url, const LLSD& body, LLCurl::ResponderPtr responder, const LLSD& headers, const F32 timeout) -{ - put_urls.push_back(url); - put_responders.push_back(responder); - put_body.push_back(body); - put_headers.push_back(headers); - -} - -std::vector<std::string> delete_urls; -std::vector<LLCurl::ResponderPtr> delete_responders; - -void LLHTTPClient::del( - const std::string& url, - LLCurl::ResponderPtr responder, - const LLSD& headers, - const F32 timeout) -{ - delete_urls.push_back(url); - delete_responders.push_back(responder); -} - -namespace tut -{ - struct LLHTTPClientAdapterData - { - LLHTTPClientAdapterData() - { - get_urls.clear(); - get_responders.clear(); - put_urls.clear(); - put_responders.clear(); - put_body.clear(); - put_headers.clear(); - delete_urls.clear(); - delete_responders.clear(); - } - }; - - typedef test_group<LLHTTPClientAdapterData> factory; - typedef factory::object object; -} - -namespace -{ - tut::factory tf("LLHTTPClientAdapterData"); -} - -namespace tut -{ - // Ensure we can create the object - template<> template<> - void object::test<1>() - { - LLHTTPClientAdapter adapter; - } - - // Does the get pass the appropriate arguments to the LLHTTPClient - template<> template<> - void object::test<2>() - { - LLHTTPClientAdapter adapter; - - LLCurl::ResponderPtr responder = new LLCurl::Responder(); - - adapter.get("Made up URL", responder); - ensure_equals(get_urls.size(), 1); - ensure_equals(get_urls[0], "Made up URL"); - } - - // Ensure the responder matches the one passed to get - template<> template<> - void object::test<3>() - { - LLHTTPClientAdapter adapter; - LLCurl::ResponderPtr responder = new LLCurl::Responder(); - - adapter.get("Made up URL", responder); - - ensure_equals(get_responders.size(), 1); - ensure_equals(get_responders[0].get(), responder.get()); - } - - // Ensure the correct url is used in the put - template<> template<> - void object::test<4>() - { - LLHTTPClientAdapter adapter; - - LLCurl::ResponderPtr responder = new LLCurl::Responder(); - - LLSD body; - body["TestBody"] = "Foobar"; - - adapter.put("Made up URL", body, responder); - ensure_equals(put_urls.size(), 1); - ensure_equals(put_urls[0], "Made up URL"); - } - - // Ensure the correct responder is used by put - template<> template<> - void object::test<5>() - { - LLHTTPClientAdapter adapter; - - LLCurl::ResponderPtr responder = new LLCurl::Responder(); - - LLSD body; - body["TestBody"] = "Foobar"; - - adapter.put("Made up URL", body, responder); - - ensure_equals(put_responders.size(), 1); - ensure_equals(put_responders[0].get(), responder.get()); - } - - // Ensure the message body is passed through the put properly - template<> template<> - void object::test<6>() - { - LLHTTPClientAdapter adapter; - - LLCurl::ResponderPtr responder = new LLCurl::Responder(); - - LLSD body; - body["TestBody"] = "Foobar"; - - adapter.put("Made up URL", body, responder); - - ensure_equals(put_body.size(), 1); - ensure_equals(put_body[0]["TestBody"].asString(), "Foobar"); - } - - // Ensure that headers are passed through put properly - template<> template<> - void object::test<7>() - { - LLHTTPClientAdapter adapter; - - LLCurl::ResponderPtr responder = new LLCurl::Responder(); - - LLSD body = LLSD::emptyMap(); - body["TestBody"] = "Foobar"; - - LLSD headers = LLSD::emptyMap(); - headers["booger"] = "omg"; - - adapter.put("Made up URL", body, responder, headers); - - ensure_equals("Header count", put_headers.size(), 1); - ensure_equals( - "First header", - put_headers[0]["booger"].asString(), - "omg"); - } - - // Ensure that del() passes appropriate arguments to the LLHTTPClient - template<> template<> - void object::test<8>() - { - LLHTTPClientAdapter adapter; - - LLCurl::ResponderPtr responder = new LLCurl::Responder(); - - adapter.del("Made up URL", responder); - - ensure_equals("URL count", delete_urls.size(), 1); - ensure_equals("Received URL", delete_urls[0], "Made up URL"); - - ensure_equals("Responder count", delete_responders.size(), 1); - //ensure_equals("Responder", delete_responders[0], responder); - } -} - diff --git a/indra/llmessage/tests/llsdmessage_test.cpp b/indra/llmessage/tests/llsdmessage_test.cpp deleted file mode 100755 index 44b024a83f..0000000000 --- a/indra/llmessage/tests/llsdmessage_test.cpp +++ /dev/null @@ -1,130 +0,0 @@ -/** - * @file llsdmessage_test.cpp - * @author Nat Goodspeed - * @date 2008-12-22 - * @brief Test of llsdmessage.h - * - * $LicenseInfo:firstyear=2008&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#if LL_WINDOWS -#pragma warning (disable : 4675) // "resolved by ADL" -- just as I want! -#endif - -// Precompiled header -#include "linden_common.h" -// associated header -#include "llsdmessage.h" -// STL headers -#include <iostream> -// std headers -#include <stdexcept> -#include <typeinfo> -// external library headers -// other Linden headers -#include "../test/lltut.h" -#include "../test/catch_and_store_what_in.h" -#include "llsdserialize.h" -#include "llevents.h" -#include "stringize.h" -#include "llhost.h" -#include "tests/networkio.h" -#include "tests/commtest.h" - -/***************************************************************************** -* TUT -*****************************************************************************/ -namespace tut -{ - struct llsdmessage_data: public commtest_data - { - LLEventPump& httpPump; - - llsdmessage_data(): - httpPump(pumps.obtain("LLHTTPClient")) - { - LLCurl::initClass(); - LLSDMessage::link(); - } - }; - typedef test_group<llsdmessage_data> llsdmessage_group; - typedef llsdmessage_group::object llsdmessage_object; - llsdmessage_group llsdmgr("llsdmessage"); - - template<> template<> - void llsdmessage_object::test<1>() - { - std::string threw; - // This should fail... - try - { - LLSDMessage localListener; - } - CATCH_AND_STORE_WHAT_IN(threw, LLEventPump::DupPumpName) - ensure("second LLSDMessage should throw", ! threw.empty()); - } - - template<> template<> - void llsdmessage_object::test<2>() - { - LLSD request, body; - body["data"] = "yes"; - request["payload"] = body; - request["reply"] = replyPump.getName(); - request["error"] = errorPump.getName(); - bool threw = false; - try - { - httpPump.post(request); - } - catch (const LLSDMessage::ArgError&) - { - threw = true; - } - ensure("missing URL", threw); - } - - template<> template<> - void llsdmessage_object::test<3>() - { - LLSD request, body; - body["data"] = "yes"; - request["url"] = server + "got-message"; - request["payload"] = body; - request["reply"] = replyPump.getName(); - request["error"] = errorPump.getName(); - httpPump.post(request); - ensure("got response", netio.pump()); - ensure("success response", success); - ensure_equals(result["reply"].asString(), "success"); - - body["status"] = 499; - body["reason"] = "custom error message"; - request["url"] = server + "fail"; - request["payload"] = body; - httpPump.post(request); - ensure("got response", netio.pump()); - ensure("failure response", ! success); - ensure_equals(result["status"].asInteger(), body["status"].asInteger()); - ensure_equals(result["reason"].asString(), body["reason"].asString()); - } -} // namespace tut diff --git a/indra/llmessage/tests/lltesthttpclientadapter.cpp b/indra/llmessage/tests/lltesthttpclientadapter.cpp deleted file mode 100755 index 4539e4a540..0000000000 --- a/indra/llmessage/tests/lltesthttpclientadapter.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/** - * @file - * @brief - * - * $LicenseInfo:firstyear=2008&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ -#include "lltesthttpclientadapter.h" - -LLTestHTTPClientAdapter::LLTestHTTPClientAdapter() -{ -} - -LLTestHTTPClientAdapter::~LLTestHTTPClientAdapter() -{ -} - -void LLTestHTTPClientAdapter::get(const std::string& url, LLCurl::ResponderPtr responder) -{ - mGetUrl.push_back(url); - mGetResponder.push_back(responder); -} - -void LLTestHTTPClientAdapter::put(const std::string& url, const LLSD& body, LLCurl::ResponderPtr responder) -{ - mPutUrl.push_back(url); - mPutBody.push_back(body); - mPutResponder.push_back(responder); -} - -U32 LLTestHTTPClientAdapter::putCalls() const -{ - return mPutUrl.size(); -} - -void LLTestHTTPClientAdapter::get(const std::string& url, LLCurl::ResponderPtr responder, const LLSD& headers) -{ - mGetUrl.push_back(url); - mGetHeaders.push_back(headers); - mGetResponder.push_back(responder); -} - - diff --git a/indra/llmessage/tests/lltesthttpclientadapter.h b/indra/llmessage/tests/lltesthttpclientadapter.h deleted file mode 100755 index c29cbb3a2a..0000000000 --- a/indra/llmessage/tests/lltesthttpclientadapter.h +++ /dev/null @@ -1,57 +0,0 @@ -/** - * @file - * @brief - * - * $LicenseInfo:firstyear=2008&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -/* Macro Definitions */ -#ifndef LL_LLTESTHTTPCLIENTADAPTER_H -#define LL_LLTESTHTTPCLIENTADAPTER_H - - -#include "linden_common.h" -#include "llhttpclientinterface.h" - -class LLTestHTTPClientAdapter : public LLHTTPClientInterface -{ -public: - LLTestHTTPClientAdapter(); - virtual ~LLTestHTTPClientAdapter(); - virtual void get(const std::string& url, LLCurl::ResponderPtr responder); - virtual void get(const std::string& url, LLCurl::ResponderPtr responder, const LLSD& headers); - - virtual void put(const std::string& url, const LLSD& body, LLCurl::ResponderPtr responder); - U32 putCalls() const; - - std::vector<LLSD> mPutBody; - std::vector<LLSD> mGetHeaders; - std::vector<std::string> mPutUrl; - std::vector<std::string> mGetUrl; - std::vector<LLCurl::ResponderPtr> mPutResponder; - std::vector<LLCurl::ResponderPtr> mGetResponder; -}; - - - -#endif //LL_LLSIMULATORPRESENCESENDER_H - diff --git a/indra/mac_crash_logger/CMakeLists.txt b/indra/mac_crash_logger/CMakeLists.txt index c59645bd70..ab20388261 100755 --- a/indra/mac_crash_logger/CMakeLists.txt +++ b/indra/mac_crash_logger/CMakeLists.txt @@ -4,6 +4,7 @@ project(mac_crash_logger) include(00-Common) include(LLCommon) +include(LLCoreHttp) include(LLCrashLogger) include(LLMath) include(LLMessage) @@ -11,8 +12,10 @@ include(LLVFS) include(LLXML) include(Linking) include(LLSharedLibs) +include(Boost) include_directories( + ${LLCOREHTTP_INCLUDE_DIRS} ${LLCOMMON_INCLUDE_DIRS} ${LLCRASHLOGGER_INCLUDE_DIRS} ${LLMATH_INCLUDE_DIRS} @@ -71,7 +74,10 @@ target_link_libraries(mac-crash-logger ${LLMESSAGE_LIBRARIES} ${LLVFS_LIBRARIES} ${LLMATH_LIBRARIES} + ${LLCOREHTTP_LIBRARIES} ${LLCOMMON_LIBRARIES} + ${BOOST_CONTEXT_LIBRARY} + ${BOOST_COROUTINE_LIBRARY} ) add_custom_command( diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 67c00576be..7a6267251e 100755 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -142,7 +142,6 @@ set(viewer_SOURCE_FILES llbuycurrencyhtml.cpp llcallbacklist.cpp llcallingcard.cpp - llcapabilitylistener.cpp llcaphttpsender.cpp llchannelmanager.cpp llchatbar.cpp @@ -152,7 +151,6 @@ set(viewer_SOURCE_FILES llchiclet.cpp llchicletbar.cpp llclassifiedinfo.cpp - llclassifiedstatsresponder.cpp llcofwearables.cpp llcolorswatch.cpp llcommanddispatcherlistener.cpp @@ -166,6 +164,7 @@ set(viewer_SOURCE_FILES llconversationloglistitem.cpp llconversationmodel.cpp llconversationview.cpp + llcoproceduremanager.cpp llcurrencyuimanager.cpp llcylinder.cpp lldateutil.cpp @@ -234,7 +233,6 @@ set(viewer_SOURCE_FILES llfloaterconversationpreview.cpp llfloaterdeleteenvpreset.cpp llfloaterdestinations.cpp - llfloaterdisplayname.cpp llfloatereditdaycycle.cpp llfloatereditsky.cpp llfloatereditwater.cpp @@ -333,7 +331,6 @@ set(viewer_SOURCE_FILES llgroupmgr.cpp llhasheduniqueid.cpp llhints.cpp - llhomelocationresponder.cpp llhttpretrypolicy.cpp llhudeffect.cpp llhudeffectbeam.cpp @@ -604,7 +601,6 @@ set(viewer_SOURCE_FILES lltwitterconnect.cpp lluilistener.cpp lluploaddialog.cpp - lluploadfloaterobservers.cpp llurl.cpp llurldispatcher.cpp llurldispatcherlistener.cpp @@ -617,6 +613,7 @@ set(viewer_SOURCE_FILES llviewerassetstats.cpp llviewerassetstorage.cpp llviewerassettype.cpp + llviewerassetupload.cpp llviewerattachmenu.cpp llvieweraudio.cpp llviewercamera.cpp @@ -624,7 +621,6 @@ set(viewer_SOURCE_FILES llviewercontrol.cpp llviewercontrollistener.cpp llviewerdisplay.cpp - llviewerdisplayname.cpp llviewerfloaterreg.cpp llviewerfoldertype.cpp llviewergenericmessage.cpp @@ -757,7 +753,6 @@ set(viewer_HEADER_FILES llbuycurrencyhtml.h llcallbacklist.h llcallingcard.h - llcapabilitylistener.h llcapabilityprovider.h llcaphttpsender.h llchannelmanager.h @@ -768,7 +763,6 @@ set(viewer_HEADER_FILES llchiclet.h llchicletbar.h llclassifiedinfo.h - llclassifiedstatsresponder.h llcofwearables.h llcolorswatch.h llcommanddispatcherlistener.h @@ -782,6 +776,7 @@ set(viewer_HEADER_FILES llconversationloglistitem.h llconversationmodel.h llconversationview.h + llcoproceduremanager.h llcurrencyuimanager.h llcylinder.h lldateutil.h @@ -850,7 +845,6 @@ set(viewer_HEADER_FILES llfloaterconversationpreview.h llfloaterdeleteenvpreset.h llfloaterdestinations.h - llfloaterdisplayname.h llfloatereditdaycycle.h llfloatereditsky.h llfloatereditwater.h @@ -952,7 +946,6 @@ set(viewer_HEADER_FILES llhasheduniqueid.h llhints.h llhttpretrypolicy.h - llhomelocationresponder.h llhudeffect.h llhudeffectbeam.h llhudeffectlookat.h @@ -1225,6 +1218,7 @@ set(viewer_HEADER_FILES llviewerassetstats.h llviewerassetstorage.h llviewerassettype.h + llviewerassetupload.h llviewerattachmenu.h llvieweraudio.h llviewercamera.h @@ -1232,7 +1226,6 @@ set(viewer_HEADER_FILES llviewercontrol.h llviewercontrollistener.h llviewerdisplay.h - llviewerdisplayname.h llviewerfloaterreg.h llviewerfoldertype.h llviewergenericmessage.h @@ -2238,9 +2231,9 @@ if (LL_TESTS) SET(viewer_TEST_SOURCE_FILES llagentaccess.cpp lldateutil.cpp - llmediadataclient.cpp +# llmediadataclient.cpp lllogininstance.cpp - llremoteparcelrequest.cpp +# llremoteparcelrequest.cpp lltranslate.cpp llviewerhelputil.cpp llversioninfo.cpp @@ -2271,7 +2264,7 @@ if (LL_TESTS) set_source_files_properties( llmediadataclient.cpp PROPERTIES - LL_TEST_ADDITIONAL_LIBRARIES "${CURL_LIBRARIES}" + LL_TEST_ADDITIONAL_LIBRARIES "${test_libs}" ) set_source_files_properties( @@ -2327,7 +2320,6 @@ if (LL_TESTS) LL_ADD_PROJECT_UNIT_TESTS(${VIEWER_BINARY_NAME} "${viewer_TEST_SOURCE_FILES}") #set(TEST_DEBUG on) - set(test_sources llcapabilitylistener.cpp) ################################################## # DISABLING PRECOMPILED HEADERS USAGE FOR TESTS ################################################## @@ -2343,26 +2335,29 @@ if (LL_TESTS) ${GOOGLEMOCK_LIBRARIES} ) - LL_ADD_INTEGRATION_TEST(llcapabilitylistener - "${test_sources}" - "${test_libs}" - ${PYTHON_EXECUTABLE} - "${CMAKE_SOURCE_DIR}/llmessage/tests/test_llsdmessage_peer.py" - ) + if (LINUX) + # llcommon uses `clock_gettime' which is provided by librt on linux. + set(LIBRT_LIBRARY + rt + ) + endif (LINUX) set(test_libs - ${LLMESSAGE_LIBRARIES} - ${LLCOREHTTP_LIBRARIES} ${WINDOWS_LIBRARIES} ${LLVFS_LIBRARIES} ${LLMATH_LIBRARIES} ${LLCOMMON_LIBRARIES} + ${LLMESSAGE_LIBRARIES} + ${LLCOREHTTP_LIBRARIES} ${GOOGLEMOCK_LIBRARIES} ${OPENSSL_LIBRARIES} ${CRYPTO_LIBRARIES} + ${LIBRT_LIBRARY} + ${BOOST_CONTEXT_LIBRARY} + ${BOOST_COROUTINE_LIBRARY} ) - LL_ADD_INTEGRATION_TEST(llsechandler_basic + LL_ADD_INTEGRATION_TEST(llsechandler_basic llsechandler_basic.cpp "${test_libs}" ) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 9d9bc43bd7..2180a7f1a1 100755 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -2370,6 +2370,17 @@ <key>Value</key> <integer>0</integer> </map> + <key>DebugSlshareLogTag</key> + <map> + <key>Comment</key> + <string>Request slshare-service debug logging</string> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>String</string> + <key>Value</key> + <string/> + </map> <key>DebugStatModeFPS</key> <map> <key>Comment</key> diff --git a/indra/newview/llaccountingcostmanager.cpp b/indra/newview/llaccountingcostmanager.cpp index a42286a9e4..cd9146ea16 100755 --- a/indra/newview/llaccountingcostmanager.cpp +++ b/indra/newview/llaccountingcostmanager.cpp @@ -27,90 +27,150 @@ #include "llviewerprecompiledheaders.h" #include "llaccountingcostmanager.h" #include "llagent.h" -#include "llcurl.h" -#include "llhttpclient.h" +#include "httpcommon.h" +#include "llcoros.h" +#include "lleventcoro.h" +#include "llcorehttputil.h" + //=============================================================================== -LLAccountingCostManager::LLAccountingCostManager() +LLAccountingCostManager::LLAccountingCostManager(): + mHttpRequest(), + mHttpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID) { + mHttpRequest = LLCore::HttpRequest::ptr_t(new LLCore::HttpRequest()); + //mHttpPolicy = LLCore::HttpRequest::DEFAULT_POLICY_ID; + } -//=============================================================================== -class LLAccountingCostResponder : public LLCurl::Responder + +// Coroutine for sending and processing avatar name cache requests. +// Do not call directly. See documentation in lleventcoro.h and llcoro.h for +// further explanation. +void LLAccountingCostManager::accountingCostCoro(std::string url, + eSelectionType selectionType, const LLHandle<LLAccountingCostObserver> observerHandle) { - LOG_CLASS(LLAccountingCostResponder); -public: - LLAccountingCostResponder( const LLSD& objectIDs, const LLHandle<LLAccountingCostObserver>& observer_handle ) - : mObjectIDs( objectIDs ), - mObserverHandle( observer_handle ) - { - LLAccountingCostObserver* observer = mObserverHandle.get(); - if (observer) - { - mTransactionID = observer->getTransactionID(); - } - } + LL_DEBUGS("LLAccountingCostManager") << "Entering coroutine " << LLCoros::instance().getName() + << " with url '" << url << LL_ENDL; - void clearPendingRequests ( void ) - { - for ( LLSD::array_iterator iter = mObjectIDs.beginArray(); iter != mObjectIDs.endArray(); ++iter ) - { - LLAccountingCostManager::getInstance()->removePendingObject( iter->asUUID() ); - } - } - -protected: - void httpFailure() - { - LL_WARNS() << dumpResponse() << LL_ENDL; - clearPendingRequests(); - - LLAccountingCostObserver* observer = mObserverHandle.get(); - if (observer && observer->getTransactionID() == mTransactionID) - { - observer->setErrorStatus(getStatus(), getReason()); - } - } - - void httpSuccess() - { - const LLSD& content = getContent(); - //Check for error - if ( !content.isMap() || content.has("error") ) - { - failureResult(HTTP_INTERNAL_ERROR, "Error on fetched data", content); - return; - } - else if (content.has("selected")) - { - F32 physicsCost = 0.0f; - F32 networkCost = 0.0f; - F32 simulationCost = 0.0f; - - physicsCost = content["selected"]["physics"].asReal(); - networkCost = content["selected"]["streaming"].asReal(); - simulationCost = content["selected"]["simulation"].asReal(); - - SelectionCost selectionCost( /*transactionID,*/ physicsCost, networkCost, simulationCost ); - - LLAccountingCostObserver* observer = mObserverHandle.get(); - if (observer && observer->getTransactionID() == mTransactionID) - { - observer->onWeightsUpdate(selectionCost); - } - } - - clearPendingRequests(); - } - -private: - //List of posted objects - LLSD mObjectIDs; + try + { + LLSD objectList; + U32 objectIndex = 0; + + IDIt IDIter = mObjectList.begin(); + IDIt IDIterEnd = mObjectList.end(); + + for (; IDIter != IDIterEnd; ++IDIter) + { + // Check to see if a request for this object has already been made. + if (mPendingObjectQuota.find(*IDIter) == mPendingObjectQuota.end()) + { + mPendingObjectQuota.insert(*IDIter); + objectList[objectIndex++] = *IDIter; + } + } + + mObjectList.clear(); + + //Post results + if (objectList.size() == 0) + return; + + std::string keystr; + if (selectionType == Roots) + { + keystr = "selected_roots"; + } + else if (selectionType == Prims) + { + keystr = "selected_prims"; + } + else + { + LL_INFOS() << "Invalid selection type " << LL_ENDL; + mObjectList.clear(); + mPendingObjectQuota.clear(); + return; + } - // Current request ID - LLUUID mTransactionID; + LLSD dataToPost = LLSD::emptyMap(); + dataToPost[keystr.c_str()] = objectList; + + LLAccountingCostObserver* observer = observerHandle.get(); + LLUUID transactionId = observer->getTransactionID(); + observer = NULL; + + LLCoreHttpUtil::HttpCoroutineAdapter httpAdapter("AccountingCost", mHttpPolicy); + + LLSD results = httpAdapter.postAndYield(mHttpRequest, url, dataToPost); + + LLSD httpResults; + httpResults = results["http_result"]; + + // do/while(false) allows error conditions to break out of following + // block while normal flow goes forward once. + do + { + observer = observerHandle.get(); + if ((!observer) || (observer->getTransactionID() != transactionId)) + { // *TODO: Rider: I've noticed that getTransactionID() does not + // always match transactionId (the new transaction Id does not show a + // corresponding request.) (ask Vir) + if (!observer) + break; + LL_WARNS() << "Request transaction Id(" << transactionId + << ") does not match observer's transaction Id(" + << observer->getTransactionID() << ")." << LL_ENDL; + break; + } + + if (!httpResults["success"].asBoolean()) + { + LL_WARNS() << "Error result from LLCoreHttpUtil::HttpCoroHandler. Code " + << httpResults["status"] << ": '" << httpResults["message"] << "'" << LL_ENDL; + if (observer) + { + observer->setErrorStatus(httpResults["status"].asInteger(), httpResults["message"].asStringRef()); + } + break; + } + + if (!results.isMap() || results.has("error")) + { + LL_WARNS() << "Error on fetched data" << LL_ENDL; + observer->setErrorStatus(499, "Error on fetched data"); + break; + } + + if (results.has("selected")) + { + F32 physicsCost = 0.0f; + F32 networkCost = 0.0f; + F32 simulationCost = 0.0f; + + physicsCost = results["selected"]["physics"].asReal(); + networkCost = results["selected"]["streaming"].asReal(); + simulationCost = results["selected"]["simulation"].asReal(); + + SelectionCost selectionCost( physicsCost, networkCost, simulationCost); + + observer->onWeightsUpdate(selectionCost); + } + + } while (false); + + } + catch (std::exception e) + { + LL_WARNS() << "Caught exception '" << e.what() << "'" << LL_ENDL; + } + catch (...) + { + LL_WARNS() << "Caught unknown exception." << LL_ENDL; + } + + mPendingObjectQuota.clear(); +} - // Cost update observer handle - LLHandle<LLAccountingCostObserver> mObserverHandle; -}; //=============================================================================== void LLAccountingCostManager::fetchCosts( eSelectionType selectionType, const std::string& url, @@ -119,50 +179,11 @@ void LLAccountingCostManager::fetchCosts( eSelectionType selectionType, // Invoking system must have already determined capability availability if ( !url.empty() ) { - LLSD objectList; - U32 objectIndex = 0; - - IDIt IDIter = mObjectList.begin(); - IDIt IDIterEnd = mObjectList.end(); - - for ( ; IDIter != IDIterEnd; ++IDIter ) - { - // Check to see if a request for this object has already been made. - if ( mPendingObjectQuota.find( *IDIter ) == mPendingObjectQuota.end() ) - { - mPendingObjectQuota.insert( *IDIter ); - objectList[objectIndex++] = *IDIter; - } - } - - mObjectList.clear(); - - //Post results - if ( objectList.size() > 0 ) - { - std::string keystr; - if ( selectionType == Roots ) - { - keystr="selected_roots"; - } - else - if ( selectionType == Prims ) - { - keystr="selected_prims"; - } - else - { - LL_INFOS()<<"Invalid selection type "<<LL_ENDL; - mObjectList.clear(); - mPendingObjectQuota.clear(); - return; - } - - LLSD dataToPost = LLSD::emptyMap(); - dataToPost[keystr.c_str()] = objectList; - - LLHTTPClient::post( url, dataToPost, new LLAccountingCostResponder( objectList, observer_handle )); - } + std::string coroname = + LLCoros::instance().launch("LLAccountingCostManager::accountingCostCoro", + boost::bind(&LLAccountingCostManager::accountingCostCoro, this, url, selectionType, observer_handle)); + LL_DEBUGS() << coroname << " with url '" << url << LL_ENDL; + } else { diff --git a/indra/newview/llaccountingcostmanager.h b/indra/newview/llaccountingcostmanager.h index 3ade34c81d..d5a94f6fda 100755 --- a/indra/newview/llaccountingcostmanager.h +++ b/indra/newview/llaccountingcostmanager.h @@ -30,6 +30,13 @@ #include "llhandle.h" #include "llaccountingcost.h" +#include "httpcommon.h" +#include "llcoros.h" +#include "lleventcoro.h" +#include "httprequest.h" +#include "httpheaders.h" +#include "httpoptions.h" + //=============================================================================== // An interface class for panels which display the parcel accounting information. class LLAccountingCostObserver @@ -69,6 +76,11 @@ private: //a fetch has been instigated. std::set<LLUUID> mPendingObjectQuota; typedef std::set<LLUUID>::iterator IDIt; + + void accountingCostCoro(std::string url, eSelectionType selectionType, const LLHandle<LLAccountingCostObserver> observerHandle); + + LLCore::HttpRequest::ptr_t mHttpRequest; + LLCore::HttpRequest::policy_t mHttpPolicy; }; //=============================================================================== diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index 359171c5bd..2060065c75 100755 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -38,7 +38,6 @@ #include "llappearancemgr.h" #include "llanimationstates.h" #include "llcallingcard.h" -#include "llcapabilitylistener.h" #include "llchannelmanager.h" #include "llchicletbar.h" #include "llconsole.h" @@ -52,7 +51,6 @@ #include "llfloatertools.h" #include "llgroupactions.h" #include "llgroupmgr.h" -#include "llhomelocationresponder.h" #include "llhudmanager.h" #include "lljoystickbutton.h" #include "llmorphview.h" @@ -63,7 +61,6 @@ #include "llpaneltopinfobar.h" #include "llparcel.h" #include "llrendersphere.h" -#include "llsdmessage.h" #include "llsdutil.h" #include "llsky.h" #include "llslurl.h" @@ -95,6 +92,7 @@ #include "lscript_byteformat.h" #include "stringize.h" #include "boost/foreach.hpp" +#include "llcorehttputil.h" using namespace LLAvatarAppearanceDefines; @@ -361,7 +359,8 @@ LLAgent::LLAgent() : mMaturityPreferenceNumRetries(0U), mLastKnownRequestMaturity(SIM_ACCESS_MIN), mLastKnownResponseMaturity(SIM_ACCESS_MIN), - mTeleportState( TELEPORT_NONE ), + mHttpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID), + mTeleportState(TELEPORT_NONE), mRegionp(NULL), mAgentOriginGlobal(), @@ -461,6 +460,10 @@ void LLAgent::init() mTeleportFailedSlot = LLViewerParcelMgr::getInstance()->setTeleportFailedCallback(boost::bind(&LLAgent::handleTeleportFailed, this)); } + LLAppCoreHttp & app_core_http(LLAppViewer::instance()->getAppCoreHttp()); + + mHttpPolicy = app_core_http.getPolicy(LLAppCoreHttp::AP_AGENT); + mInitialized = TRUE; } @@ -2326,27 +2329,9 @@ void LLAgent::setStartPosition( U32 location_id ) body["HomeLocation"] = homeLocation; - // This awkward idiom warrants explanation. - // For starters, LLSDMessage::ResponderAdapter is ONLY for testing the new - // LLSDMessage functionality with a pre-existing LLHTTPClient::Responder. - // In new code, define your reply/error methods on the same class as the - // sending method, bind them to local LLEventPump objects and pass those - // LLEventPump names in the request LLSD object. - // When testing old code, the new LLHomeLocationResponder object - // is referenced by an LLHTTPClient::ResponderPtr, so when the - // ResponderAdapter is deleted, the LLHomeLocationResponder will be too. - // We must trust that the underlying LLHTTPClient code will eventually - // fire either the reply callback or the error callback; either will cause - // the ResponderAdapter to delete itself. - LLSDMessage::ResponderAdapter* - adapter(new LLSDMessage::ResponderAdapter(new LLHomeLocationResponder())); - - request["message"] = "HomeLocation"; - request["payload"] = body; - request["reply"] = adapter->getReplyName(); - request["error"] = adapter->getErrorName(); - - gAgent.getRegion()->getCapAPI().post(request); + if (!requestPostCapability("HomeLocation", body, + boost::bind(&LLAgent::setStartPositionSuccess, this, _1))) + LL_WARNS() << "Unable to post to HomeLocation capability." << LL_ENDL; const U32 HOME_INDEX = 1; if( HOME_INDEX == location_id ) @@ -2355,32 +2340,51 @@ void LLAgent::setStartPosition( U32 location_id ) } } -struct HomeLocationMapper: public LLCapabilityListener::CapabilityMapper +void LLAgent::setStartPositionSuccess(const LLSD &result) { - // No reply message expected - HomeLocationMapper(): LLCapabilityListener::CapabilityMapper("HomeLocation") {} - virtual void buildMessage(LLMessageSystem* msg, - const LLUUID& agentID, - const LLUUID& sessionID, - const std::string& capabilityName, - const LLSD& payload) const + LLVector3 agent_pos; + bool error = true; + + do { + // was the call to /agent/<agent-id>/home-location successful? + // If not, we keep error set to true + if (!result.has("success")) + break; + + if (0 != strncmp("true", result["success"].asString().c_str(), 4)) + break; + + // did the simulator return a "justified" home location? + // If no, we keep error set to true + if (!result.has("HomeLocation")) + break; + + if ((!result["HomeLocation"].has("LocationPos")) || + (!result["HomeLocation"]["LocationPos"].has("X")) || + (!result["HomeLocation"]["LocationPos"].has("Y")) || + (!result["HomeLocation"]["LocationPos"].has("Z"))) + break; + + agent_pos.mV[VX] = result["HomeLocation"]["LocationPos"]["X"].asInteger(); + agent_pos.mV[VY] = result["HomeLocation"]["LocationPos"]["Y"].asInteger(); + agent_pos.mV[VZ] = result["HomeLocation"]["LocationPos"]["Z"].asInteger(); + + error = false; + + } while (0); + + if (error) { - msg->newMessageFast(_PREHASH_SetStartLocationRequest); - msg->nextBlockFast( _PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, agentID); - msg->addUUIDFast(_PREHASH_SessionID, sessionID); - msg->nextBlockFast( _PREHASH_StartLocationData); - // corrected by sim - msg->addStringFast(_PREHASH_SimName, ""); - msg->addU32Fast(_PREHASH_LocationID, payload["HomeLocation"]["LocationId"].asInteger()); - msg->addVector3Fast(_PREHASH_LocationPos, - ll_vector3_from_sdmap(payload["HomeLocation"]["LocationPos"])); - msg->addVector3Fast(_PREHASH_LocationLookAt, - ll_vector3_from_sdmap(payload["HomeLocation"]["LocationLookAt"])); + LL_WARNS() << "Error in response to home position set." << LL_ENDL; } -}; -// Need an instance of this class so it will self-register -static HomeLocationMapper homeLocationMapper; + else + { + LL_INFOS() << "setting home position" << LL_ENDL; + + LLViewerRegion *viewer_region = gAgent.getRegion(); + setHomePosRegion(viewer_region->getHandle(), agent_pos); + } +} void LLAgent::requestStopMotion( LLMotion* motion ) { @@ -2517,87 +2521,6 @@ int LLAgent::convertTextToMaturity(char text) return LLAgentAccess::convertTextToMaturity(text); } -class LLMaturityPreferencesResponder : public LLHTTPClient::Responder -{ - LOG_CLASS(LLMaturityPreferencesResponder); -public: - LLMaturityPreferencesResponder(LLAgent *pAgent, U8 pPreferredMaturity, U8 pPreviousMaturity); - virtual ~LLMaturityPreferencesResponder(); - -protected: - virtual void httpSuccess(); - virtual void httpFailure(); - -protected: - -private: - U8 parseMaturityFromServerResponse(const LLSD &pContent) const; - - LLAgent *mAgent; - U8 mPreferredMaturity; - U8 mPreviousMaturity; -}; - -LLMaturityPreferencesResponder::LLMaturityPreferencesResponder(LLAgent *pAgent, U8 pPreferredMaturity, U8 pPreviousMaturity) - : LLHTTPClient::Responder(), - mAgent(pAgent), - mPreferredMaturity(pPreferredMaturity), - mPreviousMaturity(pPreviousMaturity) -{ -} - -LLMaturityPreferencesResponder::~LLMaturityPreferencesResponder() -{ -} - -void LLMaturityPreferencesResponder::httpSuccess() -{ - U8 actualMaturity = parseMaturityFromServerResponse(getContent()); - - if (actualMaturity != mPreferredMaturity) - { - LL_WARNS() << "while attempting to change maturity preference from '" - << LLViewerRegion::accessToString(mPreviousMaturity) - << "' to '" << LLViewerRegion::accessToString(mPreferredMaturity) - << "', the server responded with '" - << LLViewerRegion::accessToString(actualMaturity) - << "' [value:" << static_cast<U32>(actualMaturity) - << "], " << dumpResponse() << LL_ENDL; - } - mAgent->handlePreferredMaturityResult(actualMaturity); -} - -void LLMaturityPreferencesResponder::httpFailure() -{ - LL_WARNS() << "while attempting to change maturity preference from '" - << LLViewerRegion::accessToString(mPreviousMaturity) - << "' to '" << LLViewerRegion::accessToString(mPreferredMaturity) - << "', " << dumpResponse() << LL_ENDL; - mAgent->handlePreferredMaturityError(); -} - -U8 LLMaturityPreferencesResponder::parseMaturityFromServerResponse(const LLSD &pContent) const -{ - U8 maturity = SIM_ACCESS_MIN; - - llassert(pContent.isDefined()); - llassert(pContent.isMap()); - llassert(pContent.has("access_prefs")); - llassert(pContent.get("access_prefs").isMap()); - llassert(pContent.get("access_prefs").has("max")); - llassert(pContent.get("access_prefs").get("max").isString()); - if (pContent.isDefined() && pContent.isMap() && pContent.has("access_prefs") - && pContent.get("access_prefs").isMap() && pContent.get("access_prefs").has("max") - && pContent.get("access_prefs").get("max").isString()) - { - LLSD::String actualPreference = pContent.get("access_prefs").get("max").asString(); - LLStringUtil::trim(actualPreference); - maturity = LLViewerRegion::shortStringToAccess(actualPreference); - } - - return maturity; -} - void LLAgent::handlePreferredMaturityResult(U8 pServerMaturity) { // Update the number of responses received @@ -2726,42 +2649,96 @@ void LLAgent::sendMaturityPreferenceToServer(U8 pPreferredMaturity) // Update the last know maturity request mLastKnownRequestMaturity = pPreferredMaturity; - // Create a response handler - LLHTTPClient::ResponderPtr responderPtr = LLHTTPClient::ResponderPtr(new LLMaturityPreferencesResponder(this, pPreferredMaturity, mLastKnownResponseMaturity)); - // If we don't have a region, report it as an error if (getRegion() == NULL) { - responderPtr->failureResult(0U, "region is not defined", LLSD()); + LL_WARNS("Agent") << "Region is not defined, can not change Maturity setting." << LL_ENDL; + return; } - else - { - // Find the capability to send maturity preference - std::string url = getRegion()->getCapability("UpdateAgentInformation"); - // If the capability is not defined, report it as an error - if (url.empty()) - { - responderPtr->failureResult(0U, - "capability 'UpdateAgentInformation' is not defined for region", LLSD()); - } - else - { - // Set new access preference - LLSD access_prefs = LLSD::emptyMap(); - access_prefs["max"] = LLViewerRegion::accessToShortString(pPreferredMaturity); - - LLSD body = LLSD::emptyMap(); - body["access_prefs"] = access_prefs; - LL_INFOS() << "Sending viewer preferred maturity to '" << LLViewerRegion::accessToString(pPreferredMaturity) - << "' via capability to: " << url << LL_ENDL; - LLSD headers; - LLHTTPClient::post(url, body, responderPtr, headers, 30.0f); - } - } + LLSD access_prefs = LLSD::emptyMap(); + access_prefs["max"] = LLViewerRegion::accessToShortString(pPreferredMaturity); + + LLSD postData = LLSD::emptyMap(); + postData["access_prefs"] = access_prefs; + LL_INFOS() << "Sending viewer preferred maturity to '" << LLViewerRegion::accessToString(pPreferredMaturity) << LL_ENDL; + + if (!requestPostCapability("UpdateAgentInformation", postData, + static_cast<httpCallback_t>(boost::bind(&LLAgent::processMaturityPreferenceFromServer, this, _1, pPreferredMaturity)), + static_cast<httpCallback_t>(boost::bind(&LLAgent::handlePreferredMaturityError, this)) + )) + { + LL_WARNS("Agent") << "Maturity request post failed." << LL_ENDL; + } } } + +void LLAgent::processMaturityPreferenceFromServer(const LLSD &result, U8 perferredMaturity) +{ + U8 maturity = SIM_ACCESS_MIN; + + llassert(result.isDefined()); + llassert(result.isMap()); + llassert(result.has("access_prefs")); + llassert(result.get("access_prefs").isMap()); + llassert(result.get("access_prefs").has("max")); + llassert(result.get("access_prefs").get("max").isString()); + if (result.isDefined() && result.isMap() && result.has("access_prefs") + && result.get("access_prefs").isMap() && result.get("access_prefs").has("max") + && result.get("access_prefs").get("max").isString()) + { + LLSD::String actualPreference = result.get("access_prefs").get("max").asString(); + LLStringUtil::trim(actualPreference); + maturity = LLViewerRegion::shortStringToAccess(actualPreference); + } + + if (maturity != perferredMaturity) + { + LL_WARNS() << "while attempting to change maturity preference from '" + << LLViewerRegion::accessToString(mLastKnownResponseMaturity) + << "' to '" << LLViewerRegion::accessToString(perferredMaturity) + << "', the server responded with '" + << LLViewerRegion::accessToString(maturity) + << "' [value:" << static_cast<U32>(maturity) + << "], " << LL_ENDL; + } + handlePreferredMaturityResult(maturity); +} + + +bool LLAgent::requestPostCapability(const std::string &capName, LLSD &postData, httpCallback_t cbSuccess, httpCallback_t cbFailure) +{ + std::string url; + + url = getRegion()->getCapability(capName); + + if (url.empty()) + { + LL_WARNS("Agent") << "Could not retrieve region capability \"" << capName << "\"" << LL_ENDL; + return false; + } + + LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost(url, mHttpPolicy, postData, cbSuccess, cbFailure); + return true; +} + +bool LLAgent::requestGetCapability(const std::string &capName, httpCallback_t cbSuccess, httpCallback_t cbFailure) +{ + std::string url; + + url = getRegion()->getCapability(capName); + + if (url.empty()) + { + LL_WARNS("Agent") << "Could not retrieve region capability \"" << capName << "\"" << LL_ENDL; + return false; + } + + LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpGet(url, mHttpPolicy, cbSuccess, cbFailure); + return true; +} + BOOL LLAgent::getAdminOverride() const { return mAgentAccess->getAdminOverride(); diff --git a/indra/newview/llagent.h b/indra/newview/llagent.h index 4830cb754b..0ba3dea427 100755 --- a/indra/newview/llagent.h +++ b/indra/newview/llagent.h @@ -34,7 +34,9 @@ #include "llcoordframe.h" // for mFrameAgent #include "llavatarappearancedefines.h" #include "llpermissionsflags.h" +#include "llevents.h" #include "v3dmath.h" +#include "llcorehttputil.h" #include <boost/function.hpp> #include <boost/shared_ptr.hpp> @@ -61,6 +63,8 @@ class LLPauseRequestHandle; class LLUIColor; class LLTeleportRequest; + + typedef boost::shared_ptr<LLTeleportRequest> LLTeleportRequestPtr; //-------------------------------------------------------------------- @@ -112,6 +116,8 @@ public: void init(); void cleanup(); +private: + //-------------------------------------------------------------------- // Login //-------------------------------------------------------------------- @@ -227,6 +233,8 @@ public: void setHomePosRegion(const U64& region_handle, const LLVector3& pos_region); BOOL getHomePosGlobal(LLVector3d* pos_global); private: + void setStartPositionSuccess(const LLSD &result); + BOOL mHaveHomePosition; U64 mHomeRegionHandle; LLVector3 mHomePosRegion; @@ -631,6 +639,8 @@ public: void setMaturityRatingChangeDuringTeleport(U8 pMaturityRatingChange); private: + + friend class LLTeleportRequest; friend class LLTeleportRequestViaLandmark; friend class LLTeleportRequestViaLure; @@ -758,11 +768,12 @@ private: unsigned int mMaturityPreferenceNumRetries; U8 mLastKnownRequestMaturity; U8 mLastKnownResponseMaturity; + LLCore::HttpRequest::policy_t mHttpPolicy; bool isMaturityPreferenceSyncedWithServer() const; void sendMaturityPreferenceToServer(U8 pPreferredMaturity); + void processMaturityPreferenceFromServer(const LLSD &result, U8 perferredMaturity); - friend class LLMaturityPreferencesResponder; void handlePreferredMaturityResult(U8 pServerMaturity); void handlePreferredMaturityError(); void reportPreferredMaturitySuccess(); @@ -911,6 +922,22 @@ public: /******************************************************************************** ** ** + ** UTILITY + **/ +public: + typedef LLCoreHttpUtil::HttpCoroutineAdapter::completionCallback_t httpCallback_t; + + /// Utilities for allowing the the agent sub managers to post and get via + /// HTTP using the agent's policy settings and headers. + bool requestPostCapability(const std::string &capName, LLSD &postData, httpCallback_t cbSuccess = NULL, httpCallback_t cbFailure = NULL); + bool requestGetCapability(const std::string &capName, httpCallback_t cbSuccess = NULL, httpCallback_t cbFailure = NULL); + +/** Utility + ** ** + *******************************************************************************/ + +/******************************************************************************** + ** ** ** DEBUGGING **/ diff --git a/indra/newview/llagentlanguage.cpp b/indra/newview/llagentlanguage.cpp index fe6236a32a..cdb0e3302d 100755 --- a/indra/newview/llagentlanguage.cpp +++ b/indra/newview/llagentlanguage.cpp @@ -32,6 +32,7 @@ #include "llviewerregion.h" // library includes #include "llui.h" // getLanguage() +#include "httpcommon.h" // static void LLAgentLanguage::init() @@ -54,22 +55,17 @@ void LLAgentLanguage::onChange() // static bool LLAgentLanguage::update() { - LLSD body; - std::string url; + LLSD body; - if (gAgent.getRegion()) - { - url = gAgent.getRegion()->getCapability("UpdateAgentLanguage"); - } - - if (!url.empty()) - { - std::string language = LLUI::getLanguage(); + std::string language = LLUI::getLanguage(); - body["language"] = language; - body["language_is_public"] = gSavedSettings.getBOOL("LanguageIsPublic"); + body["language"] = language; + body["language_is_public"] = gSavedSettings.getBOOL("LanguageIsPublic"); - LLHTTPClient::post(url, body, new LLHTTPClient::Responder); - } + if (!gAgent.requestPostCapability("UpdateAgentLanguage", body)) + { + LL_WARNS("Language") << "Language capability unavailable." << LL_ENDL; + } + return true; } diff --git a/indra/newview/llappcorehttp.cpp b/indra/newview/llappcorehttp.cpp index f5f224b83e..51cca273d8 100755 --- a/indra/newview/llappcorehttp.cpp +++ b/indra/newview/llappcorehttp.cpp @@ -31,6 +31,10 @@ #include "llappviewer.h" #include "llviewercontrol.h" +#include <openssl/x509_vfy.h> +#include <openssl/ssl.h> +#include "llsecapi.h" +#include <curl/curl.h> // Here is where we begin to get our connection usage under control. // This establishes llcorehttp policy classes that, among other @@ -93,6 +97,16 @@ static const struct 4, 1, 4, 0, false, "", "inventory" + }, + { // AP_MATERIALS + 2, 1, 8, 0, false, + "RenderMaterials", + "material manager requests" + }, + { // AP_AGENT + 2, 1, 32, 0, true, + "Agent", + "Agent requests" } }; @@ -151,6 +165,15 @@ void LLAppCoreHttp::init() << LL_ENDL; } + // Set up SSL Verification call back. + status = LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_SSL_VERIFY_CALLBACK, + LLCore::HttpRequest::GLOBAL_POLICY_ID, + sslVerify, NULL); + if (!status) + { + LL_WARNS("Init") << "Failed to set SSL Verification. Reason: " << status.toString() << LL_ENDL; + } + // Tracing levels for library & libcurl (note that 2 & 3 are beyond spammy): // 0 - None // 1 - Basic start, stop simple transitions @@ -182,6 +205,8 @@ void LLAppCoreHttp::init() } mHttpClasses[app_policy].mPolicy = LLCore::HttpRequest::createPolicyClass(); + // We have run out of available HTTP policies. Adjust HTTP_POLICY_CLASS_LIMIT in _httpinternal.h + llassert(mHttpClasses[app_policy].mPolicy != LLCore::HttpRequest::INVALID_POLICY_ID); if (! mHttpClasses[app_policy].mPolicy) { // Use default policy (but don't accidentally modify default) @@ -457,6 +482,62 @@ void LLAppCoreHttp::refreshSettings(bool initial) } } +LLCore::HttpStatus LLAppCoreHttp::sslVerify(const std::string &url, + LLCore::HttpHandler const * const handler, void *appdata) +{ + X509_STORE_CTX *ctx = static_cast<X509_STORE_CTX *>(appdata); + LLCore::HttpStatus result; + LLPointer<LLCertificateStore> store = gSecAPIHandler->getCertificateStore(""); + LLPointer<LLCertificateChain> chain = gSecAPIHandler->getCertificateChain(ctx); + LLSD validation_params = LLSD::emptyMap(); + LLURI uri(url); + + validation_params[CERT_HOSTNAME] = uri.hostName(); + + // *TODO: In the case of an exception while validating the cert, we need a way + // to pass the offending(?) cert back out. *Rider* + + try + { + // don't validate hostname. Let libcurl do it instead. That way, it'll handle redirects + store->validate(VALIDATION_POLICY_SSL & (~VALIDATION_POLICY_HOSTNAME), chain, validation_params); + } + catch (LLCertValidationTrustException &cert_exception) + { + // this exception is is handled differently than the general cert + // exceptions, as we allow the user to actually add the certificate + // for trust. + // therefore we pass back a different error code + // NOTE: We're currently 'wired' to pass around CURL error codes. This is + // somewhat clumsy, as we may run into errors that do not map directly to curl + // error codes. Should be refactored with login refactoring, perhaps. + result = LLCore::HttpStatus(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_SSL_CACERT); + result.setMessage(cert_exception.getMessage()); + LLPointer<LLCertificate> cert = cert_exception.getCert(); + cert->ref(); // adding an extra ref here + result.setErrorData(cert.get()); + // We should probably have a more generic way of passing information + // back to the error handlers. + } + catch (LLCertException &cert_exception) + { + result = LLCore::HttpStatus(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_SSL_PEER_CERTIFICATE); + result.setMessage(cert_exception.getMessage()); + LLPointer<LLCertificate> cert = cert_exception.getCert(); + cert->ref(); // adding an extra ref here + result.setErrorData(cert.get()); + } + catch (...) + { + // any other odd error, we just handle as a connect error. + result = LLCore::HttpStatus(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_SSL_CONNECT_ERROR); + } + + return result; +} + + + void LLAppCoreHttp::onCompleted(LLCore::HttpHandle, LLCore::HttpResponse *) { diff --git a/indra/newview/llappcorehttp.h b/indra/newview/llappcorehttp.h index 37d7a737e7..410d7c6b07 100755 --- a/indra/newview/llappcorehttp.h +++ b/indra/newview/llappcorehttp.h @@ -164,7 +164,29 @@ public: /// Pipelined: no AP_INVENTORY, AP_REPORTING = AP_INVENTORY, // Piggy-back on inventory - + + /// Material resource requests and puts. + /// + /// Destination: simhost:12043 + /// Protocol: https: + /// Transfer size: KB + /// Long poll: no + /// Concurrency: low + /// Request rate: low + /// Pipelined: no + AP_MATERIALS, + + /// Appearance resource requests and puts. + /// + /// Destination: simhost:12043 + /// Protocol: https: + /// Transfer size: KB + /// Long poll: no + /// Concurrency: mid + /// Request rate: low + /// Pipelined: yes + AP_AGENT, + AP_COUNT // Must be last }; @@ -233,7 +255,9 @@ private: bool mStopped; HttpClass mHttpClasses[AP_COUNT]; bool mPipelined; // Global setting - boost::signals2::connection mPipelinedSignal; // Signal for 'HttpPipelining' setting + boost::signals2::connection mPipelinedSignal; // Signal for 'HttpPipelining' setting + + static LLCore::HttpStatus sslVerify(const std::string &uri, LLCore::HttpHandler const * const handler, void *appdata); }; diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index 3be705dd95..ff420a3600 100755 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -23,7 +23,7 @@ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ - + #include "llviewerprecompiledheaders.h" #include <boost/lexical_cast.hpp> @@ -54,6 +54,9 @@ #include "llsdserialize.h" #include "llhttpretrypolicy.h" #include "llaisapi.h" +#include "llhttpsdhandler.h" +#include "llcorehttputil.h" +#include "llappviewer.h" #if LL_MSVC // disable boost::lexical_cast warning @@ -1257,6 +1260,8 @@ static void removeDuplicateItems(LLInventoryModel::item_array_t& items) items = new_items; } +//========================================================================= + const LLUUID LLAppearanceMgr::getCOF() const { return gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT); @@ -3261,276 +3266,6 @@ void LLAppearanceMgr::updateClothingOrderingInfo(LLUUID cat_id, } -class RequestAgentUpdateAppearanceResponder: public LLHTTPClient::Responder -{ - LOG_CLASS(RequestAgentUpdateAppearanceResponder); - - friend class LLAppearanceMgr; - -public: - RequestAgentUpdateAppearanceResponder(); - - virtual ~RequestAgentUpdateAppearanceResponder(); - -private: - // Called when sendServerAppearanceUpdate called. May or may not - // trigger a request depending on various bits of state. - void onRequestRequested(); - - // Post the actual appearance request to cap. - void sendRequest(); - - void debugCOF(const LLSD& content); - -protected: - // Successful completion. - /* virtual */ void httpSuccess(); - - // Error - /*virtual*/ void httpFailure(); - - void onFailure(); - void onSuccess(); - - S32 mInFlightCounter; - LLTimer mInFlightTimer; - LLPointer<LLHTTPRetryPolicy> mRetryPolicy; -}; - -RequestAgentUpdateAppearanceResponder::RequestAgentUpdateAppearanceResponder() -{ - bool retry_on_4xx = true; - mRetryPolicy = new LLAdaptiveRetryPolicy(1.0, 32.0, 2.0, 10, retry_on_4xx); - mInFlightCounter = 0; -} - -RequestAgentUpdateAppearanceResponder::~RequestAgentUpdateAppearanceResponder() -{ -} - -void RequestAgentUpdateAppearanceResponder::onRequestRequested() -{ - // If we have already received an update for this or higher cof version, ignore. - S32 cof_version = LLAppearanceMgr::instance().getCOFVersion(); - S32 last_rcv = gAgentAvatarp->mLastUpdateReceivedCOFVersion; - S32 last_req = gAgentAvatarp->mLastUpdateRequestCOFVersion; - LL_DEBUGS("Avatar") << "cof_version " << cof_version - << " last_rcv " << last_rcv - << " last_req " << last_req - << " in flight " << mInFlightCounter << LL_ENDL; - if ((mInFlightCounter>0) && (mInFlightTimer.hasExpired())) - { - LL_WARNS("Avatar") << "in flight timer expired, resetting " << LL_ENDL; - mInFlightCounter = 0; - } - if (cof_version < last_rcv) - { - LL_DEBUGS("Avatar") << "Have already received update for cof version " << last_rcv - << " will not request for " << cof_version << LL_ENDL; - return; - } - if (mInFlightCounter>0 && last_req >= cof_version) - { - LL_DEBUGS("Avatar") << "Request already in flight for cof version " << last_req - << " will not request for " << cof_version << LL_ENDL; - return; - } - - // Actually send the request. - LL_DEBUGS("Avatar") << "ATT sending bake request for cof_version " << cof_version << LL_ENDL; - mRetryPolicy->reset(); - sendRequest(); -} - -void RequestAgentUpdateAppearanceResponder::sendRequest() -{ - if (gAgentAvatarp->isEditingAppearance()) - { - // don't send out appearance updates if in appearance editing mode - return; - } - - if (!gAgent.getRegion()) - { - LL_WARNS() << "Region not set, cannot request server appearance update" << LL_ENDL; - return; - } - if (gAgent.getRegion()->getCentralBakeVersion()==0) - { - LL_WARNS() << "Region does not support baking" << LL_ENDL; - } - std::string url = gAgent.getRegion()->getCapability("UpdateAvatarAppearance"); - if (url.empty()) - { - LL_WARNS() << "No cap for UpdateAvatarAppearance." << LL_ENDL; - return; - } - - LLSD body; - S32 cof_version = LLAppearanceMgr::instance().getCOFVersion(); - if (gSavedSettings.getBOOL("DebugAvatarExperimentalServerAppearanceUpdate")) - { - body = LLAppearanceMgr::instance().dumpCOF(); - } - else - { - body["cof_version"] = cof_version; - if (gSavedSettings.getBOOL("DebugForceAppearanceRequestFailure")) - { - body["cof_version"] = cof_version+999; - } - } - LL_DEBUGS("Avatar") << "request url " << url << " my_cof_version " << cof_version << LL_ENDL; - - mInFlightCounter++; - mInFlightTimer.setTimerExpirySec(60.0); - LLHTTPClient::post(url, body, this); - llassert(cof_version >= gAgentAvatarp->mLastUpdateRequestCOFVersion); - gAgentAvatarp->mLastUpdateRequestCOFVersion = cof_version; -} - -void RequestAgentUpdateAppearanceResponder::debugCOF(const LLSD& content) -{ - LL_INFOS("Avatar") << "AIS COF, version received: " << content["expected"].asInteger() - << " ================================= " << LL_ENDL; - std::set<LLUUID> ais_items, local_items; - const LLSD& cof_raw = content["cof_raw"]; - for (LLSD::array_const_iterator it = cof_raw.beginArray(); - it != cof_raw.endArray(); ++it) - { - const LLSD& item = *it; - if (item["parent_id"].asUUID() == LLAppearanceMgr::instance().getCOF()) - { - ais_items.insert(item["item_id"].asUUID()); - if (item["type"].asInteger() == 24) // link - { - LL_INFOS("Avatar") << "AIS Link: item_id: " << item["item_id"].asUUID() - << " linked_item_id: " << item["asset_id"].asUUID() - << " name: " << item["name"].asString() - << LL_ENDL; - } - else if (item["type"].asInteger() == 25) // folder link - { - LL_INFOS("Avatar") << "AIS Folder link: item_id: " << item["item_id"].asUUID() - << " linked_item_id: " << item["asset_id"].asUUID() - << " name: " << item["name"].asString() - << LL_ENDL; - } - else - { - LL_INFOS("Avatar") << "AIS Other: item_id: " << item["item_id"].asUUID() - << " linked_item_id: " << item["asset_id"].asUUID() - << " name: " << item["name"].asString() - << " type: " << item["type"].asInteger() - << LL_ENDL; - } - } - } - LL_INFOS("Avatar") << LL_ENDL; - LL_INFOS("Avatar") << "Local COF, version requested: " << content["observed"].asInteger() - << " ================================= " << LL_ENDL; - LLInventoryModel::cat_array_t cat_array; - LLInventoryModel::item_array_t item_array; - gInventory.collectDescendents(LLAppearanceMgr::instance().getCOF(), - cat_array,item_array,LLInventoryModel::EXCLUDE_TRASH); - for (S32 i=0; i<item_array.size(); i++) - { - const LLViewerInventoryItem* inv_item = item_array.at(i).get(); - local_items.insert(inv_item->getUUID()); - LL_INFOS("Avatar") << "LOCAL: item_id: " << inv_item->getUUID() - << " linked_item_id: " << inv_item->getLinkedUUID() - << " name: " << inv_item->getName() - << " parent: " << inv_item->getParentUUID() - << LL_ENDL; - } - LL_INFOS("Avatar") << " ================================= " << LL_ENDL; - S32 local_only = 0, ais_only = 0; - for (std::set<LLUUID>::iterator it = local_items.begin(); it != local_items.end(); ++it) - { - if (ais_items.find(*it) == ais_items.end()) - { - LL_INFOS("Avatar") << "LOCAL ONLY: " << *it << LL_ENDL; - local_only++; - } - } - for (std::set<LLUUID>::iterator it = ais_items.begin(); it != ais_items.end(); ++it) - { - if (local_items.find(*it) == local_items.end()) - { - LL_INFOS("Avatar") << "AIS ONLY: " << *it << LL_ENDL; - ais_only++; - } - } - if (local_only==0 && ais_only==0) - { - LL_INFOS("Avatar") << "COF contents identical, only version numbers differ (req " - << content["observed"].asInteger() - << " rcv " << content["expected"].asInteger() - << ")" << LL_ENDL; - } -} - -/* virtual */ void RequestAgentUpdateAppearanceResponder::httpSuccess() -{ - const LLSD& content = getContent(); - if (!content.isMap()) - { - failureResult(HTTP_INTERNAL_ERROR, "Malformed response contents", content); - return; - } - if (content["success"].asBoolean()) - { - LL_DEBUGS("Avatar") << "succeeded" << LL_ENDL; - if (gSavedSettings.getBOOL("DebugAvatarAppearanceMessage")) - { - dump_sequential_xml(gAgentAvatarp->getFullname() + "_appearance_request_ok", content); - } - - onSuccess(); - } - else - { - failureResult(HTTP_INTERNAL_ERROR, "Non-success response", content); - } -} - -void RequestAgentUpdateAppearanceResponder::onSuccess() -{ - mInFlightCounter = llmax(mInFlightCounter-1,0); -} - -/*virtual*/ void RequestAgentUpdateAppearanceResponder::httpFailure() -{ - LL_WARNS("Avatar") << "appearance update request failed, status " - << getStatus() << " reason " << getReason() << LL_ENDL; - - if (gSavedSettings.getBOOL("DebugAvatarAppearanceMessage")) - { - const LLSD& content = getContent(); - dump_sequential_xml(gAgentAvatarp->getFullname() + "_appearance_request_error", content); - debugCOF(content); - } - onFailure(); -} - -void RequestAgentUpdateAppearanceResponder::onFailure() -{ - mInFlightCounter = llmax(mInFlightCounter-1,0); - - F32 seconds_to_wait; - mRetryPolicy->onFailure(getStatus(), getResponseHeaders()); - if (mRetryPolicy->shouldRetry(seconds_to_wait)) - { - LL_INFOS() << "retrying" << LL_ENDL; - doAfterInterval(boost::bind(&RequestAgentUpdateAppearanceResponder::sendRequest,this), - seconds_to_wait); - } - else - { - LL_WARNS() << "giving up after too many retries" << LL_ENDL; - } -} - LLSD LLAppearanceMgr::dumpCOF() const { @@ -3597,102 +3332,201 @@ LLSD LLAppearanceMgr::dumpCOF() const void LLAppearanceMgr::requestServerAppearanceUpdate() { - mAppearanceResponder->onRequestRequested(); -} -class LLIncrementCofVersionResponder : public LLHTTPClient::Responder -{ - LOG_CLASS(LLIncrementCofVersionResponder); -public: - LLIncrementCofVersionResponder() : LLHTTPClient::Responder() + if (!testCOFRequestVersion()) { - mRetryPolicy = new LLAdaptiveRetryPolicy(1.0, 16.0, 2.0, 5); + // *TODO: LL_LOG message here + return; } - virtual ~LLIncrementCofVersionResponder() + if ((mInFlightCounter > 0) && (mInFlightTimer.hasExpired())) { + LL_WARNS("Avatar") << "in flight timer expired, resetting " << LL_ENDL; + mInFlightCounter = 0; } -protected: - virtual void httpSuccess() + if (gAgentAvatarp->isEditingAppearance()) { - LL_INFOS() << "Successfully incremented agent's COF." << LL_ENDL; - const LLSD& content = getContent(); - if (!content.isMap()) - { - failureResult(HTTP_INTERNAL_ERROR, "Malformed response contents", content); - return; - } - S32 new_version = content["category"]["version"].asInteger(); - - // cof_version should have increased - llassert(new_version > gAgentAvatarp->mLastUpdateRequestCOFVersion); - - gAgentAvatarp->mLastUpdateRequestCOFVersion = new_version; + LL_WARNS("Avatar") << "Avatar editing appearance, not sending request." << LL_ENDL; + // don't send out appearance updates if in appearance editing mode + return; } - virtual void httpFailure() + if (!gAgent.getRegion()) { - LL_WARNS("Avatar") << "While attempting to increment the agent's cof we got an error " - << dumpResponse() << LL_ENDL; - F32 seconds_to_wait; - mRetryPolicy->onFailure(getStatus(), getResponseHeaders()); - if (mRetryPolicy->shouldRetry(seconds_to_wait)) - { - LL_INFOS() << "retrying" << LL_ENDL; - doAfterInterval(boost::bind(&LLAppearanceMgr::incrementCofVersion, - LLAppearanceMgr::getInstance(), - LLHTTPClient::ResponderPtr(this)), - seconds_to_wait); - } - else - { - LL_WARNS() << "giving up after too many retries" << LL_ENDL; - } + LL_WARNS("Avatar") << "Region not set, cannot request server appearance update" << LL_ENDL; + return; } - -private: - LLPointer<LLHTTPRetryPolicy> mRetryPolicy; -}; - -void LLAppearanceMgr::incrementCofVersion(LLHTTPClient::ResponderPtr responder_ptr) -{ - // If we don't have a region, report it as an error - if (gAgent.getRegion() == NULL) + if (gAgent.getRegion()->getCentralBakeVersion() == 0) { - LL_WARNS() << "Region not set, cannot request cof_version increment" << LL_ENDL; - return; + LL_WARNS("Avatar") << "Region does not support baking" << LL_ENDL; } - std::string url = gAgent.getRegion()->getCapability("IncrementCofVersion"); - if (url.empty()) + LLSD postData; + S32 cof_version = LLAppearanceMgr::instance().getCOFVersion(); + if (gSavedSettings.getBOOL("DebugAvatarExperimentalServerAppearanceUpdate")) { - LL_WARNS() << "No cap for IncrementCofVersion." << LL_ENDL; - return; + postData = LLAppearanceMgr::instance().dumpCOF(); } - - LL_INFOS() << "Requesting cof_version be incremented via capability to: " - << url << LL_ENDL; - LLSD headers; - LLSD body = LLSD::emptyMap(); - - if (!responder_ptr.get()) + else { - responder_ptr = LLHTTPClient::ResponderPtr(new LLIncrementCofVersionResponder()); + postData["cof_version"] = cof_version; + if (gSavedSettings.getBOOL("DebugForceAppearanceRequestFailure")) + { + postData["cof_version"] = cof_version + 999; + } } - LLHTTPClient::get(url, body, responder_ptr, headers, 30.0f); + mInFlightCounter++; + mInFlightTimer.setTimerExpirySec(60.0); + + llassert(cof_version >= gAgentAvatarp->mLastUpdateRequestCOFVersion); + gAgentAvatarp->mLastUpdateRequestCOFVersion = cof_version; + + if (!gAgent.requestPostCapability("UpdateAvatarAppearance", postData, + static_cast<LLAgent::httpCallback_t>(boost::bind(&LLAppearanceMgr::serverAppearanceUpdateSuccess, this, _1)), + static_cast<LLAgent::httpCallback_t>(boost::bind(&LLAppearanceMgr::decrementInFlightCounter, this)))) + { + LL_WARNS("Avatar") << "Unable to access UpdateAvatarAppearance in this region." << LL_ENDL; + } } -U32 LLAppearanceMgr::getNumAttachmentsInCOF() +void LLAppearanceMgr::serverAppearanceUpdateSuccess(const LLSD &result) { - const LLUUID cof = getCOF(); - LLInventoryModel::item_array_t obj_items; - getDescendentsOfAssetType(cof, obj_items, LLAssetType::AT_OBJECT); - return obj_items.size(); + decrementInFlightCounter(); + if (result["success"].asBoolean()) + { + LL_DEBUGS("Avatar") << "succeeded" << LL_ENDL; + if (gSavedSettings.getBOOL("DebugAvatarAppearanceMessage")) + { + dump_sequential_xml(gAgentAvatarp->getFullname() + "_appearance_request_ok", result); + } + } + else + { + LL_WARNS("Avatar") << "Non success response for change appearance" << LL_ENDL; + if (gSavedSettings.getBOOL("DebugAvatarAppearanceMessage")) + { + debugAppearanceUpdateCOF(result); + } + } +} + +/*static*/ +void LLAppearanceMgr::debugAppearanceUpdateCOF(const LLSD& content) +{ + dump_sequential_xml(gAgentAvatarp->getFullname() + "_appearance_request_error", content); + + LL_INFOS("Avatar") << "AIS COF, version received: " << content["expected"].asInteger() + << " ================================= " << LL_ENDL; + std::set<LLUUID> ais_items, local_items; + const LLSD& cof_raw = content["cof_raw"]; + for (LLSD::array_const_iterator it = cof_raw.beginArray(); + it != cof_raw.endArray(); ++it) + { + const LLSD& item = *it; + if (item["parent_id"].asUUID() == LLAppearanceMgr::instance().getCOF()) + { + ais_items.insert(item["item_id"].asUUID()); + if (item["type"].asInteger() == 24) // link + { + LL_INFOS("Avatar") << "AIS Link: item_id: " << item["item_id"].asUUID() + << " linked_item_id: " << item["asset_id"].asUUID() + << " name: " << item["name"].asString() + << LL_ENDL; + } + else if (item["type"].asInteger() == 25) // folder link + { + LL_INFOS("Avatar") << "AIS Folder link: item_id: " << item["item_id"].asUUID() + << " linked_item_id: " << item["asset_id"].asUUID() + << " name: " << item["name"].asString() + << LL_ENDL; + } + else + { + LL_INFOS("Avatar") << "AIS Other: item_id: " << item["item_id"].asUUID() + << " linked_item_id: " << item["asset_id"].asUUID() + << " name: " << item["name"].asString() + << " type: " << item["type"].asInteger() + << LL_ENDL; + } + } + } + LL_INFOS("Avatar") << LL_ENDL; + LL_INFOS("Avatar") << "Local COF, version requested: " << content["observed"].asInteger() + << " ================================= " << LL_ENDL; + LLInventoryModel::cat_array_t cat_array; + LLInventoryModel::item_array_t item_array; + gInventory.collectDescendents(LLAppearanceMgr::instance().getCOF(), + cat_array, item_array, LLInventoryModel::EXCLUDE_TRASH); + for (S32 i = 0; i < item_array.size(); i++) + { + const LLViewerInventoryItem* inv_item = item_array.at(i).get(); + local_items.insert(inv_item->getUUID()); + LL_INFOS("Avatar") << "LOCAL: item_id: " << inv_item->getUUID() + << " linked_item_id: " << inv_item->getLinkedUUID() + << " name: " << inv_item->getName() + << " parent: " << inv_item->getParentUUID() + << LL_ENDL; + } + LL_INFOS("Avatar") << " ================================= " << LL_ENDL; + S32 local_only = 0, ais_only = 0; + for (std::set<LLUUID>::iterator it = local_items.begin(); it != local_items.end(); ++it) + { + if (ais_items.find(*it) == ais_items.end()) + { + LL_INFOS("Avatar") << "LOCAL ONLY: " << *it << LL_ENDL; + local_only++; + } + } + for (std::set<LLUUID>::iterator it = ais_items.begin(); it != ais_items.end(); ++it) + { + if (local_items.find(*it) == local_items.end()) + { + LL_INFOS("Avatar") << "AIS ONLY: " << *it << LL_ENDL; + ais_only++; + } + } + if (local_only == 0 && ais_only == 0) + { + LL_INFOS("Avatar") << "COF contents identical, only version numbers differ (req " + << content["observed"].asInteger() + << " rcv " << content["expected"].asInteger() + << ")" << LL_ENDL; + } } +bool LLAppearanceMgr::testCOFRequestVersion() const +{ + // If we have already received an update for this or higher cof version, ignore. + S32 cof_version = getCOFVersion(); + S32 last_rcv = gAgentAvatarp->mLastUpdateReceivedCOFVersion; + S32 last_req = gAgentAvatarp->mLastUpdateRequestCOFVersion; + + LL_DEBUGS("Avatar") << "cof_version " << cof_version + << " last_rcv " << last_rcv + << " last_req " << last_req + << " in flight " << mInFlightCounter + << LL_ENDL; + if (cof_version < last_rcv) + { + LL_DEBUGS("Avatar") << "Have already received update for cof version " << last_rcv + << " will not request for " << cof_version << LL_ENDL; + return false; + } + if (/*mInFlightCounter > 0 &&*/ last_req >= cof_version) + { + LL_DEBUGS("Avatar") << "Request already in flight for cof version " << last_req + << " will not request for " << cof_version << LL_ENDL; + return false; + } + + // Actually send the request. + LL_DEBUGS("Avatar") << "Will send request for cof_version " << cof_version << LL_ENDL; + return true; +} + std::string LLAppearanceMgr::getAppearanceServiceURL() const { if (gSavedSettings.getString("DebugAvatarAppearanceServiceURLOverride").empty()) @@ -3961,15 +3795,17 @@ void LLAppearanceMgr::dumpItemArray(const LLInventoryModel::item_array_t& items, } } +bool LLAppearanceMgr::mActive = true; + LLAppearanceMgr::LLAppearanceMgr(): mAttachmentInvLinkEnabled(false), mOutfitIsDirty(false), mOutfitLocked(false), - mIsInUpdateAppearanceFromCOF(false), - mAppearanceResponder(new RequestAgentUpdateAppearanceResponder) + mInFlightCounter(0), + mInFlightTimer(), + mIsInUpdateAppearanceFromCOF(false) { LLOutfitObserver& outfit_observer = LLOutfitObserver::instance(); - // unlock outfit on save operation completed outfit_observer.addCOFSavedCallback(boost::bind( &LLAppearanceMgr::setOutfitLocked, this, false)); @@ -3977,11 +3813,12 @@ LLAppearanceMgr::LLAppearanceMgr(): mUnlockOutfitTimer.reset(new LLOutfitUnLockTimer(gSavedSettings.getS32( "OutfitOperationsTimeout"))); - gIdleCallbacks.addFunction(&LLAttachmentsMgr::onIdle,NULL); + gIdleCallbacks.addFunction(&LLAttachmentsMgr::onIdle, NULL); } LLAppearanceMgr::~LLAppearanceMgr() { + mActive = false; } void LLAppearanceMgr::setAttachmentInvLinkEnable(bool val) diff --git a/indra/newview/llappearancemgr.h b/indra/newview/llappearancemgr.h index 4ed8c1bfb9..9b6ceb7d3e 100755 --- a/indra/newview/llappearancemgr.h +++ b/indra/newview/llappearancemgr.h @@ -34,12 +34,10 @@ #include "llinventorymodel.h" #include "llinventoryobserver.h" #include "llviewerinventory.h" -#include "llhttpclient.h" class LLWearableHoldingPattern; class LLInventoryCallback; class LLOutfitUnLockTimer; -class RequestAgentUpdateAppearanceResponder; class LLAppearanceMgr: public LLSingleton<LLAppearanceMgr> { @@ -54,7 +52,6 @@ public: void updateAppearanceFromCOF(bool enforce_item_restrictions = true, bool enforce_ordering = true, nullary_func_t post_update_func = no_op); - bool needToSaveCOF(); void updateCOF(const LLUUID& category, bool append = false); void wearInventoryCategory(LLInventoryCategory* category, bool copy, bool append); void wearInventoryCategoryOnAvatar(LLInventoryCategory* category, bool append); @@ -224,20 +221,23 @@ public: void requestServerAppearanceUpdate(); - void incrementCofVersion(LLHTTPClient::ResponderPtr responder_ptr = NULL); + void setAppearanceServiceURL(const std::string& url) { mAppearanceServiceURL = url; } + std::string getAppearanceServiceURL() const; - U32 getNumAttachmentsInCOF(); - // *HACK Remove this after server side texture baking is deployed on all sims. - void incrementCofVersionLegacy(); + bool testCOFRequestVersion() const; + void decrementInFlightCounter() + { + mInFlightCounter = llmax(mInFlightCounter - 1, 0); + } - void setAppearanceServiceURL(const std::string& url) { mAppearanceServiceURL = url; } - std::string getAppearanceServiceURL() const; private: + void serverAppearanceUpdateSuccess(const LLSD &result); + static void debugAppearanceUpdateCOF(const LLSD& content); + std::string mAppearanceServiceURL; - protected: LLAppearanceMgr(); ~LLAppearanceMgr(); @@ -261,13 +261,14 @@ private: bool mOutfitIsDirty; bool mIsInUpdateAppearanceFromCOF; // to detect recursive calls. - LLPointer<RequestAgentUpdateAppearanceResponder> mAppearanceResponder; - /** * Lock for blocking operations on outfit until server reply or timeout exceed * to avoid unsynchronized outfit state or performing duplicate operations. */ bool mOutfitLocked; + S32 mInFlightCounter; + LLTimer mInFlightTimer; + static bool mActive; std::auto_ptr<LLOutfitUnLockTimer> mUnlockOutfitTimer; diff --git a/indra/newview/llassetuploadresponders.cpp b/indra/newview/llassetuploadresponders.cpp index d2b1dcbf35..14020af166 100755 --- a/indra/newview/llassetuploadresponders.cpp +++ b/indra/newview/llassetuploadresponders.cpp @@ -352,6 +352,7 @@ void LLAssetUploadResponder::uploadComplete(const LLSD& content) { } +#if 0 LLNewAgentInventoryResponder::LLNewAgentInventoryResponder( const LLSD& post_data, const LLUUID& vfile_id, @@ -473,7 +474,9 @@ void LLNewAgentInventoryResponder::uploadComplete(const LLSD& content) //LLImportColladaAssetCache::getInstance()->assetUploaded(mVFileID, content["new_asset"], TRUE); } +#endif +#if 0 LLUpdateAgentInventoryResponder::LLUpdateAgentInventoryResponder( const LLSD& post_data, const LLUUID& vfile_id, @@ -580,7 +583,7 @@ void LLUpdateAgentInventoryResponder::uploadComplete(const LLSD& content) break; } } - +#endif LLUpdateTaskInventoryResponder::LLUpdateTaskInventoryResponder(const LLSD& post_data, const LLUUID& vfile_id, @@ -674,6 +677,7 @@ void LLUpdateTaskInventoryResponder::uploadComplete(const LLSD& content) } +#if 0 ///////////////////////////////////////////////////// // LLNewAgentInventoryVariablePriceResponder::Impl // ///////////////////////////////////////////////////// @@ -1144,5 +1148,5 @@ void LLNewAgentInventoryVariablePriceResponder::showConfirmationDialog( LLPointer<LLNewAgentInventoryVariablePriceResponder>(this))); } } - +#endif diff --git a/indra/newview/llassetuploadresponders.h b/indra/newview/llassetuploadresponders.h index 7fbebc7481..71995873fa 100755 --- a/indra/newview/llassetuploadresponders.h +++ b/indra/newview/llassetuploadresponders.h @@ -60,6 +60,7 @@ protected: std::string mFileName; }; +#if 0 // TODO*: Remove this once deprecated class LLNewAgentInventoryResponder : public LLAssetUploadResponder { @@ -78,7 +79,8 @@ public: protected: virtual void httpFailure(); }; - +#endif +#if 0 // A base class which goes through and performs some default // actions for variable price uploads. If more specific actions // are needed (such as different confirmation messages, etc.) @@ -115,7 +117,9 @@ private: class Impl; Impl* mImpl; }; +#endif +#if 0 class LLUpdateAgentInventoryResponder : public LLAssetUploadResponder { public: @@ -127,6 +131,7 @@ public: LLAssetType::EType asset_type); virtual void uploadComplete(const LLSD& content); }; +#endif class LLUpdateTaskInventoryResponder : public LLAssetUploadResponder { diff --git a/indra/newview/llavatarrenderinfoaccountant.cpp b/indra/newview/llavatarrenderinfoaccountant.cpp index 38e153137c..e260142254 100644 --- a/indra/newview/llavatarrenderinfoaccountant.cpp +++ b/indra/newview/llavatarrenderinfoaccountant.cpp @@ -35,7 +35,6 @@ // external library headers // other Linden headers #include "llcharacter.h" -#include "llhttpclient.h" #include "lltimer.h" #include "llviewercontrol.h" #include "llviewermenu.h" @@ -43,7 +42,10 @@ #include "llviewerregion.h" #include "llvoavatar.h" #include "llworld.h" - +#include "llhttpsdhandler.h" +#include "httpheaders.h" +#include "httpoptions.h" +#include "llcorehttputil.h" static const std::string KEY_AGENTS = "agents"; // map static const std::string KEY_WEIGHT = "weight"; // integer @@ -55,166 +57,178 @@ static const std::string KEY_ERROR = "error"; // Send data updates about once per minute, only need per-frame resolution LLFrameTimer LLAvatarRenderInfoAccountant::sRenderInfoReportTimer; +//LLCore::HttpRequest::ptr_t LLAvatarRenderInfoAccountant::sHttpRequest; - -// HTTP responder class for GET request for avatar render weight information -class LLAvatarRenderInfoGetResponder : public LLHTTPClient::Responder +//========================================================================= +void LLAvatarRenderInfoAccountant::avatarRenderInfoGetCoro(std::string url, U64 regionHandle) { -public: - LLAvatarRenderInfoGetResponder(U64 region_handle) : mRegionHandle(region_handle) - { - } - - virtual void error(U32 statusNum, const std::string& reason) - { - LLViewerRegion * regionp = LLWorld::getInstance()->getRegionFromHandle(mRegionHandle); - if (regionp) - { - LL_WARNS() << "HTTP error result for avatar weight GET: " << statusNum - << ", " << reason - << " returned by region " << regionp->getName() - << LL_ENDL; - } - else - { - LL_WARNS() << "Avatar render weight GET error recieved but region not found for " - << mRegionHandle - << ", error " << statusNum - << ", " << reason - << LL_ENDL; - } - - } - - virtual void result(const LLSD& content) - { - LLViewerRegion * regionp = LLWorld::getInstance()->getRegionFromHandle(mRegionHandle); - if (regionp) - { - if (LLAvatarRenderInfoAccountant::logRenderInfo()) - { - LL_INFOS() << "LRI: Result for avatar weights request for region " << regionp->getName() << ":" << LL_ENDL; - } - - if (content.isMap()) - { - if (content.has(KEY_AGENTS)) - { - const LLSD & agents = content[KEY_AGENTS]; - if (agents.isMap()) - { - LLSD::map_const_iterator report_iter = agents.beginMap(); - while (report_iter != agents.endMap()) - { - LLUUID target_agent_id = LLUUID(report_iter->first); - const LLSD & agent_info_map = report_iter->second; - LLViewerObject* avatarp = gObjectList.findObject(target_agent_id); - if (avatarp && - avatarp->isAvatar() && - agent_info_map.isMap()) - { // Extract the data for this avatar - - if (LLAvatarRenderInfoAccountant::logRenderInfo()) - { - LL_INFOS() << "LRI: Agent " << target_agent_id - << ": " << agent_info_map << LL_ENDL; - } - - if (agent_info_map.has(KEY_WEIGHT)) - { - ((LLVOAvatar *) avatarp)->setReportedVisualComplexity(agent_info_map[KEY_WEIGHT].asInteger()); - } - } - report_iter++; - } - } - } // has "agents" - else if (content.has(KEY_ERROR)) - { - const LLSD & error = content[KEY_ERROR]; - LL_WARNS() << "Avatar render info GET error: " - << error[KEY_IDENTIFIER] - << ": " << error[KEY_MESSAGE] - << " from region " << regionp->getName() - << LL_ENDL; - } - } - } - else - { - LL_INFOS() << "Avatar render weight info recieved but region not found for " - << mRegionHandle << LL_ENDL; - } - } - -private: - U64 mRegionHandle; -}; + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("AvatarRenderInfoAccountant", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + + LLSD result = httpAdapter->getAndYield(httpRequest, url); + + LLViewerRegion * regionp = LLWorld::getInstance()->getRegionFromHandle(regionHandle); + if (!regionp) + { + LL_WARNS("AvatarRenderInfoAccountant") << "Avatar render weight info received but region not found for " + << regionHandle << LL_ENDL; + return; + } + + LLSD httpResults = result["http_result"]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) + { + LL_WARNS("AvatarRenderInfoAccountant") << "HTTP status, " << status.toTerseString() << LL_ENDL; + return; + } + + if (result.has(KEY_AGENTS)) + { + const LLSD & agents = result[KEY_AGENTS]; + if (agents.isMap()) + { + LLSD::map_const_iterator report_iter = agents.beginMap(); + while (report_iter != agents.endMap()) + { + LLUUID target_agent_id = LLUUID(report_iter->first); + const LLSD & agent_info_map = report_iter->second; + LLViewerObject* avatarp = gObjectList.findObject(target_agent_id); + if (avatarp && + avatarp->isAvatar() && + agent_info_map.isMap()) + { // Extract the data for this avatar + + if (LLAvatarRenderInfoAccountant::logRenderInfo()) + { + LL_INFOS() << "LRI: Agent " << target_agent_id + << ": " << agent_info_map << LL_ENDL; + } + + if (agent_info_map.has(KEY_WEIGHT)) + { + ((LLVOAvatar *) avatarp)->setReportedVisualComplexity(agent_info_map[KEY_WEIGHT].asInteger()); + } + } + report_iter++; + } + } + } // has "agents" + else if (result.has(KEY_ERROR)) + { + const LLSD & error = result[KEY_ERROR]; + LL_WARNS() << "Avatar render info GET error: " + << error[KEY_IDENTIFIER] + << ": " << error[KEY_MESSAGE] + << " from region " << regionp->getName() + << LL_ENDL; + } +} -// HTTP responder class for POST request for avatar render weight information -class LLAvatarRenderInfoPostResponder : public LLHTTPClient::Responder +//------------------------------------------------------------------------- +void LLAvatarRenderInfoAccountant::avatarRenderInfoReportCoro(std::string url, U64 regionHandle) { -public: - LLAvatarRenderInfoPostResponder(U64 region_handle) : mRegionHandle(region_handle) - { - } + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("AvatarRenderInfoAccountant", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + + LLViewerRegion * regionp = LLWorld::getInstance()->getRegionFromHandle(regionHandle); + if (!regionp) + { + LL_WARNS("AvatarRenderInfoAccountant") << "Avatar render weight calculation but region not found for " + << regionHandle << LL_ENDL; + return; + } + + if (logRenderInfo()) + { + LL_INFOS("AvatarRenderInfoAccountant") << "LRI: Sending avatar render info to region " << regionp->getName() + << " from " << url << LL_ENDL; + } + + // Build the render info to POST to the region + LLSD report = LLSD::emptyMap(); + LLSD agents = LLSD::emptyMap(); + + std::vector<LLCharacter*>::iterator iter = LLCharacter::sInstances.begin(); + while( iter != LLCharacter::sInstances.end() ) + { + LLVOAvatar* avatar = dynamic_cast<LLVOAvatar*>(*iter); + if (avatar && + avatar->getRezzedStatus() >= 2 && // Mostly rezzed (maybe without baked textures downloaded) + !avatar->isDead() && // Not dead yet + avatar->getObjectHost() == regionp->getHost()) // Ensure it's on the same region + { + avatar->calculateUpdateRenderCost(); // Make sure the numbers are up-to-date + + LLSD info = LLSD::emptyMap(); + if (avatar->getVisualComplexity() > 0) + { + info[KEY_WEIGHT] = avatar->getVisualComplexity(); + agents[avatar->getID().asString()] = info; + + if (logRenderInfo()) + { + LL_INFOS("AvatarRenderInfoAccountant") << "LRI: Sending avatar render info for " << avatar->getID() + << ": " << info << LL_ENDL; + LL_INFOS("AvatarRenderInfoAccountant") << "LRI: other info geometry " << avatar->getAttachmentGeometryBytes() + << ", area " << avatar->getAttachmentSurfaceArea() + << LL_ENDL; + } + } + } + iter++; + } + + if (agents.size() == 0) + return; + + report[KEY_AGENTS] = agents; + regionp = NULL; + LLSD result = httpAdapter->postAndYield(httpRequest, url, report); + + regionp = LLWorld::getInstance()->getRegionFromHandle(regionHandle); + if (!regionp) + { + LL_INFOS("AvatarRenderInfoAccountant") << "Avatar render weight POST result received but region not found for " + << regionHandle << LL_ENDL; + return; + } + + LLSD httpResults = result["http_result"]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + if (!status) + { + LL_WARNS("AvatarRenderInfoAccountant") << "HTTP status, " << status.toTerseString() << LL_ENDL; + return; + } + + if (LLAvatarRenderInfoAccountant::logRenderInfo()) + { + LL_INFOS("AvatarRenderInfoAccountant") << "LRI: Result for avatar weights POST for region " << regionp->getName() + << ": " << result << LL_ENDL; + } + + if (result.isMap()) + { + if (result.has(KEY_ERROR)) + { + const LLSD & error = result[KEY_ERROR]; + LL_WARNS("AvatarRenderInfoAccountant") << "Avatar render info POST error: " + << error[KEY_IDENTIFIER] + << ": " << error[KEY_MESSAGE] + << " from region " << regionp->getName() + << LL_ENDL; + } + } - virtual void error(U32 statusNum, const std::string& reason) - { - LLViewerRegion * regionp = LLWorld::getInstance()->getRegionFromHandle(mRegionHandle); - if (regionp) - { - LL_WARNS() << "HTTP error result for avatar weight POST: " << statusNum - << ", " << reason - << " returned by region " << regionp->getName() - << LL_ENDL; - } - else - { - LL_WARNS() << "Avatar render weight POST error recieved but region not found for " - << mRegionHandle - << ", error " << statusNum - << ", " << reason - << LL_ENDL; - } - } - - virtual void result(const LLSD& content) - { - LLViewerRegion * regionp = LLWorld::getInstance()->getRegionFromHandle(mRegionHandle); - if (regionp) - { - if (LLAvatarRenderInfoAccountant::logRenderInfo()) - { - LL_INFOS() << "LRI: Result for avatar weights POST for region " << regionp->getName() - << ": " << content << LL_ENDL; - } - - if (content.isMap()) - { - if (content.has(KEY_ERROR)) - { - const LLSD & error = content[KEY_ERROR]; - LL_WARNS() << "Avatar render info POST error: " - << error[KEY_IDENTIFIER] - << ": " << error[KEY_MESSAGE] - << " from region " << regionp->getName() - << LL_ENDL; - } - } - } - else - { - LL_INFOS() << "Avatar render weight POST result recieved but region not found for " - << mRegionHandle << LL_ENDL; - } - } - -private: - U64 mRegionHandle; -}; +} // static // Send request for one region, no timer checks @@ -223,53 +237,9 @@ void LLAvatarRenderInfoAccountant::sendRenderInfoToRegion(LLViewerRegion * regio std::string url = regionp->getCapability("AvatarRenderInfo"); if (!url.empty()) { - if (logRenderInfo()) - { - LL_INFOS() << "LRI: Sending avatar render info to region " - << regionp->getName() - << " from " << url - << LL_ENDL; - } - - // Build the render info to POST to the region - LLSD report = LLSD::emptyMap(); - LLSD agents = LLSD::emptyMap(); - - std::vector<LLCharacter*>::iterator iter = LLCharacter::sInstances.begin(); - while( iter != LLCharacter::sInstances.end() ) - { - LLVOAvatar* avatar = dynamic_cast<LLVOAvatar*>(*iter); - if (avatar && - avatar->getRezzedStatus() >= 2 && // Mostly rezzed (maybe without baked textures downloaded) - !avatar->isDead() && // Not dead yet - avatar->getObjectHost() == regionp->getHost()) // Ensure it's on the same region - { - avatar->calculateUpdateRenderCost(); // Make sure the numbers are up-to-date - - LLSD info = LLSD::emptyMap(); - if (avatar->getVisualComplexity() > 0) - { - info[KEY_WEIGHT] = avatar->getVisualComplexity(); - agents[avatar->getID().asString()] = info; - - if (logRenderInfo()) - { - LL_INFOS() << "LRI: Sending avatar render info for " << avatar->getID() - << ": " << info << LL_ENDL; - LL_INFOS() << "LRI: other info geometry " << avatar->getAttachmentGeometryBytes() - << ", area " << avatar->getAttachmentSurfaceArea() - << LL_ENDL; - } - } - } - iter++; - } - - report[KEY_AGENTS] = agents; - if (agents.size() > 0) - { - LLHTTPClient::post(url, report, new LLAvatarRenderInfoPostResponder(regionp->getHandle())); - } + std::string coroname = + LLCoros::instance().launch("LLAvatarRenderInfoAccountant::avatarRenderInfoReportCoro", + boost::bind(&LLAvatarRenderInfoAccountant::avatarRenderInfoReportCoro, url, regionp->getHandle())); } } @@ -292,7 +262,9 @@ void LLAvatarRenderInfoAccountant::getRenderInfoFromRegion(LLViewerRegion * regi } // First send a request to get the latest data - LLHTTPClient::get(url, new LLAvatarRenderInfoGetResponder(regionp->getHandle())); + std::string coroname = + LLCoros::instance().launch("LLAvatarRenderInfoAccountant::avatarRenderInfoGetCoro", + boost::bind(&LLAvatarRenderInfoAccountant::avatarRenderInfoGetCoro, url, regionp->getHandle())); } } @@ -301,6 +273,9 @@ void LLAvatarRenderInfoAccountant::getRenderInfoFromRegion(LLViewerRegion * regi // Called every frame - send render weight requests to every region void LLAvatarRenderInfoAccountant::idle() { +// if (!LLAvatarRenderInfoAccountant::sHttpRequest) +// sHttpRequest = LLCore::HttpRequest::ptr_t(new LLCore::HttpRequest()); + if (sRenderInfoReportTimer.hasExpired()) { const F32 SECS_BETWEEN_REGION_SCANS = 5.f; // Scan the region list every 5 seconds @@ -393,6 +368,7 @@ void LLAvatarRenderInfoAccountant::expireRenderInfoReportTimer(const LLUUID& reg // static bool LLAvatarRenderInfoAccountant::logRenderInfo() { - static LLCachedControl<bool> render_mute_logging_enabled(gSavedSettings, "RenderAutoMuteLogging", false); - return render_mute_logging_enabled; + return true; +// static LLCachedControl<bool> render_mute_logging_enabled(gSavedSettings, "RenderAutoMuteLogging", false); +// return render_mute_logging_enabled; } diff --git a/indra/newview/llavatarrenderinfoaccountant.h b/indra/newview/llavatarrenderinfoaccountant.h index d68f2dccfb..f7a04cca2c 100644 --- a/indra/newview/llavatarrenderinfoaccountant.h +++ b/indra/newview/llavatarrenderinfoaccountant.h @@ -29,6 +29,9 @@ #if ! defined(LL_llavatarrenderinfoaccountant_H) #define LL_llavatarrenderinfoaccountant_H +#include "httpcommon.h" +#include "llcoros.h" + class LLViewerRegion; // Class to gather avatar rendering information @@ -36,8 +39,6 @@ class LLViewerRegion; class LLAvatarRenderInfoAccountant { public: - LLAvatarRenderInfoAccountant() {}; - ~LLAvatarRenderInfoAccountant() {}; static void sendRenderInfoToRegion(LLViewerRegion * regionp); static void getRenderInfoFromRegion(LLViewerRegion * regionp); @@ -49,8 +50,16 @@ public: static bool logRenderInfo(); private: + LLAvatarRenderInfoAccountant() {}; + ~LLAvatarRenderInfoAccountant() {}; + // Send data updates about once per minute, only need per-frame resolution static LLFrameTimer sRenderInfoReportTimer; + + static void avatarRenderInfoGetCoro(std::string url, U64 regionHandle); + static void avatarRenderInfoReportCoro(std::string url, U64 regionHandle); + + }; #endif /* ! defined(LL_llavatarrenderinfoaccountant_H) */ diff --git a/indra/newview/llcapabilitylistener.cpp b/indra/newview/llcapabilitylistener.cpp deleted file mode 100755 index ef9b910ae5..0000000000 --- a/indra/newview/llcapabilitylistener.cpp +++ /dev/null @@ -1,202 +0,0 @@ -/** - * @file llcapabilitylistener.cpp - * @author Nat Goodspeed - * @date 2009-01-07 - * @brief Implementation for llcapabilitylistener. - * - * $LicenseInfo:firstyear=2009&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -// Precompiled header -#include "llviewerprecompiledheaders.h" -// associated header -#include "llcapabilitylistener.h" -// STL headers -#include <map> -// std headers -// external library headers -#include <boost/bind.hpp> -// other Linden headers -#include "stringize.h" -#include "llcapabilityprovider.h" -#include "message.h" - -class LLCapabilityListener::CapabilityMappers: public LLSingleton<LLCapabilityListener::CapabilityMappers> -{ -public: - void registerMapper(const LLCapabilityListener::CapabilityMapper*); - void unregisterMapper(const LLCapabilityListener::CapabilityMapper*); - const LLCapabilityListener::CapabilityMapper* find(const std::string& cap) const; - - struct DupCapMapper: public std::runtime_error - { - DupCapMapper(const std::string& what): - std::runtime_error(std::string("DupCapMapper: ") + what) - {} - }; - -private: - friend class LLSingleton<LLCapabilityListener::CapabilityMappers>; - CapabilityMappers(); - - typedef std::map<std::string, const LLCapabilityListener::CapabilityMapper*> CapabilityMap; - CapabilityMap mMap; -}; - -LLCapabilityListener::LLCapabilityListener(const std::string& name, - LLMessageSystem* messageSystem, - const LLCapabilityProvider& provider, - const LLUUID& agentID, - const LLUUID& sessionID): - mEventPump(name), - mMessageSystem(messageSystem), - mProvider(provider), - mAgentID(agentID), - mSessionID(sessionID) -{ - mEventPump.listen("self", boost::bind(&LLCapabilityListener::capListener, this, _1)); -} - -bool LLCapabilityListener::capListener(const LLSD& request) -{ - // Extract what we want from the request object. We do it all up front - // partly to document what we expect. - LLSD::String cap(request["message"]); - LLSD payload(request["payload"]); - LLSD::String reply(request["reply"]); - LLSD::String error(request["error"]); - LLSD::Real timeout(request["timeout"]); - // If the LLSD doesn't even have a "message" key, we doubt it was intended - // for this listener. - if (cap.empty()) - { - LL_ERRS("capListener") << "capability request event without 'message' key to '" - << getCapAPI().getName() - << "' on region\n" << mProvider.getDescription() - << LL_ENDL; - return false; // in case fatal-error function isn't - } - // Establish default timeout. This test relies on LLSD::asReal() returning - // exactly 0.0 for an undef value. - if (! timeout) - { - timeout = HTTP_REQUEST_EXPIRY_SECS; - } - // Look up the url for the requested capability name. - std::string url = mProvider.getCapability(cap); - if (! url.empty()) - { - // This capability is supported by the region to which we're talking. - LLHTTPClient::post(url, payload, - new LLSDMessage::EventResponder(LLEventPumps::instance(), - request, - mProvider.getDescription(), - cap, reply, error), - LLSD(), // headers - timeout); - } - else - { - // Capability not supported -- do we have a registered mapper? - const CapabilityMapper* mapper = CapabilityMappers::instance().find(cap); - if (! mapper) // capability neither supported nor mapped - { - LL_ERRS("capListener") << "unsupported capability '" << cap << "' request to '" - << getCapAPI().getName() << "' on region\n" - << mProvider.getDescription() - << LL_ENDL; - } - else if (! mapper->getReplyName().empty()) // mapper expects reply support - { - LL_ERRS("capListener") << "Mapper for capability '" << cap - << "' requires unimplemented support for reply message '" - << mapper->getReplyName() - << "' on '" << getCapAPI().getName() << "' on region\n" - << mProvider.getDescription() - << LL_ENDL; - } - else - { - LL_INFOS("capListener") << "fallback invoked for capability '" << cap - << "' request to '" << getCapAPI().getName() - << "' on region\n" << mProvider.getDescription() - << LL_ENDL; - mapper->buildMessage(mMessageSystem, mAgentID, mSessionID, cap, payload); - mMessageSystem->sendReliable(mProvider.getHost()); - } - } - return false; -} - -LLCapabilityListener::CapabilityMapper::CapabilityMapper(const std::string& cap, const std::string& reply): - mCapName(cap), - mReplyName(reply) -{ - LLCapabilityListener::CapabilityMappers::instance().registerMapper(this); -} - -LLCapabilityListener::CapabilityMapper::~CapabilityMapper() -{ - LLCapabilityListener::CapabilityMappers::instance().unregisterMapper(this); -} - -LLSD LLCapabilityListener::CapabilityMapper::readResponse(LLMessageSystem* messageSystem) const -{ - return LLSD(); -} - -LLCapabilityListener::CapabilityMappers::CapabilityMappers() {} - -void LLCapabilityListener::CapabilityMappers::registerMapper(const LLCapabilityListener::CapabilityMapper* mapper) -{ - // Try to insert a new map entry by which we can look up the passed mapper - // instance. - std::pair<CapabilityMap::iterator, bool> inserted = - mMap.insert(CapabilityMap::value_type(mapper->getCapName(), mapper)); - // If we already have a mapper for that name, insert() merely located the - // existing iterator and returned false. It is a coding error to try to - // register more than one mapper for the same capability name. - if (! inserted.second) - { - throw DupCapMapper(std::string("Duplicate capability name ") + mapper->getCapName()); - } -} - -void LLCapabilityListener::CapabilityMappers::unregisterMapper(const LLCapabilityListener::CapabilityMapper* mapper) -{ - CapabilityMap::iterator found = mMap.find(mapper->getCapName()); - if (found != mMap.end()) - { - mMap.erase(found); - } -} - -const LLCapabilityListener::CapabilityMapper* -LLCapabilityListener::CapabilityMappers::find(const std::string& cap) const -{ - CapabilityMap::const_iterator found = mMap.find(cap); - if (found != mMap.end()) - { - return found->second; - } - return NULL; -} diff --git a/indra/newview/llcapabilitylistener.h b/indra/newview/llcapabilitylistener.h deleted file mode 100755 index e7535013e7..0000000000 --- a/indra/newview/llcapabilitylistener.h +++ /dev/null @@ -1,131 +0,0 @@ -/** - * @file llcapabilitylistener.h - * @author Nat Goodspeed - * @date 2009-01-07 - * @brief Provide an event-based API for capability requests - * - * $LicenseInfo:firstyear=2009&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#if ! defined(LL_LLCAPABILITYLISTENER_H) -#define LL_LLCAPABILITYLISTENER_H - -#include "llevents.h" // LLEventPump -#include "llsdmessage.h" // LLSDMessage::ArgError -#include "llerror.h" // LOG_CLASS() - -class LLCapabilityProvider; -class LLMessageSystem; -class LLSD; - -class LLCapabilityListener -{ - LOG_CLASS(LLCapabilityListener); -public: - LLCapabilityListener(const std::string& name, LLMessageSystem* messageSystem, - const LLCapabilityProvider& provider, - const LLUUID& agentID, const LLUUID& sessionID); - - /// Capability-request exception - typedef LLSDMessage::ArgError ArgError; - /// Get LLEventPump on which we listen for capability requests - /// (https://wiki.lindenlab.com/wiki/Viewer:Messaging/Messaging_Notes#Capabilities) - LLEventPump& getCapAPI() { return mEventPump; } - - /** - * Base class for mapping an as-yet-undeployed capability name to a (pair - * of) LLMessageSystem message(s). To map a capability name to such - * messages, derive a subclass of CapabilityMapper and declare a static - * instance in a translation unit known to be loaded. The mapping is not - * region-specific. If an LLViewerRegion's capListener() receives a - * request for a supported capability, it will use the capability's URL. - * If not, it will look for an applicable CapabilityMapper subclass - * instance. - */ - class CapabilityMapper - { - public: - /** - * Base-class constructor. Typically your subclass constructor will - * pass these parameters as literals. - * @param cap the capability name handled by this (subclass) instance - * @param reply the name of the response LLMessageSystem message. Omit - * if the LLMessageSystem message you intend to send doesn't prompt a - * reply message, or if you already handle that message in some other - * way. - */ - CapabilityMapper(const std::string& cap, const std::string& reply = ""); - virtual ~CapabilityMapper(); - /// query the capability name - std::string getCapName() const { return mCapName; } - /// query the reply message name - std::string getReplyName() const { return mReplyName; } - /** - * Override this method to build the LLMessageSystem message we should - * send instead of the requested capability message. DO NOT send that - * message: that will be handled by the caller. - */ - virtual void buildMessage(LLMessageSystem* messageSystem, - const LLUUID& agentID, - const LLUUID& sessionID, - const std::string& capabilityName, - const LLSD& payload) const = 0; - /** - * Override this method if you pass a non-empty @a reply - * LLMessageSystem message name to the constructor: that is, if you - * expect to receive an LLMessageSystem message in response to the - * message you constructed in buildMessage(). If you don't pass a @a - * reply message name, you need not override this method as it won't - * be called. - * - * Using LLMessageSystem message-reading operations, your - * readResponse() override should construct and return an LLSD object - * of the form you expect to receive from the real implementation of - * the capability you intend to invoke, when it finally goes live. - */ - virtual LLSD readResponse(LLMessageSystem* messageSystem) const; - - private: - const std::string mCapName; - const std::string mReplyName; - }; - -private: - /// Bind the LLCapabilityProvider passed to our ctor - const LLCapabilityProvider& mProvider; - - /// Post an event to this LLEventPump to invoke a capability message on - /// the bound LLCapabilityProvider's server - /// (https://wiki.lindenlab.com/wiki/Viewer:Messaging/Messaging_Notes#Capabilities) - LLEventStream mEventPump; - - LLMessageSystem* mMessageSystem; - LLUUID mAgentID, mSessionID; - - /// listener to process capability requests - bool capListener(const LLSD&); - - /// helper class for capListener() - class CapabilityMappers; -}; - -#endif /* ! defined(LL_LLCAPABILITYLISTENER_H) */ diff --git a/indra/newview/llclassifiedstatsresponder.cpp b/indra/newview/llclassifiedstatsresponder.cpp deleted file mode 100755 index f1ef8e9a03..0000000000 --- a/indra/newview/llclassifiedstatsresponder.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/** - * @file llclassifiedstatsresponder.cpp - * @brief Receives information about classified ad click-through - * counts for display in the classified information UI. - * - * $LicenseInfo:firstyear=2007&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" - -#include "llclassifiedstatsresponder.h" - -#include "llpanelclassified.h" -#include "llpanel.h" -#include "llhttpclient.h" -#include "llsdserialize.h" -#include "llviewerregion.h" -#include "llview.h" -#include "message.h" - -LLClassifiedStatsResponder::LLClassifiedStatsResponder(LLUUID classified_id) -: mClassifiedID(classified_id) -{} - -/*virtual*/ -void LLClassifiedStatsResponder::httpSuccess() -{ - const LLSD& content = getContent(); - if (!content.isMap()) - { - failureResult(HTTP_INTERNAL_ERROR, "Malformed response contents", content); - return; - } - S32 teleport = content["teleport_clicks"].asInteger(); - S32 map = content["map_clicks"].asInteger(); - S32 profile = content["profile_clicks"].asInteger(); - S32 search_teleport = content["search_teleport_clicks"].asInteger(); - S32 search_map = content["search_map_clicks"].asInteger(); - S32 search_profile = content["search_profile_clicks"].asInteger(); - - LLPanelClassifiedInfo::setClickThrough( mClassifiedID, - teleport + search_teleport, - map + search_map, - profile + search_profile, - true); -} - -/*virtual*/ -void LLClassifiedStatsResponder::httpFailure() -{ - LL_WARNS() << dumpResponse() << LL_ENDL; -} - diff --git a/indra/newview/llclassifiedstatsresponder.h b/indra/newview/llclassifiedstatsresponder.h deleted file mode 100755 index efa4d82411..0000000000 --- a/indra/newview/llclassifiedstatsresponder.h +++ /dev/null @@ -1,50 +0,0 @@ -/** - * @file llclassifiedstatsresponder.h - * @brief Receives information about classified ad click-through - * counts for display in the classified information UI. - * - * $LicenseInfo:firstyear=2007&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ -#ifndef LL_LLCLASSIFIEDSTATSRESPONDER_H -#define LL_LLCLASSIFIEDSTATSRESPONDER_H - -#include "llhttpclient.h" -#include "llview.h" -#include "lluuid.h" - -class LLClassifiedStatsResponder : public LLHTTPClient::Responder -{ - LOG_CLASS(LLClassifiedStatsResponder); -public: - LLClassifiedStatsResponder(LLUUID classified_id); - -protected: - //If we get back a normal response, handle it here - virtual void httpSuccess(); - //If we get back an error (not found, etc...), handle it here - virtual void httpFailure(); - -protected: - LLUUID mClassifiedID; -}; - -#endif // LL_LLCLASSIFIEDSTATSRESPONDER_H diff --git a/indra/newview/llcoproceduremanager.cpp b/indra/newview/llcoproceduremanager.cpp new file mode 100644 index 0000000000..d3168985f8 --- /dev/null +++ b/indra/newview/llcoproceduremanager.cpp @@ -0,0 +1,176 @@ +/** +* @file llcoproceduremanager.cpp +* @author Rider Linden +* @brief Singleton class for managing asset uploads to the sim. +* +* $LicenseInfo:firstyear=2015&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2015, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA +* $/LicenseInfo$ +*/ + +#include "llviewerprecompiledheaders.h" +#include "linden_common.h" + +#include "llviewercontrol.h" + +#include "llcoproceduremanager.h" + +//========================================================================= +#define COROCOUNT 1 + +//========================================================================= +LLCoprocedureManager::LLCoprocedureManager(): + LLSingleton<LLCoprocedureManager>(), + mPendingCoprocs(), + mShutdown(false), + mWakeupTrigger("CoprocedureManager", true), + mCoroMapping(), + mHTTPPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID) +{ + + // *TODO: Retrieve the actual number of concurrent coroutines fro gSavedSettings and + // clamp to a "reasonable" number. + for (int count = 0; count < COROCOUNT; ++count) + { + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter = + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t( + new LLCoreHttpUtil::HttpCoroutineAdapter("uploadPostAdapter", mHTTPPolicy)); + + std::string uploadCoro = LLCoros::instance().launch("LLCoprocedureManager::coprocedureInvokerCoro", + boost::bind(&LLCoprocedureManager::coprocedureInvokerCoro, this, httpAdapter)); + + mCoroMapping.insert(CoroAdapterMap_t::value_type(uploadCoro, httpAdapter)); + } + + mWakeupTrigger.post(LLSD()); +} + +LLCoprocedureManager::~LLCoprocedureManager() +{ + shutdown(); +} + +//========================================================================= + +void LLCoprocedureManager::shutdown(bool hardShutdown) +{ + CoroAdapterMap_t::iterator it; + + for (it = mCoroMapping.begin(); it != mCoroMapping.end(); ++it) + { + if (!(*it).first.empty()) + { + if (hardShutdown) + { + LLCoros::instance().kill((*it).first); + } + } + if ((*it).second) + { + (*it).second->cancelYieldingOperation(); + } + } + + mShutdown = true; + mCoroMapping.clear(); + mPendingCoprocs.clear(); +} + +//========================================================================= +LLUUID LLCoprocedureManager::enqueueCoprocedure(const std::string &name, LLCoprocedureManager::CoProcedure_t proc) +{ + LLUUID id(LLUUID::generateNewID()); + + mPendingCoprocs.push_back(QueuedCoproc::ptr_t(new QueuedCoproc(name, id, proc))); + LL_INFOS() << "Coprocedure(" << name << ") enqueued with id=" << id.asString() << LL_ENDL; + + mWakeupTrigger.post(LLSD()); + + return id; +} + +void LLCoprocedureManager::cancelCoprocedure(const LLUUID &id) +{ + // first check the active coroutines. If there, remove it and return. + ActiveCoproc_t::iterator itActive = mActiveCoprocs.find(id); + if (itActive != mActiveCoprocs.end()) + { + LL_INFOS() << "Found and canceling active coprocedure with id=" << id.asString() << LL_ENDL; + (*itActive).second->cancelYieldingOperation(); + mActiveCoprocs.erase(itActive); + return; + } + + for (CoprocQueue_t::iterator it = mPendingCoprocs.begin(); it != mPendingCoprocs.end(); ++it) + { + if ((*it)->mId == id) + { + LL_INFOS() << "Found and removing queued coroutine(" << (*it)->mName << ") with Id=" << id.asString() << LL_ENDL; + mPendingCoprocs.erase(it); + return; + } + } + + LL_INFOS() << "Coprocedure with Id=" << id.asString() << " was not found." << LL_ENDL; +} + +//========================================================================= +void LLCoprocedureManager::coprocedureInvokerCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter) +{ + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + + while (!mShutdown) + { + llcoro::waitForEventOn(mWakeupTrigger); + if (mShutdown) + break; + + while (!mPendingCoprocs.empty()) + { + QueuedCoproc::ptr_t coproc = mPendingCoprocs.front(); + mPendingCoprocs.pop_front(); + mActiveCoprocs.insert(ActiveCoproc_t::value_type(coproc->mId, httpAdapter)); + + LL_INFOS() << "Dequeued and invoking coprocedure(" << coproc->mName << ") with id=" << coproc->mId.asString() << LL_ENDL; + + try + { + coproc->mProc(httpAdapter, coproc->mId); + } + catch (std::exception &e) + { + LL_WARNS() << "Coprocedure(" << coproc->mName << ") id=" << coproc->mId.asString() << + " threw an exception! Message=\"" << e.what() << "\"" << LL_ENDL; + } + catch (...) + { + LL_WARNS() << "A non std::exception was thrown from " << coproc->mName << " with id=" << coproc->mId << "." << LL_ENDL; + } + + LL_INFOS() << "Finished coprocedure(" << coproc->mName << ")" << LL_ENDL; + + ActiveCoproc_t::iterator itActive = mActiveCoprocs.find(coproc->mId); + if (itActive != mActiveCoprocs.end()) + { + mActiveCoprocs.erase(itActive); + } + } + } +} diff --git a/indra/newview/llcoproceduremanager.h b/indra/newview/llcoproceduremanager.h new file mode 100644 index 0000000000..6ba3891e87 --- /dev/null +++ b/indra/newview/llcoproceduremanager.h @@ -0,0 +1,117 @@ +/** +* @file llcoproceduremanager.h +* @author Rider Linden +* @brief Singleton class for managing asset uploads to the sim. +* +* $LicenseInfo:firstyear=2015&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2015, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA +* $/LicenseInfo$ +*/ + +#ifndef LL_COPROCEDURE_MANAGER_H +#define LL_COPROCEDURE_MANAGER_H + +#include "lleventcoro.h" +#include "llcoros.h" +#include "llcorehttputil.h" +#include "lluuid.h" + +class LLCoprocedureManager : public LLSingleton < LLCoprocedureManager > +{ +public: + typedef boost::function<void(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &, const LLUUID &id)> CoProcedure_t; + + LLCoprocedureManager(); + virtual ~LLCoprocedureManager(); + + /// Places the coprocedure on the queue for processing. + /// + /// @param name Is used for debugging and should identify this coroutine. + /// @param proc Is a bound function to be executed + /// + /// @return This method returns a UUID that can be used later to cancel execution. + LLUUID enqueueCoprocedure(const std::string &name, CoProcedure_t proc); + + /// Cancel a coprocedure. If the coprocedure is already being actively executed + /// this method calls cancelYieldingOperation() on the associated HttpAdapter + /// If it has not yet been dequeued it is simply removed from the queue. + void cancelCoprocedure(const LLUUID &id); + + /// Requests a shutdown of the upload manager. Passing 'true' will perform + /// an immediate kill on the upload coroutine. + void shutdown(bool hardShutdown = false); + + /// Returns the number of coprocedures in the queue awaiting processing. + /// + inline size_t countPending() const + { + return mPendingCoprocs.size(); + } + + /// Returns the number of coprocedures actively being processed. + /// + inline size_t countActive() const + { + return mActiveCoprocs.size(); + } + + /// Returns the total number of coprocedures either queued or in active processing. + /// + inline size_t count() const + { + return countPending() + countActive(); + } + +private: + struct QueuedCoproc + { + typedef boost::shared_ptr<QueuedCoproc> ptr_t; + + QueuedCoproc(const std::string &name, const LLUUID &id, CoProcedure_t proc): + mName(name), + mId(id), + mProc(proc) + {} + + std::string mName; + LLUUID mId; + CoProcedure_t mProc; + }; + + // we use a deque here rather than std::queue since we want to be able to + // iterate through the queue and potentially erase an entry from the middle. + typedef std::deque<QueuedCoproc::ptr_t> CoprocQueue_t; + typedef std::map<LLUUID, LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t> ActiveCoproc_t; + + CoprocQueue_t mPendingCoprocs; + ActiveCoproc_t mActiveCoprocs; + bool mShutdown; + LLEventStream mWakeupTrigger; + + + typedef std::map<std::string, LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t> CoroAdapterMap_t; + LLCore::HttpRequest::policy_t mHTTPPolicy; + + CoroAdapterMap_t mCoroMapping; + + void coprocedureInvokerCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter); +}; + +#endif diff --git a/indra/newview/llestateinfomodel.cpp b/indra/newview/llestateinfomodel.cpp index 78d619a315..884d1579e6 100755 --- a/indra/newview/llestateinfomodel.cpp +++ b/indra/newview/llestateinfomodel.cpp @@ -29,7 +29,6 @@ #include "llestateinfomodel.h" // libs -#include "llhttpclient.h" #include "llregionflags.h" #include "message.h" @@ -38,6 +37,8 @@ #include "llfloaterregioninfo.h" // for invoice id #include "llviewerregion.h" +#include "llcorehttputil.h" + LLEstateInfoModel::LLEstateInfoModel() : mID(0) , mFlags(0) @@ -110,24 +111,6 @@ void LLEstateInfoModel::notifyCommit() //== PRIVATE STUFF ============================================================ -class LLEstateChangeInfoResponder : public LLHTTPClient::Responder -{ - LOG_CLASS(LLEstateChangeInfoResponder); -protected: - // if we get a normal response, handle it here - virtual void httpSuccesss() - { - LL_INFOS() << "Committed estate info" << LL_ENDL; - LLEstateInfoModel::instance().notifyCommit(); - } - - // if we get an error response - virtual void httpFailure() - { - LL_WARNS() << "Failed to commit estate info " << dumpResponse() << LL_ENDL; - } -}; - // tries to send estate info using a cap; returns true if it succeeded bool LLEstateInfoModel::commitEstateInfoCaps() { @@ -139,29 +122,53 @@ bool LLEstateInfoModel::commitEstateInfoCaps() return false; } - LLSD body; - body["estate_name" ] = getName(); - body["sun_hour" ] = getSunHour(); + LLCoros::instance().launch("LLEstateInfoModel::commitEstateInfoCapsCoro", + boost::bind(&LLEstateInfoModel::commitEstateInfoCapsCoro, this, url)); - body["is_sun_fixed" ] = getUseFixedSun(); - body["is_externally_visible"] = getIsExternallyVisible(); - body["allow_direct_teleport"] = getAllowDirectTeleport(); - body["deny_anonymous" ] = getDenyAnonymous(); - body["deny_age_unverified" ] = getDenyAgeUnverified(); - body["allow_voice_chat" ] = getAllowVoiceChat(); - - body["invoice" ] = LLFloaterRegionInfo::getLastInvoice(); - - LL_DEBUGS("Windlight Sync") << "Sending estate caps: " - << "is_sun_fixed = " << getUseFixedSun() - << ", sun_hour = " << getSunHour() << LL_ENDL; - LL_DEBUGS() << body << LL_ENDL; - - // we use a responder so that we can re-get the data after committing to the database - LLHTTPClient::post(url, body, new LLEstateChangeInfoResponder); return true; } +void LLEstateInfoModel::commitEstateInfoCapsCoro(std::string url) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("EstateChangeInfo", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + + LLSD body; + body["estate_name"] = getName(); + body["sun_hour"] = getSunHour(); + + body["is_sun_fixed"] = getUseFixedSun(); + body["is_externally_visible"] = getIsExternallyVisible(); + body["allow_direct_teleport"] = getAllowDirectTeleport(); + body["deny_anonymous"] = getDenyAnonymous(); + body["deny_age_unverified"] = getDenyAgeUnverified(); + body["allow_voice_chat"] = getAllowVoiceChat(); + + body["invoice"] = LLFloaterRegionInfo::getLastInvoice(); + + LL_DEBUGS("Windlight Sync") << "Sending estate caps: " + << "is_sun_fixed = " << getUseFixedSun() + << ", sun_hour = " << getSunHour() << LL_ENDL; + LL_DEBUGS() << body << LL_ENDL; + + LLSD result = httpAdapter->postAndYield(httpRequest, url, body); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (status) + { + LL_INFOS() << "Committed estate info" << LL_ENDL; + LLEstateInfoModel::instance().notifyCommit(); + } + else + { + LL_WARNS() << "Failed to commit estate info " << LL_ENDL; + } +} + /* This is the old way of doing things, is deprecated, and should be deleted when the dataserver model can be removed */ // key = "estatechangeinfo" diff --git a/indra/newview/llestateinfomodel.h b/indra/newview/llestateinfomodel.h index 538f2f7c75..fcfbd1ce7d 100755 --- a/indra/newview/llestateinfomodel.h +++ b/indra/newview/llestateinfomodel.h @@ -30,6 +30,8 @@ class LLMessageSystem; #include "llsingleton.h" +#include "llcoros.h" +#include "lleventcoro.h" /** * Contains estate info, notifies interested parties of its changes. @@ -73,7 +75,6 @@ protected: friend class LLSingleton<LLEstateInfoModel>; friend class LLDispatchEstateUpdateInfo; - friend class LLEstateChangeInfoResponder; LLEstateInfoModel(); @@ -99,6 +100,8 @@ private: update_signal_t mUpdateSignal; /// emitted when we receive update from sim update_signal_t mCommitSignal; /// emitted when our update gets applied to sim + + void commitEstateInfoCapsCoro(std::string url); }; inline bool LLEstateInfoModel::getFlag(U64 flag) const diff --git a/indra/newview/lleventpoll.cpp b/indra/newview/lleventpoll.cpp index 4de6ad4d2f..0aad1d5ba9 100755 --- a/indra/newview/lleventpoll.cpp +++ b/indra/newview/lleventpoll.cpp @@ -30,261 +30,248 @@ #include "llappviewer.h" #include "llagent.h" -#include "llhttpclient.h" -#include "llhttpconstants.h" #include "llsdserialize.h" #include "lleventtimer.h" #include "llviewerregion.h" #include "message.h" #include "lltrans.h" +#include "llcoros.h" +#include "lleventcoro.h" +#include "llcorehttputil.h" +#include "lleventfilter.h" -namespace +namespace LLEventPolling { - // We will wait RETRY_SECONDS + (errorCount * RETRY_SECONDS_INC) before retrying after an error. - // This means we attempt to recover relatively quickly but back off giving more time to recover - // until we finally give up after MAX_EVENT_POLL_HTTP_ERRORS attempts. - const F32 EVENT_POLL_ERROR_RETRY_SECONDS = 15.f; // ~ half of a normal timeout. - const F32 EVENT_POLL_ERROR_RETRY_SECONDS_INC = 5.f; // ~ half of a normal timeout. - const S32 MAX_EVENT_POLL_HTTP_ERRORS = 10; // ~5 minutes, by the above rules. - - class LLEventPollResponder : public LLHTTPClient::Responder - { - LOG_CLASS(LLEventPollResponder); - public: - - static LLHTTPClient::ResponderPtr start(const std::string& pollURL, const LLHost& sender); - void stop(); - - void makeRequest(); - - /* virtual */ void completedRaw(const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer); - - private: - LLEventPollResponder(const std::string& pollURL, const LLHost& sender); - ~LLEventPollResponder(); - - - void handleMessage(const LLSD& content); - - /* virtual */ void httpFailure(); - /* virtual */ void httpSuccess(); - - private: - - bool mDone; - - std::string mPollURL; - std::string mSender; - - LLSD mAcknowledge; - - // these are only here for debugging so we can see which poller is which - static int sCount; - int mCount; - S32 mErrorCount; - }; - - class LLEventPollEventTimer : public LLEventTimer - { - typedef LLPointer<LLEventPollResponder> EventPollResponderPtr; - - public: - LLEventPollEventTimer(F32 period, EventPollResponderPtr responder) - : LLEventTimer(period), mResponder(responder) - { } - - virtual BOOL tick() - { - mResponder->makeRequest(); - return TRUE; // Causes this instance to be deleted. - } - - private: - - EventPollResponderPtr mResponder; - }; - - //static - LLHTTPClient::ResponderPtr LLEventPollResponder::start( - const std::string& pollURL, const LLHost& sender) - { - LLHTTPClient::ResponderPtr result = new LLEventPollResponder(pollURL, sender); - LL_INFOS() << "LLEventPollResponder::start <" << sCount << "> " - << pollURL << LL_ENDL; - return result; - } - - void LLEventPollResponder::stop() - { - LL_INFOS() << "LLEventPollResponder::stop <" << mCount << "> " - << mPollURL << LL_ENDL; - // there should be a way to stop a LLHTTPClient request in progress - mDone = true; - } - - int LLEventPollResponder::sCount = 0; - - LLEventPollResponder::LLEventPollResponder(const std::string& pollURL, const LLHost& sender) - : mDone(false), - mPollURL(pollURL), - mCount(++sCount), - mErrorCount(0) - { - //extract host and port of simulator to set as sender - LLViewerRegion *regionp = gAgent.getRegion(); - if (!regionp) - { - LL_ERRS() << "LLEventPoll initialized before region is added." << LL_ENDL; - } - mSender = sender.getIPandPort(); - LL_INFOS() << "LLEventPoll initialized with sender " << mSender << LL_ENDL; - makeRequest(); - } - - LLEventPollResponder::~LLEventPollResponder() - { - stop(); - LL_DEBUGS() << "LLEventPollResponder::~Impl <" << mCount << "> " - << mPollURL << LL_ENDL; - } - - // virtual - void LLEventPollResponder::completedRaw(const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer) - { - if (getStatus() == HTTP_BAD_GATEWAY) - { - // These errors are not parsable as LLSD, - // which LLHTTPClient::Responder::completedRaw will try to do. - httpCompleted(); - } - else - { - LLHTTPClient::Responder::completedRaw(channels,buffer); - } - } - - void LLEventPollResponder::makeRequest() - { - LLSD request; - request["ack"] = mAcknowledge; - request["done"] = mDone; - - LL_DEBUGS() << "LLEventPollResponder::makeRequest <" << mCount << "> ack = " - << LLSDXMLStreamer(mAcknowledge) << LL_ENDL; - LLHTTPClient::post(mPollURL, request, this); - } - - void LLEventPollResponder::handleMessage(const LLSD& content) - { - std::string msg_name = content["message"]; - LLSD message; - message["sender"] = mSender; - message["body"] = content["body"]; - LLMessageSystem::dispatch(msg_name, message); - } - - //virtual - void LLEventPollResponder::httpFailure() - { - if (mDone) return; - - // A HTTP_BAD_GATEWAY (502) error is our standard timeout response - // we get this when there are no events. - if ( getStatus() == HTTP_BAD_GATEWAY ) - { - mErrorCount = 0; - makeRequest(); - } - else if (mErrorCount < MAX_EVENT_POLL_HTTP_ERRORS) - { - ++mErrorCount; - - // The 'tick' will return TRUE causing the timer to delete this. - new LLEventPollEventTimer(EVENT_POLL_ERROR_RETRY_SECONDS - + mErrorCount * EVENT_POLL_ERROR_RETRY_SECONDS_INC - , this); - - LL_WARNS() << dumpResponse() << LL_ENDL; - } - else - { - LL_WARNS() << dumpResponse() - << " [count:" << mCount << "] " - << (mDone ? " -- done" : "") << LL_ENDL; - stop(); - - // At this point we have given up and the viewer will not receive HTTP messages from the simulator. - // IMs, teleports, about land, selecing land, region crossing and more will all fail. - // They are essentially disconnected from the region even though some things may still work. - // Since things won't get better until they relog we force a disconnect now. - - // *NOTE:Mani - The following condition check to see if this failing event poll - // is attached to the Agent's main region. If so we disconnect the viewer. - // Else... its a child region and we just leave the dead event poll stopped and - // continue running. - if(gAgent.getRegion() && gAgent.getRegion()->getHost().getIPandPort() == mSender) - { - LL_WARNS() << "Forcing disconnect due to stalled main region event poll." << LL_ENDL; - LLAppViewer::instance()->forceDisconnect(LLTrans::getString("AgentLostConnection")); - } - } - } - - //virtual - void LLEventPollResponder::httpSuccess() - { - LL_DEBUGS() << "LLEventPollResponder::result <" << mCount << ">" - << (mDone ? " -- done" : "") << LL_ENDL; - - if (mDone) return; - - mErrorCount = 0; - - const LLSD& content = getContent(); - if (!content.isMap() || - !content.get("events") || - !content.get("id")) - { - LL_WARNS() << "received event poll with no events or id key: " << dumpResponse() << LL_ENDL; - makeRequest(); - return; - } - - mAcknowledge = content["id"]; - LLSD events = content["events"]; - - if(mAcknowledge.isUndefined()) - { - LL_WARNS() << "LLEventPollResponder: id undefined" << LL_ENDL; - } - - // was LL_INFOS() but now that CoarseRegionUpdate is TCP @ 1/second, it'd be too verbose for viewer logs. -MG - LL_DEBUGS() << "LLEventPollResponder::httpSuccess <" << mCount << "> " << events.size() << "events (id " - << LLSDXMLStreamer(mAcknowledge) << ")" << LL_ENDL; - - LLSD::array_const_iterator i = events.beginArray(); - LLSD::array_const_iterator end = events.endArray(); - for (; i != end; ++i) - { - if (i->has("message")) - { - handleMessage(*i); - } - } - - makeRequest(); - } +namespace Details +{ + + class LLEventPollImpl + { + public: + LLEventPollImpl(const LLHost &sender); + + void start(const std::string &url); + void stop(); + + private: + // We will wait RETRY_SECONDS + (errorCount * RETRY_SECONDS_INC) before retrying after an error. + // This means we attempt to recover relatively quickly but back off giving more time to recover + // until we finally give up after MAX_EVENT_POLL_HTTP_ERRORS attempts. + static const F32 EVENT_POLL_ERROR_RETRY_SECONDS; + static const F32 EVENT_POLL_ERROR_RETRY_SECONDS_INC; + static const S32 MAX_EVENT_POLL_HTTP_ERRORS; + + void eventPollCoro(std::string url); + + void handleMessage(const LLSD &content); + + bool mDone; + LLCore::HttpRequest::ptr_t mHttpRequest; + LLCore::HttpRequest::policy_t mHttpPolicy; + std::string mSenderIp; + int mCounter; + LLCoreHttpUtil::HttpCoroutineAdapter::wptr_t mAdapter; + + static int sNextCounter; + }; + + + const F32 LLEventPollImpl::EVENT_POLL_ERROR_RETRY_SECONDS = 15.f; // ~ half of a normal timeout. + const F32 LLEventPollImpl::EVENT_POLL_ERROR_RETRY_SECONDS_INC = 5.f; // ~ half of a normal timeout. + const S32 LLEventPollImpl::MAX_EVENT_POLL_HTTP_ERRORS = 10; // ~5 minutes, by the above rules. + + int LLEventPollImpl::sNextCounter = 1; + + + LLEventPollImpl::LLEventPollImpl(const LLHost &sender) : + mDone(false), + mHttpRequest(), + mHttpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID), + mSenderIp(), + mCounter(sNextCounter++) + + { + LLAppCoreHttp & app_core_http(LLAppViewer::instance()->getAppCoreHttp()); + + mHttpRequest = LLCore::HttpRequest::ptr_t(new LLCore::HttpRequest); + mHttpPolicy = app_core_http.getPolicy(LLAppCoreHttp::AP_LONG_POLL); + mSenderIp = sender.getIPandPort(); + } + + void LLEventPollImpl::handleMessage(const LLSD& content) + { + std::string msg_name = content["message"]; + LLSD message; + message["sender"] = mSenderIp; + message["body"] = content["body"]; + LLMessageSystem::dispatch(msg_name, message); + } + + void LLEventPollImpl::start(const std::string &url) + { + if (!url.empty()) + { + std::string coroname = + LLCoros::instance().launch("LLEventPollImpl::eventPollCoro", + boost::bind(&LLEventPollImpl::eventPollCoro, this, url)); + LL_INFOS("LLEventPollImpl") << coroname << " with url '" << url << LL_ENDL; + } + } + + void LLEventPollImpl::stop() + { + LL_INFOS() << "requesting stop for event poll coroutine <" << mCounter << ">" << LL_ENDL; + mDone = true; + + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t adapter = mAdapter.lock(); + if (adapter) + { + // cancel the yielding operation if any. + adapter->cancelYieldingOperation(); + } + } + + void LLEventPollImpl::eventPollCoro(std::string url) + { + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("EventPoller", mHttpPolicy)); + LLSD acknowledge; + int errorCount = 0; + int counter = mCounter; // saved on the stack for logging. + + LL_INFOS("LLEventPollImpl") << " <" << counter << "> entering coroutine." << LL_ENDL; + + mAdapter = httpAdapter; + + // continually poll for a server update until we've been flagged as + // finished + while (!mDone) + { + LLSD request; + request["ack"] = acknowledge; + request["done"] = mDone; + +// LL_DEBUGS("LLEventPollImpl::eventPollCoro") << "<" << counter << "> request = " +// << LLSDXMLStreamer(request) << LL_ENDL; + + LL_DEBUGS("LLEventPollImpl") << " <" << counter << "> posting and yielding." << LL_ENDL; + LLSD result = httpAdapter->postAndYield(mHttpRequest, url, request); + +// LL_DEBUGS("LLEventPollImpl::eventPollCoro") << "<" << counter << "> result = " +// << LLSDXMLStreamer(result) << LL_ENDL; + + LLSD httpResults = result["http_result"]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) + { + if (status == LLCore::HttpStatus(HTTP_BAD_GATEWAY)) + { // A HTTP_BAD_GATEWAY (502) error is our standard timeout response + // we get this when there are no events. + errorCount = 0; + continue; + } + else if ((status == LLCore::HttpStatus(LLCore::HttpStatus::LLCORE, LLCore::HE_OP_CANCELED)) || + (status == LLCore::HttpStatus(HTTP_NOT_FOUND))) + { // Event polling for this server has been canceled. In + // some cases the server gets ahead of the viewer and will + // return a 404 error (Not Found) before the cancel event + // comes back in the queue + LL_WARNS() << "Canceling coroutine" << LL_ENDL; + break; + } + LL_WARNS("LLEventPollImpl") << "<" << counter << "> Error result from LLCoreHttpUtil::HttpCoroHandler. Code " + << status.toTerseString() << ": '" << httpResults["message"] << "'" << LL_ENDL; + + if (errorCount < MAX_EVENT_POLL_HTTP_ERRORS) + { // An unanticipated error has been received from our poll + // request. Calculate a timeout and wait for it to expire(sleep) + // before trying again. The sleep time is increased by 5 seconds + // for each consecutive error. + LLEventTimeout timeout; + ++errorCount; + + F32 waitToRetry = EVENT_POLL_ERROR_RETRY_SECONDS + + errorCount * EVENT_POLL_ERROR_RETRY_SECONDS_INC; + + LL_WARNS("LLEventPollImpl") << "<" << counter << "> Retrying in " << waitToRetry << + " seconds, error count is now " << errorCount << LL_ENDL; + + timeout.eventAfter(waitToRetry, LLSD()); + llcoro::waitForEventOn(timeout); + + if (mDone) + break; + LL_INFOS("LLEventPollImpl") << "<" << counter << "> About to retry request." << LL_ENDL; + continue; + } + else + { + // At this point we have given up and the viewer will not receive HTTP messages from the simulator. + // IMs, teleports, about land, selecting land, region crossing and more will all fail. + // They are essentially disconnected from the region even though some things may still work. + // Since things won't get better until they relog we force a disconnect now. + mDone = true; + + // *NOTE:Mani - The following condition check to see if this failing event poll + // is attached to the Agent's main region. If so we disconnect the viewer. + // Else... its a child region and we just leave the dead event poll stopped and + // continue running. + if (gAgent.getRegion() && gAgent.getRegion()->getHost().getIPandPort() == mSenderIp) + { + LL_WARNS("LLEventPollImpl") << "< " << counter << "> Forcing disconnect due to stalled main region event poll." << LL_ENDL; + LLAppViewer::instance()->forceDisconnect(LLTrans::getString("AgentLostConnection")); + } + break; + } + } + + errorCount = 0; + + if (!result.isMap() || + !result.get("events") || + !result.get("id")) + { + LL_WARNS("LLEventPollImpl") << " <" << counter << "> received event poll with no events or id key: " << LLSDXMLStreamer(result) << LL_ENDL; + continue; + } + + acknowledge = result["id"]; + LLSD events = result["events"]; + + if (acknowledge.isUndefined()) + { + LL_WARNS("LLEventPollImpl") << " id undefined" << LL_ENDL; + } + + // was LL_INFOS() but now that CoarseRegionUpdate is TCP @ 1/second, it'd be too verbose for viewer logs. -MG + LL_DEBUGS("LLEventPollImpl") << " <" << counter << "> " << events.size() << "events (id " << LLSDXMLStreamer(acknowledge) << ")" << LL_ENDL; + + LLSD::array_const_iterator i = events.beginArray(); + LLSD::array_const_iterator end = events.endArray(); + for (; i != end; ++i) + { + if (i->has("message")) + { + handleMessage(*i); + } + } + } + LL_INFOS("LLEventPollImpl") << " <" << counter << "> Leaving coroutine." << LL_ENDL; + } + +} } -LLEventPoll::LLEventPoll(const std::string& poll_url, const LLHost& sender) - : mImpl(LLEventPollResponder::start(poll_url, sender)) - { } +LLEventPoll::LLEventPoll(const std::string& poll_url, const LLHost& sender): + mImpl() +{ + mImpl = boost::unique_ptr<LLEventPolling::Details::LLEventPollImpl> + (new LLEventPolling::Details::LLEventPollImpl(sender)); + mImpl->start(poll_url); +} LLEventPoll::~LLEventPoll() { - LLHTTPClient::Responder* responderp = mImpl.get(); - LLEventPollResponder* event_poll_responder = dynamic_cast<LLEventPollResponder*>(responderp); - if (event_poll_responder) event_poll_responder->stop(); + mImpl->stop(); + } diff --git a/indra/newview/lleventpoll.h b/indra/newview/lleventpoll.h index e8d98062aa..e32b4ed322 100755 --- a/indra/newview/lleventpoll.h +++ b/indra/newview/lleventpoll.h @@ -28,9 +28,23 @@ #define LL_LLEVENTPOLL_H #include "llhttpclient.h" +#include "boost/move/unique_ptr.hpp" + +namespace boost +{ + using ::boost::movelib::unique_ptr; // move unique_ptr into the boost namespace. +} class LLHost; +namespace LLEventPolling +{ +namespace Details +{ + class LLEventPollImpl; +} +} + class LLEventPoll ///< implements the viewer side of server-to-viewer pushed events. @@ -40,11 +54,11 @@ public: ///< Start polling the URL. virtual ~LLEventPoll(); - ///< will stop polling, cancelling any poll in progress. + ///< will stop polling, canceling any poll in progress. private: - LLHTTPClient::ResponderPtr mImpl; + boost::unique_ptr<LLEventPolling::Details::LLEventPollImpl> mImpl; }; diff --git a/indra/newview/llfacebookconnect.cpp b/indra/newview/llfacebookconnect.cpp index 28319564e4..136e02953c 100755 --- a/indra/newview/llfacebookconnect.cpp +++ b/indra/newview/llfacebookconnect.cpp @@ -34,7 +34,6 @@ #include "llagent.h" #include "llcallingcard.h" // for LLAvatarTracker #include "llcommandhandler.h" -#include "llhttpclient.h" #include "llnotificationsutil.h" #include "llurlaction.h" #include "llimagepng.h" @@ -42,9 +41,11 @@ #include "lltrans.h" #include "llevents.h" #include "llviewerregion.h" +#include "llviewercontrol.h" #include "llfloaterwebcontent.h" #include "llfloaterreg.h" +#include "llcorehttputil.h" boost::scoped_ptr<LLEventPump> LLFacebookConnect::sStateWatcher(new LLEventStream("FacebookConnectState")); boost::scoped_ptr<LLEventPump> LLFacebookConnect::sInfoWatcher(new LLEventStream("FacebookConnectInfo")); @@ -67,6 +68,24 @@ void toast_user_for_facebook_success() LLNotificationsUtil::add("FacebookConnect", args); } +LLCore::HttpHeaders::ptr_t get_headers() +{ + LLCore::HttpHeaders::ptr_t httpHeaders(new LLCore::HttpHeaders); + // The DebugSlshareLogTag mechanism is intended to trigger slshare-service + // debug logging. slshare-service is coded to respond to an X-debug-tag + // header by engaging debug logging for that request only. This way a + // developer need not muck with the slshare-service image to engage debug + // logging. Moreover, the value of X-debug-tag is embedded in each such + // log line so the developer can quickly find the log lines pertinent to + // THIS session. + std::string logtag(gSavedSettings.getString("DebugSlshareLogTag")); + if (! logtag.empty()) + { + httpHeaders->append("X-debug-tag", logtag); + } + return httpHeaders; +} + /////////////////////////////////////////////////////////////////////////////// // class LLFacebookConnectHandler : public LLCommandHandler @@ -125,266 +144,354 @@ LLFacebookConnectHandler gFacebookConnectHandler; /////////////////////////////////////////////////////////////////////////////// // -class LLFacebookConnectResponder : public LLHTTPClient::Responder +void LLFacebookConnect::facebookConnectCoro(std::string authCode, std::string authState) { - LOG_CLASS(LLFacebookConnectResponder); -public: - - LLFacebookConnectResponder() + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("FacebookConnect", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); + + LLSD putData; + if (!authCode.empty()) { - LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_CONNECTION_IN_PROGRESS); + putData["code"] = authCode; + } + if (!authState.empty()) + { + putData["state"] = authState; } - - /* virtual */ void httpSuccess() - { - LL_DEBUGS("FacebookConnect") << "Connect successful. " << dumpResponse() << LL_ENDL; - LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_CONNECTED); - } - /* virtual */ void httpFailure() - { - if ( HTTP_FOUND == getStatus() ) - { - const std::string& location = getResponseHeader(HTTP_IN_HEADER_LOCATION); - if (location.empty()) - { - LL_WARNS("FacebookConnect") << "Missing Location header " << dumpResponse() - << "[headers:" << getResponseHeaders() << "]" << LL_ENDL; - } - else - { - LLFacebookConnect::instance().openFacebookWeb(location); - } - } - else - { - LL_WARNS("FacebookConnect") << dumpResponse() << LL_ENDL; - LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_CONNECTION_FAILED); - const LLSD& content = getContent(); - log_facebook_connect_error("Connect", getStatus(), getReason(), - content.get("error_code"), content.get("error_description")); - } - } -}; + httpOpts->setWantHeaders(true); + httpOpts->setFollowRedirects(false); + + setConnectionState(LLFacebookConnect::FB_CONNECTION_IN_PROGRESS); + + LLSD result = httpAdapter->putAndYield(httpRequest, getFacebookConnectURL("/connection"), putData, httpOpts, get_headers()); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + if (!status) + { + if (status == LLCore::HttpStatus(HTTP_FOUND)) + { + std::string location = httpResults[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_HEADERS][HTTP_IN_HEADER_LOCATION]; + if (location.empty()) + { + LL_WARNS("FacebookConnect") << "Missing Location header " << LL_ENDL; + } + else + { + openFacebookWeb(location); + } + } + } + else + { + LL_INFOS("FacebookConnect") << "Connect successful. " << LL_ENDL; + setConnectionState(LLFacebookConnect::FB_CONNECTED); + } + +} /////////////////////////////////////////////////////////////////////////////// // -class LLFacebookShareResponder : public LLHTTPClient::Responder +bool LLFacebookConnect::testShareStatus(LLSD &result) { - LOG_CLASS(LLFacebookShareResponder); -public: - - LLFacebookShareResponder() - { - LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_POSTING); - } + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); - /* virtual */ void httpSuccess() - { - toast_user_for_facebook_success(); - LL_DEBUGS("FacebookConnect") << "Post successful. " << dumpResponse() << LL_ENDL; - LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_POSTED); - } + if (status) + return true; - /* virtual */ void httpFailure() - { - if ( HTTP_FOUND == getStatus() ) - { - const std::string& location = getResponseHeader(HTTP_IN_HEADER_LOCATION); - if (location.empty()) - { - LL_WARNS("FacebookConnect") << "Missing Location header " << dumpResponse() - << "[headers:" << getResponseHeaders() << "]" << LL_ENDL; - } - else - { - LLFacebookConnect::instance().openFacebookWeb(location); - } - } - else if ( HTTP_NOT_FOUND == getStatus() ) - { - LLFacebookConnect::instance().connectToFacebook(); - } - else - { - LL_WARNS("FacebookConnect") << dumpResponse() << LL_ENDL; - LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_POST_FAILED); - const LLSD& content = getContent(); - log_facebook_connect_error("Share", getStatus(), getReason(), - content.get("error_code"), content.get("error_description")); - } - } -}; + if (status == LLCore::HttpStatus(HTTP_FOUND)) + { + std::string location = httpResults[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_HEADERS][HTTP_IN_HEADER_LOCATION]; + if (location.empty()) + { + LL_WARNS("FacebookConnect") << "Missing Location header " << LL_ENDL; + } + else + { + openFacebookWeb(location); + } + } + if (status == LLCore::HttpStatus(HTTP_NOT_FOUND)) + { + LL_DEBUGS("FacebookConnect") << "Not connected. " << LL_ENDL; + connectToFacebook(); + } + else + { + LL_WARNS("FacebookConnect") << "HTTP Status error " << status.toString() << LL_ENDL; + setConnectionState(LLFacebookConnect::FB_POST_FAILED); + log_facebook_connect_error("Share", status.getStatus(), status.toString(), + result.get("error_code"), result.get("error_description")); + } + return false; +} -/////////////////////////////////////////////////////////////////////////////// -// -class LLFacebookDisconnectResponder : public LLHTTPClient::Responder +void LLFacebookConnect::facebookShareCoro(std::string route, LLSD share) { - LOG_CLASS(LLFacebookDisconnectResponder); -public: - - LLFacebookDisconnectResponder() - { - LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_DISCONNECTING); - } + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("FacebookConnect", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); - void setUserDisconnected() - { - // Clear data - LLFacebookConnect::instance().clearInfo(); - LLFacebookConnect::instance().clearContent(); - //Notify state change - LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_NOT_CONNECTED); - } + httpOpts->setWantHeaders(true); + httpOpts->setFollowRedirects(false); - /* virtual */ void httpSuccess() - { - LL_DEBUGS("FacebookConnect") << "Disconnect successful. " << dumpResponse() << LL_ENDL; - setUserDisconnected(); - } + setConnectionState(LLFacebookConnect::FB_POSTING); - /* virtual */ void httpFailure() - { - //User not found so already disconnected - if ( HTTP_NOT_FOUND == getStatus() ) - { - LL_DEBUGS("FacebookConnect") << "Already disconnected. " << dumpResponse() << LL_ENDL; - setUserDisconnected(); - } - else - { - LL_WARNS("FacebookConnect") << dumpResponse() << LL_ENDL; - LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_DISCONNECT_FAILED); - const LLSD& content = getContent(); - log_facebook_connect_error("Disconnect", getStatus(), getReason(), - content.get("error_code"), content.get("error_description")); - } - } -}; + LLSD result = httpAdapter->postAndYield(httpRequest, getFacebookConnectURL(route, true), share, httpOpts, get_headers()); + + if (testShareStatus(result)) + { + toast_user_for_facebook_success(); + LL_DEBUGS("FacebookConnect") << "Post successful. " << LL_ENDL; + setConnectionState(LLFacebookConnect::FB_POSTED); + } +} + +void LLFacebookConnect::facebookShareImageCoro(std::string route, LLPointer<LLImageFormatted> image, std::string caption) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("FacebookConnect", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpHeaders::ptr_t httpHeaders(get_headers()); + LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); + + httpOpts->setWantHeaders(true); + httpOpts->setFollowRedirects(false); + + std::string imageFormat; + if (dynamic_cast<LLImagePNG*>(image.get())) + { + imageFormat = "png"; + } + else if (dynamic_cast<LLImageJPEG*>(image.get())) + { + imageFormat = "jpg"; + } + else + { + LL_WARNS() << "Image to upload is not a PNG or JPEG" << LL_ENDL; + return; + } + + // All this code is mostly copied from LLWebProfile::post() + static const std::string boundary = "----------------------------0123abcdefab"; + + std::string contentType = "multipart/form-data; boundary=" + boundary; + httpHeaders->append("Content-Type", contentType.c_str()); + + LLCore::BufferArray::ptr_t raw = LLCore::BufferArray::ptr_t(new LLCore::BufferArray()); // + LLCore::BufferArrayStream body(raw.get()); + + // *NOTE: The order seems to matter. + body << "--" << boundary << "\r\n" + << "Content-Disposition: form-data; name=\"caption\"\r\n\r\n" + << caption << "\r\n"; + + body << "--" << boundary << "\r\n" + << "Content-Disposition: form-data; name=\"image\"; filename=\"Untitled." << imageFormat << "\"\r\n" + << "Content-Type: image/" << imageFormat << "\r\n\r\n"; + + // Insert the image data. + // *FIX: Treating this as a string will probably screw it up ... + U8* image_data = image->getData(); + for (S32 i = 0; i < image->getDataSize(); ++i) + { + body << image_data[i]; + } + + body << "\r\n--" << boundary << "--\r\n"; + + setConnectionState(LLFacebookConnect::FB_POSTING); + + LLSD result = httpAdapter->postAndYield(httpRequest, getFacebookConnectURL(route, true), raw, httpOpts, httpHeaders); + + if (testShareStatus(result)) + { + toast_user_for_facebook_success(); + LL_DEBUGS("FacebookConnect") << "Post successful. " << LL_ENDL; + setConnectionState(LLFacebookConnect::FB_POSTED); + } +} /////////////////////////////////////////////////////////////////////////////// // -class LLFacebookConnectedResponder : public LLHTTPClient::Responder +void LLFacebookConnect::facebookDisconnectCoro() { - LOG_CLASS(LLFacebookConnectedResponder); -public: - - LLFacebookConnectedResponder(bool auto_connect) : mAutoConnect(auto_connect) + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("FacebookConnect", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); + + setConnectionState(LLFacebookConnect::FB_DISCONNECTING); + httpOpts->setFollowRedirects(false); + + LLSD result = httpAdapter->deleteAndYield(httpRequest, getFacebookConnectURL("/connection"), httpOpts, get_headers()); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + if (!status && (status != LLCore::HttpStatus(HTTP_FOUND))) + { + LL_WARNS("FacebookConnect") << "Failed to disconnect:" << status.toTerseString() << LL_ENDL; + setConnectionState(LLFacebookConnect::FB_DISCONNECT_FAILED); + log_facebook_connect_error("Disconnect", status.getStatus(), status.toString(), + result.get("error_code"), result.get("error_description")); + } + else { - LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_CONNECTION_IN_PROGRESS); + LL_DEBUGS("FacebookConnect") << "Facebook Disconnect successful. " << LL_ENDL; + clearInfo(); + clearContent(); + //Notify state change + setConnectionState(LLFacebookConnect::FB_NOT_CONNECTED); } - /* virtual */ void httpSuccess() - { - LL_DEBUGS("FacebookConnect") << "Connect successful. " << dumpResponse() << LL_ENDL; - LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_CONNECTED); - } +} - /* virtual */ void httpFailure() - { - // show the facebook login page if not connected yet - if ( HTTP_NOT_FOUND == getStatus() ) - { - LL_DEBUGS("FacebookConnect") << "Not connected. " << dumpResponse() << LL_ENDL; - if (mAutoConnect) - { - LLFacebookConnect::instance().connectToFacebook(); - } - else - { - LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_NOT_CONNECTED); - } - } - else - { - LL_WARNS("FacebookConnect") << dumpResponse() << LL_ENDL; - LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_DISCONNECT_FAILED); - const LLSD& content = getContent(); - log_facebook_connect_error("Connected", getStatus(), getReason(), - content.get("error_code"), content.get("error_description")); - } - } - -private: - bool mAutoConnect; -}; +/////////////////////////////////////////////////////////////////////////////// +// +void LLFacebookConnect::facebookConnectedCheckCoro(bool autoConnect) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("FacebookConnect", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); + + setConnectionState(LLFacebookConnect::FB_CONNECTION_IN_PROGRESS); + + httpOpts->setFollowRedirects(false); + + LLSD result = httpAdapter->getAndYield(httpRequest, getFacebookConnectURL("/connection", true), httpOpts, get_headers()); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) + { + if ( status == LLCore::HttpStatus(HTTP_NOT_FOUND) ) + { + LL_DEBUGS("FacebookConnect") << "Not connected. " << LL_ENDL; + if (autoConnect) + { + connectToFacebook(); + } + else + { + setConnectionState(LLFacebookConnect::FB_NOT_CONNECTED); + } + } + else + { + LL_WARNS("FacebookConnect") << "Failed to test connection:" << status.toTerseString() << LL_ENDL; + + setConnectionState(LLFacebookConnect::FB_DISCONNECT_FAILED); + log_facebook_connect_error("Connected", status.getStatus(), status.toString(), + result.get("error_code"), result.get("error_description")); + } + } + else + { + LL_DEBUGS("FacebookConnect") << "Connect successful. " << LL_ENDL; + setConnectionState(LLFacebookConnect::FB_CONNECTED); + } +} /////////////////////////////////////////////////////////////////////////////// // -class LLFacebookInfoResponder : public LLHTTPClient::Responder +void LLFacebookConnect::facebookConnectInfoCoro() { - LOG_CLASS(LLFacebookInfoResponder); -public: + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("FacebookConnect", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); - /* virtual */ void httpSuccess() - { - LL_INFOS("FacebookConnect") << "Facebook: Info received" << LL_ENDL; - LL_DEBUGS("FacebookConnect") << "Getting Facebook info successful. " << dumpResponse() << LL_ENDL; - LLFacebookConnect::instance().storeInfo(getContent()); - } + httpOpts->setWantHeaders(true); + httpOpts->setFollowRedirects(false); - /* virtual */ void httpFailure() - { - if ( HTTP_FOUND == getStatus() ) - { - const std::string& location = getResponseHeader(HTTP_IN_HEADER_LOCATION); - if (location.empty()) - { - LL_WARNS("FacebookConnect") << "Missing Location header " << dumpResponse() - << "[headers:" << getResponseHeaders() << "]" << LL_ENDL; - } - else - { - LLFacebookConnect::instance().openFacebookWeb(location); - } - } - else - { - LL_WARNS("FacebookConnect") << dumpResponse() << LL_ENDL; - const LLSD& content = getContent(); - log_facebook_connect_error("Info", getStatus(), getReason(), - content.get("error_code"), content.get("error_description")); - } - } -}; + LLSD result = httpAdapter->getAndYield(httpRequest, getFacebookConnectURL("/info", true), httpOpts, get_headers()); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (status == LLCore::HttpStatus(HTTP_FOUND)) + { + std::string location = httpResults[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_HEADERS][HTTP_IN_HEADER_LOCATION]; + if (location.empty()) + { + LL_WARNS("FacebookConnect") << "Missing Location header " << LL_ENDL; + } + else + { + openFacebookWeb(location); + } + } + else if (!status) + { + LL_WARNS("FacebookConnect") << "Facebook Info failed: " << status.toString() << LL_ENDL; + log_facebook_connect_error("Info", status.getStatus(), status.toString(), + result.get("error_code"), result.get("error_description")); + } + else + { + LL_INFOS("FacebookConnect") << "Facebook: Info received" << LL_ENDL; + result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS); + storeInfo(result); + } +} /////////////////////////////////////////////////////////////////////////////// // -class LLFacebookFriendsResponder : public LLHTTPClient::Responder +void LLFacebookConnect::facebookConnectFriendsCoro() { - LOG_CLASS(LLFacebookFriendsResponder); -public: - - /* virtual */ void httpSuccess() - { - LL_DEBUGS("FacebookConnect") << "Getting Facebook friends successful. " << dumpResponse() << LL_ENDL; - LLFacebookConnect::instance().storeContent(getContent()); - } + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("FacebookConnect", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); - /* virtual */ void httpFailure() - { - if ( HTTP_FOUND == getStatus() ) - { - const std::string& location = getResponseHeader(HTTP_IN_HEADER_LOCATION); - if (location.empty()) - { - LL_WARNS("FacebookConnect") << "Missing Location header " << dumpResponse() - << "[headers:" << getResponseHeaders() << "]" << LL_ENDL; - } - else - { - LLFacebookConnect::instance().openFacebookWeb(location); - } - } - else - { - LL_WARNS("FacebookConnect") << dumpResponse() << LL_ENDL; - const LLSD& content = getContent(); - log_facebook_connect_error("Friends", getStatus(), getReason(), - content.get("error_code"), content.get("error_description")); - } - } -}; + httpOpts->setFollowRedirects(false); + + LLSD result = httpAdapter->getAndYield(httpRequest, getFacebookConnectURL("/friends", true), httpOpts, get_headers()); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (status == LLCore::HttpStatus(HTTP_FOUND)) + { + std::string location = httpResults[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_HEADERS][HTTP_IN_HEADER_LOCATION]; + if (location.empty()) + { + LL_WARNS("FacebookConnect") << "Missing Location header " << LL_ENDL; + } + else + { + openFacebookWeb(location); + } + } + else if (!status) + { + LL_WARNS("FacebookConnect") << "Facebook Friends failed: " << status.toString() << LL_ENDL; + log_facebook_connect_error("Info", status.getStatus(), status.toString(), + result.get("error_code"), result.get("error_description")); + } + else + { + LL_INFOS("FacebookConnect") << "Facebook: Friends received" << LL_ENDL; + result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS); + LLSD content = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_CONTENT]; + storeContent(content); + } +} /////////////////////////////////////////////////////////////////////////////// // @@ -439,40 +546,28 @@ std::string LLFacebookConnect::getFacebookConnectURL(const std::string& route, b void LLFacebookConnect::connectToFacebook(const std::string& auth_code, const std::string& auth_state) { - LLSD body; - if (!auth_code.empty()) - { - body["code"] = auth_code; - } - if (!auth_state.empty()) - { - body["state"] = auth_state; - } - - LLHTTPClient::put(getFacebookConnectURL("/connection"), body, new LLFacebookConnectResponder()); + LLCoros::instance().launch("LLFacebookConnect::facebookConnectCoro", + boost::bind(&LLFacebookConnect::facebookConnectCoro, this, auth_code, auth_state)); } void LLFacebookConnect::disconnectFromFacebook() { - LLHTTPClient::del(getFacebookConnectURL("/connection"), new LLFacebookDisconnectResponder()); + LLCoros::instance().launch("LLFacebookConnect::facebookDisconnectCoro", + boost::bind(&LLFacebookConnect::facebookDisconnectCoro, this)); } void LLFacebookConnect::checkConnectionToFacebook(bool auto_connect) { - const bool follow_redirects = false; - const F32 timeout = HTTP_REQUEST_EXPIRY_SECS; - LLHTTPClient::get(getFacebookConnectURL("/connection", true), new LLFacebookConnectedResponder(auto_connect), - LLSD(), timeout, follow_redirects); + LLCoros::instance().launch("LLFacebookConnect::facebookConnectedCheckCoro", + boost::bind(&LLFacebookConnect::facebookConnectedCheckCoro, this, auto_connect)); } void LLFacebookConnect::loadFacebookInfo() { if(mRefreshInfo) { - const bool follow_redirects = false; - const F32 timeout = HTTP_REQUEST_EXPIRY_SECS; - LLHTTPClient::get(getFacebookConnectURL("/info", true), new LLFacebookInfoResponder(), - LLSD(), timeout, follow_redirects); + LLCoros::instance().launch("LLFacebookConnect::facebookConnectInfoCoro", + boost::bind(&LLFacebookConnect::facebookConnectInfoCoro, this)); } } @@ -480,14 +575,13 @@ void LLFacebookConnect::loadFacebookFriends() { if(mRefreshContent) { - const bool follow_redirects = false; - const F32 timeout = HTTP_REQUEST_EXPIRY_SECS; - LLHTTPClient::get(getFacebookConnectURL("/friends", true), new LLFacebookFriendsResponder(), - LLSD(), timeout, follow_redirects); + LLCoros::instance().launch("LLFacebookConnect::facebookConnectFriendsCoro", + boost::bind(&LLFacebookConnect::facebookConnectFriendsCoro, this)); } } -void LLFacebookConnect::postCheckin(const std::string& location, const std::string& name, const std::string& description, const std::string& image, const std::string& message) +void LLFacebookConnect::postCheckin(const std::string& location, const std::string& name, + const std::string& description, const std::string& image, const std::string& message) { LLSD body; if (!location.empty()) @@ -511,80 +605,34 @@ void LLFacebookConnect::postCheckin(const std::string& location, const std::stri body["message"] = message; } - // Note: we can use that route for different publish action. We should be able to use the same responder. - LLHTTPClient::post(getFacebookConnectURL("/share/checkin", true), body, new LLFacebookShareResponder()); + LLCoros::instance().launch("LLFacebookConnect::facebookShareCoro", + boost::bind(&LLFacebookConnect::facebookShareCoro, this, "/share/checkin", body)); } void LLFacebookConnect::sharePhoto(const std::string& image_url, const std::string& caption) { + // *TODO: I could not find an instace where this method is used. Remove? LLSD body; body["image"] = image_url; body["caption"] = caption; - // Note: we can use that route for different publish action. We should be able to use the same responder. - LLHTTPClient::post(getFacebookConnectURL("/share/photo", true), body, new LLFacebookShareResponder()); + LLCoros::instance().launch("LLFacebookConnect::facebookShareCoro", + boost::bind(&LLFacebookConnect::facebookShareCoro, this, "/share/photo", body)); } void LLFacebookConnect::sharePhoto(LLPointer<LLImageFormatted> image, const std::string& caption) { - std::string imageFormat; - if (dynamic_cast<LLImagePNG*>(image.get())) - { - imageFormat = "png"; - } - else if (dynamic_cast<LLImageJPEG*>(image.get())) - { - imageFormat = "jpg"; - } - else - { - LL_WARNS() << "Image to upload is not a PNG or JPEG" << LL_ENDL; - return; - } - - // All this code is mostly copied from LLWebProfile::post() - const std::string boundary = "----------------------------0123abcdefab"; - - LLSD headers; - headers["Content-Type"] = "multipart/form-data; boundary=" + boundary; - - std::ostringstream body; - - // *NOTE: The order seems to matter. - body << "--" << boundary << "\r\n" - << "Content-Disposition: form-data; name=\"caption\"\r\n\r\n" - << caption << "\r\n"; - - body << "--" << boundary << "\r\n" - << "Content-Disposition: form-data; name=\"image\"; filename=\"Untitled." << imageFormat << "\"\r\n" - << "Content-Type: image/" << imageFormat << "\r\n\r\n"; - - // Insert the image data. - // *FIX: Treating this as a string will probably screw it up ... - U8* image_data = image->getData(); - for (S32 i = 0; i < image->getDataSize(); ++i) - { - body << image_data[i]; - } - - body << "\r\n--" << boundary << "--\r\n"; - - // postRaw() takes ownership of the buffer and releases it later. - size_t size = body.str().size(); - U8 *data = new U8[size]; - memcpy(data, body.str().data(), size); - - // Note: we can use that route for different publish action. We should be able to use the same responder. - LLHTTPClient::postRaw(getFacebookConnectURL("/share/photo", true), data, size, new LLFacebookShareResponder(), headers); + LLCoros::instance().launch("LLFacebookConnect::facebookShareImageCoro", + boost::bind(&LLFacebookConnect::facebookShareImageCoro, this, "/share/photo", image, caption)); } void LLFacebookConnect::updateStatus(const std::string& message) { LLSD body; body["message"] = message; - - // Note: we can use that route for different publish action. We should be able to use the same responder. - LLHTTPClient::post(getFacebookConnectURL("/share/wall", true), body, new LLFacebookShareResponder()); + + LLCoros::instance().launch("LLFacebookConnect::facebookShareCoro", + boost::bind(&LLFacebookConnect::facebookShareCoro, this, "/share/wall", body)); } void LLFacebookConnect::storeInfo(const LLSD& info) diff --git a/indra/newview/llfacebookconnect.h b/indra/newview/llfacebookconnect.h index c157db2178..2a2cdb5499 100644 --- a/indra/newview/llfacebookconnect.h +++ b/indra/newview/llfacebookconnect.h @@ -30,6 +30,8 @@ #include "llsingleton.h" #include "llimage.h" +#include "llcoros.h" +#include "lleventcoro.h" class LLEventPump; @@ -101,6 +103,15 @@ private: static boost::scoped_ptr<LLEventPump> sStateWatcher; static boost::scoped_ptr<LLEventPump> sInfoWatcher; static boost::scoped_ptr<LLEventPump> sContentWatcher; + + bool testShareStatus(LLSD &results); + void facebookConnectCoro(std::string authCode, std::string authState); + void facebookConnectedCheckCoro(bool autoConnect); + void facebookDisconnectCoro(); + void facebookShareCoro(std::string route, LLSD share); + void facebookShareImageCoro(std::string route, LLPointer<LLImageFormatted> image, std::string caption); + void facebookConnectInfoCoro(); + void facebookConnectFriendsCoro(); }; #endif // LL_LLFACEBOOKCONNECT_H diff --git a/indra/newview/llfeaturemanager.cpp b/indra/newview/llfeaturemanager.cpp index ea39f812fd..0b76ca16a9 100755 --- a/indra/newview/llfeaturemanager.cpp +++ b/indra/newview/llfeaturemanager.cpp @@ -40,7 +40,6 @@ #include "llappviewer.h" #include "llbufferstream.h" -#include "llhttpclient.h" #include "llnotificationsutil.h" #include "llviewercontrol.h" #include "llworld.h" @@ -55,6 +54,7 @@ #include "llviewershadermgr.h" #include "llstring.h" #include "stringize.h" +#include "llcorehttputil.h" #if LL_WINDOWS #include "lldxhardware.h" @@ -492,95 +492,70 @@ bool LLFeatureManager::loadGPUClass() return true; // indicates that a gpu value was established } - -// responder saves table into file -class LLHTTPFeatureTableResponder : public LLHTTPClient::Responder +void LLFeatureManager::fetchFeatureTableCoro(std::string tableName) { - LOG_CLASS(LLHTTPFeatureTableResponder); -public: + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("FeatureManagerHTTPTable", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); - LLHTTPFeatureTableResponder(std::string filename) : - mFilename(filename) - { - } + const std::string base = gSavedSettings.getString("FeatureManagerHTTPTable"); - - virtual void completedRaw(const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer) - { - if (isGoodStatus()) - { - // write to file - - LL_INFOS() << "writing feature table to " << mFilename << LL_ENDL; - - S32 file_size = buffer->countAfter(channels.in(), NULL); - if (file_size > 0) - { - // read from buffer - U8* copy_buffer = new U8[file_size]; - buffer->readAfter(channels.in(), NULL, copy_buffer, file_size); - - // write to file - LLAPRFile out(mFilename, LL_APR_WB); - out.write(copy_buffer, file_size); - out.close(); - } - } - else - { - char body[1025]; - body[1024] = '\0'; - LLBufferStream istr(channels, buffer.get()); - istr.get(body,1024); - if (strlen(body) > 0) - { - mContent["body"] = body; - } - LL_WARNS() << dumpResponse() << LL_ENDL; - } - } - -private: - std::string mFilename; -}; - -void fetch_feature_table(std::string table) -{ - const std::string base = gSavedSettings.getString("FeatureManagerHTTPTable"); #if LL_WINDOWS - std::string os_string = LLAppViewer::instance()->getOSInfo().getOSStringSimple(); - std::string filename; - if (os_string.find("Microsoft Windows XP") == 0) - { - filename = llformat(table.c_str(), "_xp", LLVersionInfo::getVersion().c_str()); - } - else - { - filename = llformat(table.c_str(), "", LLVersionInfo::getVersion().c_str()); - } + std::string os_string = LLAppViewer::instance()->getOSInfo().getOSStringSimple(); + std::string filename; + + if (os_string.find("Microsoft Windows XP") == 0) + { + filename = llformat(tableName.c_str(), "_xp", LLVersionInfo::getVersion().c_str()); + } + else + { + filename = llformat(tableName.c_str(), "", LLVersionInfo::getVersion().c_str()); + } #else - const std::string filename = llformat(table.c_str(), LLVersionInfo::getVersion().c_str()); + const std::string filename = llformat(tableName.c_str(), LLVersionInfo::getVersion().c_str()); #endif - const std::string url = base + "/" + filename; + std::string url = base + "/" + filename; + // testing url below + //url = "http://viewer-settings.secondlife.com/featuretable.2.1.1.208406.txt"; + const std::string path = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename); - const std::string path = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename); - LL_INFOS() << "LLFeatureManager fetching " << url << " into " << path << LL_ENDL; - - LLHTTPClient::get(url, new LLHTTPFeatureTableResponder(path)); -} + LL_INFOS() << "LLFeatureManager fetching " << url << " into " << path << LL_ENDL; + + LLSD result = httpAdapter->getRawAndYield(httpRequest, url); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (status) + { // There was a newer feature table on the server. We've grabbed it and now should write it. + // write to file + const LLSD::Binary &raw = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_RAW].asBinary(); + LL_INFOS() << "writing feature table to " << filename << LL_ENDL; + + S32 size = raw.size(); + if (size > 0) + { + // write to file + LLAPRFile out(filename, LL_APR_WB); + out.write(raw.data(), size); + out.close(); + } + } +} // fetch table(s) from a website (S3) void LLFeatureManager::fetchHTTPTables() { - fetch_feature_table(FEATURE_TABLE_VER_FILENAME); + LLCoros::instance().launch("LLFeatureManager::fetchFeatureTableCoro", + boost::bind(&LLFeatureManager::fetchFeatureTableCoro, this, FEATURE_TABLE_VER_FILENAME)); } - void LLFeatureManager::cleanupFeatureTables() { std::for_each(mMaskList.begin(), mMaskList.end(), DeletePairedPointer()); diff --git a/indra/newview/llfeaturemanager.h b/indra/newview/llfeaturemanager.h index 69078ccc21..12ea691b49 100755 --- a/indra/newview/llfeaturemanager.h +++ b/indra/newview/llfeaturemanager.h @@ -32,6 +32,8 @@ #include "llsingleton.h" #include "llstring.h" #include <map> +#include "llcoros.h" +#include "lleventcoro.h" typedef enum EGPUClass { @@ -164,6 +166,7 @@ protected: void initBaseMask(); + void fetchFeatureTableCoro(std::string name); std::map<std::string, LLFeatureList *> mMaskList; std::set<std::string> mSkippedFeatures; diff --git a/indra/newview/llflickrconnect.cpp b/indra/newview/llflickrconnect.cpp index b75660ea00..83e4f19191 100644 --- a/indra/newview/llflickrconnect.cpp +++ b/indra/newview/llflickrconnect.cpp @@ -32,7 +32,6 @@ #include "llagent.h" #include "llcallingcard.h" // for LLAvatarTracker #include "llcommandhandler.h" -#include "llhttpclient.h" #include "llnotificationsutil.h" #include "llurlaction.h" #include "llimagepng.h" @@ -43,6 +42,7 @@ #include "llfloaterwebcontent.h" #include "llfloaterreg.h" +#include "llcorehttputil.h" boost::scoped_ptr<LLEventPump> LLFlickrConnect::sStateWatcher(new LLEventStream("FlickrConnectState")); boost::scoped_ptr<LLEventPump> LLFlickrConnect::sInfoWatcher(new LLEventStream("FlickrConnectInfo")); @@ -67,228 +67,324 @@ void toast_user_for_flickr_success() /////////////////////////////////////////////////////////////////////////////// // -class LLFlickrConnectResponder : public LLHTTPClient::Responder +void LLFlickrConnect::flickrConnectCoro(std::string requestToken, std::string oauthVerifier) { - LOG_CLASS(LLFlickrConnectResponder); -public: - - LLFlickrConnectResponder() + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("FlickrConnect", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); + + httpOpts->setWantHeaders(true); + httpOpts->setFollowRedirects(false); + + LLSD body; + if (!requestToken.empty()) + body["request_token"] = requestToken; + if (!oauthVerifier.empty()) + body["oauth_verifier"] = oauthVerifier; + + setConnectionState(LLFlickrConnect::FLICKR_CONNECTION_IN_PROGRESS); + + LLSD result = httpAdapter->putAndYield(httpRequest, getFlickrConnectURL("/connection"), body, httpOpts); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) + { + if ( status == LLCore::HttpStatus(HTTP_FOUND) ) + { + std::string location = httpResults[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_HEADERS][HTTP_IN_HEADER_LOCATION]; + if (location.empty()) + { + LL_WARNS("FlickrConnect") << "Missing Location header " << LL_ENDL; + } + else + { + openFlickrWeb(location); + } + } + else + { + LL_WARNS("FlickrConnect") << "Connection failed " << status.toString() << LL_ENDL; + setConnectionState(LLFlickrConnect::FLICKR_CONNECTION_FAILED); + log_flickr_connect_error("Connect", status.getStatus(), status.toString(), + result.get("error_code"), result.get("error_description")); + } + } + else { - LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_CONNECTION_IN_PROGRESS); + LL_DEBUGS("FlickrConnect") << "Connect successful. " << LL_ENDL; + setConnectionState(LLFlickrConnect::FLICKR_CONNECTED); } - - /* virtual */ void httpSuccess() - { - LL_DEBUGS("FlickrConnect") << "Connect successful. " << dumpResponse() << LL_ENDL; - LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_CONNECTED); - } - - /* virtual */ void httpFailure() - { - if ( HTTP_FOUND == getStatus() ) - { - const std::string& location = getResponseHeader(HTTP_IN_HEADER_LOCATION); - if (location.empty()) - { - LL_WARNS("FlickrConnect") << "Missing Location header " << dumpResponse() - << "[headers:" << getResponseHeaders() << "]" << LL_ENDL; - } - else - { - LLFlickrConnect::instance().openFlickrWeb(location); - } - } - else - { - LL_WARNS("FlickrConnect") << dumpResponse() << LL_ENDL; - LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_CONNECTION_FAILED); - const LLSD& content = getContent(); - log_flickr_connect_error("Connect", getStatus(), getReason(), - content.get("error_code"), content.get("error_description")); - } - } -}; +} /////////////////////////////////////////////////////////////////////////////// // -class LLFlickrShareResponder : public LLHTTPClient::Responder +bool LLFlickrConnect::testShareStatus(LLSD &result) { - LOG_CLASS(LLFlickrShareResponder); -public: - - LLFlickrShareResponder() - { - LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_POSTING); - } - - /* virtual */ void httpSuccess() - { + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (status) + return true; + + if (status == LLCore::HttpStatus(HTTP_FOUND)) + { + std::string location = httpResults[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_HEADERS][HTTP_IN_HEADER_LOCATION]; + if (location.empty()) + { + LL_WARNS("FlickrConnect") << "Missing Location header " << LL_ENDL; + } + else + { + openFlickrWeb(location); + } + } + if (status == LLCore::HttpStatus(HTTP_NOT_FOUND)) + { + LL_DEBUGS("FlickrConnect") << "Not connected. " << LL_ENDL; + connectToFlickr(); + } + else + { + LL_WARNS("FlickrConnect") << "HTTP Status error " << status.toString() << LL_ENDL; + setConnectionState(LLFlickrConnect::FLICKR_POST_FAILED); + log_flickr_connect_error("Share", status.getStatus(), status.toString(), + result.get("error_code"), result.get("error_description")); + } + return false; +} + +void LLFlickrConnect::flickrShareCoro(LLSD share) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("FlickrConnect", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); + + httpOpts->setWantHeaders(true); + httpOpts->setFollowRedirects(false); + + setConnectionState(LLFlickrConnect::FLICKR_POSTING); + + LLSD result = httpAdapter->postAndYield(httpRequest, getFlickrConnectURL("/share/photo", true), share, httpOpts); + + if (testShareStatus(result)) + { toast_user_for_flickr_success(); - LL_DEBUGS("FlickrConnect") << "Post successful. " << dumpResponse() << LL_ENDL; - LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_POSTED); - } - - /* virtual */ void httpFailure() - { - if ( HTTP_FOUND == getStatus() ) - { - const std::string& location = getResponseHeader(HTTP_IN_HEADER_LOCATION); - if (location.empty()) - { - LL_WARNS("FlickrConnect") << "Missing Location header " << dumpResponse() - << "[headers:" << getResponseHeaders() << "]" << LL_ENDL; - } - else - { - LLFlickrConnect::instance().openFlickrWeb(location); - } - } - else if ( HTTP_NOT_FOUND == getStatus() ) - { - LLFlickrConnect::instance().connectToFlickr(); - } - else - { - LL_WARNS("FlickrConnect") << dumpResponse() << LL_ENDL; - LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_POST_FAILED); - const LLSD& content = getContent(); - log_flickr_connect_error("Share", getStatus(), getReason(), - content.get("error_code"), content.get("error_description")); - } - } -}; + LL_DEBUGS("FlickrConnect") << "Post successful. " << LL_ENDL; + setConnectionState(LLFlickrConnect::FLICKR_POSTED); + } + +} + +void LLFlickrConnect::flickrShareImageCoro(LLPointer<LLImageFormatted> image, std::string title, std::string description, std::string tags, int safetyLevel) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("FlickrConnect", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpHeaders::ptr_t httpHeaders(new LLCore::HttpHeaders); + LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); + + httpOpts->setWantHeaders(true); + httpOpts->setFollowRedirects(false); + + std::string imageFormat; + if (dynamic_cast<LLImagePNG*>(image.get())) + { + imageFormat = "png"; + } + else if (dynamic_cast<LLImageJPEG*>(image.get())) + { + imageFormat = "jpg"; + } + else + { + LL_WARNS() << "Image to upload is not a PNG or JPEG" << LL_ENDL; + return; + } + + // All this code is mostly copied from LLWebProfile::post() + const std::string boundary = "----------------------------0123abcdefab"; + + std::string contentType = "multipart/form-data; boundary=" + boundary; + httpHeaders->append("Content-Type", contentType.c_str()); + + LLCore::BufferArray::ptr_t raw = LLCore::BufferArray::ptr_t(new LLCore::BufferArray()); // + LLCore::BufferArrayStream body(raw.get()); + + // *NOTE: The order seems to matter. + body << "--" << boundary << "\r\n" + << "Content-Disposition: form-data; name=\"title\"\r\n\r\n" + << title << "\r\n"; + + body << "--" << boundary << "\r\n" + << "Content-Disposition: form-data; name=\"description\"\r\n\r\n" + << description << "\r\n"; + + body << "--" << boundary << "\r\n" + << "Content-Disposition: form-data; name=\"tags\"\r\n\r\n" + << tags << "\r\n"; + + body << "--" << boundary << "\r\n" + << "Content-Disposition: form-data; name=\"safety_level\"\r\n\r\n" + << safetyLevel << "\r\n"; + + body << "--" << boundary << "\r\n" + << "Content-Disposition: form-data; name=\"image\"; filename=\"Untitled." << imageFormat << "\"\r\n" + << "Content-Type: image/" << imageFormat << "\r\n\r\n"; + + // Insert the image data. + // *FIX: Treating this as a string will probably screw it up ... + U8* image_data = image->getData(); + for (S32 i = 0; i < image->getDataSize(); ++i) + { + body << image_data[i]; + } + + body << "\r\n--" << boundary << "--\r\n"; + + LLSD result = httpAdapter->postAndYield(httpRequest, getFlickrConnectURL("/share/photo", true), raw, httpOpts, httpHeaders); + + if (testShareStatus(result)) + { + toast_user_for_flickr_success(); + LL_DEBUGS("FlickrConnect") << "Post successful. " << LL_ENDL; + setConnectionState(LLFlickrConnect::FLICKR_POSTED); + } +} /////////////////////////////////////////////////////////////////////////////// // -class LLFlickrDisconnectResponder : public LLHTTPClient::Responder +void LLFlickrConnect::flickrDisconnectCoro() { - LOG_CLASS(LLFlickrDisconnectResponder); -public: - - LLFlickrDisconnectResponder() - { - LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_DISCONNECTING); - } + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("FlickrConnect", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); - void setUserDisconnected() - { - // Clear data - LLFlickrConnect::instance().clearInfo(); + setConnectionState(LLFlickrConnect::FLICKR_DISCONNECTING); + httpOpts->setFollowRedirects(false); - //Notify state change - LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_NOT_CONNECTED); - } + LLSD result = httpAdapter->deleteAndYield(httpRequest, getFlickrConnectURL("/connection"), httpOpts); - /* virtual */ void httpSuccess() - { - LL_DEBUGS("FlickrConnect") << "Disconnect successful. " << dumpResponse() << LL_ENDL; - setUserDisconnected(); - } - - /* virtual */ void httpFailure() - { - //User not found so already disconnected - if ( HTTP_NOT_FOUND == getStatus() ) - { - LL_DEBUGS("FlickrConnect") << "Already disconnected. " << dumpResponse() << LL_ENDL; - setUserDisconnected(); - } - else - { - LL_WARNS("FlickrConnect") << dumpResponse() << LL_ENDL; - LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_DISCONNECT_FAILED); - const LLSD& content = getContent(); - log_flickr_connect_error("Disconnect", getStatus(), getReason(), - content.get("error_code"), content.get("error_description")); - } - } -}; + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status && (status != LLCore::HttpStatus(HTTP_NOT_FOUND))) + { + LL_WARNS("FlickrConnect") << "Disconnect failed!" << LL_ENDL; + setConnectionState(LLFlickrConnect::FLICKR_DISCONNECT_FAILED); + + log_flickr_connect_error("Disconnect", status.getStatus(), status.toString(), + result.get("error_code"), result.get("error_description")); + } + else + { + LL_DEBUGS("FlickrConnect") << "Disconnect successful. " << LL_ENDL; + clearInfo(); + setConnectionState(LLFlickrConnect::FLICKR_NOT_CONNECTED); + } +} /////////////////////////////////////////////////////////////////////////////// // -class LLFlickrConnectedResponder : public LLHTTPClient::Responder +void LLFlickrConnect::flickrConnectedCoro(bool autoConnect) { - LOG_CLASS(LLFlickrConnectedResponder); -public: - - LLFlickrConnectedResponder(bool auto_connect) : mAutoConnect(auto_connect) + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("FlickrConnect", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); + + setConnectionState(LLFlickrConnect::FLICKR_CONNECTION_IN_PROGRESS); + + httpOpts->setFollowRedirects(false); + + LLSD result = httpAdapter->getAndYield(httpRequest, getFlickrConnectURL("/connection", true), httpOpts); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) { - LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_CONNECTION_IN_PROGRESS); + if (status == LLCore::HttpStatus(HTTP_NOT_FOUND)) + { + LL_DEBUGS("FlickrConnect") << "Not connected. " << LL_ENDL; + if (autoConnect) + { + connectToFlickr(); + } + else + { + setConnectionState(LLFlickrConnect::FLICKR_NOT_CONNECTED); + } + } + else + { + LL_WARNS("FlickrConnect") << "Failed to test connection:" << status.toTerseString() << LL_ENDL; + + setConnectionState(LLFlickrConnect::FLICKR_CONNECTION_FAILED); + log_flickr_connect_error("Connected", status.getStatus(), status.toString(), + result.get("error_code"), result.get("error_description")); + } } - - /* virtual */ void httpSuccess() - { - LL_DEBUGS("FlickrConnect") << "Connect successful. " << dumpResponse() << LL_ENDL; - LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_CONNECTED); - } - - /* virtual */ void httpFailure() - { - // show the facebook login page if not connected yet - if ( HTTP_NOT_FOUND == getStatus() ) - { - LL_DEBUGS("FlickrConnect") << "Not connected. " << dumpResponse() << LL_ENDL; - if (mAutoConnect) - { - LLFlickrConnect::instance().connectToFlickr(); - } - else - { - LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_NOT_CONNECTED); - } - } - else - { - LL_WARNS("FlickrConnect") << dumpResponse() << LL_ENDL; - LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_CONNECTION_FAILED); - const LLSD& content = getContent(); - log_flickr_connect_error("Connected", getStatus(), getReason(), - content.get("error_code"), content.get("error_description")); - } - } - -private: - bool mAutoConnect; -}; + else + { + LL_DEBUGS("FlickrConnect") << "Connect successful. " << LL_ENDL; + setConnectionState(LLFlickrConnect::FLICKR_CONNECTED); + } + +} /////////////////////////////////////////////////////////////////////////////// // -class LLFlickrInfoResponder : public LLHTTPClient::Responder +void LLFlickrConnect::flickrInfoCoro() { - LOG_CLASS(LLFlickrInfoResponder); -public: + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("FlickrConnect", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); - /* virtual */ void httpSuccess() - { - LL_INFOS("FlickrConnect") << "Flickr: Info received" << LL_ENDL; - LL_DEBUGS("FlickrConnect") << "Getting Flickr info successful. " << dumpResponse() << LL_ENDL; - LLFlickrConnect::instance().storeInfo(getContent()); - } - - /* virtual */ void httpFailure() - { - if ( HTTP_FOUND == getStatus() ) - { - const std::string& location = getResponseHeader(HTTP_IN_HEADER_LOCATION); - if (location.empty()) - { - LL_WARNS("FlickrConnect") << "Missing Location header " << dumpResponse() - << "[headers:" << getResponseHeaders() << "]" << LL_ENDL; - } - else - { - LLFlickrConnect::instance().openFlickrWeb(location); - } - } - else - { - LL_WARNS("FlickrConnect") << dumpResponse() << LL_ENDL; - const LLSD& content = getContent(); - log_flickr_connect_error("Info", getStatus(), getReason(), - content.get("error_code"), content.get("error_description")); - } - } -}; + httpOpts->setWantHeaders(true); + httpOpts->setFollowRedirects(false); + + LLSD result = httpAdapter->getAndYield(httpRequest, getFlickrConnectURL("/info", true), httpOpts); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (status == LLCore::HttpStatus(HTTP_FOUND)) + { + std::string location = httpResults[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_HEADERS][HTTP_IN_HEADER_LOCATION]; + if (location.empty()) + { + LL_WARNS("FlickrConnect") << "Missing Location header " << LL_ENDL; + } + else + { + openFlickrWeb(location); + } + } + else if (!status) + { + LL_WARNS("FlickrConnect") << "Flickr Info failed: " << status.toString() << LL_ENDL; + log_flickr_connect_error("Info", status.getStatus(), status.toString(), + result.get("error_code"), result.get("error_description")); + } + else + { + LL_INFOS("FlickrConnect") << "Flickr: Info received" << LL_ENDL; + result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS); + storeInfo(result); + } +} /////////////////////////////////////////////////////////////////////////////// // @@ -341,36 +437,28 @@ std::string LLFlickrConnect::getFlickrConnectURL(const std::string& route, bool void LLFlickrConnect::connectToFlickr(const std::string& request_token, const std::string& oauth_verifier) { - LLSD body; - if (!request_token.empty()) - body["request_token"] = request_token; - if (!oauth_verifier.empty()) - body["oauth_verifier"] = oauth_verifier; - - LLHTTPClient::put(getFlickrConnectURL("/connection"), body, new LLFlickrConnectResponder()); + LLCoros::instance().launch("LLFlickrConnect::flickrConnectCoro", + boost::bind(&LLFlickrConnect::flickrConnectCoro, this, request_token, oauth_verifier)); } void LLFlickrConnect::disconnectFromFlickr() { - LLHTTPClient::del(getFlickrConnectURL("/connection"), new LLFlickrDisconnectResponder()); + LLCoros::instance().launch("LLFlickrConnect::flickrDisconnectCoro", + boost::bind(&LLFlickrConnect::flickrDisconnectCoro, this)); } void LLFlickrConnect::checkConnectionToFlickr(bool auto_connect) { - const bool follow_redirects = false; - const F32 timeout = HTTP_REQUEST_EXPIRY_SECS; - LLHTTPClient::get(getFlickrConnectURL("/connection", true), new LLFlickrConnectedResponder(auto_connect), - LLSD(), timeout, follow_redirects); + LLCoros::instance().launch("LLFlickrConnect::flickrConnectedCoro", + boost::bind(&LLFlickrConnect::flickrConnectedCoro, this, auto_connect)); } void LLFlickrConnect::loadFlickrInfo() { if(mRefreshInfo) { - const bool follow_redirects = false; - const F32 timeout = HTTP_REQUEST_EXPIRY_SECS; - LLHTTPClient::get(getFlickrConnectURL("/info", true), new LLFlickrInfoResponder(), - LLSD(), timeout, follow_redirects); + LLCoros::instance().launch("LLFlickrConnect::flickrInfoCoro", + boost::bind(&LLFlickrConnect::flickrInfoCoro, this)); } } @@ -382,74 +470,17 @@ void LLFlickrConnect::uploadPhoto(const std::string& image_url, const std::strin body["description"] = description; body["tags"] = tags; body["safety_level"] = safety_level; - - // Note: we can use that route for different publish action. We should be able to use the same responder. - LLHTTPClient::post(getFlickrConnectURL("/share/photo", true), body, new LLFlickrShareResponder()); + + LLCoros::instance().launch("LLFlickrConnect::flickrShareCoro", + boost::bind(&LLFlickrConnect::flickrShareCoro, this, body)); } void LLFlickrConnect::uploadPhoto(LLPointer<LLImageFormatted> image, const std::string& title, const std::string& description, const std::string& tags, int safety_level) { - std::string imageFormat; - if (dynamic_cast<LLImagePNG*>(image.get())) - { - imageFormat = "png"; - } - else if (dynamic_cast<LLImageJPEG*>(image.get())) - { - imageFormat = "jpg"; - } - else - { - LL_WARNS() << "Image to upload is not a PNG or JPEG" << LL_ENDL; - return; - } - - // All this code is mostly copied from LLWebProfile::post() - const std::string boundary = "----------------------------0123abcdefab"; - - LLSD headers; - headers["Content-Type"] = "multipart/form-data; boundary=" + boundary; - - std::ostringstream body; - - // *NOTE: The order seems to matter. - body << "--" << boundary << "\r\n" - << "Content-Disposition: form-data; name=\"title\"\r\n\r\n" - << title << "\r\n"; - - body << "--" << boundary << "\r\n" - << "Content-Disposition: form-data; name=\"description\"\r\n\r\n" - << description << "\r\n"; - - body << "--" << boundary << "\r\n" - << "Content-Disposition: form-data; name=\"tags\"\r\n\r\n" - << tags << "\r\n"; - - body << "--" << boundary << "\r\n" - << "Content-Disposition: form-data; name=\"safety_level\"\r\n\r\n" - << safety_level << "\r\n"; - - body << "--" << boundary << "\r\n" - << "Content-Disposition: form-data; name=\"image\"; filename=\"Untitled." << imageFormat << "\"\r\n" - << "Content-Type: image/" << imageFormat << "\r\n\r\n"; - - // Insert the image data. - // *FIX: Treating this as a string will probably screw it up ... - U8* image_data = image->getData(); - for (S32 i = 0; i < image->getDataSize(); ++i) - { - body << image_data[i]; - } - - body << "\r\n--" << boundary << "--\r\n"; - // postRaw() takes ownership of the buffer and releases it later. - size_t size = body.str().size(); - U8 *data = new U8[size]; - memcpy(data, body.str().data(), size); - - // Note: we can use that route for different publish action. We should be able to use the same responder. - LLHTTPClient::postRaw(getFlickrConnectURL("/share/photo", true), data, size, new LLFlickrShareResponder(), headers); + LLCoros::instance().launch("LLFlickrConnect::flickrShareImageCoro", + boost::bind(&LLFlickrConnect::flickrShareImageCoro, this, image, + title, description, tags, safety_level)); } void LLFlickrConnect::storeInfo(const LLSD& info) diff --git a/indra/newview/llflickrconnect.h b/indra/newview/llflickrconnect.h index b127e6e104..0155804da0 100644 --- a/indra/newview/llflickrconnect.h +++ b/indra/newview/llflickrconnect.h @@ -30,6 +30,8 @@ #include "llsingleton.h" #include "llimage.h" +#include "llcoros.h" +#include "lleventcoro.h" class LLEventPump; @@ -93,6 +95,15 @@ private: static boost::scoped_ptr<LLEventPump> sStateWatcher; static boost::scoped_ptr<LLEventPump> sInfoWatcher; static boost::scoped_ptr<LLEventPump> sContentWatcher; + + bool testShareStatus(LLSD &result); + void flickrConnectCoro(std::string requestToken, std::string oauthVerifier); + void flickrShareCoro(LLSD share); + void flickrShareImageCoro(LLPointer<LLImageFormatted> image, std::string title, std::string description, std::string tags, int safetyLevel); + void flickrDisconnectCoro(); + void flickrConnectedCoro(bool autoConnect); + void flickrInfoCoro(); + }; #endif // LL_LLFLICKRCONNECT_H diff --git a/indra/newview/llfloaterabout.cpp b/indra/newview/llfloaterabout.cpp index b342d8fdf3..f58a5881a8 100755 --- a/indra/newview/llfloaterabout.cpp +++ b/indra/newview/llfloaterabout.cpp @@ -61,6 +61,7 @@ #include "stringize.h" #include "llsdutil_math.h" #include "lleventapi.h" +#include "llcorehttputil.h" #if LL_WINDOWS #include "lldxhardware.h" @@ -70,18 +71,6 @@ extern LLMemoryInfo gSysMemory; extern U32 gPacketsIn; ///---------------------------------------------------------------------------- -/// Class LLServerReleaseNotesURLFetcher -///---------------------------------------------------------------------------- -class LLServerReleaseNotesURLFetcher : public LLHTTPClient::Responder -{ - LOG_CLASS(LLServerReleaseNotesURLFetcher); -public: - static void startFetch(); -private: - /* virtual */ void httpCompleted(); -}; - -///---------------------------------------------------------------------------- /// Class LLFloaterAbout ///---------------------------------------------------------------------------- class LLFloaterAbout @@ -102,6 +91,9 @@ public: private: void setSupportText(const std::string& server_release_notes_url); + + static void startFetchServerReleaseNotes(); + static void handleServerReleaseNotes(LLSD results); }; @@ -138,7 +130,7 @@ BOOL LLFloaterAbout::postBuild() { // start fetching server release notes URL setSupportText(LLTrans::getString("RetrievingData")); - LLServerReleaseNotesURLFetcher::startFetch(); + startFetchServerReleaseNotes(); } else // not logged in { @@ -201,6 +193,50 @@ LLSD LLFloaterAbout::getInfo() return LLAppViewer::instance()->getViewerInfo(); } +/*static*/ +void LLFloaterAbout::startFetchServerReleaseNotes() +{ + LLViewerRegion* region = gAgent.getRegion(); + if (!region) return; + + // We cannot display the URL returned by the ServerReleaseNotes capability + // because opening it in an external browser will trigger a warning about untrusted + // SSL certificate. + // So we query the URL ourselves, expecting to find + // an URL suitable for external browsers in the "Location:" HTTP header. + std::string cap_url = region->getCapability("ServerReleaseNotes"); + + LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpGet(cap_url, + &LLFloaterAbout::handleServerReleaseNotes, &LLFloaterAbout::handleServerReleaseNotes); + +} + +/*static*/ +void LLFloaterAbout::handleServerReleaseNotes(LLSD results) +{ + LLFloaterAbout* floater_about = LLFloaterReg::getTypedInstance<LLFloaterAbout>("sl_about"); + if (floater_about) + { + LLSD http_headers; + if (results.has(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS)) + { + LLSD http_results = results[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + http_headers = http_results[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_HEADERS]; + } + else + { + http_headers = results[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_HEADERS]; + } + + std::string location = http_headers[HTTP_IN_HEADER_LOCATION].asString(); + if (location.empty()) + { + location = LLTrans::getString("ErrorFetchingServerReleaseNotesURL"); + } + LLAppViewer::instance()->setServerReleaseNotesURL(location); + } +} + class LLFloaterAboutListener: public LLEventAPI { public: @@ -264,40 +300,3 @@ void LLFloaterAboutUtil::registerFloater() &LLFloaterReg::build<LLFloaterAbout>); } - -///---------------------------------------------------------------------------- -/// Class LLServerReleaseNotesURLFetcher implementation -///---------------------------------------------------------------------------- -// static -void LLServerReleaseNotesURLFetcher::startFetch() -{ - LLViewerRegion* region = gAgent.getRegion(); - if (!region) return; - - // We cannot display the URL returned by the ServerReleaseNotes capability - // because opening it in an external browser will trigger a warning about untrusted - // SSL certificate. - // So we query the URL ourselves, expecting to find - // an URL suitable for external browsers in the "Location:" HTTP header. - std::string cap_url = region->getCapability("ServerReleaseNotes"); - LLHTTPClient::get(cap_url, new LLServerReleaseNotesURLFetcher); -} - -// virtual -void LLServerReleaseNotesURLFetcher::httpCompleted() -{ - LL_DEBUGS("ServerReleaseNotes") << dumpResponse() - << " [headers:" << getResponseHeaders() << "]" << LL_ENDL; - - LLFloaterAbout* floater_about = LLFloaterReg::getTypedInstance<LLFloaterAbout>("sl_about"); - if (floater_about) - { - std::string location = getResponseHeader(HTTP_IN_HEADER_LOCATION); - if (location.empty()) - { - location = LLTrans::getString("ErrorFetchingServerReleaseNotesURL"); - } - LLAppViewer::instance()->setServerReleaseNotesURL(location); - } -} - diff --git a/indra/newview/llfloaterauction.cpp b/indra/newview/llfloaterauction.cpp index b661fed276..56619e818a 100755 --- a/indra/newview/llfloaterauction.cpp +++ b/indra/newview/llfloaterauction.cpp @@ -57,6 +57,7 @@ #include "llsdutil.h" #include "llsdutil_math.h" #include "lltrans.h" +#include "llcorehttputil.h" ///---------------------------------------------------------------------------- /// Local function declarations, constants, enums, and typedefs @@ -361,7 +362,10 @@ void LLFloaterAuction::doResetParcel() LL_INFOS() << "Sending parcel update to reset for auction via capability to: " << mParcelUpdateCapUrl << LL_ENDL; - LLHTTPClient::post(mParcelUpdateCapUrl, body, new LLHTTPClient::Responder()); + + LLCoreHttpUtil::HttpCoroutineAdapter::messageHttpPost(mParcelUpdateCapUrl, body, + "Parcel reset for auction", + "Parcel not set for auction."); // Send a message to clear the object return time LLMessageSystem *msg = gMessageSystem; @@ -490,7 +494,10 @@ void LLFloaterAuction::doSellToAnyone() LL_INFOS() << "Sending parcel update to sell to anyone for L$1 via capability to: " << mParcelUpdateCapUrl << LL_ENDL; - LLHTTPClient::post(mParcelUpdateCapUrl, body, new LLHTTPClient::Responder()); + + LLCoreHttpUtil::HttpCoroutineAdapter::messageHttpPost(mParcelUpdateCapUrl, body, + "Parcel set as sell to everyone.", + "Parcel sell to everyone failed."); // clean up floater, and get out cleanupAndClose(); diff --git a/indra/newview/llfloaterautoreplacesettings.cpp b/indra/newview/llfloaterautoreplacesettings.cpp index 6e56e929df..5830f2f711 100755 --- a/indra/newview/llfloaterautoreplacesettings.cpp +++ b/indra/newview/llfloaterautoreplacesettings.cpp @@ -36,7 +36,6 @@ #include "llcolorswatch.h" #include "llcombobox.h" #include "llview.h" -#include "llhttpclient.h" #include "llbufferstream.h" #include "llcheckboxctrl.h" #include "llviewercontrol.h" diff --git a/indra/newview/llfloateravatarpicker.cpp b/indra/newview/llfloateravatarpicker.cpp index 566a3c9cd3..2824038f77 100755 --- a/indra/newview/llfloateravatarpicker.cpp +++ b/indra/newview/llfloateravatarpicker.cpp @@ -42,7 +42,6 @@ #include "llavatarnamecache.h" // IDEVO #include "llbutton.h" #include "llcachename.h" -#include "llhttpclient.h" // IDEVO #include "lllineeditor.h" #include "llscrolllistctrl.h" #include "llscrolllistitem.h" @@ -52,6 +51,7 @@ #include "llfocusmgr.h" #include "lldraghandle.h" #include "message.h" +#include "llcorehttputil.h" //#include "llsdserialize.h" @@ -456,39 +456,33 @@ BOOL LLFloaterAvatarPicker::visibleItemsSelected() const return FALSE; } -class LLAvatarPickerResponder : public LLHTTPClient::Responder +/*static*/ +void LLFloaterAvatarPicker::findCoro(std::string url, LLUUID queryID, std::string name) { - LOG_CLASS(LLAvatarPickerResponder); -public: - LLUUID mQueryID; - std::string mName; + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("genericPostCoro", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); - LLAvatarPickerResponder(const LLUUID& id, const std::string& name) : mQueryID(id), mName(name) { } + LL_INFOS("HttpCoroutineAdapter", "genericPostCoro") << "Generic POST for " << url << LL_ENDL; -protected: - /*virtual*/ void httpCompleted() - { - //std::ostringstream ss; - //LLSDSerialize::toPrettyXML(content, ss); - //LL_INFOS() << ss.str() << LL_ENDL; + LLSD result = httpAdapter->getAndYield(httpRequest, url); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (status || (status == LLCore::HttpStatus(HTTP_BAD_REQUEST))) + { + LLFloaterAvatarPicker* floater = + LLFloaterReg::findTypedInstance<LLFloaterAvatarPicker>("avatar_picker", name); + if (floater) + { + result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS); + floater->processResponse(queryID, result); + } + } +} - // in case of invalid characters, the avatar picker returns a 400 - // just set it to process so it displays 'not found' - if (isGoodStatus() || getStatus() == HTTP_BAD_REQUEST) - { - LLFloaterAvatarPicker* floater = - LLFloaterReg::findTypedInstance<LLFloaterAvatarPicker>("avatar_picker", mName); - if (floater) - { - floater->processResponse(mQueryID, getContent()); - } - } - else - { - LL_WARNS() << "avatar picker failed " << dumpResponse() << LL_ENDL; - } - } -}; void LLFloaterAvatarPicker::find() { @@ -517,7 +511,9 @@ void LLFloaterAvatarPicker::find() std::replace(text.begin(), text.end(), '.', ' '); url += LLURI::escape(text); LL_INFOS() << "avatar picker " << url << LL_ENDL; - LLHTTPClient::get(url, new LLAvatarPickerResponder(mQueryID, getKey().asString())); + + LLCoros::instance().launch("LLFloaterAvatarPicker::findCoro", + boost::bind(&LLFloaterAvatarPicker::findCoro, url, mQueryID, getKey().asString())); } else { diff --git a/indra/newview/llfloateravatarpicker.h b/indra/newview/llfloateravatarpicker.h index ed3e51c56f..fbee61b054 100755 --- a/indra/newview/llfloateravatarpicker.h +++ b/indra/newview/llfloateravatarpicker.h @@ -28,6 +28,8 @@ #define LLFLOATERAVATARPICKER_H #include "llfloater.h" +#include "lleventcoro.h" +#include "llcoros.h" #include <vector> @@ -84,6 +86,7 @@ private: void populateFriend(); BOOL visibleItemsSelected() const; // Returns true if any items in the current tab are selected. + static void findCoro(std::string url, LLUUID mQueryID, std::string mName); void find(); void setAllowMultiple(BOOL allow_multiple); LLScrollListCtrl* getActiveList(); diff --git a/indra/newview/llfloaterbvhpreview.cpp b/indra/newview/llfloaterbvhpreview.cpp index 669ffa7c59..e5df417ca9 100755 --- a/indra/newview/llfloaterbvhpreview.cpp +++ b/indra/newview/llfloaterbvhpreview.cpp @@ -992,20 +992,16 @@ void LLFloaterBvhPreview::onBtnOK(void* userdata) { std::string name = floaterp->getChild<LLUICtrl>("name_form")->getValue().asString(); std::string desc = floaterp->getChild<LLUICtrl>("description_form")->getValue().asString(); - LLAssetStorage::LLStoreAssetCallback callback = NULL; S32 expected_upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload(); - void *userdata = NULL; - upload_new_resource(floaterp->mTransactionID, // tid - LLAssetType::AT_ANIMATION, - name, - desc, - 0, - LLFolderType::FT_NONE, - LLInventoryType::IT_ANIMATION, - LLFloaterPerms::getNextOwnerPerms("Uploads"), LLFloaterPerms::getGroupPerms("Uploads"), LLFloaterPerms::getEveryonePerms("Uploads"), - name, - callback, expected_upload_cost, userdata); + LLResourceUploadInfo::ptr_t assetUpdloadInfo(new LLResourceUploadInfo( + floaterp->mTransactionID, LLAssetType::AT_ANIMATION, + name, desc, 0, + LLFolderType::FT_NONE, LLInventoryType::IT_ANIMATION, + LLFloaterPerms::getNextOwnerPerms("Uploads"), LLFloaterPerms::getGroupPerms("Uploads"), LLFloaterPerms::getEveryonePerms("Uploads"), + expected_upload_cost)); + + upload_new_resource(assetUpdloadInfo); } else { diff --git a/indra/newview/llfloaterdisplayname.cpp b/indra/newview/llfloaterdisplayname.cpp deleted file mode 100755 index 596e8c0dbe..0000000000 --- a/indra/newview/llfloaterdisplayname.cpp +++ /dev/null @@ -1,217 +0,0 @@ -/** - * @file llfloaterdisplayname.cpp - * @author Leyla Farazha - * @brief Implementation of the LLFloaterDisplayName class. - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - - -#include "llviewerprecompiledheaders.h" -#include "llfloaterreg.h" -#include "llfloater.h" - -#include "llnotificationsutil.h" -#include "llviewerdisplayname.h" - -#include "llnotifications.h" -#include "llfloaterdisplayname.h" -#include "llavatarnamecache.h" - -#include "llagent.h" - - -class LLFloaterDisplayName : public LLFloater -{ -public: - LLFloaterDisplayName(const LLSD& key); - virtual ~LLFloaterDisplayName() { } - /*virtual*/ BOOL postBuild(); - void onSave(); - void onReset(); - void onCancel(); - /*virtual*/ void onOpen(const LLSD& key); - -private: - - void onCacheSetName(bool success, - const std::string& reason, - const LLSD& content); -}; - -LLFloaterDisplayName::LLFloaterDisplayName(const LLSD& key) : - LLFloater(key) -{ -} - -void LLFloaterDisplayName::onOpen(const LLSD& key) -{ - getChild<LLUICtrl>("display_name_editor")->clear(); - getChild<LLUICtrl>("display_name_confirm")->clear(); - - LLAvatarName av_name; - LLAvatarNameCache::get(gAgent.getID(), &av_name); - - F64 now_secs = LLDate::now().secondsSinceEpoch(); - - if (now_secs < av_name.mNextUpdate) - { - // ...can't update until some time in the future - F64 next_update_local_secs = - av_name.mNextUpdate - LLStringOps::getLocalTimeOffset(); - LLDate next_update_local(next_update_local_secs); - // display as "July 18 12:17 PM" - std::string next_update_string = - next_update_local.toHTTPDateString("%B %d %I:%M %p"); - getChild<LLUICtrl>("lockout_text")->setTextArg("[TIME]", next_update_string); - getChild<LLUICtrl>("lockout_text")->setVisible(true); - getChild<LLUICtrl>("save_btn")->setEnabled(false); - getChild<LLUICtrl>("display_name_editor")->setEnabled(false); - getChild<LLUICtrl>("display_name_confirm")->setEnabled(false); - getChild<LLUICtrl>("cancel_btn")->setFocus(TRUE); - - } - else - { - getChild<LLUICtrl>("lockout_text")->setVisible(false); - getChild<LLUICtrl>("save_btn")->setEnabled(true); - getChild<LLUICtrl>("display_name_editor")->setEnabled(true); - getChild<LLUICtrl>("display_name_confirm")->setEnabled(true); - - } -} - -BOOL LLFloaterDisplayName::postBuild() -{ - getChild<LLUICtrl>("reset_btn")->setCommitCallback(boost::bind(&LLFloaterDisplayName::onReset, this)); - getChild<LLUICtrl>("cancel_btn")->setCommitCallback(boost::bind(&LLFloaterDisplayName::onCancel, this)); - getChild<LLUICtrl>("save_btn")->setCommitCallback(boost::bind(&LLFloaterDisplayName::onSave, this)); - - center(); - - return TRUE; -} - -void LLFloaterDisplayName::onCacheSetName(bool success, - const std::string& reason, - const LLSD& content) -{ - if (success) - { - // Inform the user that the change took place, but will take a while - // to percolate. - LLSD args; - args["DISPLAY_NAME"] = content["display_name"]; - LLNotificationsUtil::add("SetDisplayNameSuccess", args); - return; - } - - // Request failed, notify the user - std::string error_tag = content["error_tag"].asString(); - LL_INFOS() << "set name failure error_tag " << error_tag << LL_ENDL; - - // We might have a localized string for this message - // error_args will usually be empty from the server. - if (!error_tag.empty() - && LLNotifications::getInstance()->templateExists(error_tag)) - { - LLNotificationsUtil::add(error_tag); - return; - } - - // The server error might have a localized message for us - std::string lang_code = LLUI::getLanguage(); - LLSD error_desc = content["error_description"]; - if (error_desc.has( lang_code )) - { - LLSD args; - args["MESSAGE"] = error_desc[lang_code].asString(); - LLNotificationsUtil::add("GenericAlert", args); - return; - } - - // No specific error, throw a generic one - LLNotificationsUtil::add("SetDisplayNameFailedGeneric"); -} - -void LLFloaterDisplayName::onCancel() -{ - setVisible(false); -} - -void LLFloaterDisplayName::onReset() -{ - if (LLAvatarNameCache::hasNameLookupURL()) - { - LLViewerDisplayName::set("",boost::bind(&LLFloaterDisplayName::onCacheSetName, this, _1, _2, _3)); - } - else - { - LLNotificationsUtil::add("SetDisplayNameFailedGeneric"); - } - - setVisible(false); -} - - -void LLFloaterDisplayName::onSave() -{ - std::string display_name_utf8 = getChild<LLUICtrl>("display_name_editor")->getValue().asString(); - std::string display_name_confirm = getChild<LLUICtrl>("display_name_confirm")->getValue().asString(); - - if (display_name_utf8.compare(display_name_confirm)) - { - LLNotificationsUtil::add("SetDisplayNameMismatch"); - return; - } - - const U32 DISPLAY_NAME_MAX_LENGTH = 31; // characters, not bytes - LLWString display_name_wstr = utf8string_to_wstring(display_name_utf8); - if (display_name_wstr.size() > DISPLAY_NAME_MAX_LENGTH) - { - LLSD args; - args["LENGTH"] = llformat("%d", DISPLAY_NAME_MAX_LENGTH); - LLNotificationsUtil::add("SetDisplayNameFailedLength", args); - return; - } - - if (LLAvatarNameCache::hasNameLookupURL()) - { - LLViewerDisplayName::set(display_name_utf8,boost::bind(&LLFloaterDisplayName::onCacheSetName, this, _1, _2, _3)); - } - else - { - LLNotificationsUtil::add("SetDisplayNameFailedGeneric"); - } - - setVisible(false); -} - - -////////////////////////////////////////////////////////////////////////////// -// LLInspectObjectUtil -////////////////////////////////////////////////////////////////////////////// -void LLFloaterDisplayNameUtil::registerFloater() -{ - LLFloaterReg::add("display_name", "floater_display_name.xml", - &LLFloaterReg::build<LLFloaterDisplayName>); -} diff --git a/indra/newview/llfloaterdisplayname.h b/indra/newview/llfloaterdisplayname.h deleted file mode 100755 index a00bf56712..0000000000 --- a/indra/newview/llfloaterdisplayname.h +++ /dev/null @@ -1,38 +0,0 @@ -/** - * @file llfloaterdisplayname.h - * - * $LicenseInfo:firstyear=2009&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LLFLOATERDISPLAYNAME_H -#define LLFLOATERDISPLAYNAME_H - - -namespace LLFloaterDisplayNameUtil -{ - // Register with LLFloaterReg - void registerFloater(); -} - - - -#endif diff --git a/indra/newview/llfloaterhoverheight.cpp b/indra/newview/llfloaterhoverheight.cpp index 8908626de6..003a22fa04 100755 --- a/indra/newview/llfloaterhoverheight.cpp +++ b/indra/newview/llfloaterhoverheight.cpp @@ -31,7 +31,6 @@ #include "llsliderctrl.h" #include "llviewercontrol.h" #include "llsdserialize.h" -#include "llhttpclient.h" #include "llagent.h" #include "llviewerregion.h" #include "llvoavatarself.h" diff --git a/indra/newview/llfloaterimsession.cpp b/indra/newview/llfloaterimsession.cpp index fc7fcf3ab9..6623ce0f80 100755 --- a/indra/newview/llfloaterimsession.cpp +++ b/indra/newview/llfloaterimsession.cpp @@ -41,7 +41,6 @@ #include "llchicletbar.h" #include "lldonotdisturbnotificationstorage.h" #include "llfloaterreg.h" -#include "llhttpclient.h" #include "llfloateravatarpicker.h" #include "llfloaterimcontainer.h" // to replace separate IM Floaters with multifloater container #include "llinventoryfunctions.h" @@ -62,6 +61,7 @@ #include "llviewerchat.h" #include "llnotificationmanager.h" #include "llautoreplace.h" +#include "llcorehttputil.h" const F32 ME_TYPING_TIMEOUT = 4.0f; const F32 OTHER_TYPING_TIMEOUT = 9.0f; @@ -1178,26 +1178,6 @@ BOOL LLFloaterIMSession::isInviteAllowed() const || mIsP2PChat); } -class LLSessionInviteResponder : public LLHTTPClient::Responder -{ - LOG_CLASS(LLSessionInviteResponder); -public: - LLSessionInviteResponder(const LLUUID& session_id) - { - mSessionID = session_id; - } - -protected: - void httpFailure() - { - LL_WARNS() << "Error inviting all agents to session " << dumpResponse() << LL_ENDL; - //throw something back to the viewer here? - } - -private: - LLUUID mSessionID; -}; - BOOL LLFloaterIMSession::inviteToSession(const uuid_vec_t& ids) { LLViewerRegion* region = gAgent.getRegion(); @@ -1221,7 +1201,9 @@ BOOL LLFloaterIMSession::inviteToSession(const uuid_vec_t& ids) } data["method"] = "invite"; data["session-id"] = mSessionID; - LLHTTPClient::post(url, data,new LLSessionInviteResponder(mSessionID)); + + LLCoreHttpUtil::HttpCoroutineAdapter::messageHttpPost(url, data, + "Session invite sent", "Session invite failed"); } else { diff --git a/indra/newview/llfloatermodeluploadbase.cpp b/indra/newview/llfloatermodeluploadbase.cpp index 22a8ac4705..e2f84fd990 100755 --- a/indra/newview/llfloatermodeluploadbase.cpp +++ b/indra/newview/llfloatermodeluploadbase.cpp @@ -30,6 +30,7 @@ #include "llagent.h" #include "llviewerregion.h" #include "llnotificationsutil.h" +#include "llcorehttputil.h" LLFloaterModelUploadBase::LLFloaterModelUploadBase(const LLSD& key) :LLFloater(key), @@ -47,7 +48,8 @@ void LLFloaterModelUploadBase::requestAgentUploadPermissions() LL_INFOS()<< typeid(*this).name() << "::requestAgentUploadPermissions() requesting for upload model permissions from: " << url << LL_ENDL; - LLHTTPClient::get(url, new LLUploadModelPermissionsResponder(getPermObserverHandle())); + LLCoros::instance().launch("LLFloaterModelUploadBase::requestAgentUploadPermissionsCoro", + boost::bind(&LLFloaterModelUploadBase::requestAgentUploadPermissionsCoro, this, url, getPermObserverHandle())); } else { @@ -58,3 +60,34 @@ void LLFloaterModelUploadBase::requestAgentUploadPermissions() mHasUploadPerm = true; } } + +void LLFloaterModelUploadBase::requestAgentUploadPermissionsCoro(std::string url, + LLHandle<LLUploadPermissionsObserver> observerHandle) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("MeshUploadFlag", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + + + LLSD result = httpAdapter->getAndYield(httpRequest, url); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + LLUploadPermissionsObserver* observer = observerHandle.get(); + + if (!observer) + { + LL_WARNS("MeshUploadFlag") << "Unable to get observer after call to '" << url << "' aborting." << LL_ENDL; + } + + if (!status) + { + observer->setPermissonsErrorStatus(status.getStatus(), status.getMessage()); + return; + } + + result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS); + observer->onPermissionsReceived(result); +} diff --git a/indra/newview/llfloatermodeluploadbase.h b/indra/newview/llfloatermodeluploadbase.h index d9a8879687..0d4c834122 100755 --- a/indra/newview/llfloatermodeluploadbase.h +++ b/indra/newview/llfloatermodeluploadbase.h @@ -28,6 +28,8 @@ #define LL_LLFLOATERMODELUPLOADBASE_H #include "lluploadfloaterobservers.h" +#include "llcoros.h" +#include "lleventcoro.h" class LLFloaterModelUploadBase : public LLFloater, public LLUploadPermissionsObserver, public LLWholeModelFeeObserver, public LLWholeModelUploadObserver { @@ -54,6 +56,8 @@ protected: // requests agent's permissions to upload model void requestAgentUploadPermissions(); + void requestAgentUploadPermissionsCoro(std::string url, LLHandle<LLUploadPermissionsObserver> observerHandle); + std::string mUploadModelUrl; bool mHasUploadPerm; }; diff --git a/indra/newview/llfloaternamedesc.cpp b/indra/newview/llfloaternamedesc.cpp index 0cca715fe2..46dbf85dfa 100755 --- a/indra/newview/llfloaternamedesc.cpp +++ b/indra/newview/llfloaternamedesc.cpp @@ -164,14 +164,18 @@ void LLFloaterNameDesc::onBtnOK( ) void *nruserdata = NULL; std::string display_name = LLStringUtil::null; - upload_new_resource(mFilenameAndPath, // file - getChild<LLUICtrl>("name_form")->getValue().asString(), - getChild<LLUICtrl>("description_form")->getValue().asString(), - 0, LLFolderType::FT_NONE, LLInventoryType::IT_NONE, - LLFloaterPerms::getNextOwnerPerms("Uploads"), - LLFloaterPerms::getGroupPerms("Uploads"), - LLFloaterPerms::getEveryonePerms("Uploads"), - display_name, callback, expected_upload_cost, nruserdata); + LLResourceUploadInfo::ptr_t uploadInfo(new NewFileResourceUploadInfo( + mFilenameAndPath, + getChild<LLUICtrl>("name_form")->getValue().asString(), + getChild<LLUICtrl>("description_form")->getValue().asString(), 0, + LLFolderType::FT_NONE, LLInventoryType::IT_NONE, + LLFloaterPerms::getNextOwnerPerms("Uploads"), + LLFloaterPerms::getGroupPerms("Uploads"), + LLFloaterPerms::getEveryonePerms("Uploads"), + expected_upload_cost)); + + upload_new_resource(uploadInfo, callback, nruserdata); + closeFloater(false); } diff --git a/indra/newview/llfloaterperms.cpp b/indra/newview/llfloaterperms.cpp index 042cf47070..16bb449fdb 100755 --- a/indra/newview/llfloaterperms.cpp +++ b/indra/newview/llfloaterperms.cpp @@ -37,6 +37,7 @@ #include "llnotificationsutil.h" #include "llsdserialize.h" #include "llvoavatar.h" +#include "llcorehttputil.h" LLFloaterPerms::LLFloaterPerms(const LLSD& seed) : LLFloater(seed) @@ -166,41 +167,6 @@ void LLFloaterPermsDefault::onCommitCopy(const LLSD& user_data) xfer->setEnabled(copyable); } -class LLFloaterPermsResponder : public LLHTTPClient::Responder -{ -public: - LLFloaterPermsResponder(): LLHTTPClient::Responder() {} -private: - static std::string sPreviousReason; - - void httpFailure() - { - const std::string& reason = getReason(); - // Do not display the same error more than once in a row - if (reason != sPreviousReason) - { - sPreviousReason = reason; - LLSD args; - args["REASON"] = reason; - LLNotificationsUtil::add("DefaultObjectPermissions", args); - } - } - - void httpSuccess() - { - //const LLSD& content = getContent(); - //dump_sequential_xml("perms_responder_result.xml", content); - - // Since we have had a successful POST call be sure to display the next error message - // even if it is the same as a previous one. - sPreviousReason = ""; - LLFloaterPermsDefault::setCapSent(true); - LL_INFOS("ObjectPermissionsFloater") << "Default permissions successfully sent to simulator" << LL_ENDL; - } -}; - - std::string LLFloaterPermsResponder::sPreviousReason; - void LLFloaterPermsDefault::sendInitialPerms() { if(!mCapSent) @@ -215,23 +181,8 @@ void LLFloaterPermsDefault::updateCap() if(!object_url.empty()) { - LLSD report = LLSD::emptyMap(); - report["default_object_perm_masks"]["Group"] = - (LLSD::Integer)LLFloaterPerms::getGroupPerms(sCategoryNames[CAT_OBJECTS]); - report["default_object_perm_masks"]["Everyone"] = - (LLSD::Integer)LLFloaterPerms::getEveryonePerms(sCategoryNames[CAT_OBJECTS]); - report["default_object_perm_masks"]["NextOwner"] = - (LLSD::Integer)LLFloaterPerms::getNextOwnerPerms(sCategoryNames[CAT_OBJECTS]); - - { - LL_DEBUGS("ObjectPermissionsFloater") << "Sending default permissions to '" - << object_url << "'\n"; - std::ostringstream sent_perms_log; - LLSDSerialize::toPrettyXML(report, sent_perms_log); - LL_CONT << sent_perms_log.str() << LL_ENDL; - } - - LLHTTPClient::post(object_url, report, new LLFloaterPermsResponder()); + LLCoros::instance().launch("LLFloaterPermsDefault::updateCapCoro", + boost::bind(&LLFloaterPermsDefault::updateCapCoro, object_url)); } else { @@ -239,6 +190,57 @@ void LLFloaterPermsDefault::updateCap() } } +/*static*/ +void LLFloaterPermsDefault::updateCapCoro(std::string url) +{ + static std::string previousReason; + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("genericPostCoro", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + + LLSD postData = LLSD::emptyMap(); + postData["default_object_perm_masks"]["Group"] = + (LLSD::Integer)LLFloaterPerms::getGroupPerms(sCategoryNames[CAT_OBJECTS]); + postData["default_object_perm_masks"]["Everyone"] = + (LLSD::Integer)LLFloaterPerms::getEveryonePerms(sCategoryNames[CAT_OBJECTS]); + postData["default_object_perm_masks"]["NextOwner"] = + (LLSD::Integer)LLFloaterPerms::getNextOwnerPerms(sCategoryNames[CAT_OBJECTS]); + + { + LL_DEBUGS("ObjectPermissionsFloater") << "Sending default permissions to '" + << url << "'\n"; + std::ostringstream sent_perms_log; + LLSDSerialize::toPrettyXML(postData, sent_perms_log); + LL_CONT << sent_perms_log.str() << LL_ENDL; + } + + LLSD result = httpAdapter->postAndYield(httpRequest, url, postData); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) + { + const std::string& reason = status.toString(); + // Do not display the same error more than once in a row + if (reason != previousReason) + { + previousReason = reason; + LLSD args; + args["REASON"] = reason; + LLNotificationsUtil::add("DefaultObjectPermissions", args); + } + return; + } + + // Since we have had a successful POST call be sure to display the next error message + // even if it is the same as a previous one. + previousReason.clear(); + LLFloaterPermsDefault::setCapSent(true); + LL_INFOS("ObjectPermissionsFloater") << "Default permissions successfully sent to simulator" << LL_ENDL; +} + void LLFloaterPermsDefault::setCapSent(bool cap_sent) { mCapSent = cap_sent; diff --git a/indra/newview/llfloaterperms.h b/indra/newview/llfloaterperms.h index 2bb0a19dc1..e866b6de7d 100755 --- a/indra/newview/llfloaterperms.h +++ b/indra/newview/llfloaterperms.h @@ -29,6 +29,8 @@ #define LL_LLFLOATERPERMPREFS_H #include "llfloater.h" +#include "lleventcoro.h" +#include "llcoros.h" class LLFloaterPerms : public LLFloater { @@ -80,6 +82,8 @@ private: void refresh(); static const std::string sCategoryNames[CAT_LAST]; + static void updateCapCoro(std::string url); + // cached values only for implementing cancel. bool mShareWithGroup[CAT_LAST]; diff --git a/indra/newview/llfloaterregiondebugconsole.cpp b/indra/newview/llfloaterregiondebugconsole.cpp index 40757a4d04..271fb2f9a3 100755 --- a/indra/newview/llfloaterregiondebugconsole.cpp +++ b/indra/newview/llfloaterregiondebugconsole.cpp @@ -30,11 +30,11 @@ #include "llfloaterregiondebugconsole.h" #include "llagent.h" -#include "llhttpclient.h" #include "llhttpnode.h" #include "lllineeditor.h" #include "lltexteditor.h" #include "llviewerregion.h" +#include "llcorehttputil.h" // Two versions of the sim console API are supported. // @@ -68,58 +68,6 @@ namespace const std::string CONSOLE_NOT_SUPPORTED( "This region does not support the simulator console."); - // This responder handles the initial response. Unless error() is called - // we assume that the simulator has received our request. Error will be - // called if this request times out. - class AsyncConsoleResponder : public LLHTTPClient::Responder - { - LOG_CLASS(AsyncConsoleResponder); - protected: - /* virtual */ - void httpFailure() - { - LL_WARNS("Console") << dumpResponse() << LL_ENDL; - sConsoleReplySignal(UNABLE_TO_SEND_COMMAND); - } - }; - - class ConsoleResponder : public LLHTTPClient::Responder - { - LOG_CLASS(ConsoleResponder); - public: - ConsoleResponder(LLTextEditor *output) : mOutput(output) - { - } - - protected: - /*virtual*/ - void httpFailure() - { - LL_WARNS("Console") << dumpResponse() << LL_ENDL; - if (mOutput) - { - mOutput->appendText( - UNABLE_TO_SEND_COMMAND + PROMPT, - false); - } - } - - /*virtual*/ - void httpSuccess() - { - const LLSD& content = getContent(); - LL_DEBUGS("Console") << content << LL_ENDL; - if (mOutput) - { - mOutput->appendText( - content.asString() + PROMPT, false); - } - } - - public: - LLTextEditor * mOutput; - }; - // This handles responses for console commands sent via the asynchronous // API. class ConsoleResponseNode : public LLHTTPNode @@ -202,26 +150,57 @@ void LLFloaterRegionDebugConsole::onInput(LLUICtrl* ctrl, const LLSD& param) } else { - // Using SimConsole (deprecated) - LLHTTPClient::post( - url, - LLSD(input->getText()), - new ConsoleResponder(mOutput)); + LLSD postData = LLSD(input->getText()); + LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost(url, postData, + boost::bind(&LLFloaterRegionDebugConsole::onConsoleSuccess, this, _1), + boost::bind(&LLFloaterRegionDebugConsole::onConsoleError, this, _1)); } } else { - // Using SimConsoleAsync - LLHTTPClient::post( - url, - LLSD(input->getText()), - new AsyncConsoleResponder); + LLSD postData = LLSD(input->getText()); + LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost(url, postData, + NULL, + boost::bind(&LLFloaterRegionDebugConsole::onAsyncConsoleError, this, _1)); + } mOutput->appendText(text, false); input->clear(); } +void LLFloaterRegionDebugConsole::onAsyncConsoleError(LLSD result) +{ + LL_WARNS("Console") << UNABLE_TO_SEND_COMMAND << LL_ENDL; + sConsoleReplySignal(UNABLE_TO_SEND_COMMAND); +} + +void LLFloaterRegionDebugConsole::onConsoleError(LLSD result) +{ + LL_WARNS("Console") << UNABLE_TO_SEND_COMMAND << LL_ENDL; + if (mOutput) + { + mOutput->appendText( + UNABLE_TO_SEND_COMMAND + PROMPT, + false); + } + +} + +void LLFloaterRegionDebugConsole::onConsoleSuccess(LLSD result) +{ + if (mOutput) + { + LLSD response = result; + if (response.isMap() && response.has(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_CONTENT)) + { + response = response[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_CONTENT]; + } + mOutput->appendText( + response.asString() + PROMPT, false); + } +} + void LLFloaterRegionDebugConsole::onReplyReceived(const std::string& output) { mOutput->appendText(output + PROMPT, false); diff --git a/indra/newview/llfloaterregiondebugconsole.h b/indra/newview/llfloaterregiondebugconsole.h index fd3af4152e..f55d964924 100755 --- a/indra/newview/llfloaterregiondebugconsole.h +++ b/indra/newview/llfloaterregiondebugconsole.h @@ -31,14 +31,13 @@ #include <boost/signals2.hpp> #include "llfloater.h" -#include "llhttpclient.h" class LLTextEditor; typedef boost::signals2::signal< void (const std::string& output)> console_reply_signal_t; -class LLFloaterRegionDebugConsole : public LLFloater, public LLHTTPClient::Responder +class LLFloaterRegionDebugConsole : public LLFloater { public: LLFloaterRegionDebugConsole(LLSD const & key); @@ -56,6 +55,10 @@ public: private: void onReplyReceived(const std::string& output); + void onAsyncConsoleError(LLSD result); + void onConsoleError(LLSD result); + void onConsoleSuccess(LLSD result); + boost::signals2::connection mReplySignalConnection; }; diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp index 5d1e01c1f7..37e934429f 100755 --- a/indra/newview/llfloaterregioninfo.cpp +++ b/indra/newview/llfloaterregioninfo.cpp @@ -97,6 +97,7 @@ #include "llpanelexperiencepicker.h" #include "llexperiencecache.h" #include "llpanelexperiences.h" +#include "llcorehttputil.h" const S32 TERRAIN_TEXTURE_COUNT = 4; const S32 CORNER_COUNT = 4; @@ -803,30 +804,6 @@ bool LLPanelRegionGeneralInfo::onMessageCommit(const LLSD& notification, const L return false; } -class ConsoleRequestResponder : public LLHTTPClient::Responder -{ - LOG_CLASS(ConsoleRequestResponder); -protected: - /*virtual*/ - void httpFailure() - { - LL_WARNS() << "error requesting mesh_rez_enabled " << dumpResponse() << LL_ENDL; - } -}; - - -// called if this request times out. -class ConsoleUpdateResponder : public LLHTTPClient::Responder -{ - LOG_CLASS(ConsoleUpdateResponder); -protected: - /* virtual */ - void httpFailure() - { - LL_WARNS() << "error updating mesh enabled region setting " << dumpResponse() << LL_ENDL; - } -}; - void LLFloaterRegionInfo::requestMeshRezInfo() { std::string sim_console_url = gAgent.getRegion()->getCapability("SimConsoleAsync"); @@ -835,10 +812,8 @@ void LLFloaterRegionInfo::requestMeshRezInfo() { std::string request_str = "get mesh_rez_enabled"; - LLHTTPClient::post( - sim_console_url, - LLSD(request_str), - new ConsoleRequestResponder); + LLCoreHttpUtil::HttpCoroutineAdapter::messageHttpPost(sim_console_url, LLSD(request_str), + "Requested mesh_rez_enabled", "Error requesting mesh_rez_enabled"); } } @@ -874,7 +849,8 @@ BOOL LLPanelRegionGeneralInfo::sendUpdate() body["allow_parcel_changes"] = getChild<LLUICtrl>("allow_parcel_changes_check")->getValue(); body["block_parcel_search"] = getChild<LLUICtrl>("block_parcel_search_check")->getValue(); - LLHTTPClient::post(url, body, new LLHTTPClient::Responder()); + LLCoreHttpUtil::HttpCoroutineAdapter::messageHttpPost(url, body, + "Region info update posted.", "Region info update not posted."); } else { @@ -2303,36 +2279,6 @@ void LLPanelEstateInfo::getEstateOwner() } */ -class LLEstateChangeInfoResponder : public LLHTTPClient::Responder -{ - LOG_CLASS(LLEstateChangeInfoResponder); -public: - LLEstateChangeInfoResponder(LLPanelEstateInfo* panel) - { - mpPanel = panel->getHandle(); - } - -protected: - // if we get a normal response, handle it here - virtual void httpSuccess() - { - LL_INFOS("Windlight") << "Successfully committed estate info" << LL_ENDL; - - // refresh the panel from the database - LLPanelEstateInfo* panel = dynamic_cast<LLPanelEstateInfo*>(mpPanel.get()); - if (panel) - panel->refresh(); - } - - // if we get an error response - virtual void httpFailure() - { - LL_WARNS("Windlight") << dumpResponse() << LL_ENDL; - } -private: - LLHandle<LLPanel> mpPanel; -}; - const std::string LLPanelEstateInfo::getOwnerName() const { return getChild<LLUICtrl>("estate_owner")->getValue().asString(); diff --git a/indra/newview/llfloaterregioninfo.h b/indra/newview/llfloaterregioninfo.h index e7b49d8553..90f115faaf 100755 --- a/indra/newview/llfloaterregioninfo.h +++ b/indra/newview/llfloaterregioninfo.h @@ -36,6 +36,7 @@ #include "llextendedstatus.h" #include "llenvmanager.h" // for LLEnvironmentSettings +#include "lleventcoro.h" class LLAvatarName; class LLDispatcher; @@ -107,6 +108,8 @@ private: LLFloaterRegionInfo(const LLSD& seed); ~LLFloaterRegionInfo(); + + protected: void onTabSelected(const LLSD& param); diff --git a/indra/newview/llfloaterscriptlimits.cpp b/indra/newview/llfloaterscriptlimits.cpp index 5fbdd75e97..14719a77f9 100755 --- a/indra/newview/llfloaterscriptlimits.cpp +++ b/indra/newview/llfloaterscriptlimits.cpp @@ -50,6 +50,7 @@ #include "llviewerparcelmgr.h" #include "llviewerregion.h" #include "llviewerwindow.h" +#include "llcorehttputil.h" ///---------------------------------------------------------------------------- /// LLFloaterScriptLimits @@ -180,372 +181,6 @@ void LLPanelScriptLimitsInfo::updateChild(LLUICtrl* child_ctr) } ///---------------------------------------------------------------------------- -// Responders -///---------------------------------------------------------------------------- - -void fetchScriptLimitsRegionInfoResponder::httpSuccess() -{ - const LLSD& content = getContent(); - if (!content.isMap()) - { - failureResult(HTTP_INTERNAL_ERROR, "Malformed response contents", content); - return; - } - //we don't need to test with a fake respose here (shouldn't anyway) - -#ifdef DUMP_REPLIES_TO_LLINFOS - - LLSDNotationStreamer notation_streamer(content); - std::ostringstream nice_llsd; - nice_llsd << notation_streamer; - - OSMessageBox(nice_llsd.str(), "main cap response:", 0); - - LL_INFOS() << "main cap response:" << content << LL_ENDL; - -#endif - - // at this point we have an llsd which should contain ether one or two urls to the services we want. - // first we look for the details service: - if(content.has("ScriptResourceDetails")) - { - LLHTTPClient::get(content["ScriptResourceDetails"], new fetchScriptLimitsRegionDetailsResponder(mInfo)); - } - else - { - LLFloaterScriptLimits* instance = LLFloaterReg::getTypedInstance<LLFloaterScriptLimits>("script_limits"); - if(!instance) - { - LL_WARNS() << "Failed to get llfloaterscriptlimits instance" << LL_ENDL; - } - } - - // then the summary service: - if(content.has("ScriptResourceSummary")) - { - LLHTTPClient::get(content["ScriptResourceSummary"], new fetchScriptLimitsRegionSummaryResponder(mInfo)); - } -} - -void fetchScriptLimitsRegionInfoResponder::httpFailure() -{ - LL_WARNS() << dumpResponse() << LL_ENDL; -} - -void fetchScriptLimitsRegionSummaryResponder::httpSuccess() -{ - const LLSD& content_ref = getContent(); -#ifdef USE_FAKE_RESPONSES - - LLSD fake_content; - LLSD summary = LLSD::emptyMap(); - LLSD available = LLSD::emptyArray(); - LLSD available_urls = LLSD::emptyMap(); - LLSD available_memory = LLSD::emptyMap(); - LLSD used = LLSD::emptyArray(); - LLSD used_urls = LLSD::emptyMap(); - LLSD used_memory = LLSD::emptyMap(); - - used_urls["type"] = "urls"; - used_urls["amount"] = FAKE_NUMBER_OF_URLS; - available_urls["type"] = "urls"; - available_urls["amount"] = FAKE_AVAILABLE_URLS; - used_memory["type"] = "memory"; - used_memory["amount"] = FAKE_AMOUNT_OF_MEMORY; - available_memory["type"] = "memory"; - available_memory["amount"] = FAKE_AVAILABLE_MEMORY; - -//summary response:{'summary':{'available':[{'amount':i731,'type':'urls'},{'amount':i895577,'type':'memory'},{'amount':i731,'type':'urls'},{'amount':i895577,'type':'memory'}],'used':[{'amount':i329,'type':'urls'},{'amount':i66741,'type':'memory'}]}} - - used.append(used_urls); - used.append(used_memory); - available.append(available_urls); - available.append(available_memory); - - summary["available"] = available; - summary["used"] = used; - - fake_content["summary"] = summary; - - const LLSD& content = fake_content; - -#else - - const LLSD& content = content_ref; - -#endif - - if (!content.isMap()) - { - failureResult(HTTP_INTERNAL_ERROR, "Malformed response contents", content); - return; - } - - -#ifdef DUMP_REPLIES_TO_LLINFOS - - LLSDNotationStreamer notation_streamer(content); - std::ostringstream nice_llsd; - nice_llsd << notation_streamer; - - OSMessageBox(nice_llsd.str(), "summary response:", 0); - - LL_WARNS() << "summary response:" << *content << LL_ENDL; - -#endif - - LLFloaterScriptLimits* instance = LLFloaterReg::getTypedInstance<LLFloaterScriptLimits>("script_limits"); - if(!instance) - { - LL_WARNS() << "Failed to get llfloaterscriptlimits instance" << LL_ENDL; - } - else - { - LLTabContainer* tab = instance->getChild<LLTabContainer>("scriptlimits_panels"); - if(tab) - { - LLPanelScriptLimitsRegionMemory* panel_memory = (LLPanelScriptLimitsRegionMemory*)tab->getChild<LLPanel>("script_limits_region_memory_panel"); - if(panel_memory) - { - panel_memory->getChild<LLUICtrl>("loading_text")->setValue(LLSD(std::string(""))); - - LLButton* btn = panel_memory->getChild<LLButton>("refresh_list_btn"); - if(btn) - { - btn->setEnabled(true); - } - - panel_memory->setRegionSummary(content); - } - } - } -} - -void fetchScriptLimitsRegionSummaryResponder::httpFailure() -{ - LL_WARNS() << dumpResponse() << LL_ENDL; -} - -void fetchScriptLimitsRegionDetailsResponder::httpSuccess() -{ - const LLSD& content_ref = getContent(); -#ifdef USE_FAKE_RESPONSES -/* -Updated detail service, ** denotes field added: - -result (map) -+-parcels (array of maps) - +-id (uuid) - +-local_id (S32)** - +-name (string) - +-owner_id (uuid) (in ERS as owner, but owner_id in code) - +-objects (array of maps) - +-id (uuid) - +-name (string) - +-owner_id (uuid) (in ERS as owner, in code as owner_id) - +-owner_name (sting)** - +-location (map)** - +-x (float) - +-y (float) - +-z (float) - +-resources (map) (this is wrong in the ERS but right in code) - +-type (string) - +-amount (int) -*/ - LLSD fake_content; - LLSD resource = LLSD::emptyMap(); - LLSD location = LLSD::emptyMap(); - LLSD object = LLSD::emptyMap(); - LLSD objects = LLSD::emptyArray(); - LLSD parcel = LLSD::emptyMap(); - LLSD parcels = LLSD::emptyArray(); - - resource["urls"] = FAKE_NUMBER_OF_URLS; - resource["memory"] = FAKE_AMOUNT_OF_MEMORY; - - location["x"] = 128.0f; - location["y"] = 128.0f; - location["z"] = 0.0f; - - object["id"] = LLUUID("d574a375-0c6c-fe3d-5733-da669465afc7"); - object["name"] = "Gabs fake Object!"; - object["owner_id"] = LLUUID("8dbf2d41-69a0-4e5e-9787-0c9d297bc570"); - object["owner_name"] = "Gabs Linden"; - object["location"] = location; - object["resources"] = resource; - - objects.append(object); - - parcel["id"] = LLUUID("da05fb28-0d20-e593-2728-bddb42dd0160"); - parcel["local_id"] = 42; - parcel["name"] = "Gabriel Linden\'s Sub Plot"; - parcel["objects"] = objects; - parcels.append(parcel); - - fake_content["parcels"] = parcels; - const LLSD& content = fake_content; - -#else - - const LLSD& content = content_ref; - -#endif - - if (!content.isMap()) - { - failureResult(HTTP_INTERNAL_ERROR, "Malformed response contents", content); - return; - } - -#ifdef DUMP_REPLIES_TO_LLINFOS - - LLSDNotationStreamer notation_streamer(content); - std::ostringstream nice_llsd; - nice_llsd << notation_streamer; - - OSMessageBox(nice_llsd.str(), "details response:", 0); - - LL_INFOS() << "details response:" << content << LL_ENDL; - -#endif - - LLFloaterScriptLimits* instance = LLFloaterReg::getTypedInstance<LLFloaterScriptLimits>("script_limits"); - - if(!instance) - { - LL_WARNS() << "Failed to get llfloaterscriptlimits instance" << LL_ENDL; - } - else - { - LLTabContainer* tab = instance->getChild<LLTabContainer>("scriptlimits_panels"); - if(tab) - { - LLPanelScriptLimitsRegionMemory* panel_memory = (LLPanelScriptLimitsRegionMemory*)tab->getChild<LLPanel>("script_limits_region_memory_panel"); - if(panel_memory) - { - panel_memory->setRegionDetails(content); - } - else - { - LL_WARNS() << "Failed to get scriptlimits memory panel" << LL_ENDL; - } - } - else - { - LL_WARNS() << "Failed to get scriptlimits_panels" << LL_ENDL; - } - } -} - -void fetchScriptLimitsRegionDetailsResponder::httpFailure() -{ - LL_WARNS() << dumpResponse() << LL_ENDL; -} - -void fetchScriptLimitsAttachmentInfoResponder::httpSuccess() -{ - const LLSD& content_ref = getContent(); - -#ifdef USE_FAKE_RESPONSES - - // just add the summary, as that's all I'm testing currently! - LLSD fake_content = LLSD::emptyMap(); - LLSD summary = LLSD::emptyMap(); - LLSD available = LLSD::emptyArray(); - LLSD available_urls = LLSD::emptyMap(); - LLSD available_memory = LLSD::emptyMap(); - LLSD used = LLSD::emptyArray(); - LLSD used_urls = LLSD::emptyMap(); - LLSD used_memory = LLSD::emptyMap(); - - used_urls["type"] = "urls"; - used_urls["amount"] = FAKE_NUMBER_OF_URLS; - available_urls["type"] = "urls"; - available_urls["amount"] = FAKE_AVAILABLE_URLS; - used_memory["type"] = "memory"; - used_memory["amount"] = FAKE_AMOUNT_OF_MEMORY; - available_memory["type"] = "memory"; - available_memory["amount"] = FAKE_AVAILABLE_MEMORY; - - used.append(used_urls); - used.append(used_memory); - available.append(available_urls); - available.append(available_memory); - - summary["available"] = available; - summary["used"] = used; - - fake_content["summary"] = summary; - fake_content["attachments"] = content_ref["attachments"]; - - const LLSD& content = fake_content; - -#else - - const LLSD& content = content_ref; - -#endif - - if (!content.isMap()) - { - failureResult(HTTP_INTERNAL_ERROR, "Malformed response contents", content); - return; - } - -#ifdef DUMP_REPLIES_TO_LLINFOS - - LLSDNotationStreamer notation_streamer(content); - std::ostringstream nice_llsd; - nice_llsd << notation_streamer; - - OSMessageBox(nice_llsd.str(), "attachment response:", 0); - - LL_INFOS() << "attachment response:" << content << LL_ENDL; - -#endif - - LLFloaterScriptLimits* instance = LLFloaterReg::getTypedInstance<LLFloaterScriptLimits>("script_limits"); - - if(!instance) - { - LL_WARNS() << "Failed to get llfloaterscriptlimits instance" << LL_ENDL; - } - else - { - LLTabContainer* tab = instance->getChild<LLTabContainer>("scriptlimits_panels"); - if(tab) - { - LLPanelScriptLimitsAttachment* panel = (LLPanelScriptLimitsAttachment*)tab->getChild<LLPanel>("script_limits_my_avatar_panel"); - if(panel) - { - panel->getChild<LLUICtrl>("loading_text")->setValue(LLSD(std::string(""))); - - LLButton* btn = panel->getChild<LLButton>("refresh_list_btn"); - if(btn) - { - btn->setEnabled(true); - } - - panel->setAttachmentDetails(content); - } - else - { - LL_WARNS() << "Failed to get script_limits_my_avatar_panel" << LL_ENDL; - } - } - else - { - LL_WARNS() << "Failed to get scriptlimits_panels" << LL_ENDL; - } - } -} - -void fetchScriptLimitsAttachmentInfoResponder::httpFailure() -{ - LL_WARNS() << dumpResponse() << LL_ENDL; -} - -///---------------------------------------------------------------------------- // Memory Panel ///---------------------------------------------------------------------------- @@ -564,12 +199,8 @@ BOOL LLPanelScriptLimitsRegionMemory::getLandScriptResources() std::string url = gAgent.getRegion()->getCapability("LandResources"); if (!url.empty()) { - body["parcel_id"] = mParcelId; - - LLSD info; - info["parcel_id"] = mParcelId; - LLHTTPClient::post(url, body, new fetchScriptLimitsRegionInfoResponder(info)); - + LLCoros::instance().launch("LLPanelScriptLimitsRegionMemory::getLandScriptResourcesCoro", + boost::bind(&LLPanelScriptLimitsRegionMemory::getLandScriptResourcesCoro, this, url)); return TRUE; } else @@ -578,6 +209,147 @@ BOOL LLPanelScriptLimitsRegionMemory::getLandScriptResources() } } +void LLPanelScriptLimitsRegionMemory::getLandScriptResourcesCoro(std::string url) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("getLandScriptResourcesCoro", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + + LLSD postData; + + postData["parcel_id"] = mParcelId; + + LLSD result = httpAdapter->postAndYield(httpRequest, url, postData); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) + { + LL_WARNS() << "Failed to get script resource info" << LL_ENDL; + return; + } + + // We could retrieve these sequentially inline from this coroutine. But + // since the original code retrieved them in parallel I'll spawn two + // coroutines to do the retrieval. + + // The summary service: + if (result.has("ScriptResourceSummary")) + { + std::string urlResourceSummary = result["ScriptResourceSummary"].asString(); + LLCoros::instance().launch("LLPanelScriptLimitsRegionMemory::getLandScriptSummaryCoro", + boost::bind(&LLPanelScriptLimitsRegionMemory::getLandScriptSummaryCoro, this, urlResourceSummary)); + } + + if (result.has("ScriptResourceDetails")) + { + std::string urlResourceDetails = result["ScriptResourceDetails"].asString(); + LLCoros::instance().launch("LLPanelScriptLimitsRegionMemory::getLandScriptDetailsCoro", + boost::bind(&LLPanelScriptLimitsRegionMemory::getLandScriptDetailsCoro, this, urlResourceDetails)); + } + + +} + +void LLPanelScriptLimitsRegionMemory::getLandScriptSummaryCoro(std::string url) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("getLandScriptSummaryCoro", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + + LLSD result = httpAdapter->getAndYield(httpRequest, url); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) + { + LL_WARNS() << "Unable to retrieve script summary." << LL_ENDL; + return; + } + + LLFloaterScriptLimits* instance = LLFloaterReg::getTypedInstance<LLFloaterScriptLimits>("script_limits"); + if (!instance) + { + LL_WARNS() << "Failed to get llfloaterscriptlimits instance" << LL_ENDL; + return; + } + + LLTabContainer* tab = instance->getChild<LLTabContainer>("scriptlimits_panels"); + if (!tab) + { + LL_WARNS() << "Unable to access script limits tab" << LL_ENDL; + return; + } + + LLPanelScriptLimitsRegionMemory* panelMemory = (LLPanelScriptLimitsRegionMemory*)tab->getChild<LLPanel>("script_limits_region_memory_panel"); + if (!panelMemory) + { + LL_WARNS() << "Unable to get memory panel." << LL_ENDL; + return; + } + + panelMemory->getChild<LLUICtrl>("loading_text")->setValue(LLSD(std::string(""))); + + LLButton* btn = panelMemory->getChild<LLButton>("refresh_list_btn"); + if (btn) + { + btn->setEnabled(true); + } + + result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS); + panelMemory->setRegionSummary(result); + +} + +void LLPanelScriptLimitsRegionMemory::getLandScriptDetailsCoro(std::string url) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("getLandScriptDetailsCoro", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + + LLSD result = httpAdapter->getAndYield(httpRequest, url); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) + { + LL_WARNS() << "Unable to retrieve script details." << LL_ENDL; + return; + } + + LLFloaterScriptLimits* instance = LLFloaterReg::getTypedInstance<LLFloaterScriptLimits>("script_limits"); + + if (!instance) + { + LL_WARNS() << "Failed to get llfloaterscriptlimits instance" << LL_ENDL; + return; + } + + LLTabContainer* tab = instance->getChild<LLTabContainer>("scriptlimits_panels"); + if (!tab) + { + LL_WARNS() << "Unable to access script limits tab" << LL_ENDL; + return; + } + + LLPanelScriptLimitsRegionMemory* panelMemory = (LLPanelScriptLimitsRegionMemory*)tab->getChild<LLPanel>("script_limits_region_memory_panel"); + + if (!panelMemory) + { + LL_WARNS() << "Unable to get memory panel." << LL_ENDL; + return; + } + + result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS); + panelMemory->setRegionDetails(result); +} + void LLPanelScriptLimitsRegionMemory::processParcelInfo(const LLParcelData& parcel_data) { if(!getLandScriptResources()) @@ -935,17 +707,8 @@ BOOL LLPanelScriptLimitsRegionMemory::StartRequestChain() std::string url = region->getCapability("RemoteParcelRequest"); if (!url.empty()) { - body["location"] = ll_sd_from_vector3(parcel_center); - if (!region_id.isNull()) - { - body["region_id"] = region_id; - } - if (!pos_global.isExactlyZero()) - { - U64 region_handle = to_region_handle(pos_global); - body["region_handle"] = ll_sd_from_U64(region_handle); - } - LLHTTPClient::post(url, body, new LLRemoteParcelRequestResponder(getObserverHandle())); + LLRemoteParcelInfoProcessor::getInstance()->requestRegionParcelInfo(url, + region_id, parcel_center, pos_global, getObserverHandle()); } else { @@ -1183,7 +946,8 @@ BOOL LLPanelScriptLimitsAttachment::requestAttachmentDetails() std::string url = gAgent.getRegion()->getCapability("AttachmentResources"); if (!url.empty()) { - LLHTTPClient::get(url, body, new fetchScriptLimitsAttachmentInfoResponder()); + LLCoros::instance().launch("LLPanelScriptLimitsAttachment::getAttachmentLimitsCoro", + boost::bind(&LLPanelScriptLimitsAttachment::getAttachmentLimitsCoro, this, url)); return TRUE; } else @@ -1192,6 +956,59 @@ BOOL LLPanelScriptLimitsAttachment::requestAttachmentDetails() } } +void LLPanelScriptLimitsAttachment::getAttachmentLimitsCoro(std::string url) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("getAttachmentLimitsCoro", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + + LLSD result = httpAdapter->getAndYield(httpRequest, url); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) + { + LL_WARNS() << "Unable to retrieve attachment limits." << LL_ENDL; + return; + } + + LLFloaterScriptLimits* instance = LLFloaterReg::getTypedInstance<LLFloaterScriptLimits>("script_limits"); + + if (!instance) + { + LL_WARNS() << "Failed to get llfloaterscriptlimits instance" << LL_ENDL; + return; + } + + LLTabContainer* tab = instance->getChild<LLTabContainer>("scriptlimits_panels"); + if (!tab) + { + LL_WARNS() << "Failed to get scriptlimits_panels" << LL_ENDL; + return; + } + + LLPanelScriptLimitsAttachment* panel = (LLPanelScriptLimitsAttachment*)tab->getChild<LLPanel>("script_limits_my_avatar_panel"); + if (!panel) + { + LL_WARNS() << "Failed to get script_limits_my_avatar_panel" << LL_ENDL; + return; + } + + panel->getChild<LLUICtrl>("loading_text")->setValue(LLSD(std::string(""))); + + LLButton* btn = panel->getChild<LLButton>("refresh_list_btn"); + if (btn) + { + btn->setEnabled(true); + } + + result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS); + panel->setAttachmentDetails(result); +} + + void LLPanelScriptLimitsAttachment::setAttachmentDetails(LLSD content) { LLScrollListCtrl *list = getChild<LLScrollListCtrl>("scripts_list"); diff --git a/indra/newview/llfloaterscriptlimits.h b/indra/newview/llfloaterscriptlimits.h index 5ba0185d32..e3cbbd185f 100755 --- a/indra/newview/llfloaterscriptlimits.h +++ b/indra/newview/llfloaterscriptlimits.h @@ -33,6 +33,8 @@ #include "llhost.h" #include "llpanel.h" #include "llremoteparcelrequest.h" +#include "lleventcoro.h" +#include "llcoros.h" class LLPanelScriptLimitsInfo; class LLTabContainer; @@ -80,57 +82,6 @@ protected: }; ///////////////////////////////////////////////////////////////////////////// -// Responders -///////////////////////////////////////////////////////////////////////////// - -class fetchScriptLimitsRegionInfoResponder: public LLHTTPClient::Responder -{ - LOG_CLASS(fetchScriptLimitsRegionInfoResponder); -public: - fetchScriptLimitsRegionInfoResponder(const LLSD& info) : mInfo(info) {}; - -private: - /* virtual */ void httpSuccess(); - /* virtual */ void httpFailure(); - LLSD mInfo; -}; - -class fetchScriptLimitsRegionSummaryResponder: public LLHTTPClient::Responder -{ - LOG_CLASS(fetchScriptLimitsRegionSummaryResponder); -public: - fetchScriptLimitsRegionSummaryResponder(const LLSD& info) : mInfo(info) {}; - -private: - /* virtual */ void httpSuccess(); - /* virtual */ void httpFailure(); - LLSD mInfo; -}; - -class fetchScriptLimitsRegionDetailsResponder: public LLHTTPClient::Responder -{ - LOG_CLASS(fetchScriptLimitsRegionDetailsResponder); -public: - fetchScriptLimitsRegionDetailsResponder(const LLSD& info) : mInfo(info) {}; - -private: - /* virtual */ void httpSuccess(); - /* virtual */ void httpFailure(); - LLSD mInfo; -}; - -class fetchScriptLimitsAttachmentInfoResponder: public LLHTTPClient::Responder -{ - LOG_CLASS(fetchScriptLimitsAttachmentInfoResponder); -public: - fetchScriptLimitsAttachmentInfoResponder() {}; - -private: - /* virtual */ void httpSuccess(); - /* virtual */ void httpFailure(); -}; - -///////////////////////////////////////////////////////////////////////////// // Memory panel ///////////////////////////////////////////////////////////////////////////// @@ -181,6 +132,10 @@ private: std::vector<LLSD> mObjectListItems; + void getLandScriptResourcesCoro(std::string url); + void getLandScriptSummaryCoro(std::string url); + void getLandScriptDetailsCoro(std::string url); + protected: // LLRemoteParcelInfoObserver interface: @@ -225,6 +180,7 @@ public: void clearList(); private: + void getAttachmentLimitsCoro(std::string url); bool mGotAttachmentMemoryUsed; S32 mAttachmentMemoryMax; diff --git a/indra/newview/llfloatertos.cpp b/indra/newview/llfloatertos.cpp index c1c21c593e..6dc08417d7 100755 --- a/indra/newview/llfloatertos.cpp +++ b/indra/newview/llfloatertos.cpp @@ -35,8 +35,6 @@ // linden library includes #include "llbutton.h" #include "llevents.h" -#include "llhttpclient.h" -#include "llhttpconstants.h" #include "llnotificationsutil.h" #include "llradiogroup.h" #include "lltextbox.h" @@ -45,7 +43,7 @@ #include "llvfile.h" #include "message.h" #include "llstartup.h" // login_alert_done - +#include "llcorehttputil.h" LLFloaterTOS::LLFloaterTOS(const LLSD& data) : LLModalDialog( data["message"].asString() ), @@ -57,57 +55,6 @@ LLFloaterTOS::LLFloaterTOS(const LLSD& data) { } -// helper class that trys to download a URL from a web site and calls a method -// on parent class indicating if the web server is working or not -class LLIamHere : public LLHTTPClient::Responder -{ - LOG_CLASS(LLIamHere); -private: - LLIamHere( LLFloaterTOS* parent ) : - mParent( parent ) - {} - - LLFloaterTOS* mParent; - -public: - static LLIamHere* build( LLFloaterTOS* parent ) - { - return new LLIamHere( parent ); - } - - virtual void setParent( LLFloaterTOS* parentIn ) - { - mParent = parentIn; - } - -protected: - virtual void httpSuccess() - { - if ( mParent ) - { - mParent->setSiteIsAlive( true ); - } - } - - virtual void httpFailure() - { - LL_DEBUGS("LLIamHere") << dumpResponse() << LL_ENDL; - if ( mParent ) - { - // *HACK: For purposes of this alive check, 302 Found - // (aka Moved Temporarily) is considered alive. The web site - // redirects this link to a "cache busting" temporary URL. JC - bool alive = (getStatus() == HTTP_FOUND); - mParent->setSiteIsAlive( alive ); - } - } -}; - -// this is global and not a class member to keep crud out of the header file -namespace { - LLPointer< LLIamHere > gResponsePtr = 0; -}; - BOOL LLFloaterTOS::postBuild() { childSetAction("Continue", onContinue, this); @@ -180,9 +127,6 @@ void LLFloaterTOS::setSiteIsAlive( bool alive ) LLFloaterTOS::~LLFloaterTOS() { - // tell the responder we're not here anymore - if ( gResponsePtr ) - gResponsePtr->setParent( 0 ); } // virtual @@ -243,9 +187,10 @@ void LLFloaterTOS::handleMediaEvent(LLPluginClassMedia* /*self*/, EMediaEvent ev if(!mLoadingScreenLoaded) { mLoadingScreenLoaded = true; + std::string url(getString("real_url")); - gResponsePtr = LLIamHere::build( this ); - LLHTTPClient::get( getString( "real_url" ), gResponsePtr ); + LLCoros::instance().launch("LLFloaterTOS::testSiteIsAliveCoro", + boost::bind(&LLFloaterTOS::testSiteIsAliveCoro, this, url)); } else if(mRealNavigateBegun) { @@ -257,3 +202,26 @@ void LLFloaterTOS::handleMediaEvent(LLPluginClassMedia* /*self*/, EMediaEvent ev } } +void LLFloaterTOS::testSiteIsAliveCoro(std::string url) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("genericPostCoro", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpOptions::ptr_t httpOpts = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions); + + httpOpts->setWantHeaders(true); + + LL_INFOS("HttpCoroutineAdapter", "genericPostCoro") << "Generic POST for " << url << LL_ENDL; + + LLSD result = httpAdapter->getAndYield(httpRequest, url); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + // double not. + // First ! returns a boolean error status, second ! is true if success result. + setSiteIsAlive(!!status); +} + + diff --git a/indra/newview/llfloatertos.h b/indra/newview/llfloatertos.h index 47126d06a6..2748b20513 100755 --- a/indra/newview/llfloatertos.h +++ b/indra/newview/llfloatertos.h @@ -31,6 +31,8 @@ #include "llassetstorage.h" #include "llmediactrl.h" #include <boost/function.hpp> +#include "lleventcoro.h" +#include "llcoros.h" class LLButton; class LLRadioGroup; @@ -60,12 +62,15 @@ public: /*virtual*/ void handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event); private: + void testSiteIsAliveCoro(std::string url); std::string mMessage; bool mLoadingScreenLoaded; bool mSiteAlive; bool mRealNavigateBegun; std::string mReplyPumpName; + + }; #endif // LL_LLFLOATERTOS_H diff --git a/indra/newview/llfloaterurlentry.cpp b/indra/newview/llfloaterurlentry.cpp index e02e8eeb5a..6683a6e6e6 100755 --- a/indra/newview/llfloaterurlentry.cpp +++ b/indra/newview/llfloaterurlentry.cpp @@ -26,8 +26,6 @@ #include "llviewerprecompiledheaders.h" -#include "llhttpclient.h" - #include "llfloaterurlentry.h" #include "llpanellandmedia.h" @@ -40,40 +38,10 @@ #include "lluictrlfactory.h" #include "llwindow.h" #include "llviewerwindow.h" +#include "llcorehttputil.h" static LLFloaterURLEntry* sInstance = NULL; -// Move this to its own file. -// helper class that tries to download a URL from a web site and calls a method -// on the Panel Land Media and to discover the MIME type -class LLMediaTypeResponder : public LLHTTPClient::Responder -{ - LOG_CLASS(LLMediaTypeResponder); -public: - LLMediaTypeResponder( const LLHandle<LLFloater> parent ) : - mParent( parent ) - {} - - LLHandle<LLFloater> mParent; - -private: - /* virtual */ void httpCompleted() - { - const std::string& media_type = getResponseHeader(HTTP_IN_HEADER_CONTENT_TYPE); - std::string::size_type idx1 = media_type.find_first_of(";"); - std::string mime_type = media_type.substr(0, idx1); - - // Set empty type to none/none. Empty string is reserved for legacy parcels - // which have no mime type set. - std::string resolved_mime_type = ! mime_type.empty() ? mime_type : LLMIMETypes::getDefaultMimeType(); - LLFloaterURLEntry* floater_url_entry = (LLFloaterURLEntry*)mParent.get(); - if ( floater_url_entry ) - { - floater_url_entry->headerFetchComplete( getStatus(), resolved_mime_type ); - } - } -}; - //----------------------------------------------------------------------------- // LLFloaterURLEntry() //----------------------------------------------------------------------------- @@ -225,8 +193,8 @@ void LLFloaterURLEntry::onBtnOK( void* userdata ) if(!media_url.empty() && (scheme == "http" || scheme == "https")) { - LLHTTPClient::getHeaderOnly( media_url, - new LLMediaTypeResponder(self->getHandle())); + LLCoros::instance().launch("LLFloaterURLEntry::getMediaTypeCoro", + boost::bind(&LLFloaterURLEntry::getMediaTypeCoro, media_url, self->getHandle())); } else { @@ -240,6 +208,58 @@ void LLFloaterURLEntry::onBtnOK( void* userdata ) } // static +void LLFloaterURLEntry::getMediaTypeCoro(std::string url, LLHandle<LLFloater> parentHandle) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("getMediaTypeCoro", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpOptions::ptr_t httpOpts = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions); + + httpOpts->setHeadersOnly(true); + + LL_INFOS("HttpCoroutineAdapter", "genericPostCoro") << "Generic POST for " << url << LL_ENDL; + + LLSD result = httpAdapter->getAndYield(httpRequest, url, httpOpts); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + LLFloaterURLEntry* floaterUrlEntry = (LLFloaterURLEntry*)parentHandle.get(); + if (!floaterUrlEntry) + { + LL_WARNS() << "Could not get URL entry floater." << LL_ENDL; + return; + } + + // Set empty type to none/none. Empty string is reserved for legacy parcels + // which have no mime type set. + std::string resolvedMimeType = LLMIMETypes::getDefaultMimeType(); + + if (!status) + { + floaterUrlEntry->headerFetchComplete(status.getType(), resolvedMimeType); + return; + } + + LLSD resultHeaders = httpResults[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_HEADERS]; + + if (resultHeaders.has(HTTP_IN_HEADER_CONTENT_TYPE)) + { + const std::string& mediaType = resultHeaders[HTTP_IN_HEADER_CONTENT_TYPE]; + std::string::size_type idx1 = mediaType.find_first_of(";"); + std::string mimeType = mediaType.substr(0, idx1); + if (!mimeType.empty()) + { + resolvedMimeType = mimeType; + } + } + + floaterUrlEntry->headerFetchComplete(status.getType(), resolvedMimeType); + +} + +// static //----------------------------------------------------------------------------- // onBtnCancel() //----------------------------------------------------------------------------- diff --git a/indra/newview/llfloaterurlentry.h b/indra/newview/llfloaterurlentry.h index bdd1ebe592..20f4604907 100755 --- a/indra/newview/llfloaterurlentry.h +++ b/indra/newview/llfloaterurlentry.h @@ -29,6 +29,8 @@ #include "llfloater.h" #include "llpanellandmedia.h" +#include "lleventcoro.h" +#include "llcoros.h" class LLLineEditor; class LLComboBox; @@ -56,7 +58,10 @@ private: static void onBtnOK(void*); static void onBtnCancel(void*); static void onBtnClear(void*); - bool callback_clear_url_list(const LLSD& notification, const LLSD& response); + bool callback_clear_url_list(const LLSD& notification, const LLSD& response); + + static void getMediaTypeCoro(std::string url, LLHandle<LLFloater> parentHandle); + }; #endif // LL_LLFLOATERURLENTRY_H diff --git a/indra/newview/llgroupmgr.cpp b/indra/newview/llgroupmgr.cpp index 56e671d902..edae0bfd19 100755 --- a/indra/newview/llgroupmgr.cpp +++ b/indra/newview/llgroupmgr.cpp @@ -53,6 +53,7 @@ #include "lltrans.h" #include "llviewerregion.h" #include <boost/regex.hpp> +#include "llcorehttputil.h" #if LL_MSVC #pragma warning(push) @@ -768,9 +769,9 @@ void LLGroupMgrGroupData::removeBanEntry(const LLUUID& ban_id) // LLGroupMgr // -LLGroupMgr::LLGroupMgr() +LLGroupMgr::LLGroupMgr(): + mMemberRequestInFlight(false) { - mLastGroupMembersRequestFrame = 0; } LLGroupMgr::~LLGroupMgr() @@ -1861,49 +1862,94 @@ void LLGroupMgr::sendGroupMemberEjects(const LLUUID& group_id, group_datap->mMemberVersion.generate(); } - -// Responder class for capability group management -class GroupBanDataResponder : public LLHTTPClient::Responder +void LLGroupMgr::getGroupBanRequestCoro(std::string url, LLUUID groupId) { -public: - GroupBanDataResponder(const LLUUID& gropup_id, BOOL force_refresh=false); - virtual ~GroupBanDataResponder() {} - virtual void httpSuccess(); - virtual void httpFailure(); -private: - LLUUID mGroupID; - BOOL mForceRefresh; -}; + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("groupMembersRequest", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); -GroupBanDataResponder::GroupBanDataResponder(const LLUUID& gropup_id, BOOL force_refresh) : - mGroupID(gropup_id), - mForceRefresh(force_refresh) -{} + std::string finalUrl = url + "?group_id=" + groupId.asString(); -void GroupBanDataResponder::httpFailure() -{ - LL_WARNS("GrpMgr") << "Error receiving group member data [status:" - << mStatus << "]: " << mContent << LL_ENDL; + LLSD result = httpAdapter->getAndYield(httpRequest, finalUrl); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) + { + LL_WARNS("GrpMgr") << "Error receiving group member data " << LL_ENDL; + return; + } + + if (result.has("ban_list")) + { + result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS); + // group ban data received + processGroupBanRequest(result); + } } -void GroupBanDataResponder::httpSuccess() +void LLGroupMgr::postGroupBanRequestCoro(std::string url, LLUUID groupId, + U32 action, uuid_vec_t banList, bool update) { - if (mContent.has("ban_list")) - { - // group ban data received - LLGroupMgr::processGroupBanRequest(mContent); - } - else if (mForceRefresh) - { - // no ban data received, refreshing data after successful operation - LLGroupMgr::getInstance()->sendGroupBanRequest(LLGroupMgr::REQUEST_GET, mGroupID); - } + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("groupMembersRequest", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpHeaders::ptr_t httpHeaders(new LLCore::HttpHeaders); + LLCore::HttpOptions::ptr_t httpOptions(new LLCore::HttpOptions); + + httpOptions->setFollowRedirects(false); + + httpHeaders->append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_LLSD_XML); + + + std::string finalUrl = url + "?group_id=" + groupId.asString(); + + LLSD postData = LLSD::emptyMap(); + postData["ban_action"] = (LLSD::Integer)action; + // Add our list of potential banned residents to the list + postData["ban_ids"] = LLSD::emptyArray(); + LLSD banEntry; + + uuid_vec_t::const_iterator it = banList.begin(); + for (; it != banList.end(); ++it) + { + banEntry = (*it); + postData["ban_ids"].append(banEntry); + } + + LL_WARNS() << "post: " << ll_pretty_print_sd(postData) << LL_ENDL; + + LLSD result = httpAdapter->postAndYield(httpRequest, finalUrl, postData, httpOptions, httpHeaders); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) + { + LL_WARNS("GrpMgr") << "Error posting group member data " << LL_ENDL; + return; + } + + if (result.has("ban_list")) + { + result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS); + // group ban data received + processGroupBanRequest(result); + } + + if (update) + { + getGroupBanRequestCoro(url, groupId); + } } void LLGroupMgr::sendGroupBanRequest( EBanRequestType request_type, const LLUUID& group_id, U32 ban_action, /* = BAN_NO_ACTION */ - const std::vector<LLUUID> ban_list) /* = std::vector<LLUUID>() */ + const std::vector<LLUUID> &ban_list) /* = std::vector<LLUUID>() */ { LLViewerRegion* currentRegion = gAgent.getRegion(); if(!currentRegion) @@ -1925,37 +1971,27 @@ void LLGroupMgr::sendGroupBanRequest( EBanRequestType request_type, { return; } - cap_url += "?group_id=" + group_id.asString(); - - LLSD body = LLSD::emptyMap(); - body["ban_action"] = (LLSD::Integer)(ban_action & ~BAN_UPDATE); - // Add our list of potential banned residents to the list - body["ban_ids"] = LLSD::emptyArray(); - LLSD ban_entry; - uuid_vec_t::const_iterator iter = ban_list.begin(); - for(;iter != ban_list.end(); ++iter) - { - ban_entry = (*iter); - body["ban_ids"].append(ban_entry); - } + U32 action = ban_action & ~BAN_UPDATE; + bool update = ((ban_action & BAN_UPDATE) == BAN_UPDATE); - LLHTTPClient::ResponderPtr grp_ban_responder = new GroupBanDataResponder(group_id, ban_action & BAN_UPDATE); - switch(request_type) - { - case REQUEST_GET: - LLHTTPClient::get(cap_url, grp_ban_responder); - break; - case REQUEST_POST: - LLHTTPClient::post(cap_url, body, grp_ban_responder); - break; - case REQUEST_PUT: - case REQUEST_DEL: - break; - } + switch (request_type) + { + case REQUEST_GET: + LLCoros::instance().launch("LLGroupMgr::getGroupBanRequestCoro", + boost::bind(&LLGroupMgr::getGroupBanRequestCoro, this, cap_url, group_id)); + break; + case REQUEST_POST: + LLCoros::instance().launch("LLGroupMgr::postGroupBanRequestCoro", + boost::bind(&LLGroupMgr::postGroupBanRequestCoro, this, cap_url, group_id, + action, ban_list, update)); + break; + case REQUEST_PUT: + case REQUEST_DEL: + break; + } } - void LLGroupMgr::processGroupBanRequest(const LLSD& content) { // Did we get anything in content? @@ -1992,45 +2028,42 @@ void LLGroupMgr::processGroupBanRequest(const LLSD& content) LLGroupMgr::getInstance()->notifyObservers(GC_BANLIST); } +void LLGroupMgr::groupMembersRequestCoro(std::string url, LLUUID groupId) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("groupMembersRequest", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpOptions::ptr_t httpOpts = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions); + mMemberRequestInFlight = true; -// Responder class for capability group management -class GroupMemberDataResponder : public LLHTTPClient::Responder -{ - LOG_CLASS(GroupMemberDataResponder); -public: - GroupMemberDataResponder() {} - virtual ~GroupMemberDataResponder() {} + LLSD postData = LLSD::emptyMap(); + postData["group_id"] = groupId; -private: - /* virtual */ void httpSuccess(); - /* virtual */ void httpFailure(); - LLSD mMemberData; -}; + LLSD result = httpAdapter->postAndYield(httpRequest, url, postData, httpOpts); -void GroupMemberDataResponder::httpFailure() -{ - LL_WARNS("GrpMgr") << "Error receiving group member data " - << dumpResponse() << LL_ENDL; -} + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); -void GroupMemberDataResponder::httpSuccess() -{ - const LLSD& content = getContent(); - if (!content.isMap()) - { - failureResult(HTTP_INTERNAL_ERROR, "Malformed response contents", content); - return; - } - LLGroupMgr::processCapGroupMembersRequest(content); -} + if (!status) + { + LL_WARNS("GrpMgr") << "Error receiving group member data " << LL_ENDL; + mMemberRequestInFlight = false; + return; + } + result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS); + LLGroupMgr::processCapGroupMembersRequest(result); + mMemberRequestInFlight = false; +} -// static void LLGroupMgr::sendCapGroupMembersRequest(const LLUUID& group_id) { + static U32 lastGroupMemberRequestFrame = 0; + // Have we requested the information already this frame? - if(mLastGroupMembersRequestFrame == gFrameCount) + if ((lastGroupMemberRequestFrame == gFrameCount) || (mMemberRequestInFlight)) return; LLViewerRegion* currentRegion = gAgent.getRegion(); @@ -2059,20 +2092,13 @@ void LLGroupMgr::sendCapGroupMembersRequest(const LLUUID& group_id) return; } - // Post to our service. Add a body containing the group_id. - LLSD body = LLSD::emptyMap(); - body["group_id"] = group_id; + lastGroupMemberRequestFrame = gFrameCount; - LLHTTPClient::ResponderPtr grp_data_responder = new GroupMemberDataResponder(); - - // This could take a while to finish, timeout after 5 minutes. - LLHTTPClient::post(cap_url, body, grp_data_responder, LLSD(), 300); - - mLastGroupMembersRequestFrame = gFrameCount; + LLCoros::instance().launch("LLGroupMgr::groupMembersRequestCoro", + boost::bind(&LLGroupMgr::groupMembersRequestCoro, this, cap_url, group_id)); } -// static void LLGroupMgr::processCapGroupMembersRequest(const LLSD& content) { // Did we get anything in content? @@ -2089,7 +2115,7 @@ void LLGroupMgr::processCapGroupMembersRequest(const LLSD& content) LLUUID group_id = content["group_id"].asUUID(); - LLGroupMgrGroupData* group_datap = LLGroupMgr::getInstance()->getGroupData(group_id); + LLGroupMgrGroupData* group_datap = getGroupData(group_id); if(!group_datap) { LL_WARNS("GrpMgr") << "Received incorrect, possibly stale, group or request id" << LL_ENDL; @@ -2183,7 +2209,7 @@ void LLGroupMgr::processCapGroupMembersRequest(const LLSD& content) // TODO: // Refactor to reduce multiple calls for data we already have. if(group_datap->mTitles.size() < 1) - LLGroupMgr::getInstance()->sendGroupTitlesRequest(group_id); + sendGroupTitlesRequest(group_id); group_datap->mMemberDataComplete = true; @@ -2192,11 +2218,11 @@ void LLGroupMgr::processCapGroupMembersRequest(const LLSD& content) if (group_datap->mPendingRoleMemberRequest || !group_datap->mRoleMemberDataComplete) { group_datap->mPendingRoleMemberRequest = false; - LLGroupMgr::getInstance()->sendGroupRoleMembersRequest(group_id); + sendGroupRoleMembersRequest(group_id); } group_datap->mChanged = TRUE; - LLGroupMgr::getInstance()->notifyObservers(GC_MEMBER_DATA); + notifyObservers(GC_MEMBER_DATA); } diff --git a/indra/newview/llgroupmgr.h b/indra/newview/llgroupmgr.h index 2e94e8d9a0..fd0c2de854 100755 --- a/indra/newview/llgroupmgr.h +++ b/indra/newview/llgroupmgr.h @@ -32,6 +32,8 @@ #include <vector> #include <string> #include <map> +#include "lleventcoro.h" +#include "llcoros.h" // Forward Declarations class LLMessageSystem; @@ -362,6 +364,7 @@ public: BAN_UPDATE = 4 }; + public: LLGroupMgr(); ~LLGroupMgr(); @@ -396,15 +399,13 @@ public: static void sendGroupMemberEjects(const LLUUID& group_id, uuid_vec_t& member_ids); - static void sendGroupBanRequest(EBanRequestType request_type, + void sendGroupBanRequest(EBanRequestType request_type, const LLUUID& group_id, U32 ban_action = BAN_NO_ACTION, - const uuid_vec_t ban_list = uuid_vec_t()); + const uuid_vec_t &ban_list = uuid_vec_t()); - static void processGroupBanRequest(const LLSD& content); void sendCapGroupMembersRequest(const LLUUID& group_id); - static void processCapGroupMembersRequest(const LLSD& content); void cancelGroupRoleChanges(const LLUUID& group_id); @@ -427,6 +428,14 @@ public: void clearGroupData(const LLUUID& group_id); private: + void groupMembersRequestCoro(std::string url, LLUUID groupId); + void processCapGroupMembersRequest(const LLSD& content); + + void getGroupBanRequestCoro(std::string url, LLUUID groupId); + void postGroupBanRequestCoro(std::string url, LLUUID groupId, U32 action, uuid_vec_t banList, bool update); + + static void processGroupBanRequest(const LLSD& content); + void notifyObservers(LLGroupChange gc); void notifyObserver(const LLUUID& group_id, LLGroupChange gc); void addGroup(LLGroupMgrGroupData* group_datap); @@ -442,7 +451,7 @@ private: typedef std::map<LLUUID,observer_set_t> observer_map_t; observer_map_t mParticularObservers; - S32 mLastGroupMembersRequestFrame; + bool mMemberRequestInFlight; }; diff --git a/indra/newview/llhomelocationresponder.cpp b/indra/newview/llhomelocationresponder.cpp deleted file mode 100755 index d0492bcdb4..0000000000 --- a/indra/newview/llhomelocationresponder.cpp +++ /dev/null @@ -1,108 +0,0 @@ -/** - * @file llhomelocationresponder.cpp - * @author Meadhbh Hamrick - * @brief Processes responses to the HomeLocation CapReq - * - * $LicenseInfo:firstyear=2008&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -/* File Inclusions */ -#include "llviewerprecompiledheaders.h" - -#include "llhomelocationresponder.h" -#include "llsdutil.h" -#include "llagent.h" -#include "llviewerregion.h" - -void LLHomeLocationResponder::httpSuccess() -{ - const LLSD& content = getContent(); - LLVector3 agent_pos; - bool error = true; - - do { - - // was the call to /agent/<agent-id>/home-location successful? - // If not, we keep error set to true - if( ! content.has("success") ) - { - break; - } - - if( 0 != strncmp("true", content["success"].asString().c_str(), 4 ) ) - { - break; - } - - // did the simulator return a "justified" home location? - // If no, we keep error set to true - if( ! content.has( "HomeLocation" ) ) - { - break; - } - - if( ! content["HomeLocation"].has("LocationPos") ) - { - break; - } - - if( ! content["HomeLocation"]["LocationPos"].has("X") ) - { - break; - } - - agent_pos.mV[VX] = content["HomeLocation"]["LocationPos"]["X"].asInteger(); - - if( ! content["HomeLocation"]["LocationPos"].has("Y") ) - { - break; - } - - agent_pos.mV[VY] = content["HomeLocation"]["LocationPos"]["Y"].asInteger(); - - if( ! content["HomeLocation"]["LocationPos"].has("Z") ) - { - break; - } - - agent_pos.mV[VZ] = content["HomeLocation"]["LocationPos"]["Z"].asInteger(); - - error = false; - } while( 0 ); - - if( error ) - { - failureResult(HTTP_INTERNAL_ERROR, "Invalid server response content", content); - } - else - { - LL_INFOS() << "setting home position" << LL_ENDL; - - LLViewerRegion *viewer_region = gAgent.getRegion(); - gAgent.setHomePosRegion( viewer_region->getHandle(), agent_pos ); - } -} - -void LLHomeLocationResponder::httpFailure() -{ - LL_WARNS() << dumpResponse() << LL_ENDL; -} diff --git a/indra/newview/llhomelocationresponder.h b/indra/newview/llhomelocationresponder.h deleted file mode 100755 index adc6c8cb58..0000000000 --- a/indra/newview/llhomelocationresponder.h +++ /dev/null @@ -1,44 +0,0 @@ -/** - * @file llhomelocationresponder.h - * @author Meadhbh Hamrick - * @brief Processes responses to the HomeLocation CapReq - * - * $LicenseInfo:firstyear=2008&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - - /* Macro Definitions */ -#ifndef LL_LLHOMELOCATIONRESPONDER_H -#define LL_LLHOMELOCATIONRESPONDER_H - -/* File Inclusions */ -#include "llhttpclient.h" - -/* Typedef, Enum, Class, Struct, etc. */ -class LLHomeLocationResponder : public LLHTTPClient::Responder -{ - LOG_CLASS(LLHomeLocationResponder); -private: - /* virtual */ void httpSuccess(); - /* virtual */ void httpFailure(); -}; - -#endif diff --git a/indra/newview/llhttpretrypolicy.cpp b/indra/newview/llhttpretrypolicy.cpp index 2d4ce6c883..e2e151eb63 100755 --- a/indra/newview/llhttpretrypolicy.cpp +++ b/indra/newview/llhttpretrypolicy.cpp @@ -56,7 +56,7 @@ bool LLAdaptiveRetryPolicy::getRetryAfter(const LLSD& headers, F32& retry_header && getSecondsUntilRetryAfter(headers[HTTP_IN_HEADER_RETRY_AFTER].asStringRef(), retry_header_time)); } -bool LLAdaptiveRetryPolicy::getRetryAfter(const LLCore::HttpHeaders *headers, F32& retry_header_time) +bool LLAdaptiveRetryPolicy::getRetryAfter(const LLCore::HttpHeaders::ptr_t &headers, F32& retry_header_time) { if (headers) { @@ -85,9 +85,9 @@ void LLAdaptiveRetryPolicy::onFailure(S32 status, const LLSD& headers) void LLAdaptiveRetryPolicy::onFailure(const LLCore::HttpResponse *response) { F32 retry_header_time; - const LLCore::HttpHeaders *headers = response->getHeaders(); + const LLCore::HttpHeaders::ptr_t headers = response->getHeaders(); bool has_retry_header_time = getRetryAfter(headers,retry_header_time); - onFailureCommon(response->getStatus().mType, has_retry_header_time, retry_header_time); + onFailureCommon(response->getStatus().getType(), has_retry_header_time, retry_header_time); } void LLAdaptiveRetryPolicy::onFailureCommon(S32 status, bool has_retry_header_time, F32 retry_header_time) diff --git a/indra/newview/llhttpretrypolicy.h b/indra/newview/llhttpretrypolicy.h index cf79e0b401..c0cc263546 100755 --- a/indra/newview/llhttpretrypolicy.h +++ b/indra/newview/llhttpretrypolicy.h @@ -79,7 +79,7 @@ public: protected: void init(); bool getRetryAfter(const LLSD& headers, F32& retry_header_time); - bool getRetryAfter(const LLCore::HttpHeaders *headers, F32& retry_header_time); + bool getRetryAfter(const LLCore::HttpHeaders::ptr_t &headers, F32& retry_header_time); void onFailureCommon(S32 status, bool has_retry_header_time, F32 retry_header_time); private: diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index 5d3a11e245..8d670d0b0a 100755 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -37,7 +37,6 @@ #include "llrect.h" #include "llerror.h" #include "llbutton.h" -#include "llhttpclient.h" #include "llsdutil_math.h" #include "llstring.h" #include "lltextutil.h" @@ -69,6 +68,7 @@ #include "llconversationlog.h" #include "message.h" #include "llviewerregion.h" +#include "llcorehttputil.h" const static std::string ADHOC_NAME_SUFFIX(" Conference"); @@ -79,6 +79,10 @@ const static std::string NEARBY_P2P_BY_AGENT("nearby_P2P_by_agent"); /** Timeout of outgoing session initialization (in seconds) */ const static U32 SESSION_INITIALIZATION_TIMEOUT = 30; +void startConfrenceCoro(std::string url, LLUUID tempSessionId, LLUUID creatorId, LLUUID otherParticipantId, LLSD agents); +void chatterBoxInvitationCoro(std::string url, LLUUID sessionId, LLIMMgr::EInvitationType invitationType); +void start_deprecated_conference_chat(const LLUUID& temp_session_id, const LLUUID& creator_id, const LLUUID& other_participant_id, const LLSD& agents_to_invite); + std::string LLCallDialogManager::sPreviousSessionlName = ""; LLIMModel::LLIMSession::SType LLCallDialogManager::sPreviousSessionType = LLIMModel::LLIMSession::P2P_SESSION; std::string LLCallDialogManager::sCurrentSessionlName = ""; @@ -110,7 +114,7 @@ void process_dnd_im(const LLSD& notification) { LLSD data = notification["substitutions"]; LLUUID sessionID = data["SESSION_ID"].asUUID(); - LLUUID fromID = data["FROM_ID"].asUUID(); + LLUUID fromID = data["FROM_ID"].asUUID(); //re-create the IM session if needed //(when coming out of DND mode upon app restart) @@ -131,12 +135,10 @@ void process_dnd_im(const LLSD& notification) fromID, false, false); //will need slight refactor to retrieve whether offline message or not (assume online for now) - } - - notify_of_message(data, true); } - + notify_of_message(data, true); +} static void on_avatar_name_cache_toast(const LLUUID& agent_id, @@ -387,6 +389,130 @@ void on_new_message(const LLSD& msg) notify_of_message(msg, false); } +void startConfrenceCoro(std::string url, + LLUUID tempSessionId, LLUUID creatorId, LLUUID otherParticipantId, LLSD agents) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("TwitterConnect", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + + LLSD postData; + postData["method"] = "start conference"; + postData["session-id"] = tempSessionId; + postData["params"] = agents; + + LLSD result = httpAdapter->postAndYield(httpRequest, url, postData); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) + { + LL_WARNS("LLIMModel") << "Failed to start conference" << LL_ENDL; + //try an "old school" way. + // *TODO: What about other error status codes? 4xx 5xx? + if (status == LLCore::HttpStatus(HTTP_BAD_REQUEST)) + { + start_deprecated_conference_chat( + tempSessionId, + creatorId, + otherParticipantId, + agents); + } + + //else throw an error back to the client? + //in theory we should have just have these error strings + //etc. set up in this file as opposed to the IMMgr, + //but the error string were unneeded here previously + //and it is not worth the effort switching over all + //the possible different language translations + } +} + +void chatterBoxInvitationCoro(std::string url, LLUUID sessionId, LLIMMgr::EInvitationType invitationType) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("TwitterConnect", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + + LLSD postData; + postData["method"] = "accept invitation"; + postData["session-id"] = sessionId; + + LLSD result = httpAdapter->postAndYield(httpRequest, url, postData); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!gIMMgr) + { + LL_WARNS("") << "Global IM Manager is NULL" << LL_ENDL; + return; + } + + if (!status) + { + LL_WARNS("LLIMModel") << "Bad HTTP response in chatterBoxInvitationCoro" << LL_ENDL; + //throw something back to the viewer here? + + gIMMgr->clearPendingAgentListUpdates(sessionId); + gIMMgr->clearPendingInvitation(sessionId); + + if (status == LLCore::HttpStatus(HTTP_NOT_FOUND)) + { + static const std::string error_string("session_does_not_exist_error"); + gIMMgr->showSessionStartError(error_string, sessionId); + } + return; + } + + result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS); + + LLIMSpeakerMgr* speakerMgr = LLIMModel::getInstance()->getSpeakerManager(sessionId); + if (speakerMgr) + { + //we've accepted our invitation + //and received a list of agents that were + //currently in the session when the reply was sent + //to us. Now, it is possible that there were some agents + //to slip in/out between when that message was sent to us + //and now. + + //the agent list updates we've received have been + //accurate from the time we were added to the session + //but unfortunately, our base that we are receiving here + //may not be the most up to date. It was accurate at + //some point in time though. + speakerMgr->setSpeakers(result); + + //we now have our base of users in the session + //that was accurate at some point, but maybe not now + //so now we apply all of the updates we've received + //in case of race conditions + speakerMgr->updateSpeakers(gIMMgr->getPendingAgentListUpdates(sessionId)); + } + + if (LLIMMgr::INVITATION_TYPE_VOICE == invitationType) + { + gIMMgr->startCall(sessionId, LLVoiceChannel::INCOMING_CALL); + } + + if ((invitationType == LLIMMgr::INVITATION_TYPE_VOICE + || invitationType == LLIMMgr::INVITATION_TYPE_IMMEDIATE) + && LLIMModel::getInstance()->findIMSession(sessionId)) + { + // TODO remove in 2010, for voice calls we do not open an IM window + //LLFloaterIMSession::show(mSessionID); + } + + gIMMgr->clearPendingAgentListUpdates(sessionId); + gIMMgr->clearPendingInvitation(sessionId); + +} + + LLIMModel::LLIMModel() { addNewMsgCallback(boost::bind(&LLFloaterIMSession::newIMCallback, _1)); @@ -1459,54 +1585,6 @@ void start_deprecated_conference_chat( delete[] bucket; } -class LLStartConferenceChatResponder : public LLHTTPClient::Responder -{ - LOG_CLASS(LLStartConferenceChatResponder); -public: - LLStartConferenceChatResponder( - const LLUUID& temp_session_id, - const LLUUID& creator_id, - const LLUUID& other_participant_id, - const LLSD& agents_to_invite) - { - mTempSessionID = temp_session_id; - mCreatorID = creator_id; - mOtherParticipantID = other_participant_id; - mAgents = agents_to_invite; - } - -protected: - virtual void httpFailure() - { - //try an "old school" way. - // *TODO: What about other error status codes? 4xx 5xx? - if ( getStatus() == HTTP_BAD_REQUEST ) - { - start_deprecated_conference_chat( - mTempSessionID, - mCreatorID, - mOtherParticipantID, - mAgents); - } - - LL_WARNS() << dumpResponse() << LL_ENDL; - - //else throw an error back to the client? - //in theory we should have just have these error strings - //etc. set up in this file as opposed to the IMMgr, - //but the error string were unneeded here previously - //and it is not worth the effort switching over all - //the possible different language translations - } - -private: - LLUUID mTempSessionID; - LLUUID mCreatorID; - LLUUID mOtherParticipantID; - - LLSD mAgents; -}; - // Returns true if any messages were sent, false otherwise. // Is sort of equivalent to "does the server need to do anything?" bool LLIMModel::sendStartSession( @@ -1543,20 +1621,10 @@ bool LLIMModel::sendStartSession( { std::string url = region->getCapability( "ChatSessionRequest"); - LLSD data; - data["method"] = "start conference"; - data["session-id"] = temp_session_id; - - data["params"] = agents; - LLHTTPClient::post( - url, - data, - new LLStartConferenceChatResponder( - temp_session_id, - gAgent.getID(), - other_participant_id, - data["params"])); + LLCoros::instance().launch("startConfrenceCoro", + boost::bind(&startConfrenceCoro, url, + temp_session_id, gAgent.getID(), other_participant_id, agents)); } else { @@ -1574,97 +1642,6 @@ bool LLIMModel::sendStartSession( return false; } -// -// Helper Functions -// - -class LLViewerChatterBoxInvitationAcceptResponder : - public LLHTTPClient::Responder -{ - LOG_CLASS(LLViewerChatterBoxInvitationAcceptResponder); -public: - LLViewerChatterBoxInvitationAcceptResponder( - const LLUUID& session_id, - LLIMMgr::EInvitationType invitation_type) - { - mSessionID = session_id; - mInvitiationType = invitation_type; - } - -private: - void httpSuccess() - { - const LLSD& content = getContent(); - if (!content.isMap()) - { - failureResult(HTTP_INTERNAL_ERROR, "Malformed response contents", content); - return; - } - if ( gIMMgr) - { - LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID); - if (speaker_mgr) - { - //we've accepted our invitation - //and received a list of agents that were - //currently in the session when the reply was sent - //to us. Now, it is possible that there were some agents - //to slip in/out between when that message was sent to us - //and now. - - //the agent list updates we've received have been - //accurate from the time we were added to the session - //but unfortunately, our base that we are receiving here - //may not be the most up to date. It was accurate at - //some point in time though. - speaker_mgr->setSpeakers(content); - - //we now have our base of users in the session - //that was accurate at some point, but maybe not now - //so now we apply all of the udpates we've received - //in case of race conditions - speaker_mgr->updateSpeakers(gIMMgr->getPendingAgentListUpdates(mSessionID)); - } - - if (LLIMMgr::INVITATION_TYPE_VOICE == mInvitiationType) - { - gIMMgr->startCall(mSessionID, LLVoiceChannel::INCOMING_CALL); - } - - if ((mInvitiationType == LLIMMgr::INVITATION_TYPE_VOICE - || mInvitiationType == LLIMMgr::INVITATION_TYPE_IMMEDIATE) - && LLIMModel::getInstance()->findIMSession(mSessionID)) - { - // TODO remove in 2010, for voice calls we do not open an IM window - //LLFloaterIMSession::show(mSessionID); - } - - gIMMgr->clearPendingAgentListUpdates(mSessionID); - gIMMgr->clearPendingInvitation(mSessionID); - } - } - - void httpFailure() - { - LL_WARNS() << dumpResponse() << LL_ENDL; - //throw something back to the viewer here? - if ( gIMMgr ) - { - gIMMgr->clearPendingAgentListUpdates(mSessionID); - gIMMgr->clearPendingInvitation(mSessionID); - if ( HTTP_NOT_FOUND == getStatus() ) - { - static const std::string error_string("session_does_not_exist_error"); - gIMMgr->showSessionStartError(error_string, mSessionID); - } - } - } - -private: - LLUUID mSessionID; - LLIMMgr::EInvitationType mInvitiationType; -}; - // the other_participant_id is either an agent_id, a group_id, or an inventory // folder item_id (collection of calling cards) @@ -2490,15 +2467,9 @@ void LLIncomingCallDialog::processCallResponse(S32 response, const LLSD &payload if (voice) { - LLSD data; - data["method"] = "accept invitation"; - data["session-id"] = session_id; - LLHTTPClient::post( - url, - data, - new LLViewerChatterBoxInvitationAcceptResponder( - session_id, - inv_type)); + LLCoros::instance().launch("chatterBoxInvitationCoro", + boost::bind(&chatterBoxInvitationCoro, url, + session_id, inv_type)); // send notification message to the corresponding chat if (payload["notify_box_type"].asString() == "VoiceInviteGroup" || payload["notify_box_type"].asString() == "VoiceInviteAdHoc") @@ -2533,10 +2504,10 @@ void LLIncomingCallDialog::processCallResponse(S32 response, const LLSD &payload LLSD data; data["method"] = "decline invitation"; data["session-id"] = session_id; - LLHTTPClient::post( - url, - data, - NULL); + + LLCoreHttpUtil::HttpCoroutineAdapter::messageHttpPost(url, data, + "Invitation declined", + "Invitation decline failed."); } } @@ -2583,15 +2554,9 @@ bool inviteUserResponse(const LLSD& notification, const LLSD& response) std::string url = gAgent.getRegion()->getCapability( "ChatSessionRequest"); - LLSD data; - data["method"] = "accept invitation"; - data["session-id"] = session_id; - LLHTTPClient::post( - url, - data, - new LLViewerChatterBoxInvitationAcceptResponder( - session_id, - inv_type)); + LLCoros::instance().launch("chatterBoxInvitationCoro", + boost::bind(&chatterBoxInvitationCoro, url, + session_id, inv_type)); } } break; @@ -2621,10 +2586,9 @@ bool inviteUserResponse(const LLSD& notification, const LLSD& response) LLSD data; data["method"] = "decline invitation"; data["session-id"] = session_id; - LLHTTPClient::post( - url, - data, - NULL); + LLCoreHttpUtil::HttpCoroutineAdapter::messageHttpPost(url, data, + "Invitation declined.", + "Invitation decline failed."); } } @@ -3681,15 +3645,9 @@ public: if ( url != "" ) { - LLSD data; - data["method"] = "accept invitation"; - data["session-id"] = session_id; - LLHTTPClient::post( - url, - data, - new LLViewerChatterBoxInvitationAcceptResponder( - session_id, - LLIMMgr::INVITATION_TYPE_INSTANT_MESSAGE)); + LLCoros::instance().launch("chatterBoxInvitationCoro", + boost::bind(&chatterBoxInvitationCoro, url, + session_id, LLIMMgr::INVITATION_TYPE_INSTANT_MESSAGE)); } } //end if invitation has instant message else if ( input["body"].has("voice") ) diff --git a/indra/newview/llimview.h b/indra/newview/llimview.h index f92eff4845..41a8813acb 100755 --- a/indra/newview/llimview.h +++ b/indra/newview/llimview.h @@ -34,6 +34,9 @@ #include "lllogchat.h" #include "llvoicechannel.h" +#include "llcoros.h" +#include "lleventcoro.h" + class LLAvatarName; class LLFriendObserver; class LLCallDialogManager; @@ -292,6 +295,7 @@ private: * Add message to a list of message associated with session specified by session_id */ bool addToHistory(const LLUUID& session_id, const std::string& from, const LLUUID& from_id, const std::string& utf8_text); + }; class LLIMSessionObserver diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index f92332dea5..47d3e88b3a 100755 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -148,8 +148,8 @@ LLInventoryModel::LLInventoryModel() mObservers(), mHttpRequestFG(NULL), mHttpRequestBG(NULL), - mHttpOptions(NULL), - mHttpHeaders(NULL), + mHttpOptions(), + mHttpHeaders(), mHttpPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID), mHttpPriorityFG(0), mHttpPriorityBG(0), @@ -178,16 +178,9 @@ void LLInventoryModel::cleanupInventory() mObservers.clear(); // Run down HTTP transport - if (mHttpHeaders) - { - mHttpHeaders->release(); - mHttpHeaders = NULL; - } - if (mHttpOptions) - { - mHttpOptions->release(); - mHttpOptions = NULL; - } + mHttpHeaders.reset(); + mHttpOptions.reset(); + delete mHttpRequestFG; mHttpRequestFG = NULL; delete mHttpRequestBG; @@ -524,59 +517,6 @@ const LLUUID LLInventoryModel::findLibraryCategoryUUIDForType(LLFolderType::ETyp return findCategoryUUIDForTypeInRoot(preferred_type, create_folder, gInventory.getLibraryRootFolderID()); } -class LLCreateInventoryCategoryResponder : public LLHTTPClient::Responder -{ - LOG_CLASS(LLCreateInventoryCategoryResponder); -public: - LLCreateInventoryCategoryResponder(LLInventoryModel* model, - boost::optional<inventory_func_type> callback): - mModel(model), - mCallback(callback) - { - } - -protected: - virtual void httpFailure() - { - LL_WARNS(LOG_INV) << dumpResponse() << LL_ENDL; - } - - virtual void httpSuccess() - { - //Server has created folder. - const LLSD& content = getContent(); - if (!content.isMap() || !content.has("folder_id")) - { - failureResult(HTTP_INTERNAL_ERROR, "Malformed response contents", content); - return; - } - LLUUID category_id = content["folder_id"].asUUID(); - - LL_DEBUGS(LOG_INV) << ll_pretty_print_sd(content) << LL_ENDL; - // Add the category to the internal representation - LLPointer<LLViewerInventoryCategory> cat = - new LLViewerInventoryCategory( category_id, - content["parent_id"].asUUID(), - (LLFolderType::EType)content["type"].asInteger(), - content["name"].asString(), - gAgent.getID() ); - cat->setVersion(LLViewerInventoryCategory::VERSION_INITIAL); - cat->setDescendentCount(0); - LLInventoryModel::LLCategoryUpdate update(cat->getParentUUID(), 1); - mModel->accountForUpdate(update); - mModel->updateCategory(cat); - - if (mCallback) - { - mCallback.get()(category_id); - } - } - -private: - boost::optional<inventory_func_type> mCallback; - LLInventoryModel* mModel; -}; - // Convenience function to create a new category. You could call // updateCategory() with a newly generated UUID category, but this // version will take care of details like what the name should be @@ -584,7 +524,7 @@ private: LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id, LLFolderType::EType preferred_type, const std::string& pname, - boost::optional<inventory_func_type> callback) + inventory_func_type callback) { LLUUID id; @@ -616,7 +556,7 @@ LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id, if ( viewer_region ) url = viewer_region->getCapability("CreateInventoryCategory"); - if (!url.empty() && callback.get_ptr()) + if (!url.empty() && callback) { //Let's use the new capability. @@ -630,11 +570,8 @@ LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id, request["payload"] = body; LL_DEBUGS(LOG_INV) << "create category request: " << ll_pretty_print_sd(request) << LL_ENDL; - // viewer_region->getCapAPI().post(request); - LLHTTPClient::post( - url, - body, - new LLCreateInventoryCategoryResponder(this, callback) ); + LLCoros::instance().launch("LLInventoryModel::createNewCategoryCoro", + boost::bind(&LLInventoryModel::createNewCategoryCoro, this, url, body, callback)); return LLUUID::null; } @@ -663,6 +600,57 @@ LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id, return id; } +void LLInventoryModel::createNewCategoryCoro(std::string url, LLSD postData, inventory_func_type callback) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("createNewCategoryCoro", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); + + + httpOpts->setWantHeaders(true); + + LL_INFOS("HttpCoroutineAdapter", "genericPostCoro") << "Generic POST for " << url << LL_ENDL; + + LLSD result = httpAdapter->postAndYield(httpRequest, url, postData, httpOpts); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) + { + LL_WARNS() << "HTTP failure attempting to create category." << LL_ENDL; + return; + } + + if (!result.has("folder_id")) + { + LL_WARNS() << "Malformed response contents" << ll_pretty_print_sd(result) << LL_ENDL; + return; + } + + LLUUID categoryId = result["folder_id"].asUUID(); + + // Add the category to the internal representation + LLPointer<LLViewerInventoryCategory> cat = new LLViewerInventoryCategory(categoryId, + result["parent_id"].asUUID(), (LLFolderType::EType)result["type"].asInteger(), + result["name"].asString(), gAgent.getID()); + + cat->setVersion(LLViewerInventoryCategory::VERSION_INITIAL); + cat->setDescendentCount(0); + LLInventoryModel::LLCategoryUpdate update(cat->getParentUUID(), 1); + + accountForUpdate(update); + updateCategory(cat); + + if (callback) + { + callback(categoryId); + } + +} + // This is optimized for the case that we just want to know whether a // category has any immediate children meeting a condition, without // needing to recurse or build up any lists. @@ -2423,11 +2411,11 @@ void LLInventoryModel::initHttpRequest() mHttpRequestFG = new LLCore::HttpRequest; mHttpRequestBG = new LLCore::HttpRequest; - mHttpOptions = new LLCore::HttpOptions; + mHttpOptions = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions); mHttpOptions->setTransferTimeout(300); mHttpOptions->setUseRetryAfter(true); // mHttpOptions->setTrace(2); // Do tracing of requests - mHttpHeaders = new LLCore::HttpHeaders; + mHttpHeaders = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders); mHttpHeaders->append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_LLSD_XML); mHttpHeaders->append(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_LLSD_XML); mHttpPolicyClass = app_core_http.getPolicy(LLAppCoreHttp::AP_INVENTORY); diff --git a/indra/newview/llinventorymodel.h b/indra/newview/llinventorymodel.h index ac336e347c..e8977bc7d7 100755 --- a/indra/newview/llinventorymodel.h +++ b/indra/newview/llinventorymodel.h @@ -46,6 +46,8 @@ #include "httpoptions.h" #include "httpheaders.h" #include "httphandler.h" +#include "lleventcoro.h" +#include "llcoros.h" class LLInventoryObserver; class LLInventoryObject; @@ -207,14 +209,14 @@ private: **/ //-------------------------------------------------------------------- - // Descendents + // Descendants //-------------------------------------------------------------------- public: - // Make sure we have the descendents in the structure. Returns true + // Make sure we have the descendants in the structure. Returns true // if a fetch was performed. bool fetchDescendentsOf(const LLUUID& folder_id) const; - // Return the direct descendents of the id provided.Set passed + // Return the direct descendants of the id provided.Set passed // in values to NULL if the call fails. // NOTE: The array provided points straight into the guts of // this object, and should only be used for read operations, since @@ -223,10 +225,10 @@ public: cat_array_t*& categories, item_array_t*& items) const; - // Compute a hash of direct descendent names (for detecting child name changes) + // Compute a hash of direct descendant names (for detecting child name changes) LLMD5 hashDirectDescendentNames(const LLUUID& cat_id) const; - // Starting with the object specified, add its descendents to the + // Starting with the object specified, add its descendants to the // array provided, but do not add the inventory object specified // by id. There is no guaranteed order. // NOTE: Neither array will be erased before adding objects to it. @@ -340,7 +342,7 @@ public: U32 updateItem(const LLViewerInventoryItem* item, U32 mask = 0); // Change an existing item with the matching id or add - // the category. No notifcation will be sent to observers. This + // the category. No notification will be sent to observers. This // method will only generate network traffic if the item had to be // reparented. // NOTE: In usage, you will want to perform cache accounting @@ -378,7 +380,7 @@ public: bool update_parent_version = true, bool do_notify_observers = true); - // Update model after all descendents removed from server. + // Update model after all descendants removed from server. void onDescendentsPurgedFromServer(const LLUUID& object_id, bool fix_broken_links = true); // Update model after an existing item gets updated on server. @@ -409,7 +411,7 @@ public: // Changes items order by insertion of the item identified by src_item_id // before (or after) the item identified by dest_item_id. Both items must exist in items array. // Sorting is stored after method is finished. Only src_item_id is moved before (or after) dest_item_id. - // The parameter "insert_before" controls on which side of dest_item_id src_item_id gets rensinserted. + // The parameter "insert_before" controls on which side of dest_item_id src_item_id gets reinserted. static void updateItemsOrder(LLInventoryModel::item_array_t& items, const LLUUID& src_item_id, const LLUUID& dest_item_id, @@ -433,7 +435,7 @@ public: LLUUID createNewCategory(const LLUUID& parent_id, LLFolderType::EType preferred_type, const std::string& name, - boost::optional<inventory_func_type> callback = boost::optional<inventory_func_type>()); + inventory_func_type callback = NULL); protected: // Internal methods that add inventory and make sure that all of // the internal data structures are consistent. These methods @@ -441,6 +443,8 @@ protected: // instance will take over the memory management from there. void addCategory(LLViewerInventoryCategory* category); void addItem(LLViewerInventoryItem* item); + + void createNewCategoryCoro(std::string url, LLSD postData, inventory_func_type callback); /** Mutators ** ** @@ -567,8 +571,8 @@ private: // Usual plumbing for LLCore:: HTTP operations. LLCore::HttpRequest * mHttpRequestFG; LLCore::HttpRequest * mHttpRequestBG; - LLCore::HttpOptions * mHttpOptions; - LLCore::HttpHeaders * mHttpHeaders; + LLCore::HttpOptions::ptr_t mHttpOptions; + LLCore::HttpHeaders::ptr_t mHttpHeaders; LLCore::HttpRequest::policy_t mHttpPolicyClass; LLCore::HttpRequest::priority_t mHttpPriorityFG; LLCore::HttpRequest::priority_t mHttpPriorityBG; diff --git a/indra/newview/llmarketplacefunctions.cpp b/indra/newview/llmarketplacefunctions.cpp index 4a7a4e268d..38c4382654 100755 --- a/indra/newview/llmarketplacefunctions.cpp +++ b/indra/newview/llmarketplacefunctions.cpp @@ -36,7 +36,9 @@ #include "llviewercontrol.h" #include "llviewermedia.h" #include "llviewernetwork.h" - +#include "lleventcoro.h" +#include "llcoros.h" +#include "llcorehttputil.h" // // Helpers @@ -117,11 +119,76 @@ namespace LLMarketplaceImport static S32 sImportResultStatus = 0; static LLSD sImportResults = LLSD::emptyMap(); +#if 0 static LLTimer slmGetTimer; static LLTimer slmPostTimer; - +#endif // Responders - + +#if 1 + void marketplacePostCoro(std::string url) + { + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("marketplacePostCoro", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpHeaders::ptr_t httpHeaders(new LLCore::HttpHeaders); + LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); + + httpOpts->setWantHeaders(true); + httpOpts->setFollowRedirects(true); + + httpHeaders->append(HTTP_OUT_HEADER_ACCEPT, "*/*"); + httpHeaders->append(HTTP_OUT_HEADER_CONNECTION, "Keep-Alive"); + httpHeaders->append(HTTP_OUT_HEADER_COOKIE, sMarketplaceCookie); + httpHeaders->append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_XML); + httpHeaders->append(HTTP_OUT_HEADER_USER_AGENT, LLViewerMedia::getCurrentUserAgent()); + + LLSD result = httpAdapter->postAndYield(httpRequest, url, LLSD(), httpOpts, httpHeaders); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + S32 httpCode = status.getType(); + if ((httpCode == MarketplaceErrorCodes::IMPORT_REDIRECT) || + (httpCode == MarketplaceErrorCodes::IMPORT_AUTHENTICATION_ERROR) || + // MAINT-2301 : we determined we can safely ignore that error in that context + (httpCode == MarketplaceErrorCodes::IMPORT_JOB_TIMEOUT)) + { + if (gSavedSettings.getBOOL("InventoryOutboxLogging")) + { + LL_INFOS() << " SLM POST : Ignoring time out status and treating it as success" << LL_ENDL; + } + httpCode = MarketplaceErrorCodes::IMPORT_DONE; + } + + if (httpCode >= MarketplaceErrorCodes::IMPORT_BAD_REQUEST) + { + if (gSavedSettings.getBOOL("InventoryOutboxLogging")) + { + LL_INFOS() << " SLM POST clearing marketplace cookie due to client or server error" << LL_ENDL; + } + sMarketplaceCookie.clear(); + } + + sImportInProgress = (httpCode == MarketplaceErrorCodes::IMPORT_DONE); + sImportPostPending = false; + sImportResultStatus = httpCode; + + { + std::stringstream str; + LLSDSerialize::toPrettyXML(result, str); + + LL_INFOS() << "Full results:\n" << str.str() << "\n" << LL_ENDL; + } + + result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS); + sImportId = result; + + } + + +#else class LLImportPostResponder : public LLHTTPClient::Responder { LOG_CLASS(LLImportPostResponder); @@ -167,7 +234,75 @@ namespace LLMarketplaceImport sImportId = getContent(); } }; - +#endif + +#if 1 + void marketplaceGetCoro(std::string url, bool buildHeaders) + { + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("marketplaceGetCoro", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpHeaders::ptr_t httpHeaders; + LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); + + httpOpts->setWantHeaders(true); + httpOpts->setFollowRedirects(!sMarketplaceCookie.empty()); + + if (buildHeaders) + { + httpHeaders = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders); + + httpHeaders->append(HTTP_OUT_HEADER_ACCEPT, "*/*"); + httpHeaders->append(HTTP_OUT_HEADER_COOKIE, sMarketplaceCookie); + httpHeaders->append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_LLSD_XML); + httpHeaders->append(HTTP_OUT_HEADER_USER_AGENT, LLViewerMedia::getCurrentUserAgent()); + } + else + { + httpHeaders = LLViewerMedia::getHttpHeaders(); + } + + LLSD result = httpAdapter->getAndYield(httpRequest, url, httpOpts, httpHeaders); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + LLSD resultHeaders = httpResults[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_HEADERS]; + + if (sMarketplaceCookie.empty() && resultHeaders.has(HTTP_IN_HEADER_SET_COOKIE)) + { + sMarketplaceCookie = resultHeaders[HTTP_IN_HEADER_SET_COOKIE].asString(); + } + + // MAINT-2452 : Do not clear the cookie on IMPORT_DONE_WITH_ERRORS : Happens when trying to import objects with wrong permissions + // ACME-1221 : Do not clear the cookie on IMPORT_NOT_FOUND : Happens for newly created Merchant accounts that are initially empty + S32 httpCode = status.getType(); + if ((httpCode >= MarketplaceErrorCodes::IMPORT_BAD_REQUEST) && + (httpCode != MarketplaceErrorCodes::IMPORT_DONE_WITH_ERRORS) && + (httpCode != MarketplaceErrorCodes::IMPORT_NOT_FOUND)) + { + if (gSavedSettings.getBOOL("InventoryOutboxLogging")) + { + LL_INFOS() << " SLM GET clearing marketplace cookie due to client or server error" << LL_ENDL; + } + sMarketplaceCookie.clear(); + } + else if (gSavedSettings.getBOOL("InventoryOutboxLogging") && (httpCode >= MarketplaceErrorCodes::IMPORT_BAD_REQUEST)) + { + LL_INFOS() << " SLM GET : Got error status = " << httpCode << ", but marketplace cookie not cleared." << LL_ENDL; + } + + sImportInProgress = (httpCode == MarketplaceErrorCodes::IMPORT_PROCESSING); + sImportGetPending = false; + sImportResultStatus = httpCode; + + result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS); + sImportResults = result; + + + } + +#else class LLImportGetResponder : public LLHTTPClient::Responder { LOG_CLASS(LLImportGetResponder); @@ -193,7 +328,7 @@ namespace LLMarketplaceImport } // MAINT-2452 : Do not clear the cookie on IMPORT_DONE_WITH_ERRORS : Happens when trying to import objects with wrong permissions - // ACME-1221 : Do not clear the cookie on IMPORT_NOT_FOUND : Happens for newly created Merchant accounts that are initally empty + // ACME-1221 : Do not clear the cookie on IMPORT_NOT_FOUND : Happens for newly created Merchant accounts that are initially empty S32 status = getStatus(); if ((status >= MarketplaceErrorCodes::IMPORT_BAD_REQUEST) && (status != MarketplaceErrorCodes::IMPORT_DONE_WITH_ERRORS) && @@ -216,6 +351,7 @@ namespace LLMarketplaceImport sImportResults = getContent(); } }; +#endif // Basic API @@ -266,8 +402,13 @@ namespace LLMarketplaceImport sImportGetPending = true; std::string url = getInventoryImportURL(); - - if (gSavedSettings.getBOOL("InventoryOutboxLogging")) + +#if 1 + LLCoros::instance().launch("marketplaceGetCoro", + boost::bind(&marketplaceGetCoro, url, false)); + +#else + if (gSavedSettings.getBOOL("InventoryOutboxLogging")) { LL_INFOS() << " SLM GET: establishMarketplaceSessionCookie, LLHTTPClient::get, url = " << url << LL_ENDL; LLSD headers = LLViewerMedia::getHeaders(); @@ -279,7 +420,7 @@ namespace LLMarketplaceImport slmGetTimer.start(); LLHTTPClient::get(url, new LLImportGetResponder(), LLViewerMedia::getHeaders()); - +#endif return true; } @@ -296,6 +437,11 @@ namespace LLMarketplaceImport url += sImportId.asString(); +#if 1 + LLCoros::instance().launch("marketplaceGetCoro", + boost::bind(&marketplaceGetCoro, url, true)); + +#else // Make the headers for the post LLSD headers = LLSD::emptyMap(); headers[HTTP_OUT_HEADER_ACCEPT] = "*/*"; @@ -315,7 +461,7 @@ namespace LLMarketplaceImport slmGetTimer.start(); LLHTTPClient::get(url, new LLImportGetResponder(), headers); - +#endif return true; } @@ -334,6 +480,11 @@ namespace LLMarketplaceImport std::string url = getInventoryImportURL(); +#if 1 + LLCoros::instance().launch("marketplacePostCoro", + boost::bind(&marketplacePostCoro, url)); + +#else // Make the headers for the post LLSD headers = LLSD::emptyMap(); headers[HTTP_OUT_HEADER_ACCEPT] = "*/*"; @@ -353,7 +504,7 @@ namespace LLMarketplaceImport slmPostTimer.start(); LLHTTPClient::post(url, LLSD(), new LLImportPostResponder(), headers); - +#endif return true; } } @@ -362,7 +513,6 @@ namespace LLMarketplaceImport // // Interface class // - static const F32 MARKET_IMPORTER_UPDATE_FREQUENCY = 1.0f; //static diff --git a/indra/newview/llmaterialmgr.cpp b/indra/newview/llmaterialmgr.cpp index a1f6a01aa0..1045def72e 100755 --- a/indra/newview/llmaterialmgr.cpp +++ b/indra/newview/llmaterialmgr.cpp @@ -36,6 +36,9 @@ #include "llviewerobjectlist.h" #include "llviewerregion.h" #include "llworld.h" +#include "llhttpsdhandler.h" +#include "httpcommon.h" +#include "llcorehttputil.h" /** * Materials cap parameters @@ -59,56 +62,51 @@ #define MATERIALS_PUT_THROTTLE_SECS 1.f #define MATERIALS_PUT_MAX_ENTRIES 50 -/** - * LLMaterialsResponder helper class - */ -class LLMaterialsResponder : public LLHTTPClient::Responder + +class LLMaterialHttpHandler : public LLHttpSDHandler { -public: - typedef boost::function<void (bool, const LLSD&)> CallbackFunction; +public: + typedef boost::function<void(bool, const LLSD&)> CallbackFunction; + typedef boost::shared_ptr<LLMaterialHttpHandler> ptr_t; + + LLMaterialHttpHandler(const std::string& method, CallbackFunction cback); - LLMaterialsResponder(const std::string& pMethod, const std::string& pCapabilityURL, CallbackFunction pCallback); - virtual ~LLMaterialsResponder(); + virtual ~LLMaterialHttpHandler(); - virtual void httpSuccess(); - virtual void httpFailure(); +protected: + virtual void onSuccess(LLCore::HttpResponse * response, const LLSD &content); + virtual void onFailure(LLCore::HttpResponse * response, LLCore::HttpStatus status); private: std::string mMethod; - std::string mCapabilityURL; CallbackFunction mCallback; }; -LLMaterialsResponder::LLMaterialsResponder(const std::string& pMethod, const std::string& pCapabilityURL, CallbackFunction pCallback) - : LLHTTPClient::Responder() - , mMethod(pMethod) - , mCapabilityURL(pCapabilityURL) - , mCallback(pCallback) +LLMaterialHttpHandler::LLMaterialHttpHandler(const std::string& method, CallbackFunction cback): + LLHttpSDHandler(), + mMethod(method), + mCallback(cback) { + } -LLMaterialsResponder::~LLMaterialsResponder() +LLMaterialHttpHandler::~LLMaterialHttpHandler() { } -void LLMaterialsResponder::httpSuccess() +void LLMaterialHttpHandler::onSuccess(LLCore::HttpResponse * response, const LLSD &content) { - const LLSD& pContent = getContent(); - LL_DEBUGS("Materials") << LL_ENDL; - mCallback(true, pContent); + mCallback(true, content); } -void LLMaterialsResponder::httpFailure() +void LLMaterialHttpHandler::onFailure(LLCore::HttpResponse * response, LLCore::HttpStatus status) { - U32 pStatus = (U32) getStatus(); - const std::string& pReason = getReason(); - LL_WARNS("Materials") << "\n--------------------------------------------------------------------------\n" - << mMethod << " Error[" << pStatus << "] cannot access cap '" << MATERIALS_CAPABILITY_NAME - << "'\n with url '" << mCapabilityURL << "' because " << pReason + << mMethod << " Error[" << status.toULong() << "] cannot access cap '" << MATERIALS_CAPABILITY_NAME + << "'\n with url '" << response->getRequestURL() << "' because " << status.toString() << "\n--------------------------------------------------------------------------" << LL_ENDL; @@ -116,12 +114,35 @@ void LLMaterialsResponder::httpFailure() mCallback(false, emptyResult); } + + /** * LLMaterialMgr class */ - -LLMaterialMgr::LLMaterialMgr() +LLMaterialMgr::LLMaterialMgr(): + mGetQueue(), + mGetPending(), + mGetCallbacks(), + mGetTECallbacks(), + mGetAllQueue(), + mGetAllRequested(), + mGetAllPending(), + mGetAllCallbacks(), + mPutQueue(), + mMaterials(), + mHttpRequest(), + mHttpHeaders(), + mHttpOptions(), + mHttpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID), + mHttpPriority(0) { + LLAppCoreHttp & app_core_http(LLAppViewer::instance()->getAppCoreHttp()); + + mHttpRequest = LLCore::HttpRequest::ptr_t(new LLCore::HttpRequest()); + mHttpHeaders = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders()); + mHttpOptions = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()); + mHttpPolicy = app_core_http.getPolicy(LLAppCoreHttp::AP_MATERIALS); + mMaterials.insert(std::pair<LLMaterialID, LLMaterialPtr>(LLMaterialID::null, LLMaterialPtr(NULL))); gIdleCallbacks.addFunction(&LLMaterialMgr::onIdle, NULL); LLWorld::instance().setRegionRemovedCallback(boost::bind(&LLMaterialMgr::onRegionRemoved, this, _1)); @@ -554,6 +575,8 @@ void LLMaterialMgr::onIdle(void*) { instancep->processPutQueue(); } + + instancep->mHttpRequest->update(0L); } void LLMaterialMgr::processGetQueue() @@ -629,10 +652,26 @@ void LLMaterialMgr::processGetQueue() LLSD postData = LLSD::emptyMap(); postData[MATERIALS_CAP_ZIP_FIELD] = materialBinary; - LLHTTPClient::ResponderPtr materialsResponder = new LLMaterialsResponder("POST", capURL, boost::bind(&LLMaterialMgr::onGetResponse, this, _1, _2, region_id)); - LL_DEBUGS("Materials") << "POSTing to region '" << regionp->getName() << "' at '"<< capURL << " for " << materialsData.size() << " materials." + LLMaterialHttpHandler * handler = + new LLMaterialHttpHandler("POST", + boost::bind(&LLMaterialMgr::onGetResponse, this, _1, _2, region_id) + ); + + LL_DEBUGS("Materials") << "POSTing to region '" << regionp->getName() << "' at '" << capURL << " for " << materialsData.size() << " materials." << "\ndata: " << ll_pretty_print_sd(materialsData) << LL_ENDL; - LLHTTPClient::post(capURL, postData, materialsResponder); + + LLCore::HttpHandle handle = LLCoreHttpUtil::requestPostWithLLSD(mHttpRequest, + mHttpPolicy, mHttpPriority, capURL, + postData, mHttpOptions, mHttpHeaders, handler); + + if (handle == LLCORE_HTTP_HANDLE_INVALID) + { + delete handler; + LLCore::HttpStatus status = mHttpRequest->getStatus(); + LL_ERRS("Meterials") << "Failed to execute material POST. Status = " << + status.toULong() << "\"" << status.toString() << "\"" << LL_ENDL; + } + regionp->resetMaterialsCapThrottle(); } } @@ -667,8 +706,22 @@ void LLMaterialMgr::processGetAllQueue() } LL_DEBUGS("Materials") << "GET all for region " << region_id << "url " << capURL << LL_ENDL; - LLHTTPClient::ResponderPtr materialsResponder = new LLMaterialsResponder("GET", capURL, boost::bind(&LLMaterialMgr::onGetAllResponse, this, _1, _2, *itRegion)); - LLHTTPClient::get(capURL, materialsResponder); + LLMaterialHttpHandler *handler = + new LLMaterialHttpHandler("GET", + boost::bind(&LLMaterialMgr::onGetAllResponse, this, _1, _2, *itRegion) + ); + + LLCore::HttpHandle handle = mHttpRequest->requestGet(mHttpPolicy, mHttpPriority, capURL, + mHttpOptions, mHttpHeaders, handler); + + if (handle == LLCORE_HTTP_HANDLE_INVALID) + { + delete handler; + LLCore::HttpStatus status = mHttpRequest->getStatus(); + LL_ERRS("Meterials") << "Failed to execute material GET. Status = " << + status.toULong() << "\"" << status.toString() << "\"" << LL_ENDL; + } + regionp->resetMaterialsCapThrottle(); mGetAllPending.insert(std::pair<LLUUID, F64>(region_id, LLFrameTimer::getTotalSeconds())); mGetAllQueue.erase(itRegion); // Invalidates region_id @@ -755,8 +808,24 @@ void LLMaterialMgr::processPutQueue() putData[MATERIALS_CAP_ZIP_FIELD] = materialBinary; LL_DEBUGS("Materials") << "put for " << itRequest->second.size() << " faces to region " << itRequest->first->getName() << LL_ENDL; - LLHTTPClient::ResponderPtr materialsResponder = new LLMaterialsResponder("PUT", capURL, boost::bind(&LLMaterialMgr::onPutResponse, this, _1, _2)); - LLHTTPClient::put(capURL, putData, materialsResponder); + + LLMaterialHttpHandler * handler = + new LLMaterialHttpHandler("PUT", + boost::bind(&LLMaterialMgr::onPutResponse, this, _1, _2) + ); + + LLCore::HttpHandle handle = LLCoreHttpUtil::requestPutWithLLSD( + mHttpRequest, mHttpPolicy, mHttpPriority, capURL, + putData, mHttpOptions, mHttpHeaders, handler); + + if (handle == LLCORE_HTTP_HANDLE_INVALID) + { + delete handler; + LLCore::HttpStatus status = mHttpRequest->getStatus(); + LL_ERRS("Meterials") << "Failed to execute material PUT. Status = " << + status.toULong() << "\"" << status.toString() << "\"" << LL_ENDL; + } + regionp->resetMaterialsCapThrottle(); } else diff --git a/indra/newview/llmaterialmgr.h b/indra/newview/llmaterialmgr.h index e83f1f4e01..ef202d24ba 100644 --- a/indra/newview/llmaterialmgr.h +++ b/indra/newview/llmaterialmgr.h @@ -30,6 +30,9 @@ #include "llmaterial.h" #include "llmaterialid.h" #include "llsingleton.h" +#include "httprequest.h" +#include "httpheaders.h" +#include "httpoptions.h" class LLViewerRegion; @@ -56,7 +59,7 @@ public: void put(const LLUUID& object_id, const U8 te, const LLMaterial& material); void remove(const LLUUID& object_id, const U8 te); -protected: +private: void clearGetQueues(const LLUUID& region_id); bool isGetPending(const LLUUID& region_id, const LLMaterialID& material_id) const; bool isGetAllPending(const LLUUID& region_id) const; @@ -72,16 +75,7 @@ protected: void onPutResponse(bool success, const LLSD& content); void onRegionRemoved(LLViewerRegion* regionp); -protected: - typedef std::set<LLMaterialID> material_queue_t; - typedef std::map<LLUUID, material_queue_t> get_queue_t; - get_queue_t mGetQueue; - typedef std::pair<const LLUUID, LLMaterialID> pending_material_t; - typedef std::map<const pending_material_t, F64> get_pending_map_t; - get_pending_map_t mGetPending; - typedef std::map<LLMaterialID, get_callback_t*> get_callback_map_t; - get_callback_map_t mGetCallbacks; - +private: // struct for TE-specific material ID query class TEMaterialPair { @@ -108,22 +102,37 @@ protected: bool operator()(const TEMaterialPair& left, const TEMaterialPair& right) const { return left < right; } }; - typedef boost::unordered_map<TEMaterialPair, get_callback_te_t*, TEMaterialPairHasher> get_callback_te_map_t; - get_callback_te_map_t mGetTECallbacks; + typedef std::set<LLMaterialID> material_queue_t; + typedef std::map<LLUUID, material_queue_t> get_queue_t; + typedef std::pair<const LLUUID, LLMaterialID> pending_material_t; + typedef std::map<const pending_material_t, F64> get_pending_map_t; + typedef std::map<LLMaterialID, get_callback_t*> get_callback_map_t; + + typedef boost::unordered_map<TEMaterialPair, get_callback_te_t*, TEMaterialPairHasher> get_callback_te_map_t; typedef std::set<LLUUID> getall_queue_t; - getall_queue_t mGetAllQueue; - getall_queue_t mGetAllRequested; typedef std::map<LLUUID, F64> getall_pending_map_t; - getall_pending_map_t mGetAllPending; typedef std::map<LLUUID, getall_callback_t*> getall_callback_map_t; - getall_callback_map_t mGetAllCallbacks; - typedef std::map<U8, LLMaterial> facematerial_map_t; typedef std::map<LLUUID, facematerial_map_t> put_queue_t; - put_queue_t mPutQueue; - material_map_t mMaterials; + get_queue_t mGetQueue; + get_pending_map_t mGetPending; + get_callback_map_t mGetCallbacks; + + get_callback_te_map_t mGetTECallbacks; + getall_queue_t mGetAllQueue; + getall_queue_t mGetAllRequested; + getall_pending_map_t mGetAllPending; + getall_callback_map_t mGetAllCallbacks; + put_queue_t mPutQueue; + material_map_t mMaterials; + + LLCore::HttpRequest::ptr_t mHttpRequest; + LLCore::HttpHeaders::ptr_t mHttpHeaders; + LLCore::HttpOptions::ptr_t mHttpOptions; + LLCore::HttpRequest::policy_t mHttpPolicy; + LLCore::HttpRequest::priority_t mHttpPriority; U32 getMaxEntries(const LLViewerRegion* regionp); }; diff --git a/indra/newview/llmediadataclient.cpp b/indra/newview/llmediadataclient.cpp index 2fb9e60b29..b8ff76aa6d 100755 --- a/indra/newview/llmediadataclient.cpp +++ b/indra/newview/llmediadataclient.cpp @@ -33,6 +33,7 @@ #pragma warning (disable:4702) #endif +#include <algorithm> #include <boost/lexical_cast.hpp> #include "llhttpconstants.h" @@ -40,6 +41,7 @@ #include "llmediaentry.h" #include "lltextureentry.h" #include "llviewerregion.h" +#include "llcorehttputil.h" // // When making a request @@ -91,52 +93,74 @@ const U32 LLMediaDataClient::MAX_ROUND_ROBIN_QUEUE_SIZE = 10000; std::ostream& operator<<(std::ostream &s, const LLMediaDataClient::request_queue_t &q); std::ostream& operator<<(std::ostream &s, const LLMediaDataClient::Request &q); -template <typename T> -typename T::iterator find_matching_request(T &c, const LLMediaDataClient::Request *request, LLMediaDataClient::Request::Type match_type) + +//========================================================================= +/// Uniary Predicate for matching requests in collections by either the request +/// or by UUID +/// +class PredicateMatchRequest { - for(typename T::iterator iter = c.begin(); iter != c.end(); ++iter) - { - if(request->isMatch(*iter, match_type)) - { - return iter; - } - } - - return c.end(); +public: + PredicateMatchRequest(const LLMediaDataClient::Request::ptr_t &request, LLMediaDataClient::Request::Type matchType = LLMediaDataClient::Request::ANY); + PredicateMatchRequest(const LLUUID &id, LLMediaDataClient::Request::Type matchType = LLMediaDataClient::Request::ANY); + + PredicateMatchRequest(const PredicateMatchRequest &other); + + bool operator()(const LLMediaDataClient::Request::ptr_t &test) const; + +private: + LLMediaDataClient::Request::ptr_t mRequest; + LLMediaDataClient::Request::Type mMatchType; + LLUUID mId; +}; + + +PredicateMatchRequest::PredicateMatchRequest(const LLMediaDataClient::Request::ptr_t &request, LLMediaDataClient::Request::Type matchType) : + mRequest(request), + mMatchType(matchType), + mId() +{} + +PredicateMatchRequest::PredicateMatchRequest(const LLUUID &id, LLMediaDataClient::Request::Type matchType) : + mRequest(), + mMatchType(matchType), + mId(id) +{} + +PredicateMatchRequest::PredicateMatchRequest(const PredicateMatchRequest &other) +{ + mRequest = other.mRequest; + mMatchType = other.mMatchType; + mId = other.mId; } -template <typename T> -typename T::iterator find_matching_request(T &c, const LLUUID &id, LLMediaDataClient::Request::Type match_type) +bool PredicateMatchRequest::operator()(const LLMediaDataClient::Request::ptr_t &test) const { - for(typename T::iterator iter = c.begin(); iter != c.end(); ++iter) - { - if(((*iter)->getID() == id) && ((match_type == LLMediaDataClient::Request::ANY) || (match_type == (*iter)->getType()))) - { - return iter; - } - } - - return c.end(); + if (mRequest) + return (mRequest->isMatch(test, mMatchType)); + else if (!mId.isNull()) + return ((test->getID() == mId) && ((mMatchType == LLMediaDataClient::Request::ANY) || (mMatchType == test->getType()))); + return false; } -// NOTE: remove_matching_requests will not work correctly for containers where deleting an element may invalidate iterators -// to other elements in the container (such as std::vector). -// If the implementation is changed to use a container with this property, this will need to be revisited. +//========================================================================= +/// template <typename T> -void remove_matching_requests(T &c, const LLUUID &id, LLMediaDataClient::Request::Type match_type) +void mark_dead_and_remove_if(T &c, const PredicateMatchRequest &matchPred) { - for(typename T::iterator iter = c.begin(); iter != c.end();) - { - typename T::value_type i = *iter; - typename T::iterator next = iter; - next++; - if((i->getID() == id) && ((match_type == LLMediaDataClient::Request::ANY) || (match_type == i->getType()))) - { - i->markDead(); - c.erase(iter); - } - iter = next; - } + for (typename T::iterator it = c.begin(); it != c.end();) + { + if (matchPred(*it)) + { + (*it)->markDead(); + // *TDOO: When C++11 is in change the following line to: it = c.erase(it); + c.erase(it++); + } + else + { + ++it; + } + } } ////////////////////////////////////////////////////////////////////////////////////// @@ -145,18 +169,20 @@ void remove_matching_requests(T &c, const LLUUID &id, LLMediaDataClient::Request // ////////////////////////////////////////////////////////////////////////////////////// -LLMediaDataClient::LLMediaDataClient(F32 queue_timer_delay, - F32 retry_timer_delay, - U32 max_retries, - U32 max_sorted_queue_size, - U32 max_round_robin_queue_size) - : mQueueTimerDelay(queue_timer_delay), - mRetryTimerDelay(retry_timer_delay), - mMaxNumRetries(max_retries), - mMaxSortedQueueSize(max_sorted_queue_size), - mMaxRoundRobinQueueSize(max_round_robin_queue_size), - mQueueTimerIsRunning(false) +LLMediaDataClient::LLMediaDataClient(F32 queue_timer_delay, F32 retry_timer_delay, + U32 max_retries, U32 max_sorted_queue_size, U32 max_round_robin_queue_size): + mQueueTimerDelay(queue_timer_delay), + mRetryTimerDelay(retry_timer_delay), + mMaxNumRetries(max_retries), + mMaxSortedQueueSize(max_sorted_queue_size), + mMaxRoundRobinQueueSize(max_round_robin_queue_size), + mQueueTimerIsRunning(false), + mHttpRequest(new LLCore::HttpRequest()), + mHttpHeaders(new LLCore::HttpHeaders()), + mHttpOpts(new LLCore::HttpOptions()), + mHttpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID) { + // *TODO: Look up real Policy ID } LLMediaDataClient::~LLMediaDataClient() @@ -171,20 +197,23 @@ bool LLMediaDataClient::isEmpty() const bool LLMediaDataClient::isInQueue(const LLMediaDataClientObject::ptr_t &object) { - if(find_matching_request(mQueue, object->getID(), LLMediaDataClient::Request::ANY) != mQueue.end()) - return true; - - if(find_matching_request(mUnQueuedRequests, object->getID(), LLMediaDataClient::Request::ANY) != mUnQueuedRequests.end()) - return true; - + PredicateMatchRequest upred(object->getID()); + + if (std::find_if(mQueue.begin(), mQueue.end(), upred) != mQueue.end()) + return true; + if (std::find_if(mUnQueuedRequests.begin(), mUnQueuedRequests.end(), upred) != mUnQueuedRequests.end()) + return true; + return false; } void LLMediaDataClient::removeFromQueue(const LLMediaDataClientObject::ptr_t &object) { LL_DEBUGS("LLMediaDataClient") << "removing requests matching ID " << object->getID() << LL_ENDL; - remove_matching_requests(mQueue, object->getID(), LLMediaDataClient::Request::ANY); - remove_matching_requests(mUnQueuedRequests, object->getID(), LLMediaDataClient::Request::ANY); + PredicateMatchRequest upred(object->getID()); + + mark_dead_and_remove_if(mQueue, upred); + mark_dead_and_remove_if(mUnQueuedRequests, upred); } void LLMediaDataClient::startQueueTimer() @@ -207,23 +236,24 @@ void LLMediaDataClient::stopQueueTimer() bool LLMediaDataClient::processQueueTimer() { - if(isEmpty()) + if (isDoneProcessing()) return true; LL_DEBUGS("LLMediaDataClient") << "QueueTimer::tick() started, queue size is: " << mQueue.size() << LL_ENDL; LL_DEBUGS("LLMediaDataClientQueue") << "QueueTimer::tick() started, SORTED queue is: " << mQueue << LL_ENDL; serviceQueue(); - + serviceHttp(); + LL_DEBUGS("LLMediaDataClient") << "QueueTimer::tick() finished, queue size is: " << mQueue.size() << LL_ENDL; LL_DEBUGS("LLMediaDataClientQueue") << "QueueTimer::tick() finished, SORTED queue is: " << mQueue << LL_ENDL; - return isEmpty(); + return isDoneProcessing(); } -LLMediaDataClient::request_ptr_t LLMediaDataClient::dequeue() +LLMediaDataClient::Request::ptr_t LLMediaDataClient::dequeue() { - request_ptr_t request; + Request::ptr_t request; request_queue_t *queue_p = getQueue(); if (queue_p->empty()) @@ -242,20 +272,20 @@ LLMediaDataClient::request_ptr_t LLMediaDataClient::dequeue() else { // Don't return this request -- it's not ready to be serviced. - request = NULL; + request.reset(); } } return request; } -void LLMediaDataClient::pushBack(request_ptr_t request) +void LLMediaDataClient::pushBack(Request::ptr_t request) { request_queue_t *queue_p = getQueue(); queue_p->push_front(request); } -void LLMediaDataClient::trackRequest(request_ptr_t request) +void LLMediaDataClient::trackRequest(Request::ptr_t request) { request_set_t::iterator iter = mUnQueuedRequests.find(request); @@ -269,7 +299,7 @@ void LLMediaDataClient::trackRequest(request_ptr_t request) } } -void LLMediaDataClient::stopTrackingRequest(request_ptr_t request) +void LLMediaDataClient::stopTrackingRequest(Request::ptr_t request) { request_set_t::iterator iter = mUnQueuedRequests.find(request); @@ -283,16 +313,22 @@ void LLMediaDataClient::stopTrackingRequest(request_ptr_t request) } } +bool LLMediaDataClient::isDoneProcessing() const +{ + return (isEmpty() && mUnQueuedRequests.empty()); +} + + void LLMediaDataClient::serviceQueue() { // Peel one off of the items from the queue and execute it - request_ptr_t request; + Request::ptr_t request; do { request = dequeue(); - if(request.isNull()) + if(!request) { // Queue is empty. return; @@ -317,7 +353,18 @@ void LLMediaDataClient::serviceQueue() trackRequest(request); // and make the post - LLHTTPClient::post(url, sd_payload, request->createResponder()); + LLHttpSDHandler *handler = request->createHandler(); + LLCore::HttpHandle handle = LLCoreHttpUtil::requestPostWithLLSD(mHttpRequest, mHttpPolicy, 0, + url, sd_payload, mHttpOpts, mHttpHeaders, handler); + + if (handle == LLCORE_HTTP_HANDLE_INVALID) + { + // *TODO: Change this metaphore to use boost::shared_ptr<> for handlers. Requires change in LLCore::HTTP + delete handler; + LLCore::HttpStatus status = mHttpRequest->getStatus(); + LL_WARNS("LLMediaDataClient") << "'" << url << "' request POST failed. Reason " + << status.toTerseString() << " \"" << status.toString() << "\"" << LL_ENDL; + } } else { @@ -332,13 +379,17 @@ void LLMediaDataClient::serviceQueue() } else { - // This request has exceeded its maxumim retry count. It will be dropped. + // This request has exceeded its maximum retry count. It will be dropped. LL_WARNS("LLMediaDataClient") << "Could not send request " << *request << " for " << mMaxNumRetries << " tries, dropping request." << LL_ENDL; } } } +void LLMediaDataClient::serviceHttp() +{ + mHttpRequest->update(0); +} // dump the queue std::ostream& operator<<(std::ostream &s, const LLMediaDataClient::request_queue_t &q) @@ -395,7 +446,7 @@ BOOL LLMediaDataClient::QueueTimer::tick() // ////////////////////////////////////////////////////////////////////////////////////// -LLMediaDataClient::RetryTimer::RetryTimer(F32 time, request_ptr_t request) +LLMediaDataClient::RetryTimer::RetryTimer(F32 time, Request::ptr_t request) : LLEventTimer(time), mRequest(request) { mRequest->startTracking(); @@ -417,7 +468,7 @@ BOOL LLMediaDataClient::RetryTimer::tick() } // Release the ref to the request. - mRequest = NULL; + mRequest.reset(); // Don't fire again return TRUE; @@ -490,7 +541,7 @@ void LLMediaDataClient::Request::reEnqueue() { if(mMDC) { - mMDC->enqueue(this); + mMDC->enqueue(shared_from_this()); } } @@ -533,13 +584,13 @@ bool LLMediaDataClient::Request::isDead() void LLMediaDataClient::Request::startTracking() { if(mMDC) - mMDC->trackRequest(this); + mMDC->trackRequest(shared_from_this()); } void LLMediaDataClient::Request::stopTracking() { if(mMDC) - mMDC->stopTrackingRequest(this); + mMDC->stopTrackingRequest(shared_from_this()); } std::ostream& operator<<(std::ostream &s, const LLMediaDataClient::Request &r) @@ -551,79 +602,67 @@ std::ostream& operator<<(std::ostream &s, const LLMediaDataClient::Request &r) << " #retries=" << r.getRetryCount(); return s; } - -////////////////////////////////////////////////////////////////////////////////////// -// -// LLMediaDataClient::Responder -// -////////////////////////////////////////////////////////////////////////////////////// -LLMediaDataClient::Responder::Responder(const request_ptr_t &request) -: mRequest(request) +//======================================================================== + +LLMediaDataClient::Handler::Handler(const Request::ptr_t &request): + mRequest(request) { } -/*virtual*/ -void LLMediaDataClient::Responder::httpFailure() + +void LLMediaDataClient::Handler::onSuccess(LLCore::HttpResponse * response, const LLSD &content) { - mRequest->stopTracking(); + mRequest->stopTracking(); - if(mRequest->isDead()) - { - LL_WARNS("LLMediaDataClient") << "dead request " << *mRequest << LL_ENDL; - return; - } - - if (getStatus() == HTTP_SERVICE_UNAVAILABLE) - { - F32 retry_timeout; -#if 0 - // *TODO: Honor server Retry-After header. - if (!hasResponseHeader(HTTP_IN_HEADER_RETRY_AFTER) - || !getSecondsUntilRetryAfter(getResponseHeader(HTTP_IN_HEADER_RETRY_AFTER), retry_timeout)) -#endif - { - retry_timeout = mRequest->getRetryTimerDelay(); - } - - mRequest->incRetryCount(); - - if (mRequest->getRetryCount() < mRequest->getMaxNumRetries()) - { - LL_INFOS("LLMediaDataClient") << *mRequest << " got SERVICE_UNAVAILABLE...retrying in " << retry_timeout << " seconds" << LL_ENDL; - - // Start timer (instances are automagically tracked by - // InstanceTracker<> and LLEventTimer) - new RetryTimer(F32(retry_timeout/*secs*/), mRequest); - } - else - { - LL_INFOS("LLMediaDataClient") << *mRequest << " got SERVICE_UNAVAILABLE...retry count " - << mRequest->getRetryCount() << " exceeds " << mRequest->getMaxNumRetries() << ", not retrying" << LL_ENDL; - } - } - // *TODO: Redirect on 3xx status codes. - else - { - LL_WARNS("LLMediaDataClient") << *mRequest << " http failure " - << dumpResponse() << LL_ENDL; - } + if (mRequest->isDead()) + { + LL_WARNS("LLMediaDataClient") << "dead request " << *mRequest << LL_ENDL; + return; + } + + LL_DEBUGS("LLMediaDataClientResponse") << *mRequest << LL_ENDL; } -/*virtual*/ -void LLMediaDataClient::Responder::httpSuccess() +void LLMediaDataClient::Handler::onFailure(LLCore::HttpResponse * response, LLCore::HttpStatus status) { - mRequest->stopTracking(); + mRequest->stopTracking(); - if(mRequest->isDead()) - { - LL_WARNS("LLMediaDataClient") << "dead request " << *mRequest << LL_ENDL; - return; - } + if (status == LLCore::HttpStatus(HTTP_SERVICE_UNAVAILABLE)) + { + F32 retry_timeout; +#if 0 + // *TODO: Honor server Retry-After header. + if (!hasResponseHeader(HTTP_IN_HEADER_RETRY_AFTER) + || !getSecondsUntilRetryAfter(getResponseHeader(HTTP_IN_HEADER_RETRY_AFTER), retry_timeout)) +#endif + { + retry_timeout = mRequest->getRetryTimerDelay(); + } + + mRequest->incRetryCount(); - LL_DEBUGS("LLMediaDataClientResponse") << *mRequest << " " << dumpResponse() << LL_ENDL; + if (mRequest->getRetryCount() < mRequest->getMaxNumRetries()) + { + LL_INFOS("LLMediaDataClient") << *mRequest << " got SERVICE_UNAVAILABLE...retrying in " << retry_timeout << " seconds" << LL_ENDL; + + // Start timer (instances are automagically tracked by + // InstanceTracker<> and LLEventTimer) + new RetryTimer(F32(retry_timeout/*secs*/), mRequest); + } + else + { + LL_INFOS("LLMediaDataClient") << *mRequest << " got SERVICE_UNAVAILABLE...retry count " + << mRequest->getRetryCount() << " exceeds " << mRequest->getMaxNumRetries() << ", not retrying" << LL_ENDL; + } + } + else + { + LL_WARNS("LLMediaDataClient") << *mRequest << " HTTP failure " << LL_ENDL; + } } + ////////////////////////////////////////////////////////////////////////////////////// // // LLObjectMediaDataClient @@ -634,7 +673,7 @@ void LLMediaDataClient::Responder::httpSuccess() void LLObjectMediaDataClient::fetchMedia(LLMediaDataClientObject *object) { // Create a get request and put it in the queue. - enqueue(new RequestGet(object, this)); + enqueue(Request::ptr_t(new RequestGet(object, this))); } const char *LLObjectMediaDataClient::getCapabilityName() const @@ -678,14 +717,14 @@ void LLObjectMediaDataClient::sortQueue() } // static -bool LLObjectMediaDataClient::compareRequestScores(const request_ptr_t &o1, const request_ptr_t &o2) +bool LLObjectMediaDataClient::compareRequestScores(const Request::ptr_t &o1, const Request::ptr_t &o2) { - if (o2.isNull()) return true; - if (o1.isNull()) return false; + if (!o2) return true; + if (!o1) return false; return ( o1->getScore() > o2->getScore() ); } -void LLObjectMediaDataClient::enqueue(Request *request) +void LLObjectMediaDataClient::enqueue(Request::ptr_t request) { if(request->isDead()) { @@ -703,9 +742,10 @@ void LLObjectMediaDataClient::enqueue(Request *request) { // For GET requests that are not new, if a matching request is already in the round robin queue, // in flight, or being retried, leave it at its current position. - request_queue_t::iterator iter = find_matching_request(mRoundRobinQueue, request->getID(), Request::GET); - request_set_t::iterator iter2 = find_matching_request(mUnQueuedRequests, request->getID(), Request::GET); - + PredicateMatchRequest upred(request->getID(), Request::GET); + request_queue_t::iterator iter = std::find_if(mRoundRobinQueue.begin(), mRoundRobinQueue.end(), upred); + request_set_t::iterator iter2 = std::find_if(mUnQueuedRequests.begin(), mUnQueuedRequests.end(), upred); + if( (iter != mRoundRobinQueue.end()) || (iter2 != mUnQueuedRequests.end()) ) { LL_DEBUGS("LLMediaDataClient") << "ALREADY THERE: NOT Queuing request for " << *request << LL_ENDL; @@ -718,9 +758,11 @@ void LLObjectMediaDataClient::enqueue(Request *request) // IF the update will cause an object update message to be sent out at some point in the future, it probably should. // Remove any existing requests of this type for this object - remove_matching_requests(mQueue, request->getID(), request->getType()); - remove_matching_requests(mRoundRobinQueue, request->getID(), request->getType()); - remove_matching_requests(mUnQueuedRequests, request->getID(), request->getType()); + PredicateMatchRequest upred(request->getID(), request->getType()); + + mark_dead_and_remove_if(mQueue, upred); + mark_dead_and_remove_if(mRoundRobinQueue, upred); + mark_dead_and_remove_if(mUnQueuedRequests, upred); if (is_new) { @@ -749,7 +791,7 @@ void LLObjectMediaDataClient::enqueue(Request *request) startQueueTimer(); } -bool LLObjectMediaDataClient::canServiceRequest(request_ptr_t request) +bool LLObjectMediaDataClient::canServiceRequest(Request::ptr_t request) { if(mCurrentQueueIsTheSortedQueue) { @@ -785,9 +827,9 @@ bool LLObjectMediaDataClient::isInQueue(const LLMediaDataClientObject::ptr_t &ob if(LLMediaDataClient::isInQueue(object)) return true; - if(find_matching_request(mRoundRobinQueue, object->getID(), LLMediaDataClient::Request::ANY) != mRoundRobinQueue.end()) - return true; - + if (std::find_if(mRoundRobinQueue.begin(), mRoundRobinQueue.end(), PredicateMatchRequest(object->getID())) != mRoundRobinQueue.end()) + return true; + return false; } @@ -796,12 +838,12 @@ void LLObjectMediaDataClient::removeFromQueue(const LLMediaDataClientObject::ptr // First, call parent impl. LLMediaDataClient::removeFromQueue(object); - remove_matching_requests(mRoundRobinQueue, object->getID(), LLMediaDataClient::Request::ANY); + mark_dead_and_remove_if(mRoundRobinQueue, PredicateMatchRequest(object->getID())); } bool LLObjectMediaDataClient::processQueueTimer() { - if(isEmpty()) + if (isDoneProcessing()) return true; LL_DEBUGS("LLMediaDataClient") << "started, SORTED queue size is: " << mQueue.size() @@ -816,6 +858,7 @@ bool LLObjectMediaDataClient::processQueueTimer() LL_DEBUGS("LLMediaDataClientQueue") << "after sort, SORTED queue is: " << mQueue << LL_ENDL; serviceQueue(); + serviceHttp(); swapCurrentQueue(); @@ -824,7 +867,7 @@ bool LLObjectMediaDataClient::processQueueTimer() LL_DEBUGS("LLMediaDataClientQueue") << " SORTED queue is: " << mQueue << LL_ENDL; LL_DEBUGS("LLMediaDataClientQueue") << " RR queue is: " << mRoundRobinQueue << LL_ENDL; - return isEmpty(); + return isDoneProcessing(); } LLObjectMediaDataClient::RequestGet::RequestGet(LLMediaDataClientObject *obj, LLMediaDataClient *mdc): @@ -841,16 +884,16 @@ LLSD LLObjectMediaDataClient::RequestGet::getPayload() const return result; } -LLMediaDataClient::Responder *LLObjectMediaDataClient::RequestGet::createResponder() +LLHttpSDHandler *LLObjectMediaDataClient::RequestGet::createHandler() { - return new LLObjectMediaDataClient::Responder(this); + return new LLObjectMediaDataClient::Handler(shared_from_this()); } void LLObjectMediaDataClient::updateMedia(LLMediaDataClientObject *object) { // Create an update request and put it in the queue. - enqueue(new RequestUpdate(object, this)); + enqueue(Request::ptr_t(new RequestUpdate(object, this))); } LLObjectMediaDataClient::RequestUpdate::RequestUpdate(LLMediaDataClientObject *obj, LLMediaDataClient *mdc): @@ -877,60 +920,58 @@ LLSD LLObjectMediaDataClient::RequestUpdate::getPayload() const return result; } -LLMediaDataClient::Responder *LLObjectMediaDataClient::RequestUpdate::createResponder() +LLHttpSDHandler *LLObjectMediaDataClient::RequestUpdate::createHandler() { // This just uses the base class's responder. - return new LLMediaDataClient::Responder(this); + return new LLMediaDataClient::Handler(shared_from_this()); } - -/*virtual*/ -void LLObjectMediaDataClient::Responder::httpSuccess() +void LLObjectMediaDataClient::Handler::onSuccess(LLCore::HttpResponse * response, const LLSD &content) { - getRequest()->stopTracking(); - - if(getRequest()->isDead()) - { - LL_WARNS("LLMediaDataClient") << "dead request " << *(getRequest()) << LL_ENDL; - return; - } - - const LLSD& content = getContent(); - if (!content.isMap()) - { - failureResult(HTTP_INTERNAL_ERROR, "Malformed response contents", content); - return; - } + LLMediaDataClient::Handler::onSuccess(response, content); + + if (getRequest()->isDead()) + { // warning emitted from base method. + return; + } + + if (!content.isMap()) + { + onFailure(response, LLCore::HttpStatus(HTTP_INTERNAL_ERROR, "Malformed response contents")); + return; + } + + // This responder is only used for GET requests, not UPDATE. + LL_DEBUGS("LLMediaDataClientResponse") << *(getRequest()) << " " << LL_ENDL; + + // Look for an error + if (content.has("error")) + { + const LLSD &error = content["error"]; + LL_WARNS("LLMediaDataClient") << *(getRequest()) << " Error getting media data for object: code=" << + error["code"].asString() << ": " << error["message"].asString() << LL_ENDL; + + // XXX Warn user? + } + else + { + // Check the data + const LLUUID &object_id = content[LLTextureEntry::OBJECT_ID_KEY]; + if (object_id != getRequest()->getObject()->getID()) + { + // NOT good, wrong object id!! + LL_WARNS("LLMediaDataClient") << *(getRequest()) << " DROPPING response with wrong object id (" << object_id << ")" << LL_ENDL; + return; + } + + // Otherwise, update with object media data + getRequest()->getObject()->updateObjectMediaData(content[LLTextureEntry::OBJECT_MEDIA_DATA_KEY], + content[LLTextureEntry::MEDIA_VERSION_KEY]); + } - // This responder is only used for GET requests, not UPDATE. - LL_DEBUGS("LLMediaDataClientResponse") << *(getRequest()) << " " << dumpResponse() << LL_ENDL; - - // Look for an error - if (content.has("error")) - { - const LLSD &error = content["error"]; - LL_WARNS("LLMediaDataClient") << *(getRequest()) << " Error getting media data for object: code=" << - error["code"].asString() << ": " << error["message"].asString() << LL_ENDL; - - // XXX Warn user? - } - else - { - // Check the data - const LLUUID &object_id = content[LLTextureEntry::OBJECT_ID_KEY]; - if (object_id != getRequest()->getObject()->getID()) - { - // NOT good, wrong object id!! - LL_WARNS("LLMediaDataClient") << *(getRequest()) << " DROPPING response with wrong object id (" << object_id << ")" << LL_ENDL; - return; - } - - // Otherwise, update with object media data - getRequest()->getObject()->updateObjectMediaData(content[LLTextureEntry::OBJECT_MEDIA_DATA_KEY], - content[LLTextureEntry::MEDIA_VERSION_KEY]); - } } + ////////////////////////////////////////////////////////////////////////////////////// // // LLObjectMediaNavigateClient @@ -943,16 +984,18 @@ const char *LLObjectMediaNavigateClient::getCapabilityName() const return "ObjectMediaNavigate"; } -void LLObjectMediaNavigateClient::enqueue(Request *request) +void LLObjectMediaNavigateClient::enqueue(Request::ptr_t request) { if(request->isDead()) { - LL_DEBUGS("LLMediaDataClient") << "not queueing dead request " << *request << LL_ENDL; + LL_DEBUGS("LLMediaDataClient") << "not queuing dead request " << *request << LL_ENDL; return; } + PredicateMatchRequest upred(request); + // If there's already a matching request in the queue, remove it. - request_queue_t::iterator iter = find_matching_request(mQueue, request, LLMediaDataClient::Request::ANY); + request_queue_t::iterator iter = std::find_if(mQueue.begin(), mQueue.end(), upred); if(iter != mQueue.end()) { LL_DEBUGS("LLMediaDataClient") << "removing matching queued request " << (**iter) << LL_ENDL; @@ -960,7 +1003,7 @@ void LLObjectMediaNavigateClient::enqueue(Request *request) } else { - request_set_t::iterator set_iter = find_matching_request(mUnQueuedRequests, request, LLMediaDataClient::Request::ANY); + request_set_t::iterator set_iter = std::find_if(mUnQueuedRequests.begin(), mUnQueuedRequests.end(), upred); if(set_iter != mUnQueuedRequests.end()) { LL_DEBUGS("LLMediaDataClient") << "removing matching unqueued request " << (**set_iter) << LL_ENDL; @@ -979,7 +1022,7 @@ void LLObjectMediaNavigateClient::enqueue(Request *request) else #endif { - LL_DEBUGS("LLMediaDataClient") << "queueing new request " << (*request) << LL_ENDL; + LL_DEBUGS("LLMediaDataClient") << "queuing new request " << (*request) << LL_ENDL; mQueue.push_back(request); // Start the timer if not already running @@ -993,7 +1036,7 @@ void LLObjectMediaNavigateClient::navigate(LLMediaDataClientObject *object, U8 t // LL_INFOS("LLMediaDataClient") << "navigate() initiated: " << ll_print_sd(sd_payload) << LL_ENDL; // Create a get request and put it in the queue. - enqueue(new RequestNavigate(object, this, texture_index, url)); + enqueue(Request::ptr_t(new RequestNavigate(object, this, texture_index, url))); } LLObjectMediaNavigateClient::RequestNavigate::RequestNavigate(LLMediaDataClientObject *obj, LLMediaDataClient *mdc, U8 texture_index, const std::string &url): @@ -1012,75 +1055,67 @@ LLSD LLObjectMediaNavigateClient::RequestNavigate::getPayload() const return result; } -LLMediaDataClient::Responder *LLObjectMediaNavigateClient::RequestNavigate::createResponder() +LLHttpSDHandler *LLObjectMediaNavigateClient::RequestNavigate::createHandler() { - return new LLObjectMediaNavigateClient::Responder(this); + return new LLObjectMediaNavigateClient::Handler(shared_from_this()); } -/*virtual*/ -void LLObjectMediaNavigateClient::Responder::httpFailure() +void LLObjectMediaNavigateClient::Handler::onSuccess(LLCore::HttpResponse * response, const LLSD &content) { - getRequest()->stopTracking(); + LLMediaDataClient::Handler::onSuccess(response, content); - if(getRequest()->isDead()) - { - LL_WARNS("LLMediaDataClient") << "dead request " << *(getRequest()) << LL_ENDL; - return; - } + if (getRequest()->isDead()) + { // already warned. + return; + } + + LL_INFOS("LLMediaDataClient") << *(getRequest()) << " NAVIGATE returned" << LL_ENDL; + + if (content.has("error")) + { + const LLSD &error = content["error"]; + int error_code = error["code"]; + + if (ERROR_PERMISSION_DENIED_CODE == error_code) + { + mediaNavigateBounceBack(); + } + else + { + LL_WARNS("LLMediaDataClient") << *(getRequest()) << " Error navigating: code=" << + error["code"].asString() << ": " << error["message"].asString() << LL_ENDL; + } + + // XXX Warn user? + } + else + { + // No action required. + LL_DEBUGS("LLMediaDataClientResponse") << *(getRequest()) << LL_ENDL; + } - // Bounce back (unless HTTP_SERVICE_UNAVAILABLE, in which case call base - // class - if (getStatus() == HTTP_SERVICE_UNAVAILABLE) - { - LLMediaDataClient::Responder::httpFailure(); - } - else - { - // bounce the face back - LL_WARNS("LLMediaDataClient") << *(getRequest()) << " Error navigating: " << dumpResponse() << LL_ENDL; - const LLSD &payload = getRequest()->getPayload(); - // bounce the face back - getRequest()->getObject()->mediaNavigateBounceBack((LLSD::Integer)payload[LLTextureEntry::TEXTURE_INDEX_KEY]); - } } -/*virtual*/ -void LLObjectMediaNavigateClient::Responder::httpSuccess() +void LLObjectMediaNavigateClient::Handler::onFailure(LLCore::HttpResponse * response, LLCore::HttpStatus status) { - getRequest()->stopTracking(); + LLMediaDataClient::Handler::onFailure(response, status); - if(getRequest()->isDead()) - { - LL_WARNS("LLMediaDataClient") << "dead request " << *(getRequest()) << LL_ENDL; - return; - } + if (getRequest()->isDead()) + { // already warned. + return; + } - LL_INFOS("LLMediaDataClient") << *(getRequest()) << " NAVIGATE returned " << dumpResponse() << LL_ENDL; - - const LLSD& content = getContent(); - if (content.has("error")) - { - const LLSD &error = content["error"]; - int error_code = error["code"]; - - if (ERROR_PERMISSION_DENIED_CODE == error_code) - { - LL_WARNS("LLMediaDataClient") << *(getRequest()) << " Navigation denied: bounce back" << LL_ENDL; - const LLSD &payload = getRequest()->getPayload(); - // bounce the face back - getRequest()->getObject()->mediaNavigateBounceBack((LLSD::Integer)payload[LLTextureEntry::TEXTURE_INDEX_KEY]); - } - else - { - LL_WARNS("LLMediaDataClient") << *(getRequest()) << " Error navigating: code=" << - error["code"].asString() << ": " << error["message"].asString() << LL_ENDL; - } + if (status != LLCore::HttpStatus(HTTP_SERVICE_UNAVAILABLE)) + { + mediaNavigateBounceBack(); + } +} - // XXX Warn user? - } - else - { - // No action required. - LL_DEBUGS("LLMediaDataClientResponse") << *(getRequest()) << " " << dumpResponse() << LL_ENDL; - } +void LLObjectMediaNavigateClient::Handler::mediaNavigateBounceBack() +{ + LL_WARNS("LLMediaDataClient") << *(getRequest()) << " Error navigating or denied." << LL_ENDL; + const LLSD &payload = getRequest()->getPayload(); + + // bounce the face back + getRequest()->getObject()->mediaNavigateBounceBack((LLSD::Integer)payload[LLTextureEntry::TEXTURE_INDEX_KEY]); } diff --git a/indra/newview/llmediadataclient.h b/indra/newview/llmediadataclient.h index 80dd519812..9907897613 100755 --- a/indra/newview/llmediadataclient.h +++ b/indra/newview/llmediadataclient.h @@ -27,12 +27,15 @@ #ifndef LL_LLMEDIADATACLIENT_H #define LL_LLMEDIADATACLIENT_H -#include "llhttpclient.h" #include <set> #include "llrefcount.h" #include "llpointer.h" #include "lleventtimer.h" - +#include "llhttpsdhandler.h" +#include "httpcommon.h" +#include "httprequest.h" +#include "httpoptions.h" +#include "httpheaders.h" // Link seam for LLVOVolume class LLMediaDataClientObject : public LLRefCount @@ -74,6 +77,8 @@ public: // Abstracts the Cap URL, the request, and the responder class LLMediaDataClient : public LLRefCount { + friend class PredicateMatchRequest; + protected: LOG_CLASS(LLMediaDataClient); public: @@ -109,26 +114,30 @@ protected: // Destructor virtual ~LLMediaDataClient(); // use unref - class Responder; - // Request (pure virtual base class for requests in the queue) - class Request : public LLRefCount - { - public: - // Subclasses must implement this to build a payload for their request type. - virtual LLSD getPayload() const = 0; - // and must create the correct type of responder. - virtual Responder *createResponder() = 0; + class Request: + public boost::enable_shared_from_this<Request> + { + public: + typedef boost::shared_ptr<Request> ptr_t; - virtual std::string getURL() { return ""; } + // Subclasses must implement this to build a payload for their request type. + virtual LLSD getPayload() const = 0; + // and must create the correct type of responder. + virtual LLHttpSDHandler *createHandler() = 0; + + virtual std::string getURL() { return ""; } enum Type { GET, UPDATE, NAVIGATE, - ANY + ANY }; - + + virtual ~Request() + { } + protected: // The only way to create one of these is through a subclass. Request(Type in_type, LLMediaDataClientObject *obj, LLMediaDataClient *mdc, S32 face = -1); @@ -166,7 +175,7 @@ protected: const LLUUID &getID() const { return mObjectID; } S32 getFace() const { return mFace; } - bool isMatch (const Request* other, Type match_type = ANY) const + bool isMatch (const Request::ptr_t &other, Type match_type = ANY) const { return ((match_type == ANY) || (mType == other->mType)) && (mFace == other->mFace) && @@ -188,61 +197,62 @@ protected: // Back pointer to the MDC...not a ref! LLMediaDataClient *mMDC; }; - typedef LLPointer<Request> request_ptr_t; + //typedef LLPointer<Request> request_ptr_t; - // Responder - class Responder : public LLHTTPClient::Responder - { - LOG_CLASS(Responder); - public: - Responder(const request_ptr_t &request); - request_ptr_t &getRequest() { return mRequest; } + class Handler : public LLHttpSDHandler + { + LOG_CLASS(Handler); + public: + Handler(const Request::ptr_t &request); + Request::ptr_t getRequest() const { return mRequest; } - protected: - //If we get back an error (not found, etc...), handle it here - virtual void httpFailure(); - //If we get back a normal response, handle it here. Default just logs it. - virtual void httpSuccess(); + protected: + virtual void onSuccess(LLCore::HttpResponse * response, const LLSD &content); + virtual void onFailure(LLCore::HttpResponse * response, LLCore::HttpStatus status); + + private: + Request::ptr_t mRequest; + }; - private: - request_ptr_t mRequest; - }; class RetryTimer : public LLEventTimer { public: - RetryTimer(F32 time, request_ptr_t); + RetryTimer(F32 time, Request::ptr_t); virtual BOOL tick(); private: // back-pointer - request_ptr_t mRequest; + Request::ptr_t mRequest; }; protected: - typedef std::list<request_ptr_t> request_queue_t; - typedef std::set<request_ptr_t> request_set_t; + typedef std::list<Request::ptr_t> request_queue_t; + typedef std::set<Request::ptr_t> request_set_t; // Subclasses must override to return a cap name virtual const char *getCapabilityName() const = 0; // Puts the request into a queue, appropriately handling duplicates, etc. - virtual void enqueue(Request*) = 0; + virtual void enqueue(Request::ptr_t) = 0; virtual void serviceQueue(); + virtual void serviceHttp(); virtual request_queue_t *getQueue() { return &mQueue; }; // Gets the next request, removing it from the queue - virtual request_ptr_t dequeue(); + virtual Request::ptr_t dequeue(); - virtual bool canServiceRequest(request_ptr_t request) { return true; }; + virtual bool canServiceRequest(Request::ptr_t request) { return true; }; // Returns a request to the head of the queue (should only be used for requests that came from dequeue - virtual void pushBack(request_ptr_t request); + virtual void pushBack(Request::ptr_t request); - void trackRequest(request_ptr_t request); - void stopTrackingRequest(request_ptr_t request); + void trackRequest(Request::ptr_t request); + void stopTrackingRequest(Request::ptr_t request); + + bool isDoneProcessing() const; request_queue_t mQueue; @@ -260,6 +270,11 @@ protected: void startQueueTimer(); void stopQueueTimer(); + LLCore::HttpRequest::ptr_t mHttpRequest; + LLCore::HttpHeaders::ptr_t mHttpHeaders; + LLCore::HttpOptions::ptr_t mHttpOpts; + LLCore::HttpRequest::policy_t mHttpPolicy; + private: static F64 getObjectScore(const LLMediaDataClientObject::ptr_t &obj); @@ -281,9 +296,9 @@ private: bool mQueueTimerIsRunning; - template <typename T> friend typename T::iterator find_matching_request(T &c, const LLMediaDataClient::Request *request, LLMediaDataClient::Request::Type match_type); - template <typename T> friend typename T::iterator find_matching_request(T &c, const LLUUID &id, LLMediaDataClient::Request::Type match_type); - template <typename T> friend void remove_matching_requests(T &c, const LLUUID &id, LLMediaDataClient::Request::Type match_type); +// template <typename T> friend typename T::iterator find_matching_request(T &c, const LLMediaDataClient::Request *request, LLMediaDataClient::Request::Type match_type); +// template <typename T> friend typename T::iterator find_matching_request(T &c, const LLUUID &id, LLMediaDataClient::Request::Type match_type); +// template <typename T> friend void remove_matching_requests(T &c, const LLUUID &id, LLMediaDataClient::Request::Type match_type); }; // MediaDataClient specific for the ObjectMedia cap @@ -309,7 +324,7 @@ public: public: RequestGet(LLMediaDataClientObject *obj, LLMediaDataClient *mdc); /*virtual*/ LLSD getPayload() const; - /*virtual*/ Responder *createResponder(); + /*virtual*/ LLHttpSDHandler *createHandler(); }; class RequestUpdate: public Request @@ -317,7 +332,7 @@ public: public: RequestUpdate(LLMediaDataClientObject *obj, LLMediaDataClient *mdc); /*virtual*/ LLSD getPayload() const; - /*virtual*/ Responder *createResponder(); + /*virtual*/ LLHttpSDHandler *createHandler(); }; // Returns true iff the queue is empty @@ -331,7 +346,7 @@ public: virtual bool processQueueTimer(); - virtual bool canServiceRequest(request_ptr_t request); + virtual bool canServiceRequest(Request::ptr_t request); protected: // Subclasses must override to return a cap name @@ -340,17 +355,20 @@ protected: virtual request_queue_t *getQueue(); // Puts the request into the appropriate queue - virtual void enqueue(Request*); + virtual void enqueue(Request::ptr_t); - class Responder : public LLMediaDataClient::Responder + class Handler: public LLMediaDataClient::Handler { - LOG_CLASS(Responder); + LOG_CLASS(Handler); public: - Responder(const request_ptr_t &request) - : LLMediaDataClient::Responder(request) {} + Handler(const Request::ptr_t &request): + LLMediaDataClient::Handler(request) + {} + protected: - virtual void httpSuccess(); + virtual void onSuccess(LLCore::HttpResponse * response, const LLSD &content); }; + private: // The Get/Update data client needs a second queue to avoid object updates starving load-ins. void swapCurrentQueue(); @@ -359,7 +377,7 @@ private: bool mCurrentQueueIsTheSortedQueue; // Comparator for sorting - static bool compareRequestScores(const request_ptr_t &o1, const request_ptr_t &o2); + static bool compareRequestScores(const Request::ptr_t &o1, const Request::ptr_t &o2); void sortQueue(); }; @@ -384,14 +402,14 @@ public: void navigate(LLMediaDataClientObject *object, U8 texture_index, const std::string &url); // Puts the request into the appropriate queue - virtual void enqueue(Request*); + virtual void enqueue(Request::ptr_t); class RequestNavigate: public Request { public: RequestNavigate(LLMediaDataClientObject *obj, LLMediaDataClient *mdc, U8 texture_index, const std::string &url); /*virtual*/ LLSD getPayload() const; - /*virtual*/ Responder *createResponder(); + /*virtual*/ LLHttpSDHandler *createHandler(); /*virtual*/ std::string getURL() { return mURL; } private: std::string mURL; @@ -401,15 +419,18 @@ protected: // Subclasses must override to return a cap name virtual const char *getCapabilityName() const; - class Responder : public LLMediaDataClient::Responder + class Handler : public LLMediaDataClient::Handler { - LOG_CLASS(Responder); + LOG_CLASS(Handler); public: - Responder(const request_ptr_t &request) - : LLMediaDataClient::Responder(request) {} + Handler(const Request::ptr_t &request): + LLMediaDataClient::Handler(request) + {} + protected: - virtual void httpFailure(); - virtual void httpSuccess(); + virtual void onSuccess(LLCore::HttpResponse * response, const LLSD &content); + virtual void onFailure(LLCore::HttpResponse * response, LLCore::HttpStatus status); + private: void mediaNavigateBounceBack(); }; diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 648056484e..d6aaf18cb7 100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -738,9 +738,9 @@ void log_upload_error(LLCore::HttpStatus status, const LLSD& content, LLMeshRepoThread::LLMeshRepoThread() : LLThread("mesh repo"), mHttpRequest(NULL), - mHttpOptions(NULL), - mHttpLargeOptions(NULL), - mHttpHeaders(NULL), + mHttpOptions(), + mHttpLargeOptions(), + mHttpHeaders(), mHttpPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID), mHttpLegacyPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID), mHttpLargePolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID), @@ -753,13 +753,13 @@ LLMeshRepoThread::LLMeshRepoThread() mHeaderMutex = new LLMutex(NULL); mSignal = new LLCondition(NULL); mHttpRequest = new LLCore::HttpRequest; - mHttpOptions = new LLCore::HttpOptions; + mHttpOptions = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions); mHttpOptions->setTransferTimeout(SMALL_MESH_XFER_TIMEOUT); mHttpOptions->setUseRetryAfter(gSavedSettings.getBOOL("MeshUseHttpRetryAfter")); - mHttpLargeOptions = new LLCore::HttpOptions; + mHttpLargeOptions = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions); mHttpLargeOptions->setTransferTimeout(LARGE_MESH_XFER_TIMEOUT); mHttpLargeOptions->setUseRetryAfter(gSavedSettings.getBOOL("MeshUseHttpRetryAfter")); - mHttpHeaders = new LLCore::HttpHeaders; + mHttpHeaders = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders); mHttpHeaders->append(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_VND_LL_MESH); mHttpPolicyClass = app_core_http.getPolicy(LLAppCoreHttp::AP_MESH2); mHttpLegacyPolicyClass = app_core_http.getPolicy(LLAppCoreHttp::AP_MESH1); @@ -781,22 +781,9 @@ LLMeshRepoThread::~LLMeshRepoThread() delete *iter; } mHttpRequestSet.clear(); - if (mHttpHeaders) - { - mHttpHeaders->release(); - mHttpHeaders = NULL; - } - if (mHttpOptions) - { - mHttpOptions->release(); - mHttpOptions = NULL; - } - if (mHttpLargeOptions) - { - mHttpLargeOptions->release(); - mHttpLargeOptions = NULL; - } - delete mHttpRequest; + mHttpHeaders.reset(); + + delete mHttpRequest; mHttpRequest = NULL; delete mMutex; mMutex = NULL; @@ -1882,11 +1869,11 @@ LLMeshUploadThread::LLMeshUploadThread(LLMeshUploadThread::instance_list& data, mMeshUploadTimeOut = gSavedSettings.getS32("MeshUploadTimeOut") ; mHttpRequest = new LLCore::HttpRequest; - mHttpOptions = new LLCore::HttpOptions; + mHttpOptions = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions); mHttpOptions->setTransferTimeout(mMeshUploadTimeOut); mHttpOptions->setUseRetryAfter(gSavedSettings.getBOOL("MeshUseHttpRetryAfter")); mHttpOptions->setRetries(UPLOAD_RETRY_LIMIT); - mHttpHeaders = new LLCore::HttpHeaders; + mHttpHeaders = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders); mHttpHeaders->append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_LLSD_XML); mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicy(LLAppCoreHttp::AP_UPLOADS); mHttpPriority = 0; @@ -1894,16 +1881,6 @@ LLMeshUploadThread::LLMeshUploadThread(LLMeshUploadThread::instance_list& data, LLMeshUploadThread::~LLMeshUploadThread() { - if (mHttpHeaders) - { - mHttpHeaders->release(); - mHttpHeaders = NULL; - } - if (mHttpOptions) - { - mHttpOptions->release(); - mHttpOptions = NULL; - } delete mHttpRequest; mHttpRequest = NULL; } diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index 39280bea3a..55157cc040 100755 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -322,9 +322,9 @@ public: // llcorehttp library interface objects. LLCore::HttpStatus mHttpStatus; LLCore::HttpRequest * mHttpRequest; - LLCore::HttpOptions * mHttpOptions; - LLCore::HttpOptions * mHttpLargeOptions; - LLCore::HttpHeaders * mHttpHeaders; + LLCore::HttpOptions::ptr_t mHttpOptions; + LLCore::HttpOptions::ptr_t mHttpLargeOptions; + LLCore::HttpHeaders::ptr_t mHttpHeaders; LLCore::HttpRequest::policy_t mHttpPolicyClass; LLCore::HttpRequest::policy_t mHttpLegacyPolicyClass; LLCore::HttpRequest::policy_t mHttpLargePolicyClass; @@ -493,8 +493,8 @@ private: // llcorehttp library interface objects. LLCore::HttpStatus mHttpStatus; LLCore::HttpRequest * mHttpRequest; - LLCore::HttpOptions * mHttpOptions; - LLCore::HttpHeaders * mHttpHeaders; + LLCore::HttpOptions::ptr_t mHttpOptions; + LLCore::HttpHeaders::ptr_t mHttpHeaders; LLCore::HttpRequest::policy_t mHttpPolicyClass; LLCore::HttpRequest::priority_t mHttpPriority; }; diff --git a/indra/newview/llpanelclassified.cpp b/indra/newview/llpanelclassified.cpp index 878f1af9ef..5d1ae4ff10 100755 --- a/indra/newview/llpanelclassified.cpp +++ b/indra/newview/llpanelclassified.cpp @@ -34,14 +34,12 @@ #include "lldispatcher.h" #include "llfloaterreg.h" -#include "llhttpclient.h" #include "llnotifications.h" #include "llnotificationsutil.h" #include "llparcel.h" #include "llagent.h" #include "llclassifiedflags.h" -#include "llclassifiedstatsresponder.h" #include "llcommandhandler.h" // for classified HTML detail page click tracking #include "lliconctrl.h" #include "lllineeditor.h" @@ -57,6 +55,7 @@ #include "llscrollcontainer.h" #include "llstatusbar.h" #include "llviewertexture.h" +#include "llcorehttputil.h" const S32 MINIMUM_PRICE_FOR_LISTING = 50; // L$ @@ -91,19 +90,6 @@ public: }; static LLDispatchClassifiedClickThrough sClassifiedClickThrough; -// Just to debug errors. Can be thrown away later. -class LLClassifiedClickMessageResponder : public LLHTTPClient::Responder -{ - LOG_CLASS(LLClassifiedClickMessageResponder); - -protected: - // If we get back an error (not found, etc...), handle it here - virtual void httpFailure() - { - LL_WARNS() << "Sending click message failed " << dumpResponse() << LL_ENDL; - } -}; - ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// @@ -229,8 +215,10 @@ void LLPanelClassifiedInfo::onOpen(const LLSD& key) { LL_INFOS() << "Classified stat request via capability" << LL_ENDL; LLSD body; - body["classified_id"] = getClassifiedId(); - LLHTTPClient::post(url, body, new LLClassifiedStatsResponder(getClassifiedId())); + LLUUID classifiedId = getClassifiedId(); + body["classified_id"] = classifiedId; + LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost(url, body, + boost::bind(&LLPanelClassifiedInfo::handleSearchStatResponse, classifiedId, _1)); } // Update classified click stats. @@ -240,6 +228,23 @@ void LLPanelClassifiedInfo::onOpen(const LLSD& key) setInfoLoaded(false); } +/*static*/ +void LLPanelClassifiedInfo::handleSearchStatResponse(LLUUID classifiedId, LLSD result) +{ + S32 teleport = result["teleport_clicks"].asInteger(); + S32 map = result["map_clicks"].asInteger(); + S32 profile = result["profile_clicks"].asInteger(); + S32 search_teleport = result["search_teleport_clicks"].asInteger(); + S32 search_map = result["search_map_clicks"].asInteger(); + S32 search_profile = result["search_profile_clicks"].asInteger(); + + LLPanelClassifiedInfo::setClickThrough(classifiedId, + teleport + search_teleport, + map + search_map, + profile + search_profile, + true); +} + void LLPanelClassifiedInfo::processProperties(void* data, EAvatarProcessorType type) { if(APT_CLASSIFIED_INFO == type) @@ -548,7 +553,8 @@ void LLPanelClassifiedInfo::sendClickMessage( std::string url = gAgent.getRegion()->getCapability("SearchStatTracking"); LL_INFOS() << "Sending click msg via capability (url=" << url << ")" << LL_ENDL; LL_INFOS() << "body: [" << body << "]" << LL_ENDL; - LLHTTPClient::post(url, body, new LLClassifiedClickMessageResponder()); + LLCoreHttpUtil::HttpCoroutineAdapter::messageHttpPost(url, body, + "SearchStatTracking Click report sent.", "SearchStatTracking Click report NOT sent."); } void LLPanelClassifiedInfo::sendClickMessage(const std::string& type) diff --git a/indra/newview/llpanelclassified.h b/indra/newview/llpanelclassified.h index cedd65c405..b292782615 100755 --- a/indra/newview/llpanelclassified.h +++ b/indra/newview/llpanelclassified.h @@ -37,6 +37,8 @@ #include "llrect.h" #include "lluuid.h" #include "v3dmath.h" +#include "llcoros.h" +#include "lleventcoro.h" class LLScrollContainer; class LLTextureCtrl; @@ -193,6 +195,9 @@ private: S32 mMapClicksNew; S32 mProfileClicksNew; + static void handleSearchStatResponse(LLUUID classifiedId, LLSD result); + + typedef std::list<LLPanelClassifiedInfo*> panel_list_t; static panel_list_t sAllPanels; }; diff --git a/indra/newview/llpanelexperiencepicker.cpp b/indra/newview/llpanelexperiencepicker.cpp index 70d826a407..c7a353a6af 100644 --- a/indra/newview/llpanelexperiencepicker.cpp +++ b/indra/newview/llpanelexperiencepicker.cpp @@ -42,6 +42,7 @@ #include "llviewercontrol.h" #include "llfloater.h" #include "lltrans.h" +#include "llhttpclient.h" // *TODO: Rider, remove when converting #define BTN_FIND "find" #define BTN_OK "ok_btn" diff --git a/indra/newview/llpanellandmarks.cpp b/indra/newview/llpanellandmarks.cpp index 1d73d4bd6e..cd1dc0f070 100755 --- a/indra/newview/llpanellandmarks.cpp +++ b/indra/newview/llpanellandmarks.cpp @@ -1352,17 +1352,8 @@ void LLLandmarksPanel::doCreatePick(LLLandmark* landmark) std::string url = region->getCapability("RemoteParcelRequest"); if (!url.empty()) { - body["location"] = ll_sd_from_vector3(region_pos); - if (!region_id.isNull()) - { - body["region_id"] = region_id; - } - if (!pos_global.isExactlyZero()) - { - U64 region_handle = to_region_handle(pos_global); - body["region_handle"] = ll_sd_from_U64(region_handle); - } - LLHTTPClient::post(url, body, new LLRemoteParcelRequestResponder(getObserverHandle())); + LLRemoteParcelInfoProcessor::getInstance()->requestRegionParcelInfo(url, + region_id, region_pos, pos_global, getObserverHandle()); } else { diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp index cc8c3edd51..99c9fad82d 100755 --- a/indra/newview/llpanellogin.cpp +++ b/indra/newview/llpanellogin.cpp @@ -59,7 +59,6 @@ #include "llviewernetwork.h" #include "llviewerwindow.h" // to link into child list #include "lluictrlfactory.h" -#include "llhttpclient.h" #include "llweb.h" #include "llmediactrl.h" #include "llrootview.h" diff --git a/indra/newview/llpanelme.cpp b/indra/newview/llpanelme.cpp index cedd3025fc..55e4ffff5e 100755 --- a/indra/newview/llpanelme.cpp +++ b/indra/newview/llpanelme.cpp @@ -37,7 +37,6 @@ #include "llfloaterreg.h" #include "llhints.h" #include "llviewercontrol.h" -#include "llviewerdisplayname.h" // Linden libraries #include "llavatarnamecache.h" // IDEVO diff --git a/indra/newview/llpanelplaceinfo.cpp b/indra/newview/llpanelplaceinfo.cpp index e62b5a4f1d..cec56a7ae7 100755 --- a/indra/newview/llpanelplaceinfo.cpp +++ b/indra/newview/llpanelplaceinfo.cpp @@ -45,6 +45,7 @@ #include "llpanelpick.h" #include "lltexturectrl.h" #include "llviewerregion.h" +#include "llhttpconstants.h" LLPanelPlaceInfo::LLPanelPlaceInfo() : LLPanel(), @@ -150,17 +151,8 @@ void LLPanelPlaceInfo::displayParcelInfo(const LLUUID& region_id, std::string url = region->getCapability("RemoteParcelRequest"); if (!url.empty()) { - body["location"] = ll_sd_from_vector3(mPosRegion); - if (!region_id.isNull()) - { - body["region_id"] = region_id; - } - if (!pos_global.isExactlyZero()) - { - U64 region_handle = to_region_handle(pos_global); - body["region_handle"] = ll_sd_from_U64(region_handle); - } - LLHTTPClient::post(url, body, new LLRemoteParcelRequestResponder(getObserverHandle())); + LLRemoteParcelInfoProcessor::getInstance()->requestRegionParcelInfo(url, + region_id, mPosRegion, pos_global, getObserverHandle()); } else { diff --git a/indra/newview/llpathfindingmanager.cpp b/indra/newview/llpathfindingmanager.cpp index 4977a72dc6..2e6937a79f 100755 --- a/indra/newview/llpathfindingmanager.cpp +++ b/indra/newview/llpathfindingmanager.cpp @@ -39,7 +39,6 @@ #include <boost/signals2.hpp> #include "llagent.h" -#include "llhttpclient.h" #include "llhttpnode.h" #include "llnotificationsutil.h" #include "llpathfindingcharacterlist.h" @@ -55,6 +54,8 @@ #include "lluuid.h" #include "llviewerregion.h" #include "llweb.h" +#include "llcorehttputil.h" +#include "llworld.h" #define CAP_SERVICE_RETRIEVE_NAVMESH "RetrieveNavMeshSrc" @@ -98,82 +99,6 @@ public: LLHTTPRegistration<LLAgentStateChangeNode> gHTTPRegistrationAgentStateChangeNode(SIM_MESSAGE_AGENT_STATE_UPDATE); //--------------------------------------------------------------------------- -// NavMeshStatusResponder -//--------------------------------------------------------------------------- - -class NavMeshStatusResponder : public LLHTTPClient::Responder -{ - LOG_CLASS(NavMeshStatusResponder); -public: - NavMeshStatusResponder(LLViewerRegion *pRegion, bool pIsGetStatusOnly); - virtual ~NavMeshStatusResponder(); - -protected: - virtual void httpSuccess(); - virtual void httpFailure(); - -private: - LLViewerRegion *mRegion; - LLUUID mRegionUUID; - bool mIsGetStatusOnly; -}; - -//--------------------------------------------------------------------------- -// NavMeshResponder -//--------------------------------------------------------------------------- - -class NavMeshResponder : public LLHTTPClient::Responder -{ - LOG_CLASS(NavMeshResponder); -public: - NavMeshResponder(U32 pNavMeshVersion, LLPathfindingNavMeshPtr pNavMeshPtr); - virtual ~NavMeshResponder(); - -protected: - virtual void httpSuccess(); - virtual void httpFailure(); - -private: - U32 mNavMeshVersion; - LLPathfindingNavMeshPtr mNavMeshPtr; -}; - -//--------------------------------------------------------------------------- -// AgentStateResponder -//--------------------------------------------------------------------------- - -class AgentStateResponder : public LLHTTPClient::Responder -{ - LOG_CLASS(AgentStateResponder); -public: - AgentStateResponder(); - virtual ~AgentStateResponder(); - -protected: - virtual void httpSuccess(); - virtual void httpFailure(); -}; - - -//--------------------------------------------------------------------------- -// NavMeshRebakeResponder -//--------------------------------------------------------------------------- -class NavMeshRebakeResponder : public LLHTTPClient::Responder -{ - LOG_CLASS(NavMeshRebakeResponder); -public: - NavMeshRebakeResponder(LLPathfindingManager::rebake_navmesh_callback_t pRebakeNavMeshCallback); - virtual ~NavMeshRebakeResponder(); - -protected: - virtual void httpSuccess(); - virtual void httpFailure(); - -private: - LLPathfindingManager::rebake_navmesh_callback_t mRebakeNavMeshCallback; -}; - -//--------------------------------------------------------------------------- // LinksetsResponder //--------------------------------------------------------------------------- @@ -188,6 +113,8 @@ public: void handleTerrainLinksetsResult(const LLSD &pContent); void handleTerrainLinksetsError(); + typedef boost::shared_ptr<LinksetsResponder> ptr_t; + protected: private: @@ -214,64 +141,6 @@ private: typedef boost::shared_ptr<LinksetsResponder> LinksetsResponderPtr; //--------------------------------------------------------------------------- -// ObjectLinksetsResponder -//--------------------------------------------------------------------------- - -class ObjectLinksetsResponder : public LLHTTPClient::Responder -{ - LOG_CLASS(ObjectLinksetsResponder); -public: - ObjectLinksetsResponder(LinksetsResponderPtr pLinksetsResponsderPtr); - virtual ~ObjectLinksetsResponder(); - -protected: - virtual void httpSuccess(); - virtual void httpFailure(); - -private: - LinksetsResponderPtr mLinksetsResponsderPtr; -}; - -//--------------------------------------------------------------------------- -// TerrainLinksetsResponder -//--------------------------------------------------------------------------- - -class TerrainLinksetsResponder : public LLHTTPClient::Responder -{ - LOG_CLASS(TerrainLinksetsResponder); -public: - TerrainLinksetsResponder(LinksetsResponderPtr pLinksetsResponsderPtr); - virtual ~TerrainLinksetsResponder(); - -protected: - virtual void httpSuccess(); - virtual void httpFailure(); - -private: - LinksetsResponderPtr mLinksetsResponsderPtr; -}; - -//--------------------------------------------------------------------------- -// CharactersResponder -//--------------------------------------------------------------------------- - -class CharactersResponder : public LLHTTPClient::Responder -{ - LOG_CLASS(TerrainLinksetsResponder); -public: - CharactersResponder(LLPathfindingManager::request_id_t pRequestId, LLPathfindingManager::object_request_callback_t pCharactersCallback); - virtual ~CharactersResponder(); - -protected: - virtual void httpSuccess(); - virtual void httpFailure(); - -private: - LLPathfindingManager::request_id_t mRequestId; - LLPathfindingManager::object_request_callback_t mCharactersCallback; -}; - -//--------------------------------------------------------------------------- // LLPathfindingManager //--------------------------------------------------------------------------- @@ -350,11 +219,13 @@ void LLPathfindingManager::requestGetNavMeshForRegion(LLViewerRegion *pRegion, b } else { - std::string navMeshStatusURL = getNavMeshStatusURLForRegion(pRegion); - llassert(!navMeshStatusURL.empty()); - navMeshPtr->handleNavMeshCheckVersion(); - LLHTTPClient::ResponderPtr navMeshStatusResponder = new NavMeshStatusResponder(pRegion, pIsGetStatusOnly); - LLHTTPClient::get(navMeshStatusURL, navMeshStatusResponder); + std::string navMeshStatusURL = getNavMeshStatusURLForRegion(pRegion); + llassert(!navMeshStatusURL.empty()); + navMeshPtr->handleNavMeshCheckVersion(); + + U64 regionHandle = pRegion->getHandle(); + std::string coroname = LLCoros::instance().launch("LLPathfindingManager::navMeshStatusRequestCoro", + boost::bind(&LLPathfindingManager::navMeshStatusRequestCoro, this, navMeshStatusURL, regionHandle, pIsGetStatusOnly)); } } @@ -385,15 +256,15 @@ void LLPathfindingManager::requestGetLinksets(request_id_t pRequestId, object_re pLinksetsCallback(pRequestId, kRequestStarted, emptyLinksetListPtr); bool doRequestTerrain = isAllowViewTerrainProperties(); - LinksetsResponderPtr linksetsResponderPtr(new LinksetsResponder(pRequestId, pLinksetsCallback, true, doRequestTerrain)); + LinksetsResponder::ptr_t linksetsResponderPtr(new LinksetsResponder(pRequestId, pLinksetsCallback, true, doRequestTerrain)); - LLHTTPClient::ResponderPtr objectLinksetsResponder = new ObjectLinksetsResponder(linksetsResponderPtr); - LLHTTPClient::get(objectLinksetsURL, objectLinksetsResponder); + std::string coroname = LLCoros::instance().launch("LLPathfindingManager::linksetObjectsCoro", + boost::bind(&LLPathfindingManager::linksetObjectsCoro, this, objectLinksetsURL, linksetsResponderPtr, LLSD())); - if (doRequestTerrain) + if (doRequestTerrain) { - LLHTTPClient::ResponderPtr terrainLinksetsResponder = new TerrainLinksetsResponder(linksetsResponderPtr); - LLHTTPClient::get(terrainLinksetsURL, terrainLinksetsResponder); + std::string coroname = LLCoros::instance().launch("LLPathfindingManager::linksetTerrainCoro", + boost::bind(&LLPathfindingManager::linksetTerrainCoro, this, terrainLinksetsURL, linksetsResponderPtr, LLSD())); } } } @@ -432,18 +303,18 @@ void LLPathfindingManager::requestSetLinksets(request_id_t pRequestId, const LLP { pLinksetsCallback(pRequestId, kRequestStarted, emptyLinksetListPtr); - LinksetsResponderPtr linksetsResponderPtr(new LinksetsResponder(pRequestId, pLinksetsCallback, !objectPostData.isUndefined(), !terrainPostData.isUndefined())); + LinksetsResponder::ptr_t linksetsResponderPtr(new LinksetsResponder(pRequestId, pLinksetsCallback, !objectPostData.isUndefined(), !terrainPostData.isUndefined())); if (!objectPostData.isUndefined()) { - LLHTTPClient::ResponderPtr objectLinksetsResponder = new ObjectLinksetsResponder(linksetsResponderPtr); - LLHTTPClient::put(objectLinksetsURL, objectPostData, objectLinksetsResponder); + std::string coroname = LLCoros::instance().launch("LLPathfindingManager::linksetObjectsCoro", + boost::bind(&LLPathfindingManager::linksetObjectsCoro, this, objectLinksetsURL, linksetsResponderPtr, objectPostData)); } if (!terrainPostData.isUndefined()) { - LLHTTPClient::ResponderPtr terrainLinksetsResponder = new TerrainLinksetsResponder(linksetsResponderPtr); - LLHTTPClient::put(terrainLinksetsURL, terrainPostData, terrainLinksetsResponder); + std::string coroname = LLCoros::instance().launch("LLPathfindingManager::linksetTerrainCoro", + boost::bind(&LLPathfindingManager::linksetTerrainCoro, this, terrainLinksetsURL, linksetsResponderPtr, terrainPostData)); } } } @@ -475,8 +346,8 @@ void LLPathfindingManager::requestGetCharacters(request_id_t pRequestId, object_ { pCharactersCallback(pRequestId, kRequestStarted, emptyCharacterListPtr); - LLHTTPClient::ResponderPtr charactersResponder = new CharactersResponder(pRequestId, pCharactersCallback); - LLHTTPClient::get(charactersURL, charactersResponder); + std::string coroname = LLCoros::instance().launch("LLPathfindingManager::charactersCoro", + boost::bind(&LLPathfindingManager::charactersCoro, this, charactersURL, pRequestId, pCharactersCallback)); } } } @@ -508,8 +379,9 @@ void LLPathfindingManager::requestGetAgentState() { std::string agentStateURL = getAgentStateURLForRegion(currentRegion); llassert(!agentStateURL.empty()); - LLHTTPClient::ResponderPtr responder = new AgentStateResponder(); - LLHTTPClient::get(agentStateURL, responder); + + std::string coroname = LLCoros::instance().launch("LLPathfindingManager::navAgentStateRequestCoro", + boost::bind(&LLPathfindingManager::navAgentStateRequestCoro, this, agentStateURL)); } } } @@ -530,35 +402,9 @@ void LLPathfindingManager::requestRebakeNavMesh(rebake_navmesh_callback_t pRebak { std::string navMeshStatusURL = getNavMeshStatusURLForCurrentRegion(); llassert(!navMeshStatusURL.empty()); - LLSD postData; - postData["command"] = "rebuild"; - LLHTTPClient::ResponderPtr responder = new NavMeshRebakeResponder(pRebakeNavMeshCallback); - LLHTTPClient::post(navMeshStatusURL, postData, responder); - } -} - -void LLPathfindingManager::sendRequestGetNavMeshForRegion(LLPathfindingNavMeshPtr navMeshPtr, LLViewerRegion *pRegion, const LLPathfindingNavMeshStatus &pNavMeshStatus) -{ - if ((pRegion == NULL) || !pRegion->isAlive()) - { - navMeshPtr->handleNavMeshNotEnabled(); - } - else - { - std::string navMeshURL = getRetrieveNavMeshURLForRegion(pRegion); - - if (navMeshURL.empty()) - { - navMeshPtr->handleNavMeshNotEnabled(); - } - else - { - navMeshPtr->handleNavMeshStart(pNavMeshStatus); - LLHTTPClient::ResponderPtr responder = new NavMeshResponder(pNavMeshStatus.getVersion(), navMeshPtr); - LLSD postData; - LLHTTPClient::post(navMeshURL, postData, responder); - } + std::string coroname = LLCoros::instance().launch("LLPathfindingManager::navMeshRebakeCoro", + boost::bind(&LLPathfindingManager::navMeshRebakeCoro, this, navMeshStatusURL, pRebakeNavMeshCallback)); } } @@ -602,29 +448,250 @@ void LLPathfindingManager::handleDeferredGetCharactersForRegion(const LLUUID &pR } } -void LLPathfindingManager::handleNavMeshStatusRequest(const LLPathfindingNavMeshStatus &pNavMeshStatus, LLViewerRegion *pRegion, bool pIsGetStatusOnly) +void LLPathfindingManager::navMeshStatusRequestCoro(std::string url, U64 regionHandle, bool isGetStatusOnly) { - LLPathfindingNavMeshPtr navMeshPtr = getNavMeshForRegion(pNavMeshStatus.getRegionUUID()); + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("NavMeshStatusRequest", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + + LLViewerRegion *region = LLWorld::getInstance()->getRegionFromHandle(regionHandle); + if (!region) + { + LL_WARNS("PathfindingManager") << "Attempting to retrieve navmesh status for region that has gone away." << LL_ENDL; + return; + } + LLUUID regionUUID = region->getRegionID(); + + region = NULL; + LLSD result = httpAdapter->getAndYield(httpRequest, url); + + region = LLWorld::getInstance()->getRegionFromHandle(regionHandle); - if (!pNavMeshStatus.isValid()) - { - navMeshPtr->handleNavMeshError(); - } - else - { - if (navMeshPtr->hasNavMeshVersion(pNavMeshStatus)) - { - navMeshPtr->handleRefresh(pNavMeshStatus); - } - else if (pIsGetStatusOnly) - { - navMeshPtr->handleNavMeshNewVersion(pNavMeshStatus); - } - else - { - sendRequestGetNavMeshForRegion(navMeshPtr, pRegion, pNavMeshStatus); - } - } + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + LLPathfindingNavMeshStatus navMeshStatus(regionUUID); + if (!status) + { + LL_WARNS("PathfindingManager") << "HTTP status, " << status.toTerseString() << + ". Building using empty status." << LL_ENDL; + } + else + { + result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS); + navMeshStatus = LLPathfindingNavMeshStatus(regionUUID, result); + } + + LLPathfindingNavMeshPtr navMeshPtr = getNavMeshForRegion(regionUUID); + + if (!navMeshStatus.isValid()) + { + navMeshPtr->handleNavMeshError(); + return; + } + else if (navMeshPtr->hasNavMeshVersion(navMeshStatus)) + { + navMeshPtr->handleRefresh(navMeshStatus); + return; + } + else if (isGetStatusOnly) + { + navMeshPtr->handleNavMeshNewVersion(navMeshStatus); + return; + } + + if ((!region) || !region->isAlive()) + { + LL_WARNS("PathfindingManager") << "About to update navmesh status for region that has gone away." << LL_ENDL; + navMeshPtr->handleNavMeshNotEnabled(); + return; + } + + std::string navMeshURL = getRetrieveNavMeshURLForRegion(region); + + if (navMeshURL.empty()) + { + navMeshPtr->handleNavMeshNotEnabled(); + return; + } + + navMeshPtr->handleNavMeshStart(navMeshStatus); + + LLSD postData; + result = httpAdapter->postAndYield(httpRequest, navMeshURL, postData); + + U32 navMeshVersion = navMeshStatus.getVersion(); + + if (!status) + { + LL_WARNS("PathfindingManager") << "HTTP status, " << status.toTerseString() << + ". reporting error." << LL_ENDL; + navMeshPtr->handleNavMeshError(navMeshVersion); + } + else + { + result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS); + navMeshPtr->handleNavMeshResult(result, navMeshVersion); + + } + +} + +void LLPathfindingManager::navAgentStateRequestCoro(std::string url) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("NavAgentStateRequest", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + + LLSD result = httpAdapter->getAndYield(httpRequest, url); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + bool canRebake = false; + if (!status) + { + LL_WARNS("PathfindingManager") << "HTTP status, " << status.toTerseString() << + ". Building using empty status." << LL_ENDL; + } + else + { + llassert(result.has(AGENT_STATE_CAN_REBAKE_REGION_FIELD)); + llassert(result.get(AGENT_STATE_CAN_REBAKE_REGION_FIELD).isBoolean()); + canRebake = result.get(AGENT_STATE_CAN_REBAKE_REGION_FIELD).asBoolean(); + } + + handleAgentState(canRebake); +} + +void LLPathfindingManager::navMeshRebakeCoro(std::string url, rebake_navmesh_callback_t rebakeNavMeshCallback) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("NavMeshRebake", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + + + LLSD postData = LLSD::emptyMap(); + postData["command"] = "rebuild"; + + LLSD result = httpAdapter->postAndYield(httpRequest, url, postData); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + bool success = true; + if (!status) + { + LL_WARNS("PathfindingManager") << "HTTP status, " << status.toTerseString() << + ". Rebake failed." << LL_ENDL; + success = false; + } + + rebakeNavMeshCallback(success); +} + +// If called with putData undefined this coroutine will issue a get. If there +// is data in putData it will be PUT to the URL. +void LLPathfindingManager::linksetObjectsCoro(std::string url, LinksetsResponder::ptr_t linksetsResponsderPtr, LLSD putData) const +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("LinksetObjects", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + + LLSD result; + + if (putData.isUndefined()) + { + result = httpAdapter->getAndYield(httpRequest, url); + } + else + { + result = httpAdapter->putAndYield(httpRequest, url, putData); + } + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) + { + LL_WARNS("PathfindingManager") << "HTTP status, " << status.toTerseString() << + ". linksetObjects failed." << LL_ENDL; + linksetsResponsderPtr->handleObjectLinksetsError(); + } + else + { + result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS); + linksetsResponsderPtr->handleObjectLinksetsResult(result); + } +} + +// If called with putData undefined this coroutine will issue a GET. If there +// is data in putData it will be PUT to the URL. +void LLPathfindingManager::linksetTerrainCoro(std::string url, LinksetsResponder::ptr_t linksetsResponsderPtr, LLSD putData) const +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("LinksetTerrain", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + + LLSD result; + + if (putData.isUndefined()) + { + result = httpAdapter->getAndYield(httpRequest, url); + } + else + { + result = httpAdapter->putAndYield(httpRequest, url, putData); + } + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) + { + LL_WARNS("PathfindingManager") << "HTTP status, " << status.toTerseString() << + ". linksetTerrain failed." << LL_ENDL; + linksetsResponsderPtr->handleTerrainLinksetsError(); + } + else + { + result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS); + linksetsResponsderPtr->handleTerrainLinksetsResult(result); + } + +} + +void LLPathfindingManager::charactersCoro(std::string url, request_id_t requestId, object_request_callback_t callback) const +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("LinksetTerrain", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + + LLSD result = httpAdapter->getAndYield(httpRequest, url); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) + { + LL_WARNS("PathfindingManager") << "HTTP status, " << status.toTerseString() << + ". characters failed." << LL_ENDL; + + LLPathfindingObjectListPtr characterListPtr = LLPathfindingObjectListPtr(new LLPathfindingCharacterList()); + callback(requestId, LLPathfindingManager::kRequestError, characterListPtr); + } + else + { + result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS); + LLPathfindingObjectListPtr characterListPtr = LLPathfindingObjectListPtr(new LLPathfindingCharacterList(result)); + callback(requestId, LLPathfindingManager::kRequestCompleted, characterListPtr); + } } void LLPathfindingManager::handleNavMeshStatusUpdate(const LLPathfindingNavMeshStatus &pNavMeshStatus) @@ -765,121 +832,8 @@ void LLAgentStateChangeNode::post(ResponsePtr pResponse, const LLSD &pContext, c } //--------------------------------------------------------------------------- -// NavMeshStatusResponder -//--------------------------------------------------------------------------- - -NavMeshStatusResponder::NavMeshStatusResponder(LLViewerRegion *pRegion, bool pIsGetStatusOnly) - : LLHTTPClient::Responder(), - mRegion(pRegion), - mRegionUUID(), - mIsGetStatusOnly(pIsGetStatusOnly) -{ - if (mRegion != NULL) - { - mRegionUUID = mRegion->getRegionID(); - } -} - -NavMeshStatusResponder::~NavMeshStatusResponder() -{ -} - -void NavMeshStatusResponder::httpSuccess() -{ - LLPathfindingNavMeshStatus navMeshStatus(mRegionUUID, getContent()); - LLPathfindingManager::getInstance()->handleNavMeshStatusRequest(navMeshStatus, mRegion, mIsGetStatusOnly); -} - -void NavMeshStatusResponder::httpFailure() -{ - LL_WARNS() << dumpResponse() << LL_ENDL; - LLPathfindingNavMeshStatus navMeshStatus(mRegionUUID); - LLPathfindingManager::getInstance()->handleNavMeshStatusRequest(navMeshStatus, mRegion, mIsGetStatusOnly); -} - -//--------------------------------------------------------------------------- -// NavMeshResponder -//--------------------------------------------------------------------------- - -NavMeshResponder::NavMeshResponder(U32 pNavMeshVersion, LLPathfindingNavMeshPtr pNavMeshPtr) - : LLHTTPClient::Responder(), - mNavMeshVersion(pNavMeshVersion), - mNavMeshPtr(pNavMeshPtr) -{ -} - -NavMeshResponder::~NavMeshResponder() -{ -} - -void NavMeshResponder::httpSuccess() -{ - mNavMeshPtr->handleNavMeshResult(getContent(), mNavMeshVersion); -} - -void NavMeshResponder::httpFailure() -{ - LL_WARNS() << dumpResponse() << LL_ENDL; - mNavMeshPtr->handleNavMeshError(mNavMeshVersion); -} - -//--------------------------------------------------------------------------- -// AgentStateResponder -//--------------------------------------------------------------------------- - -AgentStateResponder::AgentStateResponder() -: LLHTTPClient::Responder() -{ -} - -AgentStateResponder::~AgentStateResponder() -{ -} - -void AgentStateResponder::httpSuccess() -{ - const LLSD& pContent = getContent(); - llassert(pContent.has(AGENT_STATE_CAN_REBAKE_REGION_FIELD)); - llassert(pContent.get(AGENT_STATE_CAN_REBAKE_REGION_FIELD).isBoolean()); - BOOL canRebakeRegion = pContent.get(AGENT_STATE_CAN_REBAKE_REGION_FIELD).asBoolean(); - LLPathfindingManager::getInstance()->handleAgentState(canRebakeRegion); -} - -void AgentStateResponder::httpFailure() -{ - LL_WARNS() << dumpResponse() << LL_ENDL; - LLPathfindingManager::getInstance()->handleAgentState(FALSE); -} - - -//--------------------------------------------------------------------------- -// navmesh rebake responder -//--------------------------------------------------------------------------- -NavMeshRebakeResponder::NavMeshRebakeResponder(LLPathfindingManager::rebake_navmesh_callback_t pRebakeNavMeshCallback) - : LLHTTPClient::Responder(), - mRebakeNavMeshCallback(pRebakeNavMeshCallback) -{ -} - -NavMeshRebakeResponder::~NavMeshRebakeResponder() -{ -} - -void NavMeshRebakeResponder::httpSuccess() -{ - mRebakeNavMeshCallback(true); -} - -void NavMeshRebakeResponder::httpFailure() -{ - LL_WARNS() << dumpResponse() << LL_ENDL; - mRebakeNavMeshCallback(false); -} - -//--------------------------------------------------------------------------- // LinksetsResponder //--------------------------------------------------------------------------- - LinksetsResponder::LinksetsResponder(LLPathfindingManager::request_id_t pRequestId, LLPathfindingManager::object_request_callback_t pLinksetsCallback, bool pIsObjectRequested, bool pIsTerrainRequested) : mRequestId(pRequestId), mLinksetsCallback(pLinksetsCallback), @@ -957,82 +911,3 @@ void LinksetsResponder::sendCallback() mLinksetsCallback(mRequestId, requestStatus, mObjectLinksetListPtr); } - -//--------------------------------------------------------------------------- -// ObjectLinksetsResponder -//--------------------------------------------------------------------------- - -ObjectLinksetsResponder::ObjectLinksetsResponder(LinksetsResponderPtr pLinksetsResponsderPtr) - : LLHTTPClient::Responder(), - mLinksetsResponsderPtr(pLinksetsResponsderPtr) -{ -} - -ObjectLinksetsResponder::~ObjectLinksetsResponder() -{ -} - -void ObjectLinksetsResponder::httpSuccess() -{ - mLinksetsResponsderPtr->handleObjectLinksetsResult(getContent()); -} - -void ObjectLinksetsResponder::httpFailure() -{ - LL_WARNS() << dumpResponse() << LL_ENDL; - mLinksetsResponsderPtr->handleObjectLinksetsError(); -} - -//--------------------------------------------------------------------------- -// TerrainLinksetsResponder -//--------------------------------------------------------------------------- - -TerrainLinksetsResponder::TerrainLinksetsResponder(LinksetsResponderPtr pLinksetsResponsderPtr) - : LLHTTPClient::Responder(), - mLinksetsResponsderPtr(pLinksetsResponsderPtr) -{ -} - -TerrainLinksetsResponder::~TerrainLinksetsResponder() -{ -} - -void TerrainLinksetsResponder::httpSuccess() -{ - mLinksetsResponsderPtr->handleTerrainLinksetsResult(getContent()); -} - -void TerrainLinksetsResponder::httpFailure() -{ - LL_WARNS() << dumpResponse() << LL_ENDL; - mLinksetsResponsderPtr->handleTerrainLinksetsError(); -} - -//--------------------------------------------------------------------------- -// CharactersResponder -//--------------------------------------------------------------------------- - -CharactersResponder::CharactersResponder(LLPathfindingManager::request_id_t pRequestId, LLPathfindingManager::object_request_callback_t pCharactersCallback) - : LLHTTPClient::Responder(), - mRequestId(pRequestId), - mCharactersCallback(pCharactersCallback) -{ -} - -CharactersResponder::~CharactersResponder() -{ -} - -void CharactersResponder::httpSuccess() -{ - LLPathfindingObjectListPtr characterListPtr = LLPathfindingObjectListPtr(new LLPathfindingCharacterList(getContent())); - mCharactersCallback(mRequestId, LLPathfindingManager::kRequestCompleted, characterListPtr); -} - -void CharactersResponder::httpFailure() -{ - LL_WARNS() << dumpResponse() << LL_ENDL; - - LLPathfindingObjectListPtr characterListPtr = LLPathfindingObjectListPtr(new LLPathfindingCharacterList()); - mCharactersCallback(mRequestId, LLPathfindingManager::kRequestError, characterListPtr); -} diff --git a/indra/newview/llpathfindingmanager.h b/indra/newview/llpathfindingmanager.h index c61ff244fc..e8fad590ba 100755 --- a/indra/newview/llpathfindingmanager.h +++ b/indra/newview/llpathfindingmanager.h @@ -37,11 +37,15 @@ #include "llpathfindingobjectlist.h" #include "llpathfindingnavmesh.h" #include "llsingleton.h" +#include "llcoros.h" +#include "lleventcoro.h" class LLPathfindingNavMeshStatus; class LLUUID; class LLViewerRegion; +class LinksetsResponder; + class LLPathfindingManager : public LLSingleton<LLPathfindingManager> { friend class LLNavMeshSimStateChangeNode; @@ -92,16 +96,22 @@ public: protected: private: - typedef std::map<LLUUID, LLPathfindingNavMeshPtr> NavMeshMap; - void sendRequestGetNavMeshForRegion(LLPathfindingNavMeshPtr navMeshPtr, LLViewerRegion *pRegion, const LLPathfindingNavMeshStatus &pNavMeshStatus); + typedef std::map<LLUUID, LLPathfindingNavMeshPtr> NavMeshMap; void handleDeferredGetAgentStateForRegion(const LLUUID &pRegionUUID); void handleDeferredGetNavMeshForRegion(const LLUUID &pRegionUUID, bool pIsGetStatusOnly); void handleDeferredGetLinksetsForRegion(const LLUUID &pRegionUUID, request_id_t pRequestId, object_request_callback_t pLinksetsCallback) const; void handleDeferredGetCharactersForRegion(const LLUUID &pRegionUUID, request_id_t pRequestId, object_request_callback_t pCharactersCallback) const; - void handleNavMeshStatusRequest(const LLPathfindingNavMeshStatus &pNavMeshStatus, LLViewerRegion *pRegion, bool pIsGetStatusOnly); + void navMeshStatusRequestCoro(std::string url, U64 regionHandle, bool isGetStatusOnly); + void navAgentStateRequestCoro(std::string url); + void navMeshRebakeCoro(std::string url, rebake_navmesh_callback_t rebakeNavMeshCallback); + void linksetObjectsCoro(std::string url, boost::shared_ptr<LinksetsResponder> linksetsResponsderPtr, LLSD putData) const; + void linksetTerrainCoro(std::string url, boost::shared_ptr<LinksetsResponder> linksetsResponsderPtr, LLSD putData) const; + void charactersCoro(std::string url, request_id_t requestId, object_request_callback_t callback) const; + + //void handleNavMeshStatusRequest(const LLPathfindingNavMeshStatus &pNavMeshStatus, LLViewerRegion *pRegion, bool pIsGetStatusOnly); void handleNavMeshStatusUpdate(const LLPathfindingNavMeshStatus &pNavMeshStatus); void handleAgentState(BOOL pCanRebakeRegion); diff --git a/indra/newview/llpreviewgesture.cpp b/indra/newview/llpreviewgesture.cpp index c378738b05..c1d1b9b03c 100755 --- a/indra/newview/llpreviewgesture.cpp +++ b/indra/newview/llpreviewgesture.cpp @@ -52,6 +52,7 @@ #include "llviewerobjectlist.h" #include "llviewerregion.h" #include "llviewerstats.h" +#include "llviewerassetupload.h" std::string NONE_LABEL; std::string SHIFT_LABEL; @@ -1015,6 +1016,27 @@ struct LLSaveInfo }; +void finishInventoryUpload(LLUUID itemId, LLUUID newAssetId) +{ + // If this gesture is active, then we need to update the in-memory + // active map with the new pointer. + if (LLGestureMgr::instance().isGestureActive(itemId)) + { + //*TODO: This is crashing for some reason. Fix it. + // Active gesture edited from menu. + LLGestureMgr::instance().replaceGesture(itemId, newAssetId); + gInventory.notifyObservers(); + } + + //gesture will have a new asset_id + LLPreviewGesture* previewp = LLFloaterReg::findTypedInstance<LLPreviewGesture>("preview_gesture", LLSD(itemId)); + if (previewp) + { + previewp->onUpdateSucceeded(); + } +} + + void LLPreviewGesture::saveIfNeeded() { if (!gAssetStorage) @@ -1028,116 +1050,127 @@ void LLPreviewGesture::saveIfNeeded() return; } - // Copy the UI into a gesture - LLMultiGesture* gesture = createGesture(); - - // Serialize the gesture - S32 max_size = gesture->getMaxSerialSize(); - char* buffer = new char[max_size]; - - LLDataPackerAsciiBuffer dp(buffer, max_size); - - BOOL ok = gesture->serialize(dp); - - if (dp.getCurrentSize() > 1000) - { - LLNotificationsUtil::add("GestureSaveFailedTooManySteps"); - - delete gesture; - gesture = NULL; - } - else if (!ok) - { - LLNotificationsUtil::add("GestureSaveFailedTryAgain"); - delete gesture; - gesture = NULL; - } - else - { - LLPreview::onCommit(); - - // Every save gets a new UUID. Yup. - LLTransactionID tid; - LLAssetID asset_id; - tid.generate(); - asset_id = tid.makeAssetID(gAgent.getSecureSessionID()); - - LLVFile file(gVFS, asset_id, LLAssetType::AT_GESTURE, LLVFile::APPEND); - - S32 size = dp.getCurrentSize(); - file.setMaxSize(size); - file.write((U8*)buffer, size); - - BOOL delayedUpload = FALSE; - - // Upload that asset to the database - LLViewerInventoryItem* item = (LLViewerInventoryItem*) getItem(); - if (item) - { - std::string agent_url = gAgent.getRegion()->getCapability("UpdateGestureAgentInventory"); - std::string task_url = gAgent.getRegion()->getCapability("UpdateGestureTaskInventory"); - if (mObjectUUID.isNull() && !agent_url.empty()) - { - //need to disable the preview floater so item - //isn't re-saved before new asset arrives - //fake out refresh. - item->setComplete(FALSE); - refresh(); - item->setComplete(TRUE); - - // Saving into agent inventory - LLSD body; - body["item_id"] = mItemUUID; - LLHTTPClient::post(agent_url, body, - new LLUpdateAgentInventoryResponder(body, asset_id, LLAssetType::AT_GESTURE)); - delayedUpload = TRUE; - } - else if (!mObjectUUID.isNull() && !task_url.empty()) - { - // Saving into task inventory - LLSD body; - body["task_id"] = mObjectUUID; - body["item_id"] = mItemUUID; - LLHTTPClient::post(task_url, body, - new LLUpdateTaskInventoryResponder(body, asset_id, LLAssetType::AT_GESTURE)); - } - else if (gAssetStorage) - { - LLLineEditor* descEditor = getChild<LLLineEditor>("desc"); - LLSaveInfo* info = new LLSaveInfo(mItemUUID, mObjectUUID, descEditor->getText(), tid); - gAssetStorage->storeAssetData(tid, LLAssetType::AT_GESTURE, onSaveComplete, info, FALSE); - } - } - - // If this gesture is active, then we need to update the in-memory - // active map with the new pointer. - if (!delayedUpload && LLGestureMgr::instance().isGestureActive(mItemUUID)) - { - // gesture manager now owns the pointer - LLGestureMgr::instance().replaceGesture(mItemUUID, gesture, asset_id); - - // replaceGesture may deactivate other gestures so let the - // inventory know. - gInventory.notifyObservers(); - } - else - { - // we're done with this gesture - delete gesture; - gesture = NULL; - } - - mDirty = FALSE; - // refresh will be called when callback - // if triggered when delayedUpload - if(!delayedUpload) - { - refresh(); - } - } + // Copy the UI into a gesture + LLMultiGesture* gesture = createGesture(); + + // Serialize the gesture + S32 maxSize = gesture->getMaxSerialSize(); + char* buffer = new char[maxSize]; + + LLDataPackerAsciiBuffer dp(buffer, maxSize); + + bool ok = gesture->serialize(dp); + + if (dp.getCurrentSize() > 1000) + { + LLNotificationsUtil::add("GestureSaveFailedTooManySteps"); + + delete gesture; + gesture = NULL; + return; + } + else if (!ok) + { + LLNotificationsUtil::add("GestureSaveFailedTryAgain"); + delete gesture; + gesture = NULL; + return; + } + + LLAssetID assetId; + LLPreview::onCommit(); + bool delayedUpload(false); + + LLViewerInventoryItem* item = (LLViewerInventoryItem*) getItem(); + if (item) + { + const LLViewerRegion* region = gAgent.getRegion(); + if (!region) + { + LL_WARNS() << "Not connected to a region, cannot save notecard." << LL_ENDL; + return; + } + std::string agent_url = region->getCapability("UpdateGestureAgentInventory"); + std::string task_url = region->getCapability("UpdateGestureTaskInventory"); + + if (!agent_url.empty() && !task_url.empty()) + { + std::string url; + LLResourceUploadInfo::ptr_t uploadInfo; + + if (mObjectUUID.isNull() && !agent_url.empty()) + { + //need to disable the preview floater so item + //isn't re-saved before new asset arrives + //fake out refresh. + item->setComplete(false); + refresh(); + item->setComplete(true); + + uploadInfo = LLResourceUploadInfo::ptr_t(new LLBufferedAssetUploadInfo(mItemUUID, LLAssetType::AT_GESTURE, buffer, + boost::bind(&finishInventoryUpload, _1, _2))); + url = agent_url; + } + else if (!mObjectUUID.isNull() && !task_url.empty()) + { + uploadInfo = LLResourceUploadInfo::ptr_t(new LLBufferedAssetUploadInfo(mObjectUUID, mItemUUID, LLAssetType::AT_GESTURE, buffer, NULL)); + url = task_url; + } + + if (!url.empty() && uploadInfo) + { + delayedUpload = true; + + LLViewerAssetUpload::EnqueueInventoryUpload(url, uploadInfo); + } + + } + else if (gAssetStorage) + { + // Every save gets a new UUID. Yup. + LLTransactionID tid; + tid.generate(); + assetId = tid.makeAssetID(gAgent.getSecureSessionID()); + + LLVFile file(gVFS, assetId, LLAssetType::AT_GESTURE, LLVFile::APPEND); + + S32 size = dp.getCurrentSize(); + file.setMaxSize(size); + file.write((U8*)buffer, size); + + LLLineEditor* descEditor = getChild<LLLineEditor>("desc"); + LLSaveInfo* info = new LLSaveInfo(mItemUUID, mObjectUUID, descEditor->getText(), tid); + gAssetStorage->storeAssetData(tid, LLAssetType::AT_GESTURE, onSaveComplete, info, FALSE); + } + + } + + // If this gesture is active, then we need to update the in-memory + // active map with the new pointer. + if (!delayedUpload && LLGestureMgr::instance().isGestureActive(mItemUUID)) + { + // gesture manager now owns the pointer + LLGestureMgr::instance().replaceGesture(mItemUUID, gesture, assetId); + + // replaceGesture may deactivate other gestures so let the + // inventory know. + gInventory.notifyObservers(); + } + else + { + // we're done with this gesture + delete gesture; + gesture = NULL; + } + + mDirty = false; + // refresh will be called when callback + // if triggered when delayedUpload + if(!delayedUpload) + { + refresh(); + } - delete [] buffer; - buffer = NULL; } diff --git a/indra/newview/llpreviewnotecard.cpp b/indra/newview/llpreviewnotecard.cpp index 1308d1e9a7..be44fbd300 100755 --- a/indra/newview/llpreviewnotecard.cpp +++ b/indra/newview/llpreviewnotecard.cpp @@ -56,6 +56,7 @@ #include "llappviewer.h" // app_abort_quit() #include "lllineeditor.h" #include "lluictrlfactory.h" +#include "llviewerassetupload.h" ///---------------------------------------------------------------------------- /// Class LLPreviewNotecard @@ -404,6 +405,35 @@ struct LLSaveNotecardInfo } }; +void finishInventoryUpload(LLUUID itemId, LLUUID newAssetId, LLUUID newItemId) +{ + // Update the UI with the new asset. + LLPreviewNotecard* nc = LLFloaterReg::findTypedInstance<LLPreviewNotecard>("preview_notecard", LLSD(itemId)); + if (nc) + { + // *HACK: we have to delete the asset in the VFS so + // that the viewer will redownload it. This is only + // really necessary if the asset had to be modified by + // the uploader, so this can be optimized away in some + // cases. A better design is to have a new uuid if the + // script actually changed the asset. + if (nc->hasEmbeddedInventory()) + { + gVFS->removeFile(newAssetId, LLAssetType::AT_NOTECARD); + } + if (newItemId.isNull()) + { + nc->setAssetId(newAssetId); + nc->refreshFromInventory(); + } + else + { + nc->refreshFromInventory(newItemId); + } + } +} + + bool LLPreviewNotecard::saveIfNeeded(LLInventoryItem* copyitem) { LLViewerTextEditor* editor = getChild<LLViewerTextEditor>("Notecard Editor"); @@ -416,14 +446,6 @@ bool LLPreviewNotecard::saveIfNeeded(LLInventoryItem* copyitem) if(!editor->isPristine()) { - // We need to update the asset information - LLTransactionID tid; - LLAssetID asset_id; - tid.generate(); - asset_id = tid.makeAssetID(gAgent.getSecureSessionID()); - - LLVFile file(gVFS, asset_id, LLAssetType::AT_NOTECARD, LLVFile::APPEND); - std::string buffer; if (!editor->exportBuffer(buffer)) { @@ -432,52 +454,64 @@ bool LLPreviewNotecard::saveIfNeeded(LLInventoryItem* copyitem) editor->makePristine(); - S32 size = buffer.length() + 1; - file.setMaxSize(size); - file.write((U8*)buffer.c_str(), size); - const LLInventoryItem* item = getItem(); // save it out to database - if (item) - { - const LLViewerRegion* region = gAgent.getRegion(); - if (!region) - { - LL_WARNS() << "Not connected to a region, cannot save notecard." << LL_ENDL; - return false; - } - std::string agent_url = region->getCapability("UpdateNotecardAgentInventory"); - std::string task_url = region->getCapability("UpdateNotecardTaskInventory"); - - if (mObjectUUID.isNull() && !agent_url.empty()) - { - // Saving into agent inventory - mAssetStatus = PREVIEW_ASSET_LOADING; - setEnabled(FALSE); - LLSD body; - body["item_id"] = mItemUUID; - LL_INFOS() << "Saving notecard " << mItemUUID - << " into agent inventory via " << agent_url << LL_ENDL; - LLHTTPClient::post(agent_url, body, - new LLUpdateAgentInventoryResponder(body, asset_id, LLAssetType::AT_NOTECARD)); - } - else if (!mObjectUUID.isNull() && !task_url.empty()) - { - // Saving into task inventory - mAssetStatus = PREVIEW_ASSET_LOADING; - setEnabled(FALSE); - LLSD body; - body["task_id"] = mObjectUUID; - body["item_id"] = mItemUUID; - LL_INFOS() << "Saving notecard " << mItemUUID << " into task " - << mObjectUUID << " via " << task_url << LL_ENDL; - LLHTTPClient::post(task_url, body, - new LLUpdateTaskInventoryResponder(body, asset_id, LLAssetType::AT_NOTECARD)); - } + if (item) + { + const LLViewerRegion* region = gAgent.getRegion(); + if (!region) + { + LL_WARNS() << "Not connected to a region, cannot save notecard." << LL_ENDL; + return false; + } + std::string agent_url = region->getCapability("UpdateNotecardAgentInventory"); + std::string task_url = region->getCapability("UpdateNotecardTaskInventory"); + + if (!agent_url.empty() && !task_url.empty()) + { + std::string url; + LLResourceUploadInfo::ptr_t uploadInfo; + + if (mObjectUUID.isNull() && !agent_url.empty()) + { + uploadInfo = LLResourceUploadInfo::ptr_t(new LLBufferedAssetUploadInfo(mItemUUID, LLAssetType::AT_NOTECARD, buffer, + boost::bind(&finishInventoryUpload, _1, _2, _3))); + url = agent_url; + } + else if (!mObjectUUID.isNull() && !task_url.empty()) + { + uploadInfo = LLResourceUploadInfo::ptr_t(new LLBufferedAssetUploadInfo(mObjectUUID, mItemUUID, LLAssetType::AT_NOTECARD, buffer, + boost::bind(&finishInventoryUpload, _1, _3, LLUUID::null))); + url = task_url; + } + + if (!url.empty() && uploadInfo) + { + mAssetStatus = PREVIEW_ASSET_LOADING; + setEnabled(false); + + LLViewerAssetUpload::EnqueueInventoryUpload(url, uploadInfo); + } + + } else if (gAssetStorage) { + // We need to update the asset information + LLTransactionID tid; + LLAssetID asset_id; + tid.generate(); + asset_id = tid.makeAssetID(gAgent.getSecureSessionID()); + + LLVFile file(gVFS, asset_id, LLAssetType::AT_NOTECARD, LLVFile::APPEND); + + LLSaveNotecardInfo* info = new LLSaveNotecardInfo(this, mItemUUID, mObjectUUID, tid, copyitem); + + S32 size = buffer.length() + 1; + file.setMaxSize(size); + file.write((U8*)buffer.c_str(), size); + gAssetStorage->storeAssetData(tid, LLAssetType::AT_NOTECARD, &onSaveComplete, (void*)info, diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp index 1bbb22416d..fc565ffa20 100755 --- a/indra/newview/llpreviewscript.cpp +++ b/indra/newview/llpreviewscript.cpp @@ -89,6 +89,7 @@ #include "llexperiencecache.h" #include "llfloaterexperienceprofile.h" #include "llexperienceassociationresponder.h" +#include "llviewerassetupload.h" const std::string HELLO_LSL = "default\n" @@ -1641,20 +1642,79 @@ void LLPreviewLSL::onSave(void* userdata, BOOL close_after_save) self->saveIfNeeded(); } +void finishedLSLUpload(LLUUID itemId, LLSD response) +{ + // Find our window and close it if requested. + LLPreviewLSL* preview = LLFloaterReg::findTypedInstance<LLPreviewLSL>("preview_script", LLSD(itemId)); + if (preview) + { + // Bytecode save completed + if (response["compiled"]) + { + preview->callbackLSLCompileSucceeded(); + } + else + { + preview->callbackLSLCompileFailed(response["errors"]); + } + } +} + // Save needs to compile the text in the buffer. If the compile // succeeds, then save both assets out to the database. If the compile // fails, go ahead and save the text anyway. void LLPreviewLSL::saveIfNeeded(bool sync /*= true*/) { + if (!mScriptEd->hasChanged()) + { + return; + } + + mPendingUploads = 0; + mScriptEd->mErrorList->deleteAllItems(); + mScriptEd->mEditor->makePristine(); + +#if 1 + if (sync) + { + mScriptEd->sync(); + } + + const LLInventoryItem *inv_item = getItem(); + // save it out to asset server + std::string url = gAgent.getRegion()->getCapability("UpdateScriptAgent"); + if(inv_item) + { + getWindow()->incBusyCount(); + mPendingUploads++; + if (!url.empty()) + { + std::string buffer(mScriptEd->mEditor->getText()); + LLBufferedAssetUploadInfo::invnUploadFinish_f proc = boost::bind(&finishedLSLUpload, _1, _4); + + LLResourceUploadInfo::ptr_t uploadInfo(new LLScriptAssetUpload(mItemUUID, buffer, proc)); + + LLViewerAssetUpload::EnqueueInventoryUpload(url, uploadInfo); + + } + else if (gAssetStorage) + { + // save off asset into file + LLTransactionID tid; + tid.generate(); + LLAssetID asset_id = tid.makeAssetID(gAgent.getSecureSessionID()); + std::string filepath = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, asset_id.asString()); + std::string filename = filepath + ".lsl"; + + mScriptEd->writeToFile(filename); + + uploadAssetLegacy(filename, mItemUUID, tid); + } + } + + +#else // LL_INFOS() << "LLPreviewLSL::saveIfNeeded()" << LL_ENDL; - if(!mScriptEd->hasChanged()) - { - return; - } - - mPendingUploads = 0; - mScriptEd->mErrorList->deleteAllItems(); - mScriptEd->mEditor->makePristine(); // save off asset into file LLTransactionID tid; @@ -1686,8 +1746,10 @@ void LLPreviewLSL::saveIfNeeded(bool sync /*= true*/) uploadAssetLegacy(filename, mItemUUID, tid); } } +#endif } +#if 0 void LLPreviewLSL::uploadAssetViaCaps(const std::string& url, const std::string& filename, const LLUUID& item_id) @@ -1698,6 +1760,7 @@ void LLPreviewLSL::uploadAssetViaCaps(const std::string& url, body["target"] = "lsl2"; LLHTTPClient::post(url, body, new LLUpdateAgentInventoryResponder(body, filename, LLAssetType::AT_LSL_TEXT)); } +#endif void LLPreviewLSL::uploadAssetLegacy(const std::string& filename, const LLUUID& item_id, diff --git a/indra/newview/llpreviewscript.h b/indra/newview/llpreviewscript.h index 5f65be7383..954d040bda 100755 --- a/indra/newview/llpreviewscript.h +++ b/indra/newview/llpreviewscript.h @@ -203,9 +203,11 @@ protected: virtual void loadAsset(); /*virtual*/ void saveIfNeeded(bool sync = true); +#if 0 void uploadAssetViaCaps(const std::string& url, const std::string& filename, const LLUUID& item_id); +#endif // 0 void uploadAssetLegacy(const std::string& filename, const LLUUID& item_id, const LLTransactionID& tid); diff --git a/indra/newview/llproductinforequest.cpp b/indra/newview/llproductinforequest.cpp index e92bf4590d..467e9df482 100755 --- a/indra/newview/llproductinforequest.cpp +++ b/indra/newview/llproductinforequest.cpp @@ -32,31 +32,10 @@ #include "llagent.h" // for gAgent #include "lltrans.h" #include "llviewerregion.h" +#include "llcorehttputil.h" -class LLProductInfoRequestResponder : public LLHTTPClient::Responder -{ - LOG_CLASS(LLProductInfoRequestResponder); -private: - //If we get back a normal response, handle it here - /* virtual */ void httpSuccess() - { - const LLSD& content = getContent(); - if (!content.isArray()) - { - failureResult(HTTP_INTERNAL_ERROR, "Malformed response contents", content); - return; - } - LLProductInfoRequestManager::instance().setSkuDescriptions(getContent()); - } - - //If we get back an error (not found, etc...), handle it here - /* virtual */ void httpFailure() - { - LL_WARNS() << dumpResponse() << LL_ENDL; - } -}; - -LLProductInfoRequestManager::LLProductInfoRequestManager() : mSkuDescriptions() +LLProductInfoRequestManager::LLProductInfoRequestManager(): + mSkuDescriptions() { } @@ -65,15 +44,11 @@ void LLProductInfoRequestManager::initSingleton() std::string url = gAgent.getRegion()->getCapability("ProductInfoRequest"); if (!url.empty()) { - LLHTTPClient::get(url, new LLProductInfoRequestResponder()); + LLCoros::instance().launch("LLProductInfoRequestManager::getLandDescriptionsCoro", + boost::bind(&LLProductInfoRequestManager::getLandDescriptionsCoro, this, url)); } } -void LLProductInfoRequestManager::setSkuDescriptions(const LLSD& content) -{ - mSkuDescriptions = content; -} - std::string LLProductInfoRequestManager::getDescriptionForSku(const std::string& sku) { // The description LLSD is an array of maps; each array entry @@ -90,3 +65,31 @@ std::string LLProductInfoRequestManager::getDescriptionForSku(const std::string& } return LLTrans::getString("land_type_unknown"); } + +void LLProductInfoRequestManager::getLandDescriptionsCoro(std::string url) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("genericPostCoro", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + + LLSD result = httpAdapter->getAndYield(httpRequest, url); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) + { + return; + } + + if (result.has(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_CONTENT) && + result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_CONTENT].isArray()) + { + mSkuDescriptions = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_CONTENT]; + } + else + { + LL_WARNS() << "Land SKU description response is malformed" << LL_ENDL; + } +} diff --git a/indra/newview/llproductinforequest.h b/indra/newview/llproductinforequest.h index fe8f7093b0..75dbf220d1 100755 --- a/indra/newview/llproductinforequest.h +++ b/indra/newview/llproductinforequest.h @@ -28,27 +28,29 @@ #ifndef LL_LLPRODUCTINFOREQUEST_H #define LL_LLPRODUCTINFOREQUEST_H -#include "llhttpclient.h" #include "llmemory.h" +#include "lleventcoro.h" +#include "llcoros.h" -/* - This is a singleton to manage a cache of information about land types. - The land system provides a capability to get information about the - set of possible land sku, name, and description information. - We use description in the UI, but the sku is provided in the various - messages; this tool provides translation between the systems. +/** + * This is a singleton to manage a cache of information about land types. + * The land system provides a capability to get information about the + * set of possible land sku, name, and description information. + * We use description in the UI, but the sku is provided in the various + * messages; this tool provides translation between the systems. */ - class LLProductInfoRequestManager : public LLSingleton<LLProductInfoRequestManager> { public: LLProductInfoRequestManager(); - void setSkuDescriptions(const LLSD& content); std::string getDescriptionForSku(const std::string& sku); + private: friend class LLSingleton<LLProductInfoRequestManager>; /* virtual */ void initSingleton(); - LLSD mSkuDescriptions; + + void getLandDescriptionsCoro(std::string url); + LLSD mSkuDescriptions; }; #endif // LL_LLPRODUCTINFOREQUEST_H diff --git a/indra/newview/llremoteparcelrequest.cpp b/indra/newview/llremoteparcelrequest.cpp index 29dcc12f9e..06bf90c7cb 100755 --- a/indra/newview/llremoteparcelrequest.cpp +++ b/indra/newview/llremoteparcelrequest.cpp @@ -31,55 +31,16 @@ #include "message.h" #include "llpanel.h" -#include "llhttpclient.h" #include "llsdserialize.h" #include "llurlentry.h" #include "llviewerregion.h" #include "llview.h" - +#include "llsdutil.h" +#include "llsdutil_math.h" +#include "llregionhandle.h" #include "llagent.h" #include "llremoteparcelrequest.h" - - -LLRemoteParcelRequestResponder::LLRemoteParcelRequestResponder(LLHandle<LLRemoteParcelInfoObserver> observer_handle) - : mObserverHandle(observer_handle) -{} - -//If we get back a normal response, handle it here -//virtual -void LLRemoteParcelRequestResponder::httpSuccess() -{ - const LLSD& content = getContent(); - if (!content.isMap() || !content.has("parcel_id")) - { - failureResult(HTTP_INTERNAL_ERROR, "Malformed response contents", content); - return; - } - LLUUID parcel_id = getContent()["parcel_id"]; - - // Panel inspecting the information may be closed and destroyed - // before this response is received. - LLRemoteParcelInfoObserver* observer = mObserverHandle.get(); - if (observer) - { - observer->setParcelID(parcel_id); - } -} - -//If we get back an error (not found, etc...), handle it here -//virtual -void LLRemoteParcelRequestResponder::httpFailure() -{ - LL_WARNS() << dumpResponse() << LL_ENDL; - - // Panel inspecting the information may be closed and destroyed - // before this response is received. - LLRemoteParcelInfoObserver* observer = mObserverHandle.get(); - if (observer) - { - observer->setErrorStatus(getStatus(), getReason()); - } -} +#include "llcorehttputil.h" void LLRemoteParcelInfoProcessor::addObserver(const LLUUID& parcel_id, LLRemoteParcelInfoObserver* observer) { @@ -200,3 +161,64 @@ void LLRemoteParcelInfoProcessor::sendParcelInfoRequest(const LLUUID& parcel_id) msg->addUUID("ParcelID", parcel_id); gAgent.sendReliableMessage(); } + +bool LLRemoteParcelInfoProcessor::requestRegionParcelInfo(const std::string &url, + const LLUUID ®ionId, const LLVector3 ®ionPos, const LLVector3d&globalPos, + LLHandle<LLRemoteParcelInfoObserver> observerHandle) +{ + + if (!url.empty()) + { + LLCoros::instance().launch("LLRemoteParcelInfoProcessor::regionParcelInfoCoro", + boost::bind(&LLRemoteParcelInfoProcessor::regionParcelInfoCoro, this, url, + regionId, regionPos, globalPos, observerHandle)); + return true; + } + + return false; +} + +void LLRemoteParcelInfoProcessor::regionParcelInfoCoro(std::string url, + LLUUID regionId, LLVector3 posRegion, LLVector3d posGlobal, + LLHandle<LLRemoteParcelInfoObserver> observerHandle) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("RemoteParcelRequest", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + + LLSD bodyData; + + bodyData["location"] = ll_sd_from_vector3(posRegion); + if (!regionId.isNull()) + { + bodyData["region_id"] = regionId; + } + if (!posGlobal.isExactlyZero()) + { + U64 regionHandle = to_region_handle(posGlobal); + bodyData["region_handle"] = ll_sd_from_U64(regionHandle); + } + + LLSD result = httpAdapter->postAndYield(httpRequest, url, bodyData); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + LLRemoteParcelInfoObserver* observer = observerHandle.get(); + // Panel inspecting the information may be closed and destroyed + // before this response is received. + if (!observer) + return; + + if (!status) + { + observer->setErrorStatus(status.getStatus(), status.getMessage()); + } + else + { + LLUUID parcel_id = result["parcel_id"]; + observer->setParcelID(parcel_id); + } + +} diff --git a/indra/newview/llremoteparcelrequest.h b/indra/newview/llremoteparcelrequest.h index 35348b69ff..cb5af50c5f 100755 --- a/indra/newview/llremoteparcelrequest.h +++ b/indra/newview/llremoteparcelrequest.h @@ -29,29 +29,14 @@ #ifndef LL_LLREMOTEPARCELREQUEST_H #define LL_LLREMOTEPARCELREQUEST_H -#include "llhttpclient.h" #include "llhandle.h" #include "llsingleton.h" +#include "llcoros.h" +#include "lleventcoro.h" class LLMessageSystem; class LLRemoteParcelInfoObserver; -class LLRemoteParcelRequestResponder : public LLHTTPClient::Responder -{ - LOG_CLASS(LLRemoteParcelRequestResponder); -public: - LLRemoteParcelRequestResponder(LLHandle<LLRemoteParcelInfoObserver> observer_handle); - -private: - //If we get back a normal response, handle it here - /*virtual*/ void httpSuccess(); - - //If we get back an error (not found, etc...), handle it here - /*virtual*/ void httpFailure(); - - LLHandle<LLRemoteParcelInfoObserver> mObserverHandle; -}; - struct LLParcelData { LLUUID parcel_id; @@ -99,9 +84,14 @@ public: static void processParcelInfoReply(LLMessageSystem* msg, void**); + bool requestRegionParcelInfo(const std::string &url, const LLUUID ®ionId, + const LLVector3 ®ionPos, const LLVector3d& globalPos, LLHandle<LLRemoteParcelInfoObserver> observerHandle); + private: typedef std::multimap<LLUUID, LLHandle<LLRemoteParcelInfoObserver> > observer_multimap_t; observer_multimap_t mObservers; + + void regionParcelInfoCoro(std::string url, LLUUID regionId, LLVector3 posRegion, LLVector3d posGlobal, LLHandle<LLRemoteParcelInfoObserver> observerHandle); }; #endif // LL_LLREMOTEPARCELREQUEST_H diff --git a/indra/newview/llsnapshotlivepreview.cpp b/indra/newview/llsnapshotlivepreview.cpp index 0ae8a338e0..16f70a1c95 100644 --- a/indra/newview/llsnapshotlivepreview.cpp +++ b/indra/newview/llsnapshotlivepreview.cpp @@ -1004,9 +1004,23 @@ void LLSnapshotLivePreview::saveTexture() LLAgentUI::buildLocationString(pos_string, LLAgentUI::LOCATION_FORMAT_FULL); std::string who_took_it; LLAgentUI::buildFullname(who_took_it); - LLAssetStorage::LLStoreAssetCallback callback = NULL; S32 expected_upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload(); - void *userdata = NULL; +#if 1 + std::string name = "Snapshot: " + pos_string; + std::string desc = "Taken by " + who_took_it + " at " + pos_string; + + LLResourceUploadInfo::ptr_t assetUploadInfo(new LLResourceUploadInfo( + tid, LLAssetType::AT_TEXTURE, name, desc, 0, + LLFolderType::FT_SNAPSHOT_CATEGORY, LLInventoryType::IT_SNAPSHOT, + PERM_ALL, LLFloaterPerms::getGroupPerms("Uploads"), LLFloaterPerms::getEveryonePerms("Uploads"), + expected_upload_cost)); + + upload_new_resource(assetUploadInfo); + +#else + LLAssetStorage::LLStoreAssetCallback callback = NULL; + void *userdata = NULL; + upload_new_resource(tid, // tid LLAssetType::AT_TEXTURE, "Snapshot : " + pos_string, @@ -1019,6 +1033,7 @@ void LLSnapshotLivePreview::saveTexture() LLFloaterPerms::getEveryonePerms("Uploads"), "Snapshot : " + pos_string, callback, expected_upload_cost, userdata); +#endif gViewerWindow->playSnapshotAnimAndSound(); } else diff --git a/indra/newview/llspeakers.cpp b/indra/newview/llspeakers.cpp index 7867e1573c..3b060d8343 100755 --- a/indra/newview/llspeakers.cpp +++ b/indra/newview/llspeakers.cpp @@ -34,11 +34,11 @@ #include "llgroupmgr.h" #include "llsdutil.h" #include "lluicolortable.h" -#include "llhttpclient.h" #include "llviewerobjectlist.h" #include "llviewerregion.h" #include "llvoavatar.h" #include "llworld.h" +#include "llcorehttputil.h" extern LLControlGroup gSavedSettings; @@ -265,49 +265,6 @@ bool LLSpeakersDelayActionsStorage::isTimerStarted(const LLUUID& speaker_id) } // -// ModerationResponder -// - -class ModerationResponder : public LLHTTPClient::Responder -{ - LOG_CLASS(ModerationResponder); -public: - ModerationResponder(const LLUUID& session_id) - { - mSessionID = session_id; - } - -protected: - virtual void httpFailure() - { - LL_WARNS() << dumpResponse() << LL_ENDL; - - if ( gIMMgr ) - { - //403 == you're not a mod - //should be disabled if you're not a moderator - if ( HTTP_FORBIDDEN == getStatus() ) - { - gIMMgr->showSessionEventError( - "mute", - "not_a_mod_error", - mSessionID); - } - else - { - gIMMgr->showSessionEventError( - "mute", - "generic_request_error", - mSessionID); - } - } - } - -private: - LLUUID mSessionID; -}; - -// // LLSpeakerMgr // @@ -883,7 +840,8 @@ void LLIMSpeakerMgr::toggleAllowTextChat(const LLUUID& speaker_id) //current value represents ability to type, so invert data["params"]["mute_info"]["text"] = !speakerp->mModeratorMutedText; - LLHTTPClient::post(url, data, new ModerationResponder(getSessionID())); + LLCoros::instance().launch("LLIMSpeakerMgr::moderationActionCoro", + boost::bind(&LLIMSpeakerMgr::moderationActionCoro, this, url, data)); } void LLIMSpeakerMgr::moderateVoiceParticipant(const LLUUID& avatar_id, bool unmute) @@ -907,10 +865,50 @@ void LLIMSpeakerMgr::moderateVoiceParticipant(const LLUUID& avatar_id, bool unmu data["params"]["mute_info"] = LLSD::emptyMap(); data["params"]["mute_info"]["voice"] = !unmute; - LLHTTPClient::post( - url, - data, - new ModerationResponder(getSessionID())); + LLCoros::instance().launch("LLIMSpeakerMgr::moderationActionCoro", + boost::bind(&LLIMSpeakerMgr::moderationActionCoro, this, url, data)); +} + +void LLIMSpeakerMgr::moderationActionCoro(std::string url, LLSD action) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("moderationActionCoro", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpOptions::ptr_t httpOpts = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions); + + httpOpts->setWantHeaders(true); + + LLUUID sessionId = action["session-id"]; + + LLSD result = httpAdapter->postAndYield(httpRequest, url, action, httpOpts); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) + { + if (gIMMgr) + { + //403 == you're not a mod + //should be disabled if you're not a moderator + if (status == LLCore::HttpStatus(HTTP_FORBIDDEN)) + { + gIMMgr->showSessionEventError( + "mute", + "not_a_mod_error", + sessionId); + } + else + { + gIMMgr->showSessionEventError( + "mute", + "generic_request_error", + sessionId); + } + } + return; + } } void LLIMSpeakerMgr::moderateVoiceAllParticipants( bool unmute_everyone ) @@ -949,7 +947,8 @@ void LLIMSpeakerMgr::moderateVoiceSession(const LLUUID& session_id, bool disallo data["params"]["update_info"]["moderated_mode"] = LLSD::emptyMap(); data["params"]["update_info"]["moderated_mode"]["voice"] = disallow_voice; - LLHTTPClient::post(url, data, new ModerationResponder(session_id)); + LLCoros::instance().launch("LLIMSpeakerMgr::moderationActionCoro", + boost::bind(&LLIMSpeakerMgr::moderationActionCoro, this, url, data)); } void LLIMSpeakerMgr::forceVoiceModeratedMode(bool should_be_muted) diff --git a/indra/newview/llspeakers.h b/indra/newview/llspeakers.h index 0e69184125..5cff70f377 100755 --- a/indra/newview/llspeakers.h +++ b/indra/newview/llspeakers.h @@ -30,6 +30,8 @@ #include "llevent.h" #include "lleventtimer.h" #include "llvoicechannel.h" +#include "lleventcoro.h" +#include "llcoros.h" class LLSpeakerMgr; @@ -333,6 +335,8 @@ protected: */ void forceVoiceModeratedMode(bool should_be_muted); + void moderationActionCoro(std::string url, LLSD action); + }; class LLActiveSpeakerMgr : public LLSpeakerMgr, public LLSingleton<LLActiveSpeakerMgr> diff --git a/indra/newview/llsyntaxid.cpp b/indra/newview/llsyntaxid.cpp index 802dff1ead..7f286044d6 100644 --- a/indra/newview/llsyntaxid.cpp +++ b/indra/newview/llsyntaxid.cpp @@ -31,70 +31,11 @@ #include "llsyntaxid.h" #include "llagent.h" #include "llappviewer.h" -#include "llhttpclient.h" #include "llsdserialize.h" #include "llviewerregion.h" +#include "llcorehttputil.h" //----------------------------------------------------------------------------- -// fetchKeywordsFileResponder -//----------------------------------------------------------------------------- -class fetchKeywordsFileResponder : public LLHTTPClient::Responder -{ -public: - fetchKeywordsFileResponder(const std::string& filespec) - : mFileSpec(filespec) - { - LL_DEBUGS("SyntaxLSL") << "Instantiating with file saving to: '" << filespec << "'" << LL_ENDL; - } - - /* virtual */ void httpFailure() - { - LL_WARNS("SyntaxLSL") << "failed to fetch syntax file [status:" << getStatus() << "]: " << getContent() << LL_ENDL; - } - - /* virtual */ void httpSuccess() - { - // Continue only if a valid LLSD object was returned. - const LLSD& content = getContent(); - if (content.isMap()) - { - if (LLSyntaxIdLSL::getInstance()->isSupportedVersion(content)) - { - LLSyntaxIdLSL::getInstance()->setKeywordsXml(content); - - cacheFile(content); - LLSyntaxIdLSL::getInstance()->handleFileFetched(mFileSpec); - } - else - { - LL_WARNS("SyntaxLSL") << "Unknown or unsupported version of syntax file." << LL_ENDL; - } - } - else - { - LL_WARNS("SyntaxLSL") << "Syntax file '" << mFileSpec << "' contains invalid LLSD." << LL_ENDL; - } - } - - void cacheFile(const LLSD& content_ref) - { - std::stringstream str; - LLSDSerialize::toXML(content_ref, str); - const std::string xml = str.str(); - - // save the str to disk, usually to the cache. - llofstream file(mFileSpec.c_str(), std::ios_base::out); - file.write(xml.c_str(), str.str().size()); - file.close(); - - LL_DEBUGS("SyntaxLSL") << "Syntax file received, saving as: '" << mFileSpec << "'" << LL_ENDL; - } - -private: - std::string mFileSpec; -}; - -//----------------------------------------------------------------------------- // LLSyntaxIdLSL //----------------------------------------------------------------------------- const std::string SYNTAX_ID_CAPABILITY_NAME = "LSLSyntax"; @@ -166,13 +107,72 @@ bool LLSyntaxIdLSL::syntaxIdChanged() //----------------------------------------------------------------------------- void LLSyntaxIdLSL::fetchKeywordsFile(const std::string& filespec) { - mInflightFetches.push_back(filespec); - LLHTTPClient::get(mCapabilityURL, - new fetchKeywordsFileResponder(filespec), - LLSD(), 30.f); + LLCoros::instance().launch("LLSyntaxIdLSL::fetchKeywordsFileCoro", + boost::bind(&LLSyntaxIdLSL::fetchKeywordsFileCoro, this, mCapabilityURL, filespec)); LL_DEBUGS("SyntaxLSL") << "LSLSyntaxId capability URL is: " << mCapabilityURL << ". Filename to use is: '" << filespec << "'." << LL_ENDL; } +//----------------------------------------------------------------------------- +// fetchKeywordsFileCoro +//----------------------------------------------------------------------------- +void LLSyntaxIdLSL::fetchKeywordsFileCoro(std::string url, std::string fileSpec) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("genericPostCoro", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + + std::pair<std::set<std::string>::iterator, bool> insrt = mInflightFetches.insert(fileSpec); + if (!insrt.second) + { + LL_WARNS("SyntaxLSL") << "Already downloading keyword file called \"" << fileSpec << "\"." << LL_ENDL; + return; + } + + LLSD result = httpAdapter->getAndYield(httpRequest, url); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + mInflightFetches.erase(fileSpec); + + if (!status) + { + LL_WARNS("SyntaxLSL") << "Failed to fetch syntax file \"" << fileSpec << "\"" << LL_ENDL; + return; + } + + result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS); + + if (isSupportedVersion(result)) + { + setKeywordsXml(result); + cacheFile(fileSpec, result); + loadKeywordsIntoLLSD(); + } + else + { + LL_WARNS("SyntaxLSL") << "Unknown or unsupported version of syntax file." << LL_ENDL; + } + +} + +//----------------------------------------------------------------------------- +// cacheFile +//----------------------------------------------------------------------------- +void LLSyntaxIdLSL::cacheFile(const std::string &fileSpec, const LLSD& content_ref) +{ + std::stringstream str; + LLSDSerialize::toXML(content_ref, str); + const std::string xml = str.str(); + + // save the str to disk, usually to the cache. + llofstream file(fileSpec.c_str(), std::ios_base::out); + file.write(xml.c_str(), str.str().size()); + file.close(); + + LL_DEBUGS("SyntaxLSL") << "Syntax file received, saving as: '" << fileSpec << "'" << LL_ENDL; +} //----------------------------------------------------------------------------- // initialize @@ -260,8 +260,8 @@ void LLSyntaxIdLSL::loadDefaultKeywordsIntoLLSD() // loadKeywordsFileIntoLLSD //----------------------------------------------------------------------------- /** - * @brief Load xml serialised LLSD - * @desc Opens the specified filespec and attempts to deserialise the + * @brief Load xml serialized LLSD + * @desc Opens the specified filespec and attempts to deserializes the * contained data to the specified LLSD object. indicate success/failure with * sLoaded/sLoadFailed members. */ @@ -276,7 +276,7 @@ void LLSyntaxIdLSL::loadKeywordsIntoLLSD() { if (isSupportedVersion(content)) { - LL_DEBUGS("SyntaxLSL") << "Deserialised: " << mFullFileSpec << LL_ENDL; + LL_DEBUGS("SyntaxLSL") << "Deserialized: " << mFullFileSpec << LL_ENDL; } else { @@ -317,12 +317,6 @@ void LLSyntaxIdLSL::handleCapsReceived(const LLUUID& region_uuid) } } -void LLSyntaxIdLSL::handleFileFetched(const std::string& filepath) -{ - mInflightFetches.remove(filepath); - loadKeywordsIntoLLSD(); -} - boost::signals2::connection LLSyntaxIdLSL::addSyntaxIDCallback(const syntax_id_changed_signal_t::slot_type& cb) { return mSyntaxIDChangedSignal.connect(cb); diff --git a/indra/newview/llsyntaxid.h b/indra/newview/llsyntaxid.h index 504fb0997e..0afa6dc04b 100644 --- a/indra/newview/llsyntaxid.h +++ b/indra/newview/llsyntaxid.h @@ -31,6 +31,8 @@ #include "llviewerprecompiledheaders.h" #include "llsingleton.h" +#include "lleventcoro.h" +#include "llcoros.h" class fetchKeywordsFileResponder; @@ -40,7 +42,7 @@ class LLSyntaxIdLSL : public LLSingleton<LLSyntaxIdLSL> friend class fetchKeywordsFileResponder; private: - std::list<std::string> mInflightFetches; + std::set<std::string> mInflightFetches; typedef boost::signals2::signal<void()> syntax_id_changed_signal_t; syntax_id_changed_signal_t mSyntaxIDChangedSignal; boost::signals2::connection mRegionChangedCallback; @@ -49,13 +51,15 @@ private: bool isSupportedVersion(const LLSD& content); void handleRegionChanged(); void handleCapsReceived(const LLUUID& region_uuid); - void handleFileFetched(const std::string& filepath); void setKeywordsXml(const LLSD& content) { mKeywordsXml = content; }; void buildFullFileSpec(); void fetchKeywordsFile(const std::string& filespec); void loadDefaultKeywordsIntoLLSD(); void loadKeywordsIntoLLSD(); - + + void fetchKeywordsFileCoro(std::string url, std::string fileSpec); + void cacheFile(const std::string &fileSpec, const LLSD& content_ref); + std::string mCapabilityURL; std::string mFullFileSpec; ELLPath mFilePath; diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index fab4203ec3..e61eeb2f4e 100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -35,7 +35,6 @@ #include "lltexturefetch.h" #include "lldir.h" -#include "llhttpclient.h" #include "llhttpconstants.h" #include "llimage.h" #include "llimagej2c.h" @@ -1558,7 +1557,7 @@ bool LLTextureFetchWorker::doWork(S32 param) // Will call callbackHttpGet when curl request completes // Only server bake images use the returned headers currently, for getting retry-after field. - LLCore::HttpOptions *options = (mFTType == FTT_SERVER_BAKE) ? mFetcher->mHttpOptionsWithHeaders: mFetcher->mHttpOptions; + LLCore::HttpOptions::ptr_t options = (mFTType == FTT_SERVER_BAKE) ? mFetcher->mHttpOptionsWithHeaders: mFetcher->mHttpOptions; if (disable_range_req) { // 'Range:' requests may be disabled in which case all HTTP @@ -2510,11 +2509,11 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image mTotalHTTPRequests(0), mQAMode(qa_mode), mHttpRequest(NULL), - mHttpOptions(NULL), - mHttpOptionsWithHeaders(NULL), - mHttpHeaders(NULL), + mHttpOptions(), + mHttpOptionsWithHeaders(), + mHttpHeaders(), mHttpPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID), - mHttpMetricsHeaders(NULL), + mHttpMetricsHeaders(), mHttpMetricsPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID), mTotalCacheReadCount(0U), mTotalCacheWriteCount(0U), @@ -2529,13 +2528,13 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image LLAppCoreHttp & app_core_http(LLAppViewer::instance()->getAppCoreHttp()); mHttpRequest = new LLCore::HttpRequest; - mHttpOptions = new LLCore::HttpOptions; - mHttpOptionsWithHeaders = new LLCore::HttpOptions; + mHttpOptions = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions); + mHttpOptionsWithHeaders = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions); mHttpOptionsWithHeaders->setWantHeaders(true); - mHttpHeaders = new LLCore::HttpHeaders; + mHttpHeaders = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders); mHttpHeaders->append(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_IMAGE_X_J2C); mHttpPolicyClass = app_core_http.getPolicy(LLAppCoreHttp::AP_TEXTURE); - mHttpMetricsHeaders = new LLCore::HttpHeaders; + mHttpMetricsHeaders = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders); mHttpMetricsHeaders->append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_LLSD_XML); mHttpMetricsPolicyClass = app_core_http.getPolicy(LLAppCoreHttp::AP_REPORTING); mHttpHighWater = HTTP_NONPIPE_REQUESTS_HIGH_WATER; @@ -2569,30 +2568,6 @@ LLTextureFetch::~LLTextureFetch() delete req; } - if (mHttpOptions) - { - mHttpOptions->release(); - mHttpOptions = NULL; - } - - if (mHttpOptionsWithHeaders) - { - mHttpOptionsWithHeaders->release(); - mHttpOptionsWithHeaders = NULL; - } - - if (mHttpHeaders) - { - mHttpHeaders->release(); - mHttpHeaders = NULL; - } - - if (mHttpMetricsHeaders) - { - mHttpMetricsHeaders->release(); - mHttpMetricsHeaders = NULL; - } - mHttpWaitResource.clear(); delete mHttpRequest; @@ -4044,7 +4019,7 @@ TFReqSendMetrics::doWork(LLTextureFetch * fetcher) report_priority, mCapsURL, sd, - NULL, + LLCore::HttpOptions::ptr_t(), fetcher->getMetricsHeaders(), handler); LLTextureFetch::svMetricsDataBreak = false; @@ -4163,7 +4138,7 @@ LLTextureFetchDebugger::LLTextureFetchDebugger(LLTextureFetch* fetcher, LLTextur mFetcher(fetcher), mTextureCache(cache), mImageDecodeThread(imagedecodethread), - mHttpHeaders(NULL), + mHttpHeaders(), mHttpPolicyClass(fetcher->getPolicyClass()), mNbCurlCompleted(0), mTempIndex(0), @@ -4177,11 +4152,6 @@ LLTextureFetchDebugger::~LLTextureFetchDebugger() mFetchingHistory.clear(); mStopDebug = TRUE; tryToStopDebug(); - if (mHttpHeaders) - { - mHttpHeaders->release(); - mHttpHeaders = NULL; - } } void LLTextureFetchDebugger::init() @@ -4226,7 +4196,7 @@ void LLTextureFetchDebugger::init() if (! mHttpHeaders) { - mHttpHeaders = new LLCore::HttpHeaders; + mHttpHeaders = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders); mHttpHeaders->append(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_IMAGE_X_J2C); } } @@ -4626,7 +4596,7 @@ S32 LLTextureFetchDebugger::fillCurlQueue() texture_url, 0, requestedSize, - NULL, + LLCore::HttpOptions::ptr_t(), mHttpHeaders, this); if (LLCORE_HTTP_HANDLE_INVALID != handle) diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h index 27779a31e0..e569175e8f 100755 --- a/indra/newview/lltexturefetch.h +++ b/indra/newview/lltexturefetch.h @@ -177,7 +177,7 @@ public: // to do that to hold a reference for any length of time. // // Threads: T* - LLCore::HttpHeaders * getMetricsHeaders() const { return mHttpMetricsHeaders; } + LLCore::HttpHeaders::ptr_t getMetricsHeaders() const { return mHttpMetricsHeaders; } // Threads: T* LLCore::HttpRequest::policy_t getMetricsPolicyClass() const { return mHttpMetricsPolicyClass; } @@ -354,11 +354,11 @@ private: // to make our HTTP requests. These replace the various // LLCurl interfaces used in the past. LLCore::HttpRequest * mHttpRequest; // Ttf - LLCore::HttpOptions * mHttpOptions; // Ttf - LLCore::HttpOptions * mHttpOptionsWithHeaders; // Ttf - LLCore::HttpHeaders * mHttpHeaders; // Ttf + LLCore::HttpOptions::ptr_t mHttpOptions; // Ttf + LLCore::HttpOptions::ptr_t mHttpOptionsWithHeaders; // Ttf + LLCore::HttpHeaders::ptr_t mHttpHeaders; // Ttf LLCore::HttpRequest::policy_t mHttpPolicyClass; // T* - LLCore::HttpHeaders * mHttpMetricsHeaders; // Ttf + LLCore::HttpHeaders::ptr_t mHttpMetricsHeaders; // Ttf LLCore::HttpRequest::policy_t mHttpMetricsPolicyClass; // T* S32 mHttpHighWater; // Ttf S32 mHttpLowWater; // Ttf @@ -510,7 +510,7 @@ private: LLTextureFetch* mFetcher; LLTextureCache* mTextureCache; LLImageDecodeThread* mImageDecodeThread; - LLCore::HttpHeaders* mHttpHeaders; + LLCore::HttpHeaders::ptr_t mHttpHeaders; LLCore::HttpRequest::policy_t mHttpPolicyClass; S32 mNumFetchedTextures; diff --git a/indra/newview/lltwitterconnect.cpp b/indra/newview/lltwitterconnect.cpp index e983bc883f..c6a0a15759 100644 --- a/indra/newview/lltwitterconnect.cpp +++ b/indra/newview/lltwitterconnect.cpp @@ -32,7 +32,6 @@ #include "llagent.h" #include "llcallingcard.h" // for LLAvatarTracker #include "llcommandhandler.h" -#include "llhttpclient.h" #include "llnotificationsutil.h" #include "llurlaction.h" #include "llimagepng.h" @@ -43,6 +42,7 @@ #include "llfloaterwebcontent.h" #include "llfloaterreg.h" +#include "llcorehttputil.h" boost::scoped_ptr<LLEventPump> LLTwitterConnect::sStateWatcher(new LLEventStream("TwitterConnectState")); boost::scoped_ptr<LLEventPump> LLTwitterConnect::sInfoWatcher(new LLEventStream("TwitterConnectInfo")); @@ -67,228 +67,311 @@ void toast_user_for_twitter_success() /////////////////////////////////////////////////////////////////////////////// // -class LLTwitterConnectResponder : public LLHTTPClient::Responder +void LLTwitterConnect::twitterConnectCoro(std::string requestToken, std::string oauthVerifier) { - LOG_CLASS(LLTwitterConnectResponder); -public: - - LLTwitterConnectResponder() + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("TwitterConnect", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); + + httpOpts->setWantHeaders(true); + httpOpts->setFollowRedirects(false); + + LLSD body; + if (!requestToken.empty()) + body["request_token"] = requestToken; + if (!oauthVerifier.empty()) + body["oauth_verifier"] = oauthVerifier; + + setConnectionState(LLTwitterConnect::TWITTER_CONNECTION_IN_PROGRESS); + + LLSD result = httpAdapter->putAndYield(httpRequest, getTwitterConnectURL("/connection"), body, httpOpts); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) { - LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_CONNECTION_IN_PROGRESS); + if ( status == LLCore::HttpStatus(HTTP_FOUND) ) + { + std::string location = httpResults[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_HEADERS][HTTP_IN_HEADER_LOCATION]; + if (location.empty()) + { + LL_WARNS("FlickrConnect") << "Missing Location header " << LL_ENDL; + } + else + { + openTwitterWeb(location); + } + } + else + { + LL_WARNS("TwitterConnect") << "Connection failed " << status.toString() << LL_ENDL; + setConnectionState(LLTwitterConnect::TWITTER_CONNECTION_FAILED); + log_twitter_connect_error("Connect", status.getStatus(), status.toString(), + result.get("error_code"), result.get("error_description")); + } } - - /* virtual */ void httpSuccess() - { - LL_DEBUGS("TwitterConnect") << "Connect successful. " << dumpResponse() << LL_ENDL; - LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_CONNECTED); - } - - /* virtual */ void httpFailure() - { - if ( HTTP_FOUND == getStatus() ) - { - const std::string& location = getResponseHeader(HTTP_IN_HEADER_LOCATION); - if (location.empty()) - { - LL_WARNS("TwitterConnect") << "Missing Location header " << dumpResponse() - << "[headers:" << getResponseHeaders() << "]" << LL_ENDL; - } - else - { - LLTwitterConnect::instance().openTwitterWeb(location); - } - } - else - { - LL_WARNS("TwitterConnect") << dumpResponse() << LL_ENDL; - LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_CONNECTION_FAILED); - const LLSD& content = getContent(); - log_twitter_connect_error("Connect", getStatus(), getReason(), - content.get("error_code"), content.get("error_description")); - } - } -}; + else + { + LL_DEBUGS("TwitterConnect") << "Connect successful. " << LL_ENDL; + setConnectionState(LLTwitterConnect::TWITTER_CONNECTED); + } +} /////////////////////////////////////////////////////////////////////////////// // -class LLTwitterShareResponder : public LLHTTPClient::Responder +bool LLTwitterConnect::testShareStatus(LLSD &result) { - LOG_CLASS(LLTwitterShareResponder); -public: - - LLTwitterShareResponder() - { - LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_POSTING); - } - - /* virtual */ void httpSuccess() - { + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (status) + return true; + + if (status == LLCore::HttpStatus(HTTP_FOUND)) + { + std::string location = httpResults[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_HEADERS][HTTP_IN_HEADER_LOCATION]; + if (location.empty()) + { + LL_WARNS("TwitterConnect") << "Missing Location header " << LL_ENDL; + } + else + { + openTwitterWeb(location); + } + } + if (status == LLCore::HttpStatus(HTTP_NOT_FOUND)) + { + LL_DEBUGS("TwitterConnect") << "Not connected. " << LL_ENDL; + connectToTwitter(); + } + else + { + LL_WARNS("TwitterConnect") << "HTTP Status error " << status.toString() << LL_ENDL; + setConnectionState(LLTwitterConnect::TWITTER_POST_FAILED); + log_twitter_connect_error("Share", status.getStatus(), status.toString(), + result.get("error_code"), result.get("error_description")); + } + return false; +} + +void LLTwitterConnect::twitterShareCoro(std::string route, LLSD share) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("TwitterConnect", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); + + httpOpts->setWantHeaders(true); + httpOpts->setFollowRedirects(false); + + setConnectionState(LLTwitterConnect::TWITTER_POSTING); + + LLSD result = httpAdapter->postAndYield(httpRequest, getTwitterConnectURL(route, true), share, httpOpts); + + if (testShareStatus(result)) + { toast_user_for_twitter_success(); - LL_DEBUGS("TwitterConnect") << "Post successful. " << dumpResponse() << LL_ENDL; - LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_POSTED); - } - - /* virtual */ void httpFailure() - { - if ( HTTP_FOUND == getStatus() ) - { - const std::string& location = getResponseHeader(HTTP_IN_HEADER_LOCATION); - if (location.empty()) - { - LL_WARNS("TwitterConnect") << "Missing Location header " << dumpResponse() - << "[headers:" << getResponseHeaders() << "]" << LL_ENDL; - } - else - { - LLTwitterConnect::instance().openTwitterWeb(location); - } - } - else if ( HTTP_NOT_FOUND == getStatus() ) - { - LLTwitterConnect::instance().connectToTwitter(); - } - else - { - LL_WARNS("TwitterConnect") << dumpResponse() << LL_ENDL; - LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_POST_FAILED); - const LLSD& content = getContent(); - log_twitter_connect_error("Share", getStatus(), getReason(), - content.get("error_code"), content.get("error_description")); - } - } -}; + LL_DEBUGS("TwitterConnect") << "Post successful. " << LL_ENDL; + setConnectionState(LLTwitterConnect::TWITTER_POSTED); + } +} + +void LLTwitterConnect::twitterShareImageCoro(LLPointer<LLImageFormatted> image, std::string status) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("FlickrConnect", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpHeaders::ptr_t httpHeaders(new LLCore::HttpHeaders); + LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); + + httpOpts->setWantHeaders(true); + httpOpts->setFollowRedirects(false); + + std::string imageFormat; + if (dynamic_cast<LLImagePNG*>(image.get())) + { + imageFormat = "png"; + } + else if (dynamic_cast<LLImageJPEG*>(image.get())) + { + imageFormat = "jpg"; + } + else + { + LL_WARNS() << "Image to upload is not a PNG or JPEG" << LL_ENDL; + return; + } + + // All this code is mostly copied from LLWebProfile::post() + const std::string boundary = "----------------------------0123abcdefab"; + + std::string contentType = "multipart/form-data; boundary=" + boundary; + httpHeaders->append("Content-Type", contentType.c_str()); + + LLCore::BufferArray::ptr_t raw = LLCore::BufferArray::ptr_t(new LLCore::BufferArray()); // + LLCore::BufferArrayStream body(raw.get()); + + // *NOTE: The order seems to matter. + body << "--" << boundary << "\r\n" + << "Content-Disposition: form-data; name=\"status\"\r\n\r\n" + << status << "\r\n"; + + body << "--" << boundary << "\r\n" + << "Content-Disposition: form-data; name=\"image\"; filename=\"Untitled." << imageFormat << "\"\r\n" + << "Content-Type: image/" << imageFormat << "\r\n\r\n"; + + // Insert the image data. + // *FIX: Treating this as a string will probably screw it up ... + U8* image_data = image->getData(); + for (S32 i = 0; i < image->getDataSize(); ++i) + { + body << image_data[i]; + } + + body << "\r\n--" << boundary << "--\r\n"; + + LLSD result = httpAdapter->postAndYield(httpRequest, getTwitterConnectURL("/share/photo", true), raw, httpOpts, httpHeaders); + + if (testShareStatus(result)) + { + toast_user_for_twitter_success(); + LL_DEBUGS("TwitterConnect") << "Post successful. " << LL_ENDL; + setConnectionState(LLTwitterConnect::TWITTER_POSTED); + } +} /////////////////////////////////////////////////////////////////////////////// // -class LLTwitterDisconnectResponder : public LLHTTPClient::Responder +void LLTwitterConnect::twitterDisconnectCoro() { - LOG_CLASS(LLTwitterDisconnectResponder); -public: - - LLTwitterDisconnectResponder() - { - LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_DISCONNECTING); - } + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("TwitterConnect", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); - void setUserDisconnected() - { - // Clear data - LLTwitterConnect::instance().clearInfo(); + httpOpts->setFollowRedirects(false); - //Notify state change - LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_NOT_CONNECTED); - } + setConnectionState(LLTwitterConnect::TWITTER_DISCONNECTING); - /* virtual */ void httpSuccess() - { - LL_DEBUGS("TwitterConnect") << "Disconnect successful. " << dumpResponse() << LL_ENDL; - setUserDisconnected(); - } - - /* virtual */ void httpFailure() - { - //User not found so already disconnected - if ( HTTP_NOT_FOUND == getStatus() ) - { - LL_DEBUGS("TwitterConnect") << "Already disconnected. " << dumpResponse() << LL_ENDL; - setUserDisconnected(); - } - else - { - LL_WARNS("TwitterConnect") << dumpResponse() << LL_ENDL; - LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_DISCONNECT_FAILED); - const LLSD& content = getContent(); - log_twitter_connect_error("Disconnect", getStatus(), getReason(), - content.get("error_code"), content.get("error_description")); - } - } -}; + LLSD result = httpAdapter->deleteAndYield(httpRequest, getTwitterConnectURL("/connection"), httpOpts); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status && (status != LLCore::HttpStatus(HTTP_NOT_FOUND))) + { + LL_WARNS("TwitterConnect") << "Disconnect failed!" << LL_ENDL; + setConnectionState(LLTwitterConnect::TWITTER_DISCONNECT_FAILED); + + log_twitter_connect_error("Disconnect", status.getStatus(), status.toString(), + result.get("error_code"), result.get("error_description")); + } + else + { + LL_DEBUGS("TwitterConnect") << "Disconnect successful. " << LL_ENDL; + clearInfo(); + setConnectionState(LLTwitterConnect::TWITTER_NOT_CONNECTED); + } +} /////////////////////////////////////////////////////////////////////////////// // -class LLTwitterConnectedResponder : public LLHTTPClient::Responder +void LLTwitterConnect::twitterConnectedCoro(bool autoConnect) { - LOG_CLASS(LLTwitterConnectedResponder); -public: - - LLTwitterConnectedResponder(bool auto_connect) : mAutoConnect(auto_connect) + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("TwitterConnect", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); + + httpOpts->setFollowRedirects(false); + setConnectionState(LLTwitterConnect::TWITTER_CONNECTION_IN_PROGRESS); + + LLSD result = httpAdapter->getAndYield(httpRequest, getTwitterConnectURL("/connection", true), httpOpts); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) { - LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_CONNECTION_IN_PROGRESS); + if (status == LLCore::HttpStatus(HTTP_NOT_FOUND)) + { + LL_DEBUGS("TwitterConnect") << "Not connected. " << LL_ENDL; + if (autoConnect) + { + connectToTwitter(); + } + else + { + setConnectionState(LLTwitterConnect::TWITTER_NOT_CONNECTED); + } + } + else + { + LL_WARNS("TwitterConnect") << "Failed to test connection:" << status.toTerseString() << LL_ENDL; + + setConnectionState(LLTwitterConnect::TWITTER_CONNECTION_FAILED); + log_twitter_connect_error("Connected", status.getStatus(), status.toString(), + result.get("error_code"), result.get("error_description")); + } + } + else + { + LL_DEBUGS("TwitterConnect") << "Connect successful. " << LL_ENDL; + setConnectionState(LLTwitterConnect::TWITTER_CONNECTED); } - - /* virtual */ void httpSuccess() - { - LL_DEBUGS("TwitterConnect") << "Connect successful. " << dumpResponse() << LL_ENDL; - LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_CONNECTED); - } - - /* virtual */ void httpFailure() - { - // show the facebook login page if not connected yet - if ( HTTP_NOT_FOUND == getStatus() ) - { - LL_DEBUGS("TwitterConnect") << "Not connected. " << dumpResponse() << LL_ENDL; - if (mAutoConnect) - { - LLTwitterConnect::instance().connectToTwitter(); - } - else - { - LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_NOT_CONNECTED); - } - } - else - { - LL_WARNS("TwitterConnect") << dumpResponse() << LL_ENDL; - LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_CONNECTION_FAILED); - const LLSD& content = getContent(); - log_twitter_connect_error("Connected", getStatus(), getReason(), - content.get("error_code"), content.get("error_description")); - } - } - -private: - bool mAutoConnect; -}; + +} /////////////////////////////////////////////////////////////////////////////// // -class LLTwitterInfoResponder : public LLHTTPClient::Responder +void LLTwitterConnect::twitterInfoCoro() { - LOG_CLASS(LLTwitterInfoResponder); -public: + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("TwitterConnect", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); - /* virtual */ void httpSuccess() - { - LL_INFOS("TwitterConnect") << "Twitter: Info received" << LL_ENDL; - LL_DEBUGS("TwitterConnect") << "Getting Twitter info successful. " << dumpResponse() << LL_ENDL; - LLTwitterConnect::instance().storeInfo(getContent()); - } - - /* virtual */ void httpFailure() - { - if ( HTTP_FOUND == getStatus() ) - { - const std::string& location = getResponseHeader(HTTP_IN_HEADER_LOCATION); - if (location.empty()) - { - LL_WARNS("TwitterConnect") << "Missing Location header " << dumpResponse() - << "[headers:" << getResponseHeaders() << "]" << LL_ENDL; - } - else - { - LLTwitterConnect::instance().openTwitterWeb(location); - } - } - else - { - LL_WARNS("TwitterConnect") << dumpResponse() << LL_ENDL; - const LLSD& content = getContent(); - log_twitter_connect_error("Info", getStatus(), getReason(), - content.get("error_code"), content.get("error_description")); - } - } -}; + httpOpts->setWantHeaders(true); + httpOpts->setFollowRedirects(false); + + LLSD result = httpAdapter->getAndYield(httpRequest, getTwitterConnectURL("/info", true), httpOpts); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (status == LLCore::HttpStatus(HTTP_FOUND)) + { + std::string location = httpResults[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_HEADERS][HTTP_IN_HEADER_LOCATION]; + if (location.empty()) + { + LL_WARNS("TwitterConnect") << "Missing Location header " << LL_ENDL; + } + else + { + openTwitterWeb(location); + } + } + else if (!status) + { + LL_WARNS("TwitterConnect") << "Twitter Info failed: " << status.toString() << LL_ENDL; + log_twitter_connect_error("Info", status.getStatus(), status.toString(), + result.get("error_code"), result.get("error_description")); + } + else + { + LL_INFOS("TwitterConnect") << "Twitter: Info received" << LL_ENDL; + result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS); + storeInfo(result); + } +} /////////////////////////////////////////////////////////////////////////////// // @@ -341,36 +424,28 @@ std::string LLTwitterConnect::getTwitterConnectURL(const std::string& route, boo void LLTwitterConnect::connectToTwitter(const std::string& request_token, const std::string& oauth_verifier) { - LLSD body; - if (!request_token.empty()) - body["request_token"] = request_token; - if (!oauth_verifier.empty()) - body["oauth_verifier"] = oauth_verifier; - - LLHTTPClient::put(getTwitterConnectURL("/connection"), body, new LLTwitterConnectResponder()); + LLCoros::instance().launch("LLTwitterConnect::twitterConnectCoro", + boost::bind(&LLTwitterConnect::twitterConnectCoro, this, request_token, oauth_verifier)); } void LLTwitterConnect::disconnectFromTwitter() { - LLHTTPClient::del(getTwitterConnectURL("/connection"), new LLTwitterDisconnectResponder()); + LLCoros::instance().launch("LLTwitterConnect::twitterDisconnectCoro", + boost::bind(&LLTwitterConnect::twitterDisconnectCoro, this)); } void LLTwitterConnect::checkConnectionToTwitter(bool auto_connect) { - const bool follow_redirects = false; - const F32 timeout = HTTP_REQUEST_EXPIRY_SECS; - LLHTTPClient::get(getTwitterConnectURL("/connection", true), new LLTwitterConnectedResponder(auto_connect), - LLSD(), timeout, follow_redirects); + LLCoros::instance().launch("LLTwitterConnect::twitterConnectedCoro", + boost::bind(&LLTwitterConnect::twitterConnectedCoro, this, auto_connect)); } void LLTwitterConnect::loadTwitterInfo() { if(mRefreshInfo) { - const bool follow_redirects = false; - const F32 timeout = HTTP_REQUEST_EXPIRY_SECS; - LLHTTPClient::get(getTwitterConnectURL("/info", true), new LLTwitterInfoResponder(), - LLSD(), timeout, follow_redirects); + LLCoros::instance().launch("LLTwitterConnect::twitterInfoCoro", + boost::bind(&LLTwitterConnect::twitterInfoCoro, this)); } } @@ -379,62 +454,15 @@ void LLTwitterConnect::uploadPhoto(const std::string& image_url, const std::stri LLSD body; body["image"] = image_url; body["status"] = status; - - // Note: we can use that route for different publish action. We should be able to use the same responder. - LLHTTPClient::post(getTwitterConnectURL("/share/photo", true), body, new LLTwitterShareResponder()); + + LLCoros::instance().launch("LLTwitterConnect::twitterShareCoro", + boost::bind(&LLTwitterConnect::twitterShareCoro, this, "/share/photo", body)); } void LLTwitterConnect::uploadPhoto(LLPointer<LLImageFormatted> image, const std::string& status) { - std::string imageFormat; - if (dynamic_cast<LLImagePNG*>(image.get())) - { - imageFormat = "png"; - } - else if (dynamic_cast<LLImageJPEG*>(image.get())) - { - imageFormat = "jpg"; - } - else - { - LL_WARNS() << "Image to upload is not a PNG or JPEG" << LL_ENDL; - return; - } - - // All this code is mostly copied from LLWebProfile::post() - const std::string boundary = "----------------------------0123abcdefab"; - - LLSD headers; - headers["Content-Type"] = "multipart/form-data; boundary=" + boundary; - - std::ostringstream body; - - // *NOTE: The order seems to matter. - body << "--" << boundary << "\r\n" - << "Content-Disposition: form-data; name=\"status\"\r\n\r\n" - << status << "\r\n"; - - body << "--" << boundary << "\r\n" - << "Content-Disposition: form-data; name=\"image\"; filename=\"Untitled." << imageFormat << "\"\r\n" - << "Content-Type: image/" << imageFormat << "\r\n\r\n"; - - // Insert the image data. - // *FIX: Treating this as a string will probably screw it up ... - U8* image_data = image->getData(); - for (S32 i = 0; i < image->getDataSize(); ++i) - { - body << image_data[i]; - } - - body << "\r\n--" << boundary << "--\r\n"; - - // postRaw() takes ownership of the buffer and releases it later. - size_t size = body.str().size(); - U8 *data = new U8[size]; - memcpy(data, body.str().data(), size); - - // Note: we can use that route for different publish action. We should be able to use the same responder. - LLHTTPClient::postRaw(getTwitterConnectURL("/share/photo", true), data, size, new LLTwitterShareResponder(), headers); + LLCoros::instance().launch("LLTwitterConnect::twitterShareImageCoro", + boost::bind(&LLTwitterConnect::twitterShareImageCoro, this, image, status)); } void LLTwitterConnect::updateStatus(const std::string& status) @@ -442,8 +470,8 @@ void LLTwitterConnect::updateStatus(const std::string& status) LLSD body; body["status"] = status; - // Note: we can use that route for different publish action. We should be able to use the same responder. - LLHTTPClient::post(getTwitterConnectURL("/share/status", true), body, new LLTwitterShareResponder()); + LLCoros::instance().launch("LLTwitterConnect::twitterShareCoro", + boost::bind(&LLTwitterConnect::twitterShareCoro, this, "/share/status", body)); } void LLTwitterConnect::storeInfo(const LLSD& info) diff --git a/indra/newview/lltwitterconnect.h b/indra/newview/lltwitterconnect.h index c1df13f18c..be481a17c1 100644 --- a/indra/newview/lltwitterconnect.h +++ b/indra/newview/lltwitterconnect.h @@ -30,6 +30,8 @@ #include "llsingleton.h" #include "llimage.h" +#include "llcoros.h" +#include "lleventcoro.h" class LLEventPump; @@ -94,6 +96,14 @@ private: static boost::scoped_ptr<LLEventPump> sStateWatcher; static boost::scoped_ptr<LLEventPump> sInfoWatcher; static boost::scoped_ptr<LLEventPump> sContentWatcher; + + bool testShareStatus(LLSD &result); + void twitterConnectCoro(std::string requestToken, std::string oauthVerifier); + void twitterDisconnectCoro(); + void twitterConnectedCoro(bool autoConnect); + void twitterInfoCoro(); + void twitterShareCoro(std::string route, LLSD share); + void twitterShareImageCoro(LLPointer<LLImageFormatted> image, std::string status); }; #endif // LL_LLTWITTERCONNECT_H diff --git a/indra/newview/lluploadfloaterobservers.cpp b/indra/newview/lluploadfloaterobservers.cpp deleted file mode 100755 index 69b9b1f9f1..0000000000 --- a/indra/newview/lluploadfloaterobservers.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/** - * @file lluploadfloaterobservers.cpp - * @brief LLUploadModelPermissionsResponder definition - * - * $LicenseInfo:firstyear=2011&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2011, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" - -#include "lluploadfloaterobservers.h" - -LLUploadModelPermissionsResponder::LLUploadModelPermissionsResponder(const LLHandle<LLUploadPermissionsObserver>& observer) -:mObserverHandle(observer) -{ -} - -void LLUploadModelPermissionsResponder::httpFailure() -{ - LL_WARNS() << dumpResponse() << LL_ENDL; - - LLUploadPermissionsObserver* observer = mObserverHandle.get(); - - if (observer) - { - observer->setPermissonsErrorStatus(getStatus(), getReason()); - } -} - -void LLUploadModelPermissionsResponder::httpSuccess() -{ - const LLSD& content = getContent(); - if (!content.isMap()) - { - failureResult(HTTP_INTERNAL_ERROR, "Malformed response contents", content); - return; - } - LLUploadPermissionsObserver* observer = mObserverHandle.get(); - - if (observer) - { - observer->onPermissionsReceived(content); - } -} - diff --git a/indra/newview/lluploadfloaterobservers.h b/indra/newview/lluploadfloaterobservers.h index 4ff4a827a5..02baf8f1c0 100755 --- a/indra/newview/lluploadfloaterobservers.h +++ b/indra/newview/lluploadfloaterobservers.h @@ -79,18 +79,4 @@ protected: LLRootHandle<LLWholeModelUploadObserver> mWholeModelUploadObserverHandle; }; - -class LLUploadModelPermissionsResponder : public LLHTTPClient::Responder -{ - LOG_CLASS(LLUploadModelPermissionsResponder); -public: - LLUploadModelPermissionsResponder(const LLHandle<LLUploadPermissionsObserver>& observer); - -private: - /* virtual */ void httpSuccess(); - /* virtual */ void httpFailure(); - - LLHandle<LLUploadPermissionsObserver> mObserverHandle; -}; - #endif /* LL_LLUPLOADFLOATEROBSERVERS_H */ diff --git a/indra/newview/llviewerassetupload.cpp b/indra/newview/llviewerassetupload.cpp new file mode 100644 index 0000000000..426e89b9d5 --- /dev/null +++ b/indra/newview/llviewerassetupload.cpp @@ -0,0 +1,757 @@ +/** +* @file llviewerassetupload.cpp +* @author optional +* @brief brief description of the file +* +* $LicenseInfo:firstyear=2011&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2011, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA +* $/LicenseInfo$ +*/ + +#include "llviewerprecompiledheaders.h" + +#include "linden_common.h" +#include "llviewertexturelist.h" +#include "llimage.h" +#include "lltrans.h" +#include "lluuid.h" +#include "llvorbisencode.h" +#include "lluploaddialog.h" +#include "llpreviewscript.h" +#include "llnotificationsutil.h" +#include "lleconomy.h" +#include "llagent.h" +#include "llfloaterreg.h" +#include "llstatusbar.h" +#include "llinventorypanel.h" +#include "llsdutil.h" +#include "llviewerassetupload.h" +#include "llappviewer.h" +#include "llviewerstats.h" +#include "llvfile.h" +#include "llgesturemgr.h" +#include "llpreviewnotecard.h" +#include "llpreviewgesture.h" +#include "llcoproceduremanager.h" + +void dialog_refresh_all(); + +LLResourceUploadInfo::LLResourceUploadInfo(LLTransactionID transactId, + LLAssetType::EType assetType, std::string name, std::string description, + S32 compressionInfo, LLFolderType::EType destinationType, + LLInventoryType::EType inventoryType, U32 nextOWnerPerms, + U32 groupPerms, U32 everyonePerms, S32 expectedCost) : + mTransactionId(transactId), + mAssetType(assetType), + mName(name), + mDescription(description), + mCompressionInfo(compressionInfo), + mDestinationFolderType(destinationType), + mInventoryType(inventoryType), + mNextOwnerPerms(nextOWnerPerms), + mGroupPerms(groupPerms), + mEveryonePerms(everyonePerms), + mExpectedUploadCost(expectedCost), + mFolderId(LLUUID::null), + mItemId(LLUUID::null), + mAssetId(LLAssetID::null) +{ } + + +LLResourceUploadInfo::LLResourceUploadInfo(std::string name, + std::string description, S32 compressionInfo, + LLFolderType::EType destinationType, LLInventoryType::EType inventoryType, + U32 nextOWnerPerms, U32 groupPerms, U32 everyonePerms, S32 expectedCost): + mName(name), + mDescription(description), + mCompressionInfo(compressionInfo), + mDestinationFolderType(destinationType), + mInventoryType(inventoryType), + mNextOwnerPerms(nextOWnerPerms), + mGroupPerms(groupPerms), + mEveryonePerms(everyonePerms), + mExpectedUploadCost(expectedCost), + mTransactionId(), + mAssetType(LLAssetType::AT_NONE), + mFolderId(LLUUID::null), + mItemId(LLUUID::null), + mAssetId(LLAssetID::null) +{ + mTransactionId.generate(); +} + + +LLSD LLResourceUploadInfo::prepareUpload() +{ + if (mAssetId.isNull()) + generateNewAssetId(); + + incrementUploadStats(); + assignDefaults(); + + return LLSD().with("success", LLSD::Boolean(true)); +} + +std::string LLResourceUploadInfo::getAssetTypeString() const +{ + return LLAssetType::lookup(mAssetType); +} + +std::string LLResourceUploadInfo::getInventoryTypeString() const +{ + return LLInventoryType::lookup(mInventoryType); +} + +LLSD LLResourceUploadInfo::generatePostBody() +{ + LLSD body; + + body["folder_id"] = mFolderId; + body["asset_type"] = getAssetTypeString(); + body["inventory_type"] = getInventoryTypeString(); + body["name"] = mName; + body["description"] = mDescription; + body["next_owner_mask"] = LLSD::Integer(mNextOwnerPerms); + body["group_mask"] = LLSD::Integer(mGroupPerms); + body["everyone_mask"] = LLSD::Integer(mEveryonePerms); + + return body; + +} + +void LLResourceUploadInfo::logPreparedUpload() +{ + LL_INFOS() << "*** Uploading: " << std::endl << + "Type: " << LLAssetType::lookup(mAssetType) << std::endl << + "UUID: " << mAssetId.asString() << std::endl << + "Name: " << mName << std::endl << + "Desc: " << mDescription << std::endl << + "Expected Upload Cost: " << mExpectedUploadCost << std::endl << + "Folder: " << mFolderId << std::endl << + "Asset Type: " << LLAssetType::lookup(mAssetType) << LL_ENDL; +} + +LLUUID LLResourceUploadInfo::finishUpload(LLSD &result) +{ + if (getFolderId().isNull()) + { + return LLUUID::null; + } + + U32 permsEveryone = PERM_NONE; + U32 permsGroup = PERM_NONE; + U32 permsNextOwner = PERM_ALL; + + if (result.has("new_next_owner_mask")) + { + // The server provided creation perms so use them. + // Do not assume we got the perms we asked for in + // since the server may not have granted them all. + permsEveryone = result["new_everyone_mask"].asInteger(); + permsGroup = result["new_group_mask"].asInteger(); + permsNextOwner = result["new_next_owner_mask"].asInteger(); + } + else + { + // The server doesn't provide creation perms + // so use old assumption-based perms. + if (getAssetTypeString() != "snapshot") + { + permsNextOwner = PERM_MOVE | PERM_TRANSFER; + } + } + + LLPermissions new_perms; + new_perms.init( + gAgent.getID(), + gAgent.getID(), + LLUUID::null, + LLUUID::null); + + new_perms.initMasks( + PERM_ALL, + PERM_ALL, + permsEveryone, + permsGroup, + permsNextOwner); + + U32 flagsInventoryItem = 0; + if (result.has("inventory_flags")) + { + flagsInventoryItem = static_cast<U32>(result["inventory_flags"].asInteger()); + if (flagsInventoryItem != 0) + { + LL_INFOS() << "inventory_item_flags " << flagsInventoryItem << LL_ENDL; + } + } + S32 creationDate = time_corrected(); + + LLUUID serverInventoryItem = result["new_inventory_item"].asUUID(); + LLUUID serverAssetId = result["new_asset"].asUUID(); + + LLPointer<LLViewerInventoryItem> item = new LLViewerInventoryItem( + serverInventoryItem, + getFolderId(), + new_perms, + serverAssetId, + getAssetType(), + getInventoryType(), + getName(), + getDescription(), + LLSaleInfo::DEFAULT, + flagsInventoryItem, + creationDate); + + gInventory.updateItem(item); + gInventory.notifyObservers(); + + return serverInventoryItem; +} + + +LLAssetID LLResourceUploadInfo::generateNewAssetId() +{ + if (gDisconnected) + { + LLAssetID rv; + + rv.setNull(); + return rv; + } + mAssetId = mTransactionId.makeAssetID(gAgent.getSecureSessionID()); + + return mAssetId; +} + +void LLResourceUploadInfo::incrementUploadStats() const +{ + if (LLAssetType::AT_SOUND == mAssetType) + { + add(LLStatViewer::UPLOAD_SOUND, 1); + } + else if (LLAssetType::AT_TEXTURE == mAssetType) + { + add(LLStatViewer::UPLOAD_TEXTURE, 1); + } + else if (LLAssetType::AT_ANIMATION == mAssetType) + { + add(LLStatViewer::ANIMATION_UPLOADS, 1); + } +} + +void LLResourceUploadInfo::assignDefaults() +{ + if (LLInventoryType::IT_NONE == mInventoryType) + { + mInventoryType = LLInventoryType::defaultForAssetType(mAssetType); + } + LLStringUtil::stripNonprintable(mName); + LLStringUtil::stripNonprintable(mDescription); + + if (mName.empty()) + { + mName = "(No Name)"; + } + if (mDescription.empty()) + { + mDescription = "(No Description)"; + } + + mFolderId = gInventory.findCategoryUUIDForType( + (mDestinationFolderType == LLFolderType::FT_NONE) ? + (LLFolderType::EType)mAssetType : mDestinationFolderType); + +} + +std::string LLResourceUploadInfo::getDisplayName() const +{ + return (mName.empty()) ? mAssetId.asString() : mName; +}; + +//========================================================================= +NewFileResourceUploadInfo::NewFileResourceUploadInfo( + std::string fileName, + std::string name, + std::string description, + S32 compressionInfo, + LLFolderType::EType destinationType, + LLInventoryType::EType inventoryType, + U32 nextOWnerPerms, + U32 groupPerms, + U32 everyonePerms, + S32 expectedCost) : + LLResourceUploadInfo(name, description, compressionInfo, + destinationType, inventoryType, + nextOWnerPerms, groupPerms, everyonePerms, expectedCost), + mFileName(fileName) +{ +} + + + +LLSD NewFileResourceUploadInfo::prepareUpload() +{ + if (getAssetId().isNull()) + generateNewAssetId(); + + LLSD result = exportTempFile(); + if (result.has("error")) + return result; + + return LLResourceUploadInfo::prepareUpload(); +} + +LLSD NewFileResourceUploadInfo::exportTempFile() +{ + std::string filename = gDirUtilp->getTempFilename(); + + std::string exten = gDirUtilp->getExtension(getFileName()); + U32 codec = LLImageBase::getCodecFromExtension(exten); + + LLAssetType::EType assetType = LLAssetType::AT_NONE; + std::string errorMessage; + std::string errorLabel; + + bool error = false; + + if (exten.empty()) + { + std::string shortName = gDirUtilp->getBaseFileName(filename); + + // No extension + errorMessage = llformat( + "No file extension for the file: '%s'\nPlease make sure the file has a correct file extension", + shortName.c_str()); + errorLabel = "NoFileExtension"; + error = true; + } + else if (codec != IMG_CODEC_INVALID) + { + // It's an image file, the upload procedure is the same for all + assetType = LLAssetType::AT_TEXTURE; + if (!LLViewerTextureList::createUploadFile(getFileName(), filename, codec)) + { + errorMessage = llformat("Problem with file %s:\n\n%s\n", + getFileName().c_str(), LLImage::getLastError().c_str()); + errorLabel = "ProblemWithFile"; + error = true; + } + } + else if (exten == "wav") + { + assetType = LLAssetType::AT_SOUND; // tag it as audio + S32 encodeResult = 0; + + LL_INFOS() << "Attempting to encode wav as an ogg file" << LL_ENDL; + + encodeResult = encode_vorbis_file(getFileName(), filename); + + if (LLVORBISENC_NOERR != encodeResult) + { + switch (encodeResult) + { + case LLVORBISENC_DEST_OPEN_ERR: + errorMessage = llformat("Couldn't open temporary compressed sound file for writing: %s\n", filename.c_str()); + errorLabel = "CannotOpenTemporarySoundFile"; + break; + + default: + errorMessage = llformat("Unknown vorbis encode failure on: %s\n", getFileName().c_str()); + errorLabel = "UnknownVorbisEncodeFailure"; + break; + } + error = true; + } + } + else if (exten == "bvh") + { + errorMessage = llformat("We do not currently support bulk upload of animation files\n"); + errorLabel = "DoNotSupportBulkAnimationUpload"; + error = true; + } + else if (exten == "anim") + { + assetType = LLAssetType::AT_ANIMATION; + filename = getFileName(); + } + else + { + // Unknown extension + errorMessage = llformat(LLTrans::getString("UnknownFileExtension").c_str(), exten.c_str()); + errorLabel = "ErrorMessage"; + error = TRUE;; + } + + if (error) + { + LLSD errorResult(LLSD::emptyMap()); + + errorResult["error"] = LLSD::Binary(true); + errorResult["message"] = errorMessage; + errorResult["label"] = errorLabel; + return errorResult; + } + + setAssetType(assetType); + + // copy this file into the vfs for upload + S32 file_size; + LLAPRFile infile; + infile.open(filename, LL_APR_RB, NULL, &file_size); + if (infile.getFileHandle()) + { + LLVFile file(gVFS, getAssetId(), assetType, LLVFile::WRITE); + + file.setMaxSize(file_size); + + const S32 buf_size = 65536; + U8 copy_buf[buf_size]; + while ((file_size = infile.read(copy_buf, buf_size))) + { + file.write(copy_buf, file_size); + } + } + else + { + errorMessage = llformat("Unable to access output file: %s", filename.c_str()); + LLSD errorResult(LLSD::emptyMap()); + + errorResult["error"] = LLSD::Binary(true); + errorResult["message"] = errorMessage; + return errorResult; + } + + return LLSD(); + +} + +//========================================================================= +LLBufferedAssetUploadInfo::LLBufferedAssetUploadInfo(LLUUID itemId, LLAssetType::EType assetType, std::string buffer, invnUploadFinish_f finish) : + LLResourceUploadInfo(std::string(), std::string(), 0, LLFolderType::FT_NONE, LLInventoryType::IT_NONE, + 0, 0, 0, 0), + mTaskUpload(false), + mTaskId(LLUUID::null), + mContents(buffer), + mInvnFinishFn(finish), + mTaskFinishFn(NULL), + mStoredToVFS(false) +{ + setItemId(itemId); + setAssetType(assetType); + +} + +LLBufferedAssetUploadInfo::LLBufferedAssetUploadInfo(LLUUID taskId, LLUUID itemId, LLAssetType::EType assetType, std::string buffer, taskUploadFinish_f finish) : + LLResourceUploadInfo(std::string(), std::string(), 0, LLFolderType::FT_NONE, LLInventoryType::IT_NONE, + 0, 0, 0, 0), + mTaskUpload(true), + mTaskId(taskId), + mContents(buffer), + mInvnFinishFn(NULL), + mTaskFinishFn(finish), + mStoredToVFS(false) +{ + setItemId(itemId); + setAssetType(assetType); +} + + +LLSD LLBufferedAssetUploadInfo::prepareUpload() +{ + if (getAssetId().isNull()) + generateNewAssetId(); + + LLVFile file(gVFS, getAssetId(), getAssetType(), LLVFile::APPEND); + + S32 size = mContents.length() + 1; + file.setMaxSize(size); + file.write((U8*)mContents.c_str(), size); + + mStoredToVFS = true; + + + return LLSD().with("success", LLSD::Boolean(true)); +} + +LLSD LLBufferedAssetUploadInfo::generatePostBody() +{ + LLSD body; + + if (!getTaskId().isNull()) + { + body["task_id"] = getTaskId(); + } + body["item_id"] = getItemId(); + + return body; +} + +LLUUID LLBufferedAssetUploadInfo::finishUpload(LLSD &result) +{ + LLUUID newAssetId = result["new_asset"].asUUID(); + LLUUID itemId = getItemId(); + + if (mStoredToVFS) + { + LLAssetType::EType assetType(getAssetType()); + gVFS->renameFile(getAssetId(), assetType, newAssetId, assetType); + } + + if (mTaskUpload) + { + LLUUID taskId = getTaskId(); + + dialog_refresh_all(); + + if (mTaskFinishFn) + { + mTaskFinishFn(itemId, taskId, newAssetId, result); + } + } + else + { + LLViewerInventoryItem* item = (LLViewerInventoryItem*)gInventory.getItem(itemId); + if(!item) + { + LL_WARNS() << "Inventory item for " << getDisplayName() << " is no longer in agent inventory." << LL_ENDL; + return newAssetId; + } + + // Update viewer inventory item + LLPointer<LLViewerInventoryItem> newItem = new LLViewerInventoryItem(item); + newItem->setAssetUUID(newAssetId); + + gInventory.updateItem(newItem); + + LL_INFOS() << "Inventory item " << item->getName() << " saved into " << newAssetId.asString() << LL_ENDL; + + if (mInvnFinishFn) + { + mInvnFinishFn(itemId, newAssetId, newItem->getUUID(), result); + } + gInventory.notifyObservers(); + } + + return newAssetId; +} + +//========================================================================= + +LLScriptAssetUpload::LLScriptAssetUpload(LLUUID itemId, std::string buffer, invnUploadFinish_f finish): + LLBufferedAssetUploadInfo(itemId, LLAssetType::AT_LSL_TEXT, buffer, finish) +{ +} + +// LLScriptAssetUpload::LLScriptAssetUpload(LLUUID taskId, LLUUID itemId, LLAssetType::EType assetType, std::string buffer, taskUploadFinish_f finish): +// LLBufferedAssetUploadInfo() +// { +// } + +LLSD LLScriptAssetUpload::generatePostBody() +{ + LLSD body; + + if (getTaskId().isNull()) + { + body["item_id"] = getItemId(); + body["target"] = "lsl2"; + } + + return body; +} + +//========================================================================= +/*static*/ +LLUUID LLViewerAssetUpload::EnqueueInventoryUpload(const std::string &url, const LLResourceUploadInfo::ptr_t &uploadInfo) +{ + std::string procName("LLViewerAssetUpload::AssetInventoryUploadCoproc("); + + LLUUID queueId = LLCoprocedureManager::getInstance()->enqueueCoprocedure(procName + LLAssetType::lookup(uploadInfo->getAssetType()) + ")", + boost::bind(&LLViewerAssetUpload::AssetInventoryUploadCoproc, _1, _2, url, uploadInfo)); + + return queueId; +} + +//========================================================================= +/*static*/ +void LLViewerAssetUpload::AssetInventoryUploadCoproc(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &httpAdapter, + const LLUUID &id, std::string url, LLResourceUploadInfo::ptr_t uploadInfo) +{ + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + + LLSD result = uploadInfo->prepareUpload(); + uploadInfo->logPreparedUpload(); + + if (result.has("error")) + { + HandleUploadError(LLCore::HttpStatus(499), result, uploadInfo); + return; + } + + //self.yield(); + + if (uploadInfo->showUploadDialog()) + { + std::string uploadMessage = "Uploading...\n\n"; + uploadMessage.append(uploadInfo->getDisplayName()); + LLUploadDialog::modalUploadDialog(uploadMessage); + } + + LLSD body = uploadInfo->generatePostBody(); + + result = httpAdapter->postAndYield(httpRequest, url, body); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if ((!status) || (result.has("error"))) + { + HandleUploadError(status, result, uploadInfo); + if (uploadInfo->showUploadDialog()) + LLUploadDialog::modalUploadFinished(); + return; + } + + std::string uploader = result["uploader"].asString(); + + result = httpAdapter->postFileAndYield(httpRequest, uploader, uploadInfo->getAssetId(), uploadInfo->getAssetType()); + httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) + { + HandleUploadError(status, result, uploadInfo); + if (uploadInfo->showUploadDialog()) + LLUploadDialog::modalUploadFinished(); + return; + } + + S32 uploadPrice = 0; + + // Update L$ and ownership credit information + // since it probably changed on the server + if (uploadInfo->getAssetType() == LLAssetType::AT_TEXTURE || + uploadInfo->getAssetType() == LLAssetType::AT_SOUND || + uploadInfo->getAssetType() == LLAssetType::AT_ANIMATION || + uploadInfo->getAssetType() == LLAssetType::AT_MESH) + { + uploadPrice = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload(); + } + + bool success = false; + + if (uploadPrice > 0) + { + // this upload costed us L$, update our balance + // and display something saying that it cost L$ + LLStatusBar::sendMoneyBalanceRequest(); + + LLSD args; + args["AMOUNT"] = llformat("%d", uploadPrice); + LLNotificationsUtil::add("UploadPayment", args); + } + + LLUUID serverInventoryItem = uploadInfo->finishUpload(result); + + if (serverInventoryItem.notNull()) + { + success = true; + + // Show the preview panel for textures and sounds to let + // user know that the image (or snapshot) arrived intact. + LLInventoryPanel* panel = LLInventoryPanel::getActiveInventoryPanel(); + if (panel) + { + LLFocusableElement* focus = gFocusMgr.getKeyboardFocus(); + panel->setSelection(serverInventoryItem, TAKE_FOCUS_NO); + + // restore keyboard focus + gFocusMgr.setKeyboardFocus(focus); + } + } + else + { + LL_WARNS() << "Can't find a folder to put it in" << LL_ENDL; + } + + // remove the "Uploading..." message + if (uploadInfo->showUploadDialog()) + LLUploadDialog::modalUploadFinished(); + + // Let the Snapshot floater know we have finished uploading a snapshot to inventory. + LLFloater* floater_snapshot = LLFloaterReg::findInstance("snapshot"); + if (uploadInfo->getAssetType() == LLAssetType::AT_TEXTURE && floater_snapshot) + { + floater_snapshot->notify(LLSD().with("set-finished", LLSD().with("ok", success).with("msg", "inventory"))); + } +} + +//========================================================================= +/*static*/ +void LLViewerAssetUpload::HandleUploadError(LLCore::HttpStatus status, LLSD &result, LLResourceUploadInfo::ptr_t &uploadInfo) +{ + std::string reason; + std::string label("CannotUploadReason"); + + LL_WARNS() << ll_pretty_print_sd(result) << LL_ENDL; + + if (result.has("label")) + { + label = result["label"].asString(); + } + + if (result.has("message")) + { + reason = result["message"].asString(); + } + else + { + if (status.getType() == 499) + { + reason = "The server is experiencing unexpected difficulties."; + } + else + { + reason = "Error in upload request. Please visit " + "http://secondlife.com/support for help fixing this problem."; + } + } + + LLSD args; + args["FILE"] = uploadInfo->getDisplayName(); + args["REASON"] = reason; + + LLNotificationsUtil::add(label, args); + + // unfreeze script preview + if (uploadInfo->getAssetType() == LLAssetType::AT_LSL_TEXT) + { + LLPreviewLSL* preview = LLFloaterReg::findTypedInstance<LLPreviewLSL>("preview_script", + uploadInfo->getItemId()); + if (preview) + { + LLSD errors; + errors.append(LLTrans::getString("UploadFailed") + reason); + preview->callbackLSLCompileFailed(errors); + } + } + +} + diff --git a/indra/newview/llviewerassetupload.h b/indra/newview/llviewerassetupload.h new file mode 100644 index 0000000000..fa8247cb64 --- /dev/null +++ b/indra/newview/llviewerassetupload.h @@ -0,0 +1,208 @@ +/** +* @file llviewerassetupload.h +* @author optional +* @brief brief description of the file +* +* $LicenseInfo:firstyear=2011&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2011, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA +* $/LicenseInfo$ +*/ + +#ifndef LL_VIEWER_ASSET_UPLOAD_H +#define LL_VIEWER_ASSET_UPLOAD_H + +#include "llfoldertype.h" +#include "llassettype.h" +#include "llinventorytype.h" +#include "lleventcoro.h" +#include "llcoros.h" +#include "llcorehttputil.h" + +//========================================================================= +class LLResourceUploadInfo +{ +public: + typedef boost::shared_ptr<LLResourceUploadInfo> ptr_t; + + LLResourceUploadInfo( + LLTransactionID transactId, + LLAssetType::EType assetType, + std::string name, + std::string description, + S32 compressionInfo, + LLFolderType::EType destinationType, + LLInventoryType::EType inventoryType, + U32 nextOWnerPerms, + U32 groupPerms, + U32 everyonePerms, + S32 expectedCost); + + virtual ~LLResourceUploadInfo() + { } + + virtual LLSD prepareUpload(); + virtual LLSD generatePostBody(); + virtual void logPreparedUpload(); + virtual LLUUID finishUpload(LLSD &result); + + LLTransactionID getTransactionId() const { return mTransactionId; } + LLAssetType::EType getAssetType() const { return mAssetType; } + std::string getAssetTypeString() const; + std::string getName() const { return mName; }; + std::string getDescription() const { return mDescription; }; + S32 getCompressionInfo() const { return mCompressionInfo; }; + LLFolderType::EType getDestinationFolderType() const { return mDestinationFolderType; }; + LLInventoryType::EType getInventoryType() const { return mInventoryType; }; + std::string getInventoryTypeString() const; + U32 getNextOwnerPerms() const { return mNextOwnerPerms; }; + U32 getGroupPerms() const { return mGroupPerms; }; + U32 getEveryonePerms() const { return mEveryonePerms; }; + S32 getExpectedUploadCost() const { return mExpectedUploadCost; }; + + virtual bool showUploadDialog() const { return true; } + + virtual std::string getDisplayName() const; + + LLUUID getFolderId() const { return mFolderId; } + LLUUID getItemId() const { return mItemId; } + LLAssetID getAssetId() const { return mAssetId; } + +protected: + LLResourceUploadInfo( + std::string name, + std::string description, + S32 compressionInfo, + LLFolderType::EType destinationType, + LLInventoryType::EType inventoryType, + U32 nextOWnerPerms, + U32 groupPerms, + U32 everyonePerms, + S32 expectedCost); + + void setTransactionId(LLTransactionID tid) { mTransactionId = tid; } + void setAssetType(LLAssetType::EType assetType) { mAssetType = assetType; } + void setItemId(LLUUID itemId) { mItemId = itemId; } + + LLAssetID generateNewAssetId(); + void incrementUploadStats() const; + virtual void assignDefaults(); + +private: + LLTransactionID mTransactionId; + LLAssetType::EType mAssetType; + std::string mName; + std::string mDescription; + S32 mCompressionInfo; + LLFolderType::EType mDestinationFolderType; + LLInventoryType::EType mInventoryType; + U32 mNextOwnerPerms; + U32 mGroupPerms; + U32 mEveryonePerms; + S32 mExpectedUploadCost; + + LLUUID mFolderId; + LLUUID mItemId; + LLAssetID mAssetId; +}; + +//------------------------------------------------------------------------- +class NewFileResourceUploadInfo : public LLResourceUploadInfo +{ +public: + NewFileResourceUploadInfo( + std::string fileName, + std::string name, + std::string description, + S32 compressionInfo, + LLFolderType::EType destinationType, + LLInventoryType::EType inventoryType, + U32 nextOWnerPerms, + U32 groupPerms, + U32 everyonePerms, + S32 expectedCost); + + virtual LLSD prepareUpload(); + + std::string getFileName() const { return mFileName; }; + +protected: + + virtual LLSD exportTempFile(); + +private: + std::string mFileName; + +}; + +//------------------------------------------------------------------------- +class LLBufferedAssetUploadInfo : public LLResourceUploadInfo +{ +public: + typedef boost::function<void(LLUUID itemId, LLUUID newAssetId, LLUUID newItemId, LLSD response)> invnUploadFinish_f; + typedef boost::function<void(LLUUID itemId, LLUUID taskId, LLUUID newAssetId, LLSD response)> taskUploadFinish_f; + + LLBufferedAssetUploadInfo(LLUUID itemId, LLAssetType::EType assetType, std::string buffer, invnUploadFinish_f finish); + LLBufferedAssetUploadInfo(LLUUID taskId, LLUUID itemId, LLAssetType::EType assetType, std::string buffer, taskUploadFinish_f finish); + + virtual LLSD prepareUpload(); + virtual LLSD generatePostBody(); + virtual LLUUID finishUpload(LLSD &result); + + LLUUID getTaskId() const { return mTaskId; } + const std::string & getContents() const { return mContents; } + + virtual bool showUploadDialog() const { return false; } + +protected: + + +private: + bool mTaskUpload; + LLUUID mTaskId; + std::string mContents; + invnUploadFinish_f mInvnFinishFn; + taskUploadFinish_f mTaskFinishFn; + bool mStoredToVFS; +}; + +//------------------------------------------------------------------------- +class LLScriptAssetUpload : public LLBufferedAssetUploadInfo +{ +public: + LLScriptAssetUpload(LLUUID itemId, std::string buffer, invnUploadFinish_f finish); + LLScriptAssetUpload(LLUUID taskId, LLUUID itemId, LLAssetType::EType assetType, std::string buffer, taskUploadFinish_f finish); + + virtual LLSD generatePostBody(); + +}; + +//========================================================================= +class LLViewerAssetUpload +{ +public: + static LLUUID EnqueueInventoryUpload(const std::string &url, const LLResourceUploadInfo::ptr_t &uploadInfo); + + static void AssetInventoryUploadCoproc(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &httpAdapter, const LLUUID &id, std::string url, LLResourceUploadInfo::ptr_t uploadInfo); + +private: + static void HandleUploadError(LLCore::HttpStatus status, LLSD &result, LLResourceUploadInfo::ptr_t &uploadInfo); +}; + +#endif // !VIEWER_ASSET_UPLOAD_H diff --git a/indra/newview/llviewerdisplayname.cpp b/indra/newview/llviewerdisplayname.cpp deleted file mode 100755 index e390e8776d..0000000000 --- a/indra/newview/llviewerdisplayname.cpp +++ /dev/null @@ -1,211 +0,0 @@ -/** - * @file llviewerdisplayname.cpp - * @brief Wrapper for display name functionality - * - * $LicenseInfo:firstyear=2010&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" - -#include "llviewerdisplayname.h" - -// viewer includes -#include "llagent.h" -#include "llviewerregion.h" -#include "llvoavatar.h" - -// library includes -#include "llavatarnamecache.h" -#include "llhttpclient.h" -#include "llhttpnode.h" -#include "llnotificationsutil.h" -#include "llui.h" // getLanguage() - -namespace LLViewerDisplayName -{ - // Fired when viewer receives server response to display name change - set_name_signal_t sSetDisplayNameSignal; - - // Fired when there is a change in the agent's name - name_changed_signal_t sNameChangedSignal; - - void addNameChangedCallback(const name_changed_signal_t::slot_type& cb) - { - sNameChangedSignal.connect(cb); - } - - void doNothing() { } -} - -class LLSetDisplayNameResponder : public LLHTTPClient::Responder -{ - LOG_CLASS(LLSetDisplayNameResponder); -private: - // only care about errors - /*virtual*/ void httpFailure() - { - LL_WARNS() << dumpResponse() << LL_ENDL; - LLViewerDisplayName::sSetDisplayNameSignal(false, "", LLSD()); - LLViewerDisplayName::sSetDisplayNameSignal.disconnect_all_slots(); - } -}; - -void LLViewerDisplayName::set(const std::string& display_name, const set_name_slot_t& slot) -{ - // TODO: simple validation here - - LLViewerRegion* region = gAgent.getRegion(); - llassert(region); - std::string cap_url = region->getCapability("SetDisplayName"); - if (cap_url.empty()) - { - // this server does not support display names, report error - slot(false, "unsupported", LLSD()); - return; - } - - // People API can return localized error messages. Indicate our - // language preference via header. - LLSD headers; - headers[HTTP_OUT_HEADER_ACCEPT_LANGUAGE] = LLUI::getLanguage(); - - // People API requires both the old and new value to change a variable. - // Our display name will be in cache before the viewer's UI is available - // to request a change, so we can use direct lookup without callback. - LLAvatarName av_name; - if (!LLAvatarNameCache::get( gAgent.getID(), &av_name)) - { - slot(false, "name unavailable", LLSD()); - return; - } - - // People API expects array of [ "old value", "new value" ] - LLSD change_array = LLSD::emptyArray(); - change_array.append(av_name.getDisplayName()); - change_array.append(display_name); - - LL_INFOS() << "Set name POST to " << cap_url << LL_ENDL; - - // Record our caller for when the server sends back a reply - sSetDisplayNameSignal.connect(slot); - - // POST the requested change. The sim will not send a response back to - // this request directly, rather it will send a separate message after it - // communicates with the back-end. - LLSD body; - body["display_name"] = change_array; - LLHTTPClient::post(cap_url, body, new LLSetDisplayNameResponder, headers); -} - -class LLSetDisplayNameReply : public LLHTTPNode -{ - LOG_CLASS(LLSetDisplayNameReply); -public: - /*virtual*/ void post( - LLHTTPNode::ResponsePtr response, - const LLSD& context, - const LLSD& input) const - { - LLSD body = input["body"]; - - S32 status = body["status"].asInteger(); - bool success = (status == HTTP_OK); - std::string reason = body["reason"].asString(); - LLSD content = body["content"]; - - LL_INFOS() << "status " << status << " reason " << reason << LL_ENDL; - - // If viewer's concept of display name is out-of-date, the set request - // will fail with 409 Conflict. If that happens, fetch up-to-date - // name information. - if (status == HTTP_CONFLICT) - { - LLUUID agent_id = gAgent.getID(); - // Flush stale data - LLAvatarNameCache::erase( agent_id ); - // Queue request for new data: nothing to do on callback though... - // Note: no need to disconnect the callback as it never gets out of scope - LLAvatarNameCache::get(agent_id, boost::bind(&LLViewerDisplayName::doNothing)); - // Kill name tag, as it is wrong - LLVOAvatar::invalidateNameTag( agent_id ); - } - - // inform caller of result - LLViewerDisplayName::sSetDisplayNameSignal(success, reason, content); - LLViewerDisplayName::sSetDisplayNameSignal.disconnect_all_slots(); - } -}; - - -class LLDisplayNameUpdate : public LLHTTPNode -{ - /*virtual*/ void post( - LLHTTPNode::ResponsePtr response, - const LLSD& context, - const LLSD& input) const - { - LLSD body = input["body"]; - LLUUID agent_id = body["agent_id"]; - std::string old_display_name = body["old_display_name"]; - // By convention this record is called "agent" in the People API - LLSD name_data = body["agent"]; - - // Inject the new name data into cache - LLAvatarName av_name; - av_name.fromLLSD( name_data ); - - LL_INFOS() << "name-update now " << LLDate::now() - << " next_update " << LLDate(av_name.mNextUpdate) - << LL_ENDL; - - // Name expiration time may be provided in headers, or we may use a - // default value - // *TODO: get actual headers out of ResponsePtr - //LLSD headers = response->mHeaders; - LLSD headers; - av_name.mExpires = - LLAvatarNameCache::nameExpirationFromHeaders(headers); - - LLAvatarNameCache::insert(agent_id, av_name); - - // force name tag to update - LLVOAvatar::invalidateNameTag(agent_id); - - LLSD args; - args["OLD_NAME"] = old_display_name; - args["SLID"] = av_name.getUserName(); - args["NEW_NAME"] = av_name.getDisplayName(); - LLNotificationsUtil::add("DisplayNameUpdate", args); - if (agent_id == gAgent.getID()) - { - LLViewerDisplayName::sNameChangedSignal(); - } - } -}; - -LLHTTPRegistration<LLSetDisplayNameReply> - gHTTPRegistrationMessageSetDisplayNameReply( - "/message/SetDisplayNameReply"); - -LLHTTPRegistration<LLDisplayNameUpdate> - gHTTPRegistrationMessageDisplayNameUpdate( - "/message/DisplayNameUpdate"); diff --git a/indra/newview/llviewerdisplayname.h b/indra/newview/llviewerdisplayname.h deleted file mode 100755 index 16d59ae43b..0000000000 --- a/indra/newview/llviewerdisplayname.h +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file llviewerdisplayname.h - * @brief Wrapper for display name functionality - * - * $LicenseInfo:firstyear=2010&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LLVIEWERDISPLAYNAME_H -#define LLVIEWERDISPLAYNAME_H - -#include <boost/signals2.hpp> - -class LLSD; -class LLUUID; - -namespace LLViewerDisplayName -{ - typedef boost::signals2::signal< - void (bool success, const std::string& reason, const LLSD& content)> - set_name_signal_t; - typedef set_name_signal_t::slot_type set_name_slot_t; - - typedef boost::signals2::signal<void (void)> name_changed_signal_t; - typedef name_changed_signal_t::slot_type name_changed_slot_t; - - // Sends an update to the server to change a display name - // and call back when done. May not succeed due to service - // unavailable or name not available. - void set(const std::string& display_name, const set_name_slot_t& slot); - - void addNameChangedCallback(const name_changed_signal_t::slot_type& cb); -} - -#endif // LLVIEWERDISPLAYNAME_H diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index 1dad9dd506..8627c848b5 100755 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -56,7 +56,6 @@ #include "llfloaterconversationpreview.h" #include "llfloaterdeleteenvpreset.h" #include "llfloaterdestinations.h" -#include "llfloaterdisplayname.h" #include "llfloatereditdaycycle.h" #include "llfloatereditsky.h" #include "llfloatereditwater.h" @@ -244,7 +243,6 @@ void LLViewerFloaterReg::registerFloaters() LLInspectRemoteObjectUtil::registerFloater(); LLFloaterVoiceVolumeUtil::registerFloater(); LLNotificationsUI::registerFloater(); - LLFloaterDisplayNameUtil::registerFloater(); LLFloaterReg::add("lagmeter", "floater_lagmeter.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterLagMeter>); LLFloaterReg::add("land_holdings", "floater_land_holdings.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterLandHoldings>); diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index a8634d1e93..f6db5d5d77 100755 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -1750,27 +1750,20 @@ void copy_inventory_from_notecard(const LLUUID& destination_id, return; } - // check capability to prevent a crash while LL_ERRS in LLCapabilityListener::capListener. See EXT-8459. - std::string url = viewer_region->getCapability("CopyInventoryFromNotecard"); - if (url.empty()) - { - LL_WARNS(LOG_NOTECARD) << "There is no 'CopyInventoryFromNotecard' capability" - << " for region: " << viewer_region->getName() - << LL_ENDL; - return; - } - - LLSD request, body; + LLSD body; body["notecard-id"] = notecard_inv_id; body["object-id"] = object_id; body["item-id"] = src->getUUID(); body["folder-id"] = destination_id; body["callback-id"] = (LLSD::Integer)callback_id; - request["message"] = "CopyInventoryFromNotecard"; - request["payload"] = body; - - viewer_region->getCapAPI().post(request); + /// *TODO: RIDER: This posts the request under the agents policy. + /// When I convert the inventory over this call should be moved under that + /// policy as well. + if (!gAgent.requestPostCapability("CopyInventoryFromNotecard", body)) + { + LL_WARNS() << "SIM does not have the capability to copy from notecard." << LL_ENDL; + } } void create_new_item(const std::string& name, diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index 3eae0f8d86..f332a4e98e 100755 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -69,6 +69,7 @@ #include "llwebprofile.h" #include "llwindow.h" #include "llvieweraudio.h" +#include "llcorehttputil.h" #include "llfloaterwebcontent.h" // for handling window close requests and geometry change requests in media browser windows. @@ -152,190 +153,6 @@ LLViewerMediaObserver::~LLViewerMediaObserver() } -// Move this to its own file. -// helper class that tries to download a URL from a web site and calls a method -// on the Panel Land Media and to discover the MIME type -class LLMimeDiscoveryResponder : public LLHTTPClient::Responder -{ - LOG_CLASS(LLMimeDiscoveryResponder); -public: - LLMimeDiscoveryResponder( viewer_media_t media_impl) - : mMediaImpl(media_impl), - mInitialized(false) - { - if(mMediaImpl->mMimeTypeProbe != NULL) - { - LL_ERRS() << "impl already has an outstanding responder" << LL_ENDL; - } - - mMediaImpl->mMimeTypeProbe = this; - } - - ~LLMimeDiscoveryResponder() - { - disconnectOwner(); - } - -private: - /* virtual */ void httpCompleted() - { - if (!isGoodStatus()) - { - LL_WARNS() << dumpResponse() - << " [headers:" << getResponseHeaders() << "]" << LL_ENDL; - } - const std::string& media_type = getResponseHeader(HTTP_IN_HEADER_CONTENT_TYPE); - std::string::size_type idx1 = media_type.find_first_of(";"); - std::string mime_type = media_type.substr(0, idx1); - - LL_DEBUGS() << "status is " << getStatus() << ", media type \"" << media_type << "\"" << LL_ENDL; - - // 2xx status codes indicate success. - // Most 4xx status codes are successful enough for our purposes. - // 499 is the error code for host not found, timeout, etc. - // 500 means "Internal Server error" but we decided it's okay to - // accept this and go past it in the MIME type probe - // 302 means the resource can be found temporarily in a different place - added this for join.secondlife.com - // 499 is a code specifc to join.secondlife.com apparently safe to ignore -// if( ((status >= 200) && (status < 300)) || -// ((status >= 400) && (status < 499)) || -// (status == 500) || -// (status == 302) || -// (status == 499) -// ) - // We now no longer check the error code returned from the probe. - // If we have a mime type, use it. If not, default to the web plugin and let it handle error reporting. - //if(1) - { - // The probe was successful. - if(mime_type.empty()) - { - // Some sites don't return any content-type header at all. - // Treat an empty mime type as text/html. - mime_type = HTTP_CONTENT_TEXT_HTML; - } - } - //else - //{ - // LL_WARNS() << "responder failed with status " << dumpResponse() << LL_ENDL; - // - // if(mMediaImpl) - // { - // mMediaImpl->mMediaSourceFailed = true; - // } - // return; - //} - - // the call to initializeMedia may disconnect the responder, which will clear mMediaImpl. - // Make a local copy so we can call loadURI() afterwards. - LLViewerMediaImpl *impl = mMediaImpl; - - if(impl && !mInitialized && ! mime_type.empty()) - { - if(impl->initializeMedia(mime_type)) - { - mInitialized = true; - impl->loadURI(); - disconnectOwner(); - } - } - } - -public: - void cancelRequest() - { - disconnectOwner(); - } - -private: - void disconnectOwner() - { - if(mMediaImpl) - { - if(mMediaImpl->mMimeTypeProbe != this) - { - LL_ERRS() << "internal error: mMediaImpl->mMimeTypeProbe != this" << LL_ENDL; - } - - mMediaImpl->mMimeTypeProbe = NULL; - } - mMediaImpl = NULL; - } - - -public: - LLViewerMediaImpl *mMediaImpl; - bool mInitialized; -}; - -class LLViewerMediaOpenIDResponder : public LLHTTPClient::Responder -{ - LOG_CLASS(LLViewerMediaOpenIDResponder); -public: - LLViewerMediaOpenIDResponder( ) - { - } - - ~LLViewerMediaOpenIDResponder() - { - } - - /* virtual */ void completedRaw( - const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer) - { - // We don't care about the content of the response, only the Set-Cookie header. - LL_DEBUGS("MediaAuth") << dumpResponse() - << " [headers:" << getResponseHeaders() << "]" << LL_ENDL; - const std::string& cookie = getResponseHeader(HTTP_IN_HEADER_SET_COOKIE); - - // *TODO: What about bad status codes? Does this destroy previous cookies? - LLViewerMedia::openIDCookieResponse(cookie); - } - -}; - -class LLViewerMediaWebProfileResponder : public LLHTTPClient::Responder -{ -LOG_CLASS(LLViewerMediaWebProfileResponder); -public: - LLViewerMediaWebProfileResponder(std::string host) - { - mHost = host; - } - - ~LLViewerMediaWebProfileResponder() - { - } - - void completedRaw( - const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer) - { - // We don't care about the content of the response, only the set-cookie header. - LL_WARNS("MediaAuth") << dumpResponse() - << " [headers:" << getResponseHeaders() << "]" << LL_ENDL; - - LLSD stripped_content = getResponseHeaders(); - // *TODO: Check that this works. - stripped_content.erase(HTTP_IN_HEADER_SET_COOKIE); - LL_WARNS("MediaAuth") << stripped_content << LL_ENDL; - - const std::string& cookie = getResponseHeader(HTTP_IN_HEADER_SET_COOKIE); - LL_DEBUGS("MediaAuth") << "cookie = " << cookie << LL_ENDL; - - // *TODO: What about bad status codes? Does this destroy previous cookies? - LLViewerMedia::getCookieStore()->setCookiesFromHost(cookie, mHost); - - // Set cookie for snapshot publishing. - std::string auth_cookie = cookie.substr(0, cookie.find(";")); // strip path - LLWebProfile::setAuthCookie(auth_cookie); - } - - std::string mHost; -}; - - LLPluginCookieStore *LLViewerMedia::sCookieStore = NULL; LLURL LLViewerMedia::sOpenIDURL; std::string LLViewerMedia::sOpenIDCookie; @@ -1388,87 +1205,172 @@ LLSD LLViewerMedia::getHeaders() return headers; } +LLCore::HttpHeaders::ptr_t LLViewerMedia::getHttpHeaders() +{ + LLCore::HttpHeaders::ptr_t headers(new LLCore::HttpHeaders); + + headers->append(HTTP_OUT_HEADER_ACCEPT, "*/*"); + headers->append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_XML); + headers->append(HTTP_OUT_HEADER_COOKIE, sOpenIDCookie); + headers->append(HTTP_OUT_HEADER_USER_AGENT, getCurrentUserAgent()); + + return headers; +} + ///////////////////////////////////////////////////////////////////////////////////////// // static void LLViewerMedia::setOpenIDCookie() { if(!sOpenIDCookie.empty()) { - // The LLURL can give me the 'authority', which is of the form: [username[:password]@]hostname[:port] - // We want just the hostname for the cookie code, but LLURL doesn't seem to have a way to extract that. - // We therefore do it here. - std::string authority = sOpenIDURL.mAuthority; - std::string::size_type host_start = authority.find('@'); - if(host_start == std::string::npos) - { - // no username/password - host_start = 0; - } - else - { - // Hostname starts after the @. - // (If the hostname part is empty, this may put host_start at the end of the string. In that case, it will end up passing through an empty hostname, which is correct.) - ++host_start; - } - std::string::size_type host_end = authority.rfind(':'); - if((host_end == std::string::npos) || (host_end < host_start)) - { - // no port - host_end = authority.size(); - } - - getCookieStore()->setCookiesFromHost(sOpenIDCookie, authority.substr(host_start, host_end - host_start)); + std::string profileUrl = getProfileURL(""); + + LLCoros::instance().launch("LLViewerMedia::getOpenIDCookieCoro", + boost::bind(&LLViewerMedia::getOpenIDCookieCoro, profileUrl)); + } +} + +/*static*/ +void LLViewerMedia::getOpenIDCookieCoro(std::string url) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("getOpenIDCookieCoro", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); + LLCore::HttpHeaders::ptr_t httpHeaders(new LLCore::HttpHeaders); + + httpOpts->setFollowRedirects(true); + httpOpts->setWantHeaders(true); + + LLURL hostUrl(url.c_str()); + std::string hostAuth = hostUrl.getAuthority(); + + // *TODO: Expand LLURL to split and extract this information better. + // The structure of a URL is well defined and needing to retrieve parts of it are common. + // original comment: + // The LLURL can give me the 'authority', which is of the form: [username[:password]@]hostname[:port] + // We want just the hostname for the cookie code, but LLURL doesn't seem to have a way to extract that. + // We therefore do it here. + std::string authority = sOpenIDURL.mAuthority; + std::string::size_type hostStart = authority.find('@'); + if (hostStart == std::string::npos) + { // no username/password + hostStart = 0; + } + else + { // Hostname starts after the @. + // (If the hostname part is empty, this may put host_start at the end of the string. In that case, it will end up passing through an empty hostname, which is correct.) + ++hostStart; + } + std::string::size_type hostEnd = authority.rfind(':'); + if ((hostEnd == std::string::npos) || (hostEnd < hostStart)) + { // no port + hostEnd = authority.size(); + } - // Do a web profile get so we can store the cookie - LLSD headers = LLSD::emptyMap(); - headers[HTTP_OUT_HEADER_ACCEPT] = "*/*"; - headers[HTTP_OUT_HEADER_COOKIE] = sOpenIDCookie; - headers[HTTP_OUT_HEADER_USER_AGENT] = getCurrentUserAgent(); + getCookieStore()->setCookiesFromHost(sOpenIDCookie, authority.substr(hostStart, hostEnd - hostStart)); - std::string profile_url = getProfileURL(""); - LLURL raw_profile_url( profile_url.c_str() ); + // Do a web profile get so we can store the cookie + httpHeaders->append(HTTP_OUT_HEADER_ACCEPT, "*/*"); + httpHeaders->append(HTTP_OUT_HEADER_COOKIE, sOpenIDCookie); + httpHeaders->append(HTTP_OUT_HEADER_USER_AGENT, getCurrentUserAgent()); + + + LL_DEBUGS("MediaAuth") << "Requesting " << url << LL_ENDL; + LL_DEBUGS("MediaAuth") << "sOpenIDCookie = [" << sOpenIDCookie << "]" << LL_ENDL; + + LLSD result = httpAdapter->getRawAndYield(httpRequest, url, httpOpts, httpHeaders); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) + { + LL_WARNS("MediaAuth") << "Error getting web profile." << LL_ENDL; + return; + } + + LLSD resultHeaders = httpResults[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_HEADERS]; + if (!resultHeaders.has(HTTP_IN_HEADER_SET_COOKIE)) + { + LL_WARNS("MediaAuth") << "No cookie in response." << LL_ENDL; + return; + } + + const std::string& cookie = resultHeaders[HTTP_IN_HEADER_SET_COOKIE].asStringRef(); + LL_DEBUGS("MediaAuth") << "cookie = " << cookie << LL_ENDL; + + // *TODO: What about bad status codes? Does this destroy previous cookies? + LLViewerMedia::getCookieStore()->setCookiesFromHost(cookie, hostAuth); + + // Set cookie for snapshot publishing. + std::string authCookie = cookie.substr(0, cookie.find(";")); // strip path + LLWebProfile::setAuthCookie(authCookie); - LL_DEBUGS("MediaAuth") << "Requesting " << profile_url << LL_ENDL; - LL_DEBUGS("MediaAuth") << "sOpenIDCookie = [" << sOpenIDCookie << "]" << LL_ENDL; - LLHTTPClient::get(profile_url, - new LLViewerMediaWebProfileResponder(raw_profile_url.getAuthority()), - headers); - } } ///////////////////////////////////////////////////////////////////////////////////////// // static -void LLViewerMedia::openIDSetup(const std::string &openid_url, const std::string &openid_token) +void LLViewerMedia::openIDSetup(const std::string &openidUrl, const std::string &openidToken) { - LL_DEBUGS("MediaAuth") << "url = \"" << openid_url << "\", token = \"" << openid_token << "\"" << LL_ENDL; + LL_DEBUGS("MediaAuth") << "url = \"" << openidUrl << "\", token = \"" << openidToken << "\"" << LL_ENDL; - // post the token to the url - // the responder will need to extract the cookie(s). + LLCoros::instance().launch("LLViewerMedia::openIDSetupCoro", + boost::bind(&LLViewerMedia::openIDSetupCoro, openidUrl, openidToken)); +} - // Save the OpenID URL for later -- we may need the host when adding the cookie. - sOpenIDURL.init(openid_url.c_str()); - - // We shouldn't ever do this twice, but just in case this code gets repurposed later, clear existing cookies. - sOpenIDCookie.clear(); +/*static*/ +void LLViewerMedia::openIDSetupCoro(std::string openidUrl, std::string openidToken) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("openIDSetupCoro", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); + LLCore::HttpHeaders::ptr_t httpHeaders(new LLCore::HttpHeaders); - LLSD headers = LLSD::emptyMap(); - // Keep LLHTTPClient from adding an "Accept: application/llsd+xml" header - headers[HTTP_OUT_HEADER_ACCEPT] = "*/*"; - // and use the expected content-type for a post, instead of the LLHTTPClient::postRaw() default of "application/octet-stream" - headers[HTTP_OUT_HEADER_CONTENT_TYPE] = "application/x-www-form-urlencoded"; - - // postRaw() takes ownership of the buffer and releases it later, so we need to allocate a new buffer here. - size_t size = openid_token.size(); - U8 *data = new U8[size]; - memcpy(data, openid_token.data(), size); - - LLHTTPClient::postRaw( - openid_url, - data, - size, - new LLViewerMediaOpenIDResponder(), - headers); - + httpOpts->setWantHeaders(true); + + // post the token to the url + // the responder will need to extract the cookie(s). + // Save the OpenID URL for later -- we may need the host when adding the cookie. + sOpenIDURL.init(openidUrl.c_str()); + // We shouldn't ever do this twice, but just in case this code gets repurposed later, clear existing cookies. + sOpenIDCookie.clear(); + + httpHeaders->append(HTTP_OUT_HEADER_ACCEPT, "*/*"); + httpHeaders->append(HTTP_OUT_HEADER_CONTENT_TYPE, "application/x-www-form-urlencoded"); + + LLCore::BufferArray::ptr_t rawbody(new LLCore::BufferArray); + LLCore::BufferArrayStream bas(rawbody.get()); + + bas << std::noskipws << openidToken; + + LLSD result = httpAdapter->postRawAndYield(httpRequest, openidUrl, rawbody, httpOpts, httpHeaders); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) + { + LL_WARNS("MediaAuth") << "Error getting Open ID cookie" << LL_ENDL; + return; + } + + LLSD resultHeaders = httpResults[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_HEADERS]; + if (!resultHeaders.has(HTTP_IN_HEADER_SET_COOKIE)) + { + LL_WARNS("MediaAuth") << "No cookie in response." << LL_ENDL; + return; + } + + // We don't care about the content of the response, only the Set-Cookie header. + const std::string &cookie = resultHeaders[HTTP_IN_HEADER_SET_COOKIE]; + + // *TODO: What about bad status codes? Does this destroy previous cookies? + LLViewerMedia::openIDCookieResponse(cookie); + LL_DEBUGS("MediaAuth") << "OpenID cookie set." << LL_ENDL; } ///////////////////////////////////////////////////////////////////////////////////////// @@ -1661,7 +1563,6 @@ LLViewerMediaImpl::LLViewerMediaImpl( const LLUUID& texture_id, mIsParcelMedia(false), mProximity(-1), mProximityDistance(0.0f), - mMimeTypeProbe(NULL), mMediaAutoPlay(false), mInNearbyMediaList(false), mClearCache(false), @@ -1671,8 +1572,10 @@ LLViewerMediaImpl::LLViewerMediaImpl( const LLUUID& texture_id, mIsUpdated(false), mTrustedBrowser(false), mZoomFactor(1.0), - mCleanBrowser(false) -{ + mCleanBrowser(false), + mMimeProbe(), + mCanceling(false) +{ // Set up the mute list observer if it hasn't been set up already. if(!sViewerMediaMuteListObserverInitialized) @@ -2610,7 +2513,8 @@ void LLViewerMediaImpl::navigateInternal() return; } - if(mMimeTypeProbe != NULL) + + if (!mMimeProbe.expired()) { LL_WARNS() << "MIME type probe already in progress -- bailing out." << LL_ENDL; return; @@ -2648,14 +2552,8 @@ void LLViewerMediaImpl::navigateInternal() if(scheme.empty() || "http" == scheme || "https" == scheme) { - // If we don't set an Accept header, LLHTTPClient will add one like this: - // Accept: application/llsd+xml - // which is really not what we want. - LLSD headers = LLSD::emptyMap(); - headers[HTTP_OUT_HEADER_ACCEPT] = "*/*"; - // Allow cookies in the response, to prevent a redirect loop when accessing join.secondlife.com - headers[HTTP_OUT_HEADER_COOKIE] = ""; - LLHTTPClient::getHeaderOnly( mMediaURL, new LLMimeDiscoveryResponder(this), headers, 10.0f); + LLCoros::instance().launch("LLViewerMediaImpl::mimeDiscoveryCoro", + boost::bind(&LLViewerMediaImpl::mimeDiscoveryCoro, this, mMediaURL)); } else if("data" == scheme || "file" == scheme || "about" == scheme) { @@ -2685,6 +2583,66 @@ void LLViewerMediaImpl::navigateInternal() } } +void LLViewerMediaImpl::mimeDiscoveryCoro(std::string url) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("mimeDiscoveryCoro", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); + LLCore::HttpHeaders::ptr_t httpHeaders(new LLCore::HttpHeaders); + + mMimeProbe = httpAdapter; + + httpOpts->setFollowRedirects(true); + httpOpts->setHeadersOnly(true); + + httpHeaders->append(HTTP_OUT_HEADER_ACCEPT, "*/*"); + httpHeaders->append(HTTP_OUT_HEADER_COOKIE, ""); + + LLSD result = httpAdapter->getRawAndYield(httpRequest, url, httpOpts, httpHeaders); + + mMimeProbe.reset(); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) + { + LL_WARNS() << "Error retrieving media headers." << LL_ENDL; + } + + LLSD resultHeaders = httpResults[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_HEADERS]; + + const std::string& mediaType = resultHeaders[HTTP_IN_HEADER_CONTENT_TYPE].asStringRef(); + + std::string::size_type idx1 = mediaType.find_first_of(";"); + std::string mimeType = mediaType.substr(0, idx1); + + // We now no longer need to check the error code returned from the probe. + // If we have a mime type, use it. If not, default to the web plugin and let it handle error reporting. + // The probe was successful. + if (mimeType.empty()) + { + // Some sites don't return any content-type header at all. + // Treat an empty mime type as text/html. + mimeType = HTTP_CONTENT_TEXT_HTML; + } + + LL_DEBUGS() << "Media type \"" << mediaType << "\", mime type is \"" << mimeType << "\"" << LL_ENDL; + + // the call to initializeMedia may disconnect the responder, which will clear mMediaImpl. + // Make a local copy so we can call loadURI() afterwards. + + if (!mimeType.empty()) + { + if (initializeMedia(mimeType)) + { + loadURI(); + } + } +} + ////////////////////////////////////////////////////////////////////////////////////////// void LLViewerMediaImpl::navigateStop() { @@ -2783,7 +2741,7 @@ void LLViewerMediaImpl::update() { // Don't load new instances that are at PRIORITY_SLIDESHOW or below. They're just kept around to preserve state. } - else if(mMimeTypeProbe != NULL) + else if (!mMimeProbe.expired()) { // this media source is doing a MIME type probe -- don't try loading it again. } @@ -3673,18 +3631,10 @@ void LLViewerMediaImpl::setNavigateSuspended(bool suspend) void LLViewerMediaImpl::cancelMimeTypeProbe() { - if(mMimeTypeProbe != NULL) - { - // There doesn't seem to be a way to actually cancel an outstanding request. - // Simulate it by telling the LLMimeDiscoveryResponder not to write back any results. - mMimeTypeProbe->cancelRequest(); - - // The above should already have set mMimeTypeProbe to NULL. - if(mMimeTypeProbe != NULL) - { - LL_ERRS() << "internal error: mMimeTypeProbe is not NULL after cancelling request." << LL_ENDL; - } - } + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t probeAdapter = mMimeProbe.lock(); + + if (probeAdapter) + probeAdapter->cancelYieldingOperation(); } void LLViewerMediaImpl::addObject(LLVOVolume* obj) diff --git a/indra/newview/llviewermedia.h b/indra/newview/llviewermedia.h index 6803adfaa2..92d644c900 100755 --- a/indra/newview/llviewermedia.h +++ b/indra/newview/llviewermedia.h @@ -40,6 +40,9 @@ #include "llnotificationptr.h" #include "llurl.h" +#include "lleventcoro.h" +#include "llcoros.h" +#include "llcorehttputil.h" class LLViewerMediaImpl; class LLUUID; @@ -161,11 +164,15 @@ public: static void setOnlyAudibleMediaTextureID(const LLUUID& texture_id); static LLSD getHeaders(); + static LLCore::HttpHeaders::ptr_t getHttpHeaders(); private: static void setOpenIDCookie(); static void onTeleportFinished(); - + + static void openIDSetupCoro(std::string openidUrl, std::string openidToken); + static void getOpenIDCookieCoro(std::string url); + static LLPluginCookieStore *sCookieStore; static LLURL sOpenIDURL; static std::string sOpenIDCookie; @@ -180,7 +187,6 @@ class LLViewerMediaImpl public: friend class LLViewerMedia; - friend class LLMimeDiscoveryResponder; LLViewerMediaImpl( const LLUUID& texture_id, @@ -453,7 +459,6 @@ private: S32 mProximity; F64 mProximityDistance; F64 mProximityCamera; - LLMimeDiscoveryResponder *mMimeTypeProbe; bool mMediaAutoPlay; std::string mMediaEntryURL; bool mInNearbyMediaList; // used by LLPanelNearbyMedia::refreshList() for performance reasons @@ -470,6 +475,10 @@ private: BOOL mIsUpdated ; std::list< LLVOVolume* > mObjectList ; + void mimeDiscoveryCoro(std::string url); + LLCoreHttpUtil::HttpCoroutineAdapter::wptr_t mMimeProbe; + bool mCanceling; + private: LLViewerMediaTexture *updatePlaceholderImage(); }; diff --git a/indra/newview/llviewermenufile.cpp b/indra/newview/llviewermenufile.cpp index f8e50ba463..163ae4f4ec 100755 --- a/indra/newview/llviewermenufile.cpp +++ b/indra/newview/llviewermenufile.cpp @@ -62,6 +62,7 @@ #include "lluploaddialog.h" #include "lltrans.h" #include "llfloaterbuycurrency.h" +#include "llviewerassetupload.h" // linden libraries #include "llassetuploadresponders.h" @@ -83,8 +84,9 @@ class LLFileEnableUpload : public view_listener_t { bool handleEvent(const LLSD& userdata) { - bool new_value = gStatusBar && LLGlobalEconomy::Singleton::getInstance() && (gStatusBar->getBalance() >= LLGlobalEconomy::Singleton::getInstance()->getPriceUpload()); - return new_value; + return true; +// bool new_value = gStatusBar && LLGlobalEconomy::Singleton::getInstance() && (gStatusBar->getBalance() >= LLGlobalEconomy::Singleton::getInstance()->getPriceUpload()); +// return new_value; } }; @@ -410,7 +412,6 @@ class LLFileUploadBulk : public view_listener_t } // TODO: - // Iterate over all files // Check extensions for uploadability, cost // Check user balance for entire cost // Charge user entire cost @@ -422,37 +423,33 @@ class LLFileUploadBulk : public view_listener_t LLFilePicker& picker = LLFilePicker::instance(); if (picker.getMultipleOpenFiles()) { - const std::string& filename = picker.getFirstFile(); - std::string name = gDirUtilp->getBaseFileName(filename, true); - - std::string asset_name = name; - LLStringUtil::replaceNonstandardASCII( asset_name, '?' ); - LLStringUtil::replaceChar(asset_name, '|', '?'); - LLStringUtil::stripNonprintable(asset_name); - LLStringUtil::trim(asset_name); - - std::string display_name = LLStringUtil::null; - LLAssetStorage::LLStoreAssetCallback callback = NULL; - S32 expected_upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload(); - void *userdata = NULL; - - upload_new_resource( - filename, - asset_name, - asset_name, - 0, - LLFolderType::FT_NONE, - LLInventoryType::IT_NONE, - LLFloaterPerms::getNextOwnerPerms("Uploads"), - LLFloaterPerms::getGroupPerms("Uploads"), - LLFloaterPerms::getEveryonePerms("Uploads"), - display_name, - callback, - expected_upload_cost, - userdata); - - // *NOTE: Ew, we don't iterate over the file list here, - // we handle the next files in upload_done_callback() + std::string filename = picker.getFirstFile(); + S32 expected_upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload(); + + while (!filename.empty()) + { + std::string name = gDirUtilp->getBaseFileName(filename, true); + + std::string asset_name = name; + LLStringUtil::replaceNonstandardASCII( asset_name, '?' ); + LLStringUtil::replaceChar(asset_name, '|', '?'); + LLStringUtil::stripNonprintable(asset_name); + LLStringUtil::trim(asset_name); + + LLResourceUploadInfo::ptr_t uploadInfo(new NewFileResourceUploadInfo( + filename, + asset_name, + asset_name, 0, + LLFolderType::FT_NONE, LLInventoryType::IT_NONE, + LLFloaterPerms::getNextOwnerPerms("Uploads"), + LLFloaterPerms::getGroupPerms("Uploads"), + LLFloaterPerms::getEveryonePerms("Uploads"), + expected_upload_cost)); + + upload_new_resource(uploadInfo, NULL, NULL); + + filename = picker.getNextFile(); + } } else { @@ -621,6 +618,7 @@ void handle_compress_image(void*) } } + LLUUID upload_new_resource( const std::string& src_filename, std::string name, @@ -636,276 +634,16 @@ LLUUID upload_new_resource( S32 expected_upload_cost, void *userdata) { - // Generate the temporary UUID. - std::string filename = gDirUtilp->getTempFilename(); - LLTransactionID tid; - LLAssetID uuid; - - LLSD args; - - std::string exten = gDirUtilp->getExtension(src_filename); - U32 codec = LLImageBase::getCodecFromExtension(exten); - LLAssetType::EType asset_type = LLAssetType::AT_NONE; - std::string error_message; - - BOOL error = FALSE; - - if (exten.empty()) - { - std::string short_name = gDirUtilp->getBaseFileName(filename); - - // No extension - error_message = llformat( - "No file extension for the file: '%s'\nPlease make sure the file has a correct file extension", - short_name.c_str()); - args["FILE"] = short_name; - upload_error(error_message, "NoFileExtension", filename, args); - return LLUUID(); - } - else if (codec != IMG_CODEC_INVALID) - { - // It's an image file, the upload procedure is the same for all - asset_type = LLAssetType::AT_TEXTURE; - if (!LLViewerTextureList::createUploadFile(src_filename, filename, codec )) - { - error_message = llformat( "Problem with file %s:\n\n%s\n", - src_filename.c_str(), LLImage::getLastError().c_str()); - args["FILE"] = src_filename; - args["ERROR"] = LLImage::getLastError(); - upload_error(error_message, "ProblemWithFile", filename, args); - return LLUUID(); - } - } - else if(exten == "wav") - { - asset_type = LLAssetType::AT_SOUND; // tag it as audio - S32 encode_result = 0; - - LL_INFOS() << "Attempting to encode wav as an ogg file" << LL_ENDL; - - encode_result = encode_vorbis_file(src_filename, filename); - - if (LLVORBISENC_NOERR != encode_result) - { - switch(encode_result) - { - case LLVORBISENC_DEST_OPEN_ERR: - error_message = llformat( "Couldn't open temporary compressed sound file for writing: %s\n", filename.c_str()); - args["FILE"] = filename; - upload_error(error_message, "CannotOpenTemporarySoundFile", filename, args); - break; - - default: - error_message = llformat("Unknown vorbis encode failure on: %s\n", src_filename.c_str()); - args["FILE"] = src_filename; - upload_error(error_message, "UnknownVorbisEncodeFailure", filename, args); - break; - } - return LLUUID(); - } - } - else if(exten == "tmp") - { - // This is a generic .lin resource file - asset_type = LLAssetType::AT_OBJECT; - LLFILE* in = LLFile::fopen(src_filename, "rb"); /* Flawfinder: ignore */ - if (in) - { - // read in the file header - char buf[16384]; /* Flawfinder: ignore */ - size_t readbytes; - S32 version; - if (fscanf(in, "LindenResource\nversion %d\n", &version)) - { - if (2 == version) - { - // *NOTE: This buffer size is hard coded into scanf() below. - char label[MAX_STRING]; /* Flawfinder: ignore */ - char value[MAX_STRING]; /* Flawfinder: ignore */ - S32 tokens_read; - while (fgets(buf, 1024, in)) - { - label[0] = '\0'; - value[0] = '\0'; - tokens_read = sscanf( /* Flawfinder: ignore */ - buf, - "%254s %254s\n", - label, value); - - LL_INFOS() << "got: " << label << " = " << value - << LL_ENDL; - - if (EOF == tokens_read) - { - fclose(in); - error_message = llformat("corrupt resource file: %s", src_filename.c_str()); - args["FILE"] = src_filename; - upload_error(error_message, "CorruptResourceFile", filename, args); - return LLUUID(); - } - - if (2 == tokens_read) - { - if (! strcmp("type", label)) - { - asset_type = (LLAssetType::EType)(atoi(value)); - } - } - else - { - if (! strcmp("_DATA_", label)) - { - // below is the data section - break; - } - } - // other values are currently discarded - } - - } - else - { - fclose(in); - error_message = llformat("unknown linden resource file version in file: %s", src_filename.c_str()); - args["FILE"] = src_filename; - upload_error(error_message, "UnknownResourceFileVersion", filename, args); - return LLUUID(); - } - } - else - { - // this is an original binary formatted .lin file - // start over at the beginning of the file - fseek(in, 0, SEEK_SET); - - const S32 MAX_ASSET_DESCRIPTION_LENGTH = 256; - const S32 MAX_ASSET_NAME_LENGTH = 64; - S32 header_size = 34 + MAX_ASSET_DESCRIPTION_LENGTH + MAX_ASSET_NAME_LENGTH; - S16 type_num; - - // read in and throw out most of the header except for the type - if (fread(buf, header_size, 1, in) != 1) - { - LL_WARNS() << "Short read" << LL_ENDL; - } - memcpy(&type_num, buf + 16, sizeof(S16)); /* Flawfinder: ignore */ - asset_type = (LLAssetType::EType)type_num; - } - - // copy the file's data segment into another file for uploading - LLFILE* out = LLFile::fopen(filename, "wb"); /* Flawfinder: ignore */ - if (out) - { - while((readbytes = fread(buf, 1, 16384, in))) /* Flawfinder: ignore */ - { - if (fwrite(buf, 1, readbytes, out) != readbytes) - { - LL_WARNS() << "Short write" << LL_ENDL; - } - } - fclose(out); - } - else - { - fclose(in); - error_message = llformat( "Unable to create output file: %s", filename.c_str()); - args["FILE"] = filename; - upload_error(error_message, "UnableToCreateOutputFile", filename, args); - return LLUUID(); - } - - fclose(in); - } - else - { - LL_INFOS() << "Couldn't open .lin file " << src_filename << LL_ENDL; - } - } - else if (exten == "bvh") - { - error_message = llformat("We do not currently support bulk upload of animation files\n"); - upload_error(error_message, "DoNotSupportBulkAnimationUpload", filename, args); - return LLUUID(); - } - else if (exten == "anim") - { - asset_type = LLAssetType::AT_ANIMATION; - filename = src_filename; - } - else - { - // Unknown extension - error_message = llformat(LLTrans::getString("UnknownFileExtension").c_str(), exten.c_str()); - error = TRUE;; - } - - // gen a new transaction ID for this asset - tid.generate(); - if (!error) - { - uuid = tid.makeAssetID(gAgent.getSecureSessionID()); - // copy this file into the vfs for upload - S32 file_size; - LLAPRFile infile ; - infile.open(filename, LL_APR_RB, NULL, &file_size); - if (infile.getFileHandle()) - { - LLVFile file(gVFS, uuid, asset_type, LLVFile::WRITE); - - file.setMaxSize(file_size); - - const S32 buf_size = 65536; - U8 copy_buf[buf_size]; - while ((file_size = infile.read(copy_buf, buf_size))) - { - file.write(copy_buf, file_size); - } - } - else - { - error_message = llformat( "Unable to access output file: %s", filename.c_str()); - error = TRUE; - } - } - - if (!error) - { - std::string t_disp_name = display_name; - if (t_disp_name.empty()) - { - t_disp_name = src_filename; - } - upload_new_resource( - tid, - asset_type, - name, - desc, - compression_info, // tid - destination_folder_type, - inv_type, - next_owner_perms, - group_perms, - everyone_perms, - display_name, - callback, - expected_upload_cost, - userdata); - } - else - { - LL_WARNS() << error_message << LL_ENDL; - LLSD args; - args["ERROR_MESSAGE"] = error_message; - LLNotificationsUtil::add("ErrorMessage", args); - if(LLFile::remove(filename) == -1) - { - LL_DEBUGS() << "unable to remove temp file" << LL_ENDL; - } - LLFilePicker::instance().reset(); - } + LLResourceUploadInfo::ptr_t uploadInfo(new NewFileResourceUploadInfo( + src_filename, + name, desc, compression_info, + destination_folder_type, inv_type, + next_owner_perms, group_perms, everyone_perms, + expected_upload_cost)); + upload_new_resource(uploadInfo, callback, userdata); - return uuid; + return LLUUID::null; } void upload_done_callback( @@ -1035,189 +773,62 @@ void upload_done_callback( } } -static LLAssetID upload_new_resource_prep( - const LLTransactionID& tid, - LLAssetType::EType asset_type, - LLInventoryType::EType& inventory_type, - std::string& name, - const std::string& display_name, - std::string& description) -{ - LLAssetID uuid = generate_asset_id_for_new_upload(tid); - - increase_new_upload_stats(asset_type); - - assign_defaults_and_show_upload_message( - asset_type, - inventory_type, - name, - display_name, - description); - - return uuid; -} - -LLSD generate_new_resource_upload_capability_body( - LLAssetType::EType asset_type, - const std::string& name, - const std::string& desc, - LLFolderType::EType destination_folder_type, - LLInventoryType::EType inv_type, - U32 next_owner_perms, - U32 group_perms, - U32 everyone_perms) -{ - LLSD body; - - body["folder_id"] = gInventory.findCategoryUUIDForType( - (destination_folder_type == LLFolderType::FT_NONE) ? - (LLFolderType::EType) asset_type : - destination_folder_type); - - body["asset_type"] = LLAssetType::lookup(asset_type); - body["inventory_type"] = LLInventoryType::lookup(inv_type); - body["name"] = name; - body["description"] = desc; - body["next_owner_mask"] = LLSD::Integer(next_owner_perms); - body["group_mask"] = LLSD::Integer(group_perms); - body["everyone_mask"] = LLSD::Integer(everyone_perms); - - return body; -} - void upload_new_resource( - const LLTransactionID &tid, - LLAssetType::EType asset_type, - std::string name, - std::string desc, - S32 compression_info, - LLFolderType::EType destination_folder_type, - LLInventoryType::EType inv_type, - U32 next_owner_perms, - U32 group_perms, - U32 everyone_perms, - const std::string& display_name, - LLAssetStorage::LLStoreAssetCallback callback, - S32 expected_upload_cost, - void *userdata) + LLResourceUploadInfo::ptr_t &uploadInfo, + LLAssetStorage::LLStoreAssetCallback callback, + void *userdata) { if(gDisconnected) { return ; } - - LLAssetID uuid = - upload_new_resource_prep( - tid, - asset_type, - inv_type, - name, - display_name, - desc); - - if( LLAssetType::AT_SOUND == asset_type ) - { - add(LLStatViewer::UPLOAD_SOUND, 1); - } - else - if( LLAssetType::AT_TEXTURE == asset_type ) - { - add(LLStatViewer::UPLOAD_TEXTURE, 1); - } - else - if( LLAssetType::AT_ANIMATION == asset_type) - { - add(LLStatViewer::ANIMATION_UPLOADS, 1); - } - if(LLInventoryType::IT_NONE == inv_type) - { - inv_type = LLInventoryType::defaultForAssetType(asset_type); - } - LLStringUtil::stripNonprintable(name); - LLStringUtil::stripNonprintable(desc); - if(name.empty()) - { - name = "(No Name)"; - } - if(desc.empty()) - { - desc = "(No Description)"; - } - - // At this point, we're ready for the upload. - std::string upload_message = "Uploading...\n\n"; - upload_message.append(display_name); - LLUploadDialog::modalUploadDialog(upload_message); - - LL_INFOS() << "*** Uploading: " << LL_ENDL; - LL_INFOS() << "Type: " << LLAssetType::lookup(asset_type) << LL_ENDL; - LL_INFOS() << "UUID: " << uuid << LL_ENDL; - LL_INFOS() << "Name: " << name << LL_ENDL; - LL_INFOS() << "Desc: " << desc << LL_ENDL; - LL_INFOS() << "Expected Upload Cost: " << expected_upload_cost << LL_ENDL; - LL_DEBUGS() << "Folder: " << gInventory.findCategoryUUIDForType((destination_folder_type == LLFolderType::FT_NONE) ? LLFolderType::assetTypeToFolderType(asset_type) : destination_folder_type) << LL_ENDL; - LL_DEBUGS() << "Asset Type: " << LLAssetType::lookup(asset_type) << LL_ENDL; - - std::string url = gAgent.getRegion()->getCapability( - "NewFileAgentInventory"); +// uploadInfo->setAssetType(assetType); +// uploadInfo->setTransactionId(tid); + + + std::string url = gAgent.getRegion()->getCapability("NewFileAgentInventory"); if ( !url.empty() ) { - LL_INFOS() << "New Agent Inventory via capability" << LL_ENDL; - - LLSD body; - body = generate_new_resource_upload_capability_body( - asset_type, - name, - desc, - destination_folder_type, - inv_type, - next_owner_perms, - group_perms, - everyone_perms); - - LLHTTPClient::post( - url, - body, - new LLNewAgentInventoryResponder( - body, - uuid, - asset_type)); + LLViewerAssetUpload::EnqueueInventoryUpload(url, uploadInfo); } else { + uploadInfo->prepareUpload(); + uploadInfo->logPreparedUpload(); + LL_INFOS() << "NewAgentInventory capability not found, new agent inventory via asset system." << LL_ENDL; // check for adequate funds // TODO: do this check on the sim - if (LLAssetType::AT_SOUND == asset_type || - LLAssetType::AT_TEXTURE == asset_type || - LLAssetType::AT_ANIMATION == asset_type) + if (LLAssetType::AT_SOUND == uploadInfo->getAssetType() || + LLAssetType::AT_TEXTURE == uploadInfo->getAssetType() || + LLAssetType::AT_ANIMATION == uploadInfo->getAssetType()) { S32 balance = gStatusBar->getBalance(); - if (balance < expected_upload_cost) + if (balance < uploadInfo->getExpectedUploadCost()) { // insufficient funds, bail on this upload LLStringUtil::format_map_t args; - args["NAME"] = name; - args["AMOUNT"] = llformat("%d", expected_upload_cost); - LLBuyCurrencyHTML::openCurrencyFloater( LLTrans::getString("UploadingCosts", args), expected_upload_cost ); + args["NAME"] = uploadInfo->getName(); + args["AMOUNT"] = llformat("%d", uploadInfo->getExpectedUploadCost()); + LLBuyCurrencyHTML::openCurrencyFloater(LLTrans::getString("UploadingCosts", args), uploadInfo->getExpectedUploadCost()); return; } } LLResourceData* data = new LLResourceData; - data->mAssetInfo.mTransactionID = tid; - data->mAssetInfo.mUuid = uuid; - data->mAssetInfo.mType = asset_type; + data->mAssetInfo.mTransactionID = uploadInfo->getTransactionId(); + data->mAssetInfo.mUuid = uploadInfo->getAssetId(); + data->mAssetInfo.mType = uploadInfo->getAssetType(); data->mAssetInfo.mCreatorID = gAgentID; - data->mInventoryType = inv_type; - data->mNextOwnerPerm = next_owner_perms; - data->mExpectedUploadCost = expected_upload_cost; + data->mInventoryType = uploadInfo->getInventoryType(); + data->mNextOwnerPerm = uploadInfo->getNextOwnerPerms(); + data->mExpectedUploadCost = uploadInfo->getExpectedUploadCost(); data->mUserData = userdata; - data->mAssetInfo.setName(name); - data->mAssetInfo.setDescription(desc); - data->mPreferredLocation = destination_folder_type; + data->mAssetInfo.setName(uploadInfo->getName()); + data->mAssetInfo.setDescription(uploadInfo->getDescription()); + data->mPreferredLocation = uploadInfo->getDestinationFolderType(); LLAssetStorage::LLStoreAssetCallback asset_callback = &upload_done_callback; if (callback) @@ -1233,66 +844,6 @@ void upload_new_resource( } } -LLAssetID generate_asset_id_for_new_upload(const LLTransactionID& tid) -{ - if ( gDisconnected ) - { - LLAssetID rv; - - rv.setNull(); - return rv; - } - - LLAssetID uuid = tid.makeAssetID(gAgent.getSecureSessionID()); - - return uuid; -} - -void increase_new_upload_stats(LLAssetType::EType asset_type) -{ - if ( LLAssetType::AT_SOUND == asset_type ) - { - add(LLStatViewer::UPLOAD_SOUND, 1); - } - else if ( LLAssetType::AT_TEXTURE == asset_type ) - { - add(LLStatViewer::UPLOAD_TEXTURE, 1); - } - else if ( LLAssetType::AT_ANIMATION == asset_type ) - { - add(LLStatViewer::ANIMATION_UPLOADS, 1); - } -} - -void assign_defaults_and_show_upload_message( - LLAssetType::EType asset_type, - LLInventoryType::EType& inventory_type, - std::string& name, - const std::string& display_name, - std::string& description) -{ - if ( LLInventoryType::IT_NONE == inventory_type ) - { - inventory_type = LLInventoryType::defaultForAssetType(asset_type); - } - LLStringUtil::stripNonprintable(name); - LLStringUtil::stripNonprintable(description); - - if ( name.empty() ) - { - name = "(No Name)"; - } - if ( description.empty() ) - { - description = "(No Description)"; - } - - // At this point, we're ready for the upload. - std::string upload_message = "Uploading...\n\n"; - upload_message.append(display_name); - LLUploadDialog::modalUploadDialog(upload_message); -} - void init_menu_file() { diff --git a/indra/newview/llviewermenufile.h b/indra/newview/llviewermenufile.h index 3034d00b22..0f8fa56b52 100755 --- a/indra/newview/llviewermenufile.h +++ b/indra/newview/llviewermenufile.h @@ -34,45 +34,35 @@ #include "llthread.h" #include <queue> +#include "llviewerassetupload.h" + class LLTransactionID; void init_menu_file(); + LLUUID upload_new_resource( - const std::string& src_filename, - std::string name, - std::string desc, - S32 compression_info, - LLFolderType::EType destination_folder_type, - LLInventoryType::EType inv_type, - U32 next_owner_perms, - U32 group_perms, - U32 everyone_perms, - const std::string& display_name, - LLAssetStorage::LLStoreAssetCallback callback, - S32 expected_upload_cost, - void *userdata); + const std::string& src_filename, + std::string name, + std::string desc, + S32 compression_info, + LLFolderType::EType destination_folder_type, + LLInventoryType::EType inv_type, + U32 next_owner_perms, + U32 group_perms, + U32 everyone_perms, + const std::string& display_name, + LLAssetStorage::LLStoreAssetCallback callback, + S32 expected_upload_cost, + void *userdata); void upload_new_resource( - const LLTransactionID &tid, - LLAssetType::EType type, - std::string name, - std::string desc, - S32 compression_info, - LLFolderType::EType destination_folder_type, - LLInventoryType::EType inv_type, - U32 next_owner_perms, - U32 group_perms, - U32 everyone_perms, - const std::string& display_name, - LLAssetStorage::LLStoreAssetCallback callback, - S32 expected_upload_cost, - void *userdata); + LLResourceUploadInfo::ptr_t &uploadInfo, + LLAssetStorage::LLStoreAssetCallback callback = NULL, + void *userdata = NULL); -LLAssetID generate_asset_id_for_new_upload(const LLTransactionID& tid); -void increase_new_upload_stats(LLAssetType::EType asset_type); void assign_defaults_and_show_upload_message( LLAssetType::EType asset_type, LLInventoryType::EType& inventory_type, @@ -80,16 +70,6 @@ void assign_defaults_and_show_upload_message( const std::string& display_name, std::string& description); -LLSD generate_new_resource_upload_capability_body( - LLAssetType::EType asset_type, - const std::string& name, - const std::string& desc, - LLFolderType::EType destination_folder_type, - LLInventoryType::EType inv_type, - U32 next_owner_perms, - U32 group_perms, - U32 everyone_perms); - void on_new_single_inventory_upload_complete( LLAssetType::EType asset_type, LLInventoryType::EType inventory_type, diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp index 75732a1e19..9b0035d547 100755 --- a/indra/newview/llviewerobjectlist.cpp +++ b/indra/newview/llviewerobjectlist.cpp @@ -78,6 +78,7 @@ #include "llappviewer.h" #include "llfloaterperms.h" #include "llvocache.h" +#include "llcorehttputil.h" extern F32 gMinObjectDistance; extern BOOL gAnimateTextures; @@ -795,190 +796,6 @@ void LLViewerObjectList::updateApparentAngles(LLAgent &agent) LLVOAvatar::cullAvatarsByPixelArea(); } -class LLObjectCostResponder : public LLCurl::Responder -{ - LOG_CLASS(LLObjectCostResponder); -public: - LLObjectCostResponder(const LLSD& object_ids) - : mObjectIDs(object_ids) - { - } - - // Clear's the global object list's pending - // request list for all objects requested - void clear_object_list_pending_requests() - { - // TODO*: No more hard coding - for ( - LLSD::array_iterator iter = mObjectIDs.beginArray(); - iter != mObjectIDs.endArray(); - ++iter) - { - gObjectList.onObjectCostFetchFailure(iter->asUUID()); - } - } - -private: - /* virtual */ void httpFailure() - { - LL_WARNS() << dumpResponse() << LL_ENDL; - - // TODO*: Error message to user - // For now just clear the request from the pending list - clear_object_list_pending_requests(); - } - - /* virtual */ void httpSuccess() - { - const LLSD& content = getContent(); - if ( !content.isMap() || content.has("error") ) - { - // Improper response or the request had an error, - // show an error to the user? - LL_WARNS() - << "Application level error when fetching object " - << "cost. Message: " << content["error"]["message"].asString() - << ", identifier: " << content["error"]["identifier"].asString() - << LL_ENDL; - - // TODO*: Adaptively adjust request size if the - // service says we've requested too many and retry - - // TODO*: Error message if not retrying - clear_object_list_pending_requests(); - return; - } - - // Success, grab the resource cost and linked set costs - // for an object if one was returned - for ( - LLSD::array_iterator iter = mObjectIDs.beginArray(); - iter != mObjectIDs.endArray(); - ++iter) - { - LLUUID object_id = iter->asUUID(); - - // Check to see if the request contains data for the object - if ( content.has(iter->asString()) ) - { - F32 link_cost = - content[iter->asString()]["linked_set_resource_cost"].asReal(); - F32 object_cost = - content[iter->asString()]["resource_cost"].asReal(); - - F32 physics_cost = content[iter->asString()]["physics_cost"].asReal(); - F32 link_physics_cost = content[iter->asString()]["linked_set_physics_cost"].asReal(); - - gObjectList.updateObjectCost(object_id, object_cost, link_cost, physics_cost, link_physics_cost); - } - else - { - // TODO*: Give user feedback about the missing data? - gObjectList.onObjectCostFetchFailure(object_id); - } - } - } - -private: - LLSD mObjectIDs; -}; - - -class LLPhysicsFlagsResponder : public LLCurl::Responder -{ - LOG_CLASS(LLPhysicsFlagsResponder); -public: - LLPhysicsFlagsResponder(const LLSD& object_ids) - : mObjectIDs(object_ids) - { - } - - // Clear's the global object list's pending - // request list for all objects requested - void clear_object_list_pending_requests() - { - // TODO*: No more hard coding - for ( - LLSD::array_iterator iter = mObjectIDs.beginArray(); - iter != mObjectIDs.endArray(); - ++iter) - { - gObjectList.onPhysicsFlagsFetchFailure(iter->asUUID()); - } - } - -private: - /* virtual */ void httpFailure() - { - LL_WARNS() << dumpResponse() << LL_ENDL; - - // TODO*: Error message to user - // For now just clear the request from the pending list - clear_object_list_pending_requests(); - } - - /* virtual void */ void httpSuccess() - { - const LLSD& content = getContent(); - if ( !content.isMap() || content.has("error") ) - { - // Improper response or the request had an error, - // show an error to the user? - LL_WARNS() - << "Application level error when fetching object " - << "physics flags. Message: " << content["error"]["message"].asString() - << ", identifier: " << content["error"]["identifier"].asString() - << LL_ENDL; - - // TODO*: Adaptively adjust request size if the - // service says we've requested too many and retry - - // TODO*: Error message if not retrying - clear_object_list_pending_requests(); - return; - } - - // Success, grab the resource cost and linked set costs - // for an object if one was returned - for ( - LLSD::array_iterator iter = mObjectIDs.beginArray(); - iter != mObjectIDs.endArray(); - ++iter) - { - LLUUID object_id = iter->asUUID(); - - // Check to see if the request contains data for the object - if ( content.has(iter->asString()) ) - { - const LLSD& data = content[iter->asString()]; - - S32 shape_type = data["PhysicsShapeType"].asInteger(); - - gObjectList.updatePhysicsShapeType(object_id, shape_type); - - if (data.has("Density")) - { - F32 density = data["Density"].asReal(); - F32 friction = data["Friction"].asReal(); - F32 restitution = data["Restitution"].asReal(); - F32 gravity_multiplier = data["GravityMultiplier"].asReal(); - - gObjectList.updatePhysicsProperties(object_id, - density, friction, restitution, gravity_multiplier); - } - } - else - { - // TODO*: Give user feedback about the missing data? - gObjectList.onPhysicsFlagsFetchFailure(object_id); - } - } - } - -private: - LLSD mObjectIDs; -}; - static LLTrace::BlockTimerStatHandle FTM_IDLE_COPY("Idle Copy"); void LLViewerObjectList::update(LLAgent &agent) @@ -1174,41 +991,8 @@ void LLViewerObjectList::fetchObjectCosts() if (!url.empty()) { - LLSD id_list; - U32 object_index = 0; - - for ( - std::set<LLUUID>::iterator iter = mStaleObjectCost.begin(); - iter != mStaleObjectCost.end(); - ) - { - // Check to see if a request for this object - // has already been made. - if ( mPendingObjectCost.find(*iter) == - mPendingObjectCost.end() ) - { - mPendingObjectCost.insert(*iter); - id_list[object_index++] = *iter; - } - - mStaleObjectCost.erase(iter++); - - if (object_index >= MAX_CONCURRENT_PHYSICS_REQUESTS) - { - break; - } - } - - if ( id_list.size() > 0 ) - { - LLSD post_data = LLSD::emptyMap(); - - post_data["object_ids"] = id_list; - LLHTTPClient::post( - url, - post_data, - new LLObjectCostResponder(id_list)); - } + LLCoros::instance().launch("LLViewerObjectList::fetchObjectCostsCoro", + boost::bind(&LLViewerObjectList::fetchObjectCostsCoro, this, url)); } else { @@ -1219,6 +1003,111 @@ void LLViewerObjectList::fetchObjectCosts() } } +/*static*/ +void LLViewerObjectList::reportObjectCostFailure(LLSD &objectList) +{ + // TODO*: No more hard coding + for (LLSD::array_iterator it = objectList.beginArray(); it != objectList.endArray(); ++it) + { + gObjectList.onObjectCostFetchFailure(it->asUUID()); + } +} + + +void LLViewerObjectList::fetchObjectCostsCoro(std::string url) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("genericPostCoro", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + + LLSD idList; + U32 objectIndex = 0; + + for (std::set<LLUUID>::iterator it = mStaleObjectCost.begin(); it != mStaleObjectCost.end(); ) + { + // Check to see if a request for this object + // has already been made. + if (mPendingObjectCost.find(*it) == mPendingObjectCost.end()) + { + mPendingObjectCost.insert(*it); + idList[objectIndex++] = *it; + } + + mStaleObjectCost.erase(it++); + + if (objectIndex >= MAX_CONCURRENT_PHYSICS_REQUESTS) + { + break; + } + } + + if (idList.size() < 1) + { + LL_INFOS() << "No outstanding object IDs to request." << LL_ENDL; + return; + } + + LLSD postData = LLSD::emptyMap(); + + postData["object_ids"] = idList; + + LLSD result = httpAdapter->postAndYield(httpRequest, url, postData); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status || result.has("error")) + { + if (result.has("error")) + { + LL_WARNS() << "Application level error when fetching object " + << "cost. Message: " << result["error"]["message"].asString() + << ", identifier: " << result["error"]["identifier"].asString() + << LL_ENDL; + + // TODO*: Adaptively adjust request size if the + // service says we've requested too many and retry + } + reportObjectCostFailure(idList); + + return; + } + + // Success, grab the resource cost and linked set costs + // for an object if one was returned + for (LLSD::array_iterator it = idList.beginArray(); it != idList.endArray(); ++it) + { + LLUUID objectId = it->asUUID(); + + // Check to see if the request contains data for the object + if (result.has(it->asString())) + { + const LLSD& data = result[it->asString()]; + + S32 shapeType = data["PhysicsShapeType"].asInteger(); + + gObjectList.updatePhysicsShapeType(objectId, shapeType); + + if (data.has("Density")) + { + F32 density = data["Density"].asReal(); + F32 friction = data["Friction"].asReal(); + F32 restitution = data["Restitution"].asReal(); + F32 gravityMult = data["GravityMultiplier"].asReal(); + + gObjectList.updatePhysicsProperties(objectId, density, friction, restitution, gravityMult); + } + } + else + { + // TODO*: Give user feedback about the missing data? + gObjectList.onPhysicsFlagsFetchFailure(objectId); + } + } + +} + void LLViewerObjectList::fetchPhysicsFlags() { // issue http request for stale object physics flags @@ -1232,41 +1121,8 @@ void LLViewerObjectList::fetchPhysicsFlags() if (!url.empty()) { - LLSD id_list; - U32 object_index = 0; - - for ( - std::set<LLUUID>::iterator iter = mStalePhysicsFlags.begin(); - iter != mStalePhysicsFlags.end(); - ) - { - // Check to see if a request for this object - // has already been made. - if ( mPendingPhysicsFlags.find(*iter) == - mPendingPhysicsFlags.end() ) - { - mPendingPhysicsFlags.insert(*iter); - id_list[object_index++] = *iter; - } - - mStalePhysicsFlags.erase(iter++); - - if (object_index >= MAX_CONCURRENT_PHYSICS_REQUESTS) - { - break; - } - } - - if ( id_list.size() > 0 ) - { - LLSD post_data = LLSD::emptyMap(); - - post_data["object_ids"] = id_list; - LLHTTPClient::post( - url, - post_data, - new LLPhysicsFlagsResponder(id_list)); - } + LLCoros::instance().launch("LLViewerObjectList::fetchPhisicsFlagsCoro", + boost::bind(&LLViewerObjectList::fetchPhisicsFlagsCoro, this, url)); } else { @@ -1277,6 +1133,109 @@ void LLViewerObjectList::fetchPhysicsFlags() } } +/*static*/ +void LLViewerObjectList::reportPhysicsFlagFailure(LLSD &objectList) +{ + // TODO*: No more hard coding + for (LLSD::array_iterator it = objectList.beginArray(); it != objectList.endArray(); ++it) + { + gObjectList.onPhysicsFlagsFetchFailure(it->asUUID()); + } +} + +void LLViewerObjectList::fetchPhisicsFlagsCoro(std::string url) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("genericPostCoro", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + + LLSD idList; + U32 objectIndex = 0; + + for (std::set<LLUUID>::iterator it = mStalePhysicsFlags.begin(); it != mStalePhysicsFlags.end(); ) + { + // Check to see if a request for this object + // has already been made. + if (mPendingPhysicsFlags.find(*it) == mPendingPhysicsFlags.end()) + { + mPendingPhysicsFlags.insert(*it); + idList[objectIndex++] = *it; + } + + mStalePhysicsFlags.erase(it++); + + if (objectIndex >= MAX_CONCURRENT_PHYSICS_REQUESTS) + { + break; + } + } + + if (idList.size() < 1) + { + LL_INFOS() << "No outstanding object physics flags to request." << LL_ENDL; + return; + } + + LLSD postData = LLSD::emptyMap(); + + postData["object_ids"] = idList; + + LLSD result = httpAdapter->postAndYield(httpRequest, url, postData); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status || result.has("error")) + { + if (result.has("error")) + { + LL_WARNS() << "Application level error when fetching object " + << "physics flags. Message: " << result["error"]["message"].asString() + << ", identifier: " << result["error"]["identifier"].asString() + << LL_ENDL; + + // TODO*: Adaptively adjust request size if the + // service says we've requested too many and retry + } + reportPhysicsFlagFailure(idList); + + return; + } + + // Success, grab the resource cost and linked set costs + // for an object if one was returned + for (LLSD::array_iterator it = idList.beginArray(); it != idList.endArray(); ++it) + { + LLUUID objectId = it->asUUID(); + + // Check to see if the request contains data for the object + if (result.has(it->asString())) + { + const LLSD& data = result[it->asString()]; + + S32 shapeType = data["PhysicsShapeType"].asInteger(); + + gObjectList.updatePhysicsShapeType(objectId, shapeType); + + if (data.has("Density")) + { + F32 density = data["Density"].asReal(); + F32 friction = data["Friction"].asReal(); + F32 restitution = data["Restitution"].asReal(); + F32 gravityMult = data["GravityMultiplier"].asReal(); + + gObjectList.updatePhysicsProperties(objectId, density, + friction, restitution, gravityMult); + } + } + else + { + // TODO*: Give user feedback about the missing data? + gObjectList.onPhysicsFlagsFetchFailure(objectId); + } + } +} void LLViewerObjectList::clearDebugText() { diff --git a/indra/newview/llviewerobjectlist.h b/indra/newview/llviewerobjectlist.h index 594317cd9f..9ec7c4bc22 100755 --- a/indra/newview/llviewerobjectlist.h +++ b/indra/newview/llviewerobjectlist.h @@ -36,6 +36,8 @@ // project includes #include "llviewerobject.h" +#include "lleventcoro.h" +#include "llcoros.h" class LLCamera; class LLNetMap; @@ -227,6 +229,14 @@ protected: std::set<LLViewerObject *> mSelectPickList; friend class LLViewerObject; + +private: + static void reportObjectCostFailure(LLSD &objectList); + void fetchObjectCostsCoro(std::string url); + + static void reportPhysicsFlagFailure(LLSD &obejectList); + void fetchPhisicsFlagsCoro(std::string url); + }; diff --git a/indra/newview/llviewerparcelmedia.cpp b/indra/newview/llviewerparcelmedia.cpp index 37b249dddd..828271da7a 100755 --- a/indra/newview/llviewerparcelmedia.cpp +++ b/indra/newview/llviewerparcelmedia.cpp @@ -43,6 +43,7 @@ //#include "llfirstuse.h" #include "llpluginclassmedia.h" #include "llviewertexture.h" +#include "llcorehttputil.h" // Static Variables @@ -457,6 +458,7 @@ void LLViewerParcelMedia::processParcelMediaUpdate( LLMessageSystem *msg, void * } // Static ///////////////////////////////////////////////////////////////////////////////////////// +// *TODO: I can not find any active code where this method is called... void LLViewerParcelMedia::sendMediaNavigateMessage(const std::string& url) { std::string region_url = gAgent.getRegion()->getCapability("ParcelNavigateMedia"); @@ -467,7 +469,9 @@ void LLViewerParcelMedia::sendMediaNavigateMessage(const std::string& url) body["agent-id"] = gAgent.getID(); body["local-id"] = LLViewerParcelMgr::getInstance()->getAgentParcel()->getLocalID(); body["url"] = url; - LLHTTPClient::post(region_url, body, new LLHTTPClient::Responder); + + LLCoreHttpUtil::HttpCoroutineAdapter::messageHttpPost(region_url, body, + "Media Navigation sent to sim.", "Media Navigation failed to send to sim."); } else { diff --git a/indra/newview/llviewerparcelmgr.cpp b/indra/newview/llviewerparcelmgr.cpp index 59f57c218e..0fa6c432db 100755 --- a/indra/newview/llviewerparcelmgr.cpp +++ b/indra/newview/llviewerparcelmgr.cpp @@ -67,6 +67,7 @@ #include "roles_constants.h" #include "llweb.h" #include "llvieweraudio.h" +#include "llcorehttputil.h" const F32 PARCEL_COLLISION_DRAW_SECS = 1.f; @@ -1286,10 +1287,13 @@ const std::string& LLViewerParcelMgr::getAgentParcelName() const void LLViewerParcelMgr::sendParcelPropertiesUpdate(LLParcel* parcel, bool use_agent_region) { - if(!parcel) return; + if(!parcel) + return; LLViewerRegion *region = use_agent_region ? gAgent.getRegion() : LLWorld::getInstance()->getRegionFromPosGlobal( mWestSouth ); - if (!region) return; + if (!region) + return; + //LL_INFOS() << "found region: " << region->getName() << LL_ENDL; LLSD body; @@ -1302,7 +1306,9 @@ void LLViewerParcelMgr::sendParcelPropertiesUpdate(LLParcel* parcel, bool use_ag parcel->packMessage(body); LL_INFOS() << "Sending parcel properties update via capability to: " << url << LL_ENDL; - LLHTTPClient::post(url, body, new LLHTTPClient::Responder()); + + LLCoreHttpUtil::HttpCoroutineAdapter::messageHttpPost(url, body, + "Parcel Properties sent to sim.", "Parcel Properties failed to send to sim."); } else { diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index ec28461201..291af28bc3 100755 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -48,7 +48,6 @@ #include "llavatarrenderinfoaccountant.h" #include "llcallingcard.h" #include "llcaphttpsender.h" -#include "llcapabilitylistener.h" #include "llcommandhandler.h" #include "lldir.h" #include "lleventpoll.h" @@ -78,6 +77,9 @@ #include "llviewerdisplay.h" #include "llviewerwindow.h" #include "llprogressview.h" +#include "llcoros.h" +#include "lleventcoro.h" +#include "llcorehttputil.h" #ifdef LL_WINDOWS #pragma warning(disable:4355) @@ -92,7 +94,6 @@ // We want to allow for seed cap retry, but its not useful after that 60 seconds. // Give it 3 chances, each at 18 seconds to give ourselves a few seconds to connect anyways if we give up. const S32 MAX_SEED_CAP_ATTEMPTS_BEFORE_LOGIN = 3; -const F32 CAP_REQUEST_TIMEOUT = 18; // Even though we gave up on login, keep trying for caps after we are logged in: const S32 MAX_CAP_REQUEST_ATTEMPTS = 30; const U32 DEFAULT_MAX_REGION_WIDE_PRIM_COUNT = 15000; @@ -105,31 +106,62 @@ typedef std::map<std::string, std::string> CapabilityMap; static void log_capabilities(const CapabilityMap &capmap); -class LLViewerRegionImpl { +// support for secondlife:///app/region/{REGION} SLapps +// N.B. this is defined to work exactly like the classic secondlife://{REGION} +// However, the later syntax cannot support spaces in the region name because +// spaces (and %20 chars) are illegal in the hostname of an http URL. Some +// browsers let you get away with this, but some do not (such as Qt's Webkit). +// Hence we introduced the newer secondlife:///app/region alternative. +class LLRegionHandler : public LLCommandHandler +{ +public: + // requests will be throttled from a non-trusted browser + LLRegionHandler() : LLCommandHandler("region", UNTRUSTED_THROTTLE) {} + + bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web) + { + // make sure that we at least have a region name + int num_params = params.size(); + if (num_params < 1) + { + return false; + } + + // build a secondlife://{PLACE} SLurl from this SLapp + std::string url = "secondlife://"; + for (int i = 0; i < num_params; i++) + { + if (i > 0) + { + url += "/"; + } + url += params[i].asString(); + } + + // Process the SLapp as if it was a secondlife://{PLACE} SLurl + LLURLDispatcher::dispatch(url, "clicked", web, true); + return true; + } + +}; +LLRegionHandler gRegionHandler; + + +class LLViewerRegionImpl +{ public: - LLViewerRegionImpl(LLViewerRegion * region, LLHost const & host) - : mHost(host), - mCompositionp(NULL), - mEventPoll(NULL), - mSeedCapMaxAttempts(MAX_CAP_REQUEST_ATTEMPTS), - mSeedCapMaxAttemptsBeforeLogin(MAX_SEED_CAP_ATTEMPTS_BEFORE_LOGIN), - mSeedCapAttempts(0), - mHttpResponderID(0), - mLastCameraUpdate(0), - mLastCameraOrigin(), - mVOCachePartition(NULL), - mLandp(NULL), - // I'd prefer to set the LLCapabilityListener name to match the region - // name -- it's disappointing that's not available at construction time. - // We could instead store an LLCapabilityListener*, making - // setRegionNameAndZone() replace the instance. Would that pose - // consistency problems? Can we even request a capability before calling - // setRegionNameAndZone()? - // For testability -- the new Michael Feathers paradigm -- - // LLCapabilityListener binds all the globals it expects to need at - // construction time. - mCapabilityListener(host.getString(), gMessageSystem, *region, - gAgent.getID(), gAgent.getSessionID()) + LLViewerRegionImpl(LLViewerRegion * region, LLHost const & host): + mHost(host), + mCompositionp(NULL), + mEventPoll(NULL), + mSeedCapMaxAttempts(MAX_CAP_REQUEST_ATTEMPTS), + mSeedCapMaxAttemptsBeforeLogin(MAX_SEED_CAP_ATTEMPTS_BEFORE_LOGIN), + mSeedCapAttempts(0), + mHttpResponderID(0), + mLastCameraUpdate(0), + mLastCameraOrigin(), + mVOCachePartition(NULL), + mLandp(NULL) {} void buildCapabilityNames(LLSD& capabilityNames); @@ -181,220 +213,284 @@ public: S32 mHttpResponderID; - /// Post an event to this LLCapabilityListener to invoke a capability message on - /// this LLViewerRegion's server - /// (https://wiki.lindenlab.com/wiki/Viewer:Messaging/Messaging_Notes#Capabilities) - LLCapabilityListener mCapabilityListener; - //spatial partitions for objects in this region std::vector<LLViewerOctreePartition*> mObjectPartition; - LLVector3 mLastCameraOrigin; - U32 mLastCameraUpdate; -}; - -// support for secondlife:///app/region/{REGION} SLapps -// N.B. this is defined to work exactly like the classic secondlife://{REGION} -// However, the later syntax cannot support spaces in the region name because -// spaces (and %20 chars) are illegal in the hostname of an http URL. Some -// browsers let you get away with this, but some do not (such as Qt's Webkit). -// Hence we introduced the newer secondlife:///app/region alternative. -class LLRegionHandler : public LLCommandHandler -{ -public: - // requests will be throttled from a non-trusted browser - LLRegionHandler() : LLCommandHandler("region", UNTRUSTED_THROTTLE) {} - - bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web) - { - // make sure that we at least have a region name - int num_params = params.size(); - if (num_params < 1) - { - return false; - } - - // build a secondlife://{PLACE} SLurl from this SLapp - std::string url = "secondlife://"; - for (int i = 0; i < num_params; i++) - { - if (i > 0) - { - url += "/"; - } - url += params[i].asString(); - } + LLVector3 mLastCameraOrigin; + U32 mLastCameraUpdate; - // Process the SLapp as if it was a secondlife://{PLACE} SLurl - LLURLDispatcher::dispatch(url, "clicked", web, true); - return true; - } + void requestBaseCapabilitiesCoro(U64 regionHandle); + void requestBaseCapabilitiesCompleteCoro(U64 regionHandle); + void requestSimulatorFeatureCoro(std::string url, U64 regionHandle); }; -LLRegionHandler gRegionHandler; -class BaseCapabilitiesComplete : public LLHTTPClient::Responder +void LLViewerRegionImpl::requestBaseCapabilitiesCoro(U64 regionHandle) { - LOG_CLASS(BaseCapabilitiesComplete); -public: - BaseCapabilitiesComplete(U64 region_handle, S32 id) - : mRegionHandle(region_handle), mID(id) - { } - virtual ~BaseCapabilitiesComplete() - { } - - static BaseCapabilitiesComplete* build( U64 region_handle, S32 id ) - { - return new BaseCapabilitiesComplete(region_handle, id); - } - -private: - /* virtual */void httpFailure() - { - LL_WARNS("AppInit", "Capabilities") << dumpResponse() << LL_ENDL; - LLViewerRegion *regionp = LLWorld::getInstance()->getRegionFromHandle(mRegionHandle); - if (regionp) - { - regionp->failedSeedCapability(); - } - } + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("BaseCapabilitiesRequest", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); - /* virtual */ void httpSuccess() - { - LLViewerRegion *regionp = LLWorld::getInstance()->getRegionFromHandle(mRegionHandle); - if(!regionp) //region was removed - { - LL_WARNS("AppInit", "Capabilities") << "Received results for region that no longer exists!" << LL_ENDL; - return ; - } - if( mID != regionp->getHttpResponderID() ) // region is no longer referring to this responder - { - LL_WARNS("AppInit", "Capabilities") << "Received results for a stale http responder!" << LL_ENDL; - regionp->failedSeedCapability(); - return ; - } - - const LLSD& content = getContent(); - if (!content.isMap()) - { - failureResult(HTTP_INTERNAL_ERROR, "Malformed response contents", content); - return; - } - LLSD::map_const_iterator iter; - for(iter = content.beginMap(); iter != content.endMap(); ++iter) - { - regionp->setCapability(iter->first, iter->second); + LLSD result; + LLViewerRegion *regionp = NULL; - LL_DEBUGS("AppInit", "Capabilities") - << "Capability '" << iter->first << "' is '" << iter->second << "'" << LL_ENDL; - - /* HACK we're waiting for the ServerReleaseNotes */ - if (iter->first == "ServerReleaseNotes" && regionp->getReleaseNotesRequested()) - { - regionp->showReleaseNotes(); - } - } - - regionp->setCapabilitiesReceived(true); + // This loop is used for retrying a capabilities request. + do + { + regionp = LLWorld::getInstance()->getRegionFromHandle(regionHandle); + if (!regionp) //region was removed + { + LL_WARNS("AppInit", "Capabilities") << "Attempting to get capabilities for region that no longer exists!" << LL_ENDL; + return; // this error condition is not recoverable. + } + + std::string url = regionp->getCapability("Seed"); + if (url.empty()) + { + LL_WARNS("AppInit", "Capabilities") << "Failed to get seed capabilities, and can not determine url!" << LL_ENDL; + return; // this error condition is not recoverable. + } + + // After a few attempts, continue login. But keep trying to get the caps: + if (mSeedCapAttempts >= mSeedCapMaxAttemptsBeforeLogin && + STATE_SEED_GRANTED_WAIT == LLStartUp::getStartupState()) + { + LLStartUp::setStartupState(STATE_SEED_CAP_GRANTED); + } + + if (mSeedCapAttempts > mSeedCapMaxAttempts) + { + // *TODO: Give a user pop-up about this error? + LL_WARNS("AppInit", "Capabilities") << "Failed to get seed capabilities from '" << url << "' after " << mSeedCapAttempts << " attempts. Giving up!" << LL_ENDL; + return; // this error condition is not recoverable. + } + + S32 id = ++mHttpResponderID; + ++mSeedCapAttempts; + + LLSD capabilityNames = LLSD::emptyArray(); + buildCapabilityNames(capabilityNames); + + LL_INFOS("AppInit", "Capabilities") << "Requesting seed from " << url + << " (attempt #" << mSeedCapAttempts << ")" << LL_ENDL; + + regionp = NULL; + result = httpAdapter->postAndYield(httpRequest, url, capabilityNames); + + regionp = LLWorld::getInstance()->getRegionFromHandle(regionHandle); + if (!regionp) //region was removed + { + LL_WARNS("AppInit", "Capabilities") << "Received capabilities for region that no longer exists!" << LL_ENDL; + return; // this error condition is not recoverable. + } + + if (id != mHttpResponderID) // region is no longer referring to this request + { + LL_WARNS("AppInit", "Capabilities") << "Received results for a stale capabilities request!" << LL_ENDL; + // setup for retry. + continue; + } + + LLSD httpResults = result["http_result"]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + if (!status) + { + LL_WARNS("AppInit", "Capabilities") << "HttpStatus error " << LL_ENDL; + // setup for retry. + continue; + } + + // remove the http_result from the llsd + result.erase("http_result"); + + LLSD::map_const_iterator iter; + for (iter = result.beginMap(); iter != result.endMap(); ++iter) + { + regionp->setCapability(iter->first, iter->second); + + LL_DEBUGS("AppInit", "Capabilities") + << "Capability '" << iter->first << "' is '" << iter->second << "'" << LL_ENDL; + } + + regionp->setCapabilitiesReceived(true); + + if (STATE_SEED_GRANTED_WAIT == LLStartUp::getStartupState()) + { + LLStartUp::setStartupState(STATE_SEED_CAP_GRANTED); + } + + break; + } + while (true); + + if (regionp && regionp->isCapabilityAvailable("ServerReleaseNotes") && + regionp->getReleaseNotesRequested()) + { // *HACK: we're waiting for the ServerReleaseNotes + regionp->showReleaseNotes(); + } - if (STATE_SEED_GRANTED_WAIT == LLStartUp::getStartupState()) - { - LLStartUp::setStartupState( STATE_SEED_CAP_GRANTED ); - } - } +} -private: - U64 mRegionHandle; - S32 mID; -}; -class BaseCapabilitiesCompleteTracker : public LLHTTPClient::Responder +void LLViewerRegionImpl::requestBaseCapabilitiesCompleteCoro(U64 regionHandle) { - LOG_CLASS(BaseCapabilitiesCompleteTracker); -public: - BaseCapabilitiesCompleteTracker( U64 region_handle) - : mRegionHandle(region_handle) - { } - - virtual ~BaseCapabilitiesCompleteTracker() - { } - - static BaseCapabilitiesCompleteTracker* build( U64 region_handle ) - { - return new BaseCapabilitiesCompleteTracker( region_handle ); - } - -private: - /* virtual */ void httpFailure() - { - LL_WARNS() << dumpResponse() << LL_ENDL; - } + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("BaseCapabilitiesRequest", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); - /* virtual */ void httpSuccess() - { - LLViewerRegion *regionp = LLWorld::getInstance()->getRegionFromHandle(mRegionHandle); - if( !regionp ) - { - LL_WARNS("AppInit", "Capabilities") << "Received results for region that no longer exists!" << LL_ENDL; - return ; - } + LLSD result; + LLViewerRegion *regionp = NULL; - const LLSD& content = getContent(); - if (!content.isMap()) - { - failureResult(HTTP_INTERNAL_ERROR, "Malformed response contents", content); - return; - } - LLSD::map_const_iterator iter; - for(iter = content.beginMap(); iter != content.endMap(); ++iter) - { - regionp->setCapabilityDebug(iter->first, iter->second); - //LL_INFOS()<<"BaseCapabilitiesCompleteTracker New Caps "<<iter->first<<" "<< iter->second<<LL_ENDL; - } - - if ( regionp->getRegionImpl()->mCapabilities.size() != regionp->getRegionImpl()->mSecondCapabilitiesTracker.size() ) - { - LL_WARNS("AppInit", "Capabilities") - << "Sim sent duplicate base caps that differ in size from what we initially received - most likely content. " - << "mCapabilities == " << regionp->getRegionImpl()->mCapabilities.size() - << " mSecondCapabilitiesTracker == " << regionp->getRegionImpl()->mSecondCapabilitiesTracker.size() - << LL_ENDL; + // This loop is used for retrying a capabilities request. + do + { + regionp = LLWorld::getInstance()->getRegionFromHandle(regionHandle); + if (!regionp) //region was removed + { + LL_WARNS("AppInit", "Capabilities") << "Attempting to get capabilities for region that no longer exists!" << LL_ENDL; + break; // this error condition is not recoverable. + } + + std::string url = regionp->getCapabilityDebug("Seed"); + if (url.empty()) + { + LL_WARNS("AppInit", "Capabilities") << "Failed to get seed capabilities, and can not determine url!" << LL_ENDL; + break; // this error condition is not recoverable. + } + + LLSD capabilityNames = LLSD::emptyArray(); + buildCapabilityNames(capabilityNames); + + LL_INFOS("AppInit", "Capabilities") << "Requesting second Seed from " << url << LL_ENDL; + + regionp = NULL; + result = httpAdapter->postAndYield(httpRequest, url, capabilityNames); + + LLSD httpResults = result["http_result"]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + if (!status) + { + LL_WARNS("AppInit", "Capabilities") << "HttpStatus error " << LL_ENDL; + break; // no retry + } + + regionp = LLWorld::getInstance()->getRegionFromHandle(regionHandle); + if (!regionp) //region was removed + { + LL_WARNS("AppInit", "Capabilities") << "Received capabilities for region that no longer exists!" << LL_ENDL; + break; // this error condition is not recoverable. + } + + // remove the http_result from the llsd + result.erase("http_result"); + + LLSD::map_const_iterator iter; + for (iter = result.beginMap(); iter != result.endMap(); ++iter) + { + regionp->setCapabilityDebug(iter->first, iter->second); + //LL_INFOS()<<"BaseCapabilitiesCompleteTracker New Caps "<<iter->first<<" "<< iter->second<<LL_ENDL; + } + + if (mCapabilities.size() != mSecondCapabilitiesTracker.size()) + { + LL_WARNS("AppInit", "Capabilities") + << "Sim sent duplicate base caps that differ in size from what we initially received - most likely content. " + << "mCapabilities == " << mCapabilities.size() + << " mSecondCapabilitiesTracker == " << mSecondCapabilitiesTracker.size() + << LL_ENDL; #ifdef DEBUG_CAPS_GRANTS - LL_WARNS("AppInit", "Capabilities") - << "Initial Base capabilities: " << LL_ENDL; + LL_WARNS("AppInit", "Capabilities") + << "Initial Base capabilities: " << LL_ENDL; - log_capabilities(regionp->getRegionImpl()->mCapabilities); + log_capabilities(mCapabilities); - LL_WARNS("AppInit", "Capabilities") - << "Latest base capabilities: " << LL_ENDL; + LL_WARNS("AppInit", "Capabilities") + << "Latest base capabilities: " << LL_ENDL; - log_capabilities(regionp->getRegionImpl()->mSecondCapabilitiesTracker); + log_capabilities(mSecondCapabilitiesTracker); #endif - if (regionp->getRegionImpl()->mSecondCapabilitiesTracker.size() > regionp->getRegionImpl()->mCapabilities.size() ) - { - // *HACK Since we were granted more base capabilities in this grant request than the initial, replace - // the old with the new. This shouldn't happen i.e. we should always get the same capabilities from a - // sim. The simulator fix from SH-3895 should prevent it from happening, at least in the case of the - // inventory api capability grants. - - // Need to clear a std::map before copying into it because old keys take precedence. - regionp->getRegionImplNC()->mCapabilities.clear(); - regionp->getRegionImplNC()->mCapabilities = regionp->getRegionImpl()->mSecondCapabilitiesTracker; - } - } - else - { - LL_DEBUGS("CrossingCaps") << "Sim sent multiple base cap grants with matching sizes." << LL_ENDL; - } - regionp->getRegionImplNC()->mSecondCapabilitiesTracker.clear(); - } + if (mSecondCapabilitiesTracker.size() > mCapabilities.size()) + { + // *HACK Since we were granted more base capabilities in this grant request than the initial, replace + // the old with the new. This shouldn't happen i.e. we should always get the same capabilities from a + // sim. The simulator fix from SH-3895 should prevent it from happening, at least in the case of the + // inventory api capability grants. + // Need to clear a std::map before copying into it because old keys take precedence. + mCapabilities.clear(); + mCapabilities = mSecondCapabilitiesTracker; + } + } + else + { + LL_DEBUGS("CrossingCaps") << "Sim sent multiple base cap grants with matching sizes." << LL_ENDL; + } + mSecondCapabilitiesTracker.clear(); + } + while (false); -private: - U64 mRegionHandle; -}; +} + +void LLViewerRegionImpl::requestSimulatorFeatureCoro(std::string url, U64 regionHandle) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("BaseCapabilitiesRequest", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + + LLViewerRegion *regionp = NULL; + S32 attemptNumber = 0; + // This loop is used for retrying a capabilities request. + do + { + ++attemptNumber; + + if (attemptNumber > MAX_CAP_REQUEST_ATTEMPTS) + { + LL_WARNS("AppInit", "SimulatorFeatures") << "Retries count exceeded attempting to get Simulator feature from " + << url << LL_ENDL; + break; + } + + regionp = LLWorld::getInstance()->getRegionFromHandle(regionHandle); + if (!regionp) //region was removed + { + LL_WARNS("AppInit", "SimulatorFeatures") << "Attempting to request Sim Feature for region that no longer exists!" << LL_ENDL; + break; // this error condition is not recoverable. + } + + regionp = NULL; + LLSD result = httpAdapter->getAndYield(httpRequest, url); + + LLSD httpResults = result["http_result"]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + if (!status) + { + LL_WARNS("AppInit", "SimulatorFeatures") << "HttpStatus error retrying" << LL_ENDL; + continue; + } + + // remove the http_result from the llsd + result.erase("http_result"); + + regionp = LLWorld::getInstance()->getRegionFromHandle(regionHandle); + if (!regionp) //region was removed + { + LL_WARNS("AppInit", "SimulatorFeatures") << "Attempting to set Sim Feature for region that no longer exists!" << LL_ENDL; + break; // this error condition is not recoverable. + } + + regionp->setSimulatorFeatures(result); + + break; + } + while (true); + +} LLViewerRegion::LLViewerRegion(const U64 &handle, const LLHost &host, @@ -525,11 +621,6 @@ LLViewerRegion::~LLViewerRegion() mImpl = NULL; } -LLEventPump& LLViewerRegion::getCapAPI() const -{ - return mImpl->mCapabilityListener.getCapAPI(); -} - /*virtual*/ const LLHost& LLViewerRegion::getHost() const { @@ -2810,15 +2901,14 @@ void LLViewerRegion::setSeedCapability(const std::string& url) if (getCapability("Seed") == url) { setCapabilityDebug("Seed", url); - LL_DEBUGS("CrossingCaps") << "Received duplicate seed capability, posting to seed " << + LL_WARNS("CrossingCaps") << "Received duplicate seed capability, posting to seed " << url << LL_ENDL; //Instead of just returning we build up a second set of seed caps and compare them //to the "original" seed cap received and determine why there is problem! - LLSD capabilityNames = LLSD::emptyArray(); - mImpl->buildCapabilityNames( capabilityNames ); - LLHTTPClient::post( url, capabilityNames, BaseCapabilitiesCompleteTracker::build(getHandle() ), - LLSD(), CAP_REQUEST_TIMEOUT ); + std::string coroname = + LLCoros::instance().launch("LLEnvironmentRequest::requestBaseCapabilitiesCompleteCoro", + boost::bind(&LLViewerRegionImpl::requestBaseCapabilitiesCompleteCoro, mImpl, getHandle())); return; } @@ -2828,15 +2918,11 @@ void LLViewerRegion::setSeedCapability(const std::string& url) mImpl->mCapabilities.clear(); setCapability("Seed", url); - LLSD capabilityNames = LLSD::emptyArray(); - mImpl->buildCapabilityNames(capabilityNames); - - LL_INFOS() << "posting to seed " << url << LL_ENDL; + std::string coroname = + LLCoros::instance().launch("LLViewerRegionImpl::requestBaseCapabilitiesCoro", + boost::bind(&LLViewerRegionImpl::requestBaseCapabilitiesCoro, mImpl, getHandle())); - S32 id = ++mImpl->mHttpResponderID; - LLHTTPClient::post(url, capabilityNames, - BaseCapabilitiesComplete::build(getHandle(), id), - LLSD(), CAP_REQUEST_TIMEOUT); + LL_INFOS("AppInit", "Capabilities") << "Launching " << coroname << " requesting seed capabilities from " << url << LL_ENDL; } S32 LLViewerRegion::getNumSeedCapRetries() @@ -2844,94 +2930,6 @@ S32 LLViewerRegion::getNumSeedCapRetries() return mImpl->mSeedCapAttempts; } -void LLViewerRegion::failedSeedCapability() -{ - // Should we retry asking for caps? - mImpl->mSeedCapAttempts++; - std::string url = getCapability("Seed"); - if ( url.empty() ) - { - LL_WARNS("AppInit", "Capabilities") << "Failed to get seed capabilities, and can not determine url for retries!" << LL_ENDL; - return; - } - // After a few attempts, continue login. We will keep trying once in-world: - if ( mImpl->mSeedCapAttempts >= mImpl->mSeedCapMaxAttemptsBeforeLogin && - STATE_SEED_GRANTED_WAIT == LLStartUp::getStartupState() ) - { - LLStartUp::setStartupState( STATE_SEED_CAP_GRANTED ); - } - - if ( mImpl->mSeedCapAttempts < mImpl->mSeedCapMaxAttempts) - { - LLSD capabilityNames = LLSD::emptyArray(); - mImpl->buildCapabilityNames(capabilityNames); - - LL_INFOS() << "posting to seed " << url << " (retry " - << mImpl->mSeedCapAttempts << ")" << LL_ENDL; - - S32 id = ++mImpl->mHttpResponderID; - LLHTTPClient::post(url, capabilityNames, - BaseCapabilitiesComplete::build(getHandle(), id), - LLSD(), CAP_REQUEST_TIMEOUT); - } - else - { - // *TODO: Give a user pop-up about this error? - LL_WARNS("AppInit", "Capabilities") << "Failed to get seed capabilities from '" << url << "' after " << mImpl->mSeedCapAttempts << " attempts. Giving up!" << LL_ENDL; - } -} - -class SimulatorFeaturesReceived : public LLHTTPClient::Responder -{ - LOG_CLASS(SimulatorFeaturesReceived); -public: - SimulatorFeaturesReceived(const std::string& retry_url, U64 region_handle, - S32 attempt = 0, S32 max_attempts = MAX_CAP_REQUEST_ATTEMPTS) - : mRetryURL(retry_url), mRegionHandle(region_handle), mAttempt(attempt), mMaxAttempts(max_attempts) - { } - - /* virtual */ void httpFailure() - { - LL_WARNS("AppInit", "SimulatorFeatures") << dumpResponse() << LL_ENDL; - retry(); - } - - /* virtual */ void httpSuccess() - { - LLViewerRegion *regionp = LLWorld::getInstance()->getRegionFromHandle(mRegionHandle); - if(!regionp) //region is removed or responder is not created. - { - LL_WARNS("AppInit", "SimulatorFeatures") - << "Received results for region that no longer exists!" << LL_ENDL; - return ; - } - - const LLSD& content = getContent(); - if (!content.isMap()) - { - failureResult(HTTP_INTERNAL_ERROR, "Malformed response contents", content); - return; - } - regionp->setSimulatorFeatures(content); - } - - void retry() - { - if (mAttempt < mMaxAttempts) - { - mAttempt++; - LL_WARNS("AppInit", "SimulatorFeatures") << "Re-trying '" << mRetryURL << "'. Retry #" << mAttempt << LL_ENDL; - LLHTTPClient::get(mRetryURL, new SimulatorFeaturesReceived(*this), LLSD(), CAP_REQUEST_TIMEOUT); - } - } - - std::string mRetryURL; - U64 mRegionHandle; - S32 mAttempt; - S32 mMaxAttempts; -}; - - void LLViewerRegion::setCapability(const std::string& name, const std::string& url) { if(name == "EventQueueGet") @@ -2947,7 +2945,11 @@ void LLViewerRegion::setCapability(const std::string& name, const std::string& u else if (name == "SimulatorFeatures") { // kick off a request for simulator features - LLHTTPClient::get(url, new SimulatorFeaturesReceived(url, getHandle()), LLSD(), CAP_REQUEST_TIMEOUT); + std::string coroname = + LLCoros::instance().launch("LLViewerRegionImpl::requestSimulatorFeatureCoro", + boost::bind(&LLViewerRegionImpl::requestSimulatorFeatureCoro, mImpl, url, getHandle())); + + LL_INFOS("AppInit", "SimulatorFeatures") << "Launching " << coroname << " requesting simulator features from " << url << LL_ENDL; } else { @@ -2970,9 +2972,20 @@ void LLViewerRegion::setCapabilityDebug(const std::string& name, const std::stri mHttpUrl = url ; } } +} +std::string LLViewerRegion::getCapabilityDebug(const std::string& name) const +{ + CapabilityMap::const_iterator iter = mImpl->mSecondCapabilitiesTracker.find(name); + if (iter == mImpl->mSecondCapabilitiesTracker.end()) + { + return ""; + } + + return iter->second; } + bool LLViewerRegion::isSpecialCapabilityName(const std::string &name) { return name == "EventQueueGet" || name == "UntrustedSimulatorMessage"; diff --git a/indra/newview/llviewerregion.h b/indra/newview/llviewerregion.h index c14fa5aee8..8c4966369c 100755 --- a/indra/newview/llviewerregion.h +++ b/indra/newview/llviewerregion.h @@ -40,7 +40,6 @@ #include "llweb.h" #include "llcapabilityprovider.h" #include "m4math.h" // LLMatrix4 -#include "llhttpclient.h" #include "llframetimer.h" // Surface id's @@ -62,7 +61,6 @@ class LLVOCache; class LLVOCacheEntry; class LLSpatialPartition; class LLEventPump; -class LLCapabilityListener; class LLDataPacker; class LLDataPackerBinaryBuffer; class LLHost; @@ -253,13 +251,14 @@ public: // Get/set named capability URLs for this region. void setSeedCapability(const std::string& url); - void failedSeedCapability(); S32 getNumSeedCapRetries(); void setCapability(const std::string& name, const std::string& url); void setCapabilityDebug(const std::string& name, const std::string& url); bool isCapabilityAvailable(const std::string& name) const; // implements LLCapabilityProvider virtual std::string getCapability(const std::string& name) const; + std::string getCapabilityDebug(const std::string& name) const; + // has region received its final (not seed) capability list? bool capabilitiesReceived() const; @@ -269,10 +268,6 @@ public: static bool isSpecialCapabilityName(const std::string &name); void logActiveCapabilities() const; - /// Get LLEventPump on which we listen for capability requests - /// (https://wiki.lindenlab.com/wiki/Viewer:Messaging/Messaging_Notes#Capabilities) - LLEventPump& getCapAPI() const; - /// implements LLCapabilityProvider /*virtual*/ const LLHost& getHost() const; const U64 &getHandle() const { return mHandle; } diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp index f60829e9e8..2c3067cd3a 100755 --- a/indra/newview/llviewerstats.cpp +++ b/indra/newview/llviewerstats.cpp @@ -60,6 +60,7 @@ #include "llfeaturemanager.h" #include "llviewernetwork.h" #include "llmeshrepository.h" //for LLMeshRepository::sBytesReceived +#include "llcorehttputil.h" namespace LLStatViewer { @@ -410,24 +411,6 @@ void update_statistics() } } -class ViewerStatsResponder : public LLHTTPClient::Responder -{ - LOG_CLASS(ViewerStatsResponder); -public: - ViewerStatsResponder() { } - -private: - /* virtual */ void httpFailure() - { - LL_WARNS() << dumpResponse() << LL_ENDL; - } - - /* virtual */ void httpSuccess() - { - LL_INFOS() << "OK" << LL_ENDL; - } -}; - /* * The sim-side LLSD is in newsim/llagentinfo.cpp:forwardViewerStats. * @@ -618,8 +601,8 @@ void send_stats() body["MinimalSkin"] = false; LLViewerStats::getInstance()->addToMessage(body); - LLHTTPClient::post(url, body, new ViewerStatsResponder()); - + LLCoreHttpUtil::HttpCoroutineAdapter::messageHttpPost(url, body, + "Statistics posted to sim", "Failed to post statistics to sim"); LLViewerStats::instance().getRecording().resume(); } diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp index c1ca0aed69..fb2171ccc2 100755 --- a/indra/newview/llvoavatarself.cpp +++ b/indra/newview/llvoavatarself.cpp @@ -65,6 +65,7 @@ #include "llsdutil.h" #include "llstartup.h" #include "llsdserialize.h" +#include "llcorehttputil.h" #if LL_MSVC // disable boost::lexical_cast warning @@ -106,6 +107,9 @@ void selfClearPhases() using namespace LLAvatarAppearanceDefines; + +LLSD summarize_by_buckets(std::vector<LLSD> in_records, std::vector<std::string> by_fields, std::string val_field); + /********************************************************************************* ** ** ** Begin private LLVOAvatarSelf Support classes @@ -128,25 +132,6 @@ struct LocalTextureData LLTextureEntry *mTexEntry; }; -// TODO - this class doesn't really do anything, could just use a base -// class responder if nothing else gets added. -class LLHoverHeightResponder: public LLHTTPClient::Responder -{ -public: - LLHoverHeightResponder(): LLHTTPClient::Responder() {} - -private: - void httpFailure() - { - LL_WARNS() << dumpResponse() << LL_ENDL; - } - - void httpSuccess() - { - LL_INFOS() << dumpResponse() << LL_ENDL; - } -}; - //----------------------------------------------------------------------------- // Callback data //----------------------------------------------------------------------------- @@ -181,7 +166,9 @@ LLVOAvatarSelf::LLVOAvatarSelf(const LLUUID& id, mRegionCrossingCount(0), // Value outside legal range, so will always be a mismatch the // first time through. - mLastHoverOffsetSent(LLVector3(0.0f, 0.0f, -999.0f)) + mLastHoverOffsetSent(LLVector3(0.0f, 0.0f, -999.0f)), + mInitialMetric(true), + mMetricSequence(0) { mMotionController.mIsSelf = TRUE; @@ -2164,43 +2151,76 @@ const std::string LLVOAvatarSelf::debugDumpAllLocalTextureDataInfo() const return text; } -class ViewerAppearanceChangeMetricsResponder: public LLCurl::Responder -{ - LOG_CLASS(ViewerAppearanceChangeMetricsResponder); -public: - ViewerAppearanceChangeMetricsResponder( S32 expected_sequence, - volatile const S32 & live_sequence, - volatile bool & reporting_started): - mExpectedSequence(expected_sequence), - mLiveSequence(live_sequence), - mReportingStarted(reporting_started) - { - } - -private: - /* virtual */ void httpSuccess() - { - LL_DEBUGS("Avatar") << "OK" << LL_ENDL; - - gPendingMetricsUploads--; - if (mLiveSequence == mExpectedSequence) - { - mReportingStarted = true; - } - } - - /* virtual */ void httpFailure() - { - // if we add retry, this should be removed from the httpFailure case - LL_WARNS("Avatar") << dumpResponse() << LL_ENDL; - gPendingMetricsUploads--; - } - -private: - S32 mExpectedSequence; - volatile const S32 & mLiveSequence; - volatile bool & mReportingStarted; -}; +void LLVOAvatarSelf::appearanceChangeMetricsCoro(std::string url) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("appearanceChangeMetrics", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpOptions::ptr_t httpOpts = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions); + + S32 currentSequence = mMetricSequence; + if (S32_MAX == ++mMetricSequence) + mMetricSequence = 0; + + LLSD msg; + msg["message"] = "ViewerAppearanceChangeMetrics"; + msg["session_id"] = gAgentSessionID; + msg["agent_id"] = gAgentID; + msg["sequence"] = currentSequence; + msg["initial"] = mInitialMetric; + msg["break"] = false; + msg["duration"] = mTimeSinceLastRezMessage.getElapsedTimeF32(); + + // Status of our own rezzing. + msg["rez_status"] = LLVOAvatar::rezStatusToString(getRezzedStatus()); + + // Status of all nearby avs including ourself. + msg["nearby"] = LLSD::emptyArray(); + std::vector<S32> rez_counts; + LLVOAvatar::getNearbyRezzedStats(rez_counts); + for (S32 rez_stat = 0; rez_stat < rez_counts.size(); ++rez_stat) + { + std::string rez_status_name = LLVOAvatar::rezStatusToString(rez_stat); + msg["nearby"][rez_status_name] = rez_counts[rez_stat]; + } + + // std::vector<std::string> bucket_fields("timer_name","is_self","grid_x","grid_y","is_using_server_bake"); + std::vector<std::string> by_fields; + by_fields.push_back("timer_name"); + by_fields.push_back("completed"); + by_fields.push_back("grid_x"); + by_fields.push_back("grid_y"); + by_fields.push_back("is_using_server_bakes"); + by_fields.push_back("is_self"); + by_fields.push_back("central_bake_version"); + LLSD summary = summarize_by_buckets(mPendingTimerRecords, by_fields, std::string("elapsed")); + msg["timers"] = summary; + + mPendingTimerRecords.clear(); + + LL_DEBUGS("Avatar") << avString() << "message: " << ll_pretty_print_sd(msg) << LL_ENDL; + + gPendingMetricsUploads++; + + LLSD result = httpAdapter->postAndYield(httpRequest, url, msg); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + gPendingMetricsUploads--; + + if (!status) + { + LL_WARNS("Avatar") << "Unable to upload statistics" << LL_ENDL; + return; + } + else + { + LL_INFOS("Avatar") << "Statistics upload OK" << LL_ENDL; + mInitialMetric = false; + } +} bool LLVOAvatarSelf::updateAvatarRezMetrics(bool force_send) { @@ -2278,51 +2298,7 @@ LLSD summarize_by_buckets(std::vector<LLSD> in_records, void LLVOAvatarSelf::sendViewerAppearanceChangeMetrics() { - static volatile bool reporting_started(false); - static volatile S32 report_sequence(0); - - LLSD msg; - msg["message"] = "ViewerAppearanceChangeMetrics"; - msg["session_id"] = gAgentSessionID; - msg["agent_id"] = gAgentID; - msg["sequence"] = report_sequence; - msg["initial"] = !reporting_started; - msg["break"] = false; - msg["duration"] = mTimeSinceLastRezMessage.getElapsedTimeF32(); - - // Status of our own rezzing. - msg["rez_status"] = LLVOAvatar::rezStatusToString(getRezzedStatus()); - - // Status of all nearby avs including ourself. - msg["nearby"] = LLSD::emptyArray(); - std::vector<S32> rez_counts; - LLVOAvatar::getNearbyRezzedStats(rez_counts); - for (S32 rez_stat=0; rez_stat < rez_counts.size(); ++rez_stat) - { - std::string rez_status_name = LLVOAvatar::rezStatusToString(rez_stat); - msg["nearby"][rez_status_name] = rez_counts[rez_stat]; - } - - // std::vector<std::string> bucket_fields("timer_name","is_self","grid_x","grid_y","is_using_server_bake"); - std::vector<std::string> by_fields; - by_fields.push_back("timer_name"); - by_fields.push_back("completed"); - by_fields.push_back("grid_x"); - by_fields.push_back("grid_y"); - by_fields.push_back("is_using_server_bakes"); - by_fields.push_back("is_self"); - by_fields.push_back("central_bake_version"); - LLSD summary = summarize_by_buckets(mPendingTimerRecords, by_fields, std::string("elapsed")); - msg["timers"] = summary; - - mPendingTimerRecords.clear(); - - // Update sequence number - if (S32_MAX == ++report_sequence) - report_sequence = 0; - - LL_DEBUGS("Avatar") << avString() << "message: " << ll_pretty_print_sd(msg) << LL_ENDL; - std::string caps_url; + std::string caps_url; if (getRegion()) { // runway - change here to activate. @@ -2330,13 +2306,9 @@ void LLVOAvatarSelf::sendViewerAppearanceChangeMetrics() } if (!caps_url.empty()) { - gPendingMetricsUploads++; - LLCurlRequest::headers_t headers; - LLHTTPClient::post(caps_url, - msg, - new ViewerAppearanceChangeMetricsResponder(report_sequence, - report_sequence, - reporting_started)); + + LLCoros::instance().launch("LLVOAvatarSelf::appearanceChangeMetricsCoro", + boost::bind(&LLVOAvatarSelf::appearanceChangeMetricsCoro, this, caps_url)); mTimeSinceLastRezMessage.reset(); } } @@ -2759,8 +2731,12 @@ void LLVOAvatarSelf::sendHoverHeight() const update["hover_height"] = hover_offset[2]; LL_DEBUGS("Avatar") << avString() << "sending hover height value " << hover_offset[2] << LL_ENDL; - LLHTTPClient::post(url, update, new LLHoverHeightResponder); + // *TODO: - this class doesn't really do anything, could just use a base + // class responder if nothing else gets added. + // (comment from removed Responder) + LLCoreHttpUtil::HttpCoroutineAdapter::messageHttpPost(url, update, + "Hover hight sent to sim", "Hover hight not sent to sim"); mLastHoverOffsetSent = hover_offset; } } diff --git a/indra/newview/llvoavatarself.h b/indra/newview/llvoavatarself.h index 332a6353ad..5bdd662152 100755 --- a/indra/newview/llvoavatarself.h +++ b/indra/newview/llvoavatarself.h @@ -31,6 +31,8 @@ #include "llviewertexture.h" #include "llvoavatar.h" #include <map> +#include "lleventcoro.h" +#include "llcoros.h" struct LocalTextureData; class LLInventoryCallback; @@ -393,6 +395,9 @@ private: F32 mDebugBakedTextureTimes[LLAvatarAppearanceDefines::BAKED_NUM_INDICES][2]; // time to start upload and finish upload of each baked texture void debugTimingLocalTexLoaded(BOOL success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata); + void appearanceChangeMetricsCoro(std::string url); + bool mInitialMetric; + S32 mMetricSequence; /** Diagnostics ** ** *******************************************************************************/ diff --git a/indra/newview/llvoicechannel.cpp b/indra/newview/llvoicechannel.cpp index 426ca332e4..192d50ae9b 100755 --- a/indra/newview/llvoicechannel.cpp +++ b/indra/newview/llvoicechannel.cpp @@ -28,7 +28,6 @@ #include "llagent.h" #include "llfloaterreg.h" -#include "llhttpclient.h" #include "llimview.h" #include "llnotifications.h" #include "llnotificationsutil.h" @@ -37,7 +36,7 @@ #include "llviewercontrol.h" #include "llviewerregion.h" #include "llvoicechannel.h" - +#include "llcorehttputil.h" LLVoiceChannel::voice_channel_map_t LLVoiceChannel::sVoiceChannelMap; LLVoiceChannel::voice_channel_map_uri_t LLVoiceChannel::sVoiceChannelURIMap; @@ -52,71 +51,6 @@ BOOL LLVoiceChannel::sSuspended = FALSE; // const U32 DEFAULT_RETRIES_COUNT = 3; - -class LLVoiceCallCapResponder : public LLHTTPClient::Responder -{ - LOG_CLASS(LLVoiceCallCapResponder); -public: - LLVoiceCallCapResponder(const LLUUID& session_id) : mSessionID(session_id) {}; - -protected: - // called with bad status codes - virtual void httpFailure(); - virtual void httpSuccess(); - -private: - LLUUID mSessionID; -}; - - -void LLVoiceCallCapResponder::httpFailure() -{ - LL_WARNS("Voice") << dumpResponse() << LL_ENDL; - LLVoiceChannel* channelp = LLVoiceChannel::getChannelByID(mSessionID); - if ( channelp ) - { - if ( HTTP_FORBIDDEN == getStatus() ) - { - //403 == no ability - LLNotificationsUtil::add( - "VoiceNotAllowed", - channelp->getNotifyArgs()); - } - else - { - LLNotificationsUtil::add( - "VoiceCallGenericError", - channelp->getNotifyArgs()); - } - channelp->deactivate(); - } -} - -void LLVoiceCallCapResponder::httpSuccess() -{ - LLVoiceChannel* channelp = LLVoiceChannel::getChannelByID(mSessionID); - if (channelp) - { - //*TODO: DEBUG SPAM - const LLSD& content = getContent(); - if (!content.isMap()) - { - failureResult(HTTP_INTERNAL_ERROR, "Malformed response contents", content); - return; - } - LLSD::map_const_iterator iter; - for(iter = content.beginMap(); iter != content.endMap(); ++iter) - { - LL_DEBUGS("Voice") << "LLVoiceCallCapResponder::result got " - << iter->first << LL_ENDL; - } - - channelp->setChannelInfo( - content["voice_credentials"]["channel_uri"].asString(), - content["voice_credentials"]["channel_credentials"].asString()); - } -} - // // LLVoiceChannel // @@ -545,12 +479,9 @@ void LLVoiceChannelGroup::getChannelInfo() if (region) { std::string url = region->getCapability("ChatSessionRequest"); - LLSD data; - data["method"] = "call"; - data["session-id"] = mSessionID; - LLHTTPClient::post(url, - data, - new LLVoiceCallCapResponder(mSessionID)); + + LLCoros::instance().launch("LLVoiceChannelGroup::voiceCallCapCoro", + boost::bind(&LLVoiceChannelGroup::voiceCallCapCoro, this, url)); } } @@ -673,6 +604,66 @@ void LLVoiceChannelGroup::setState(EState state) } } +void LLVoiceChannelGroup::voiceCallCapCoro(std::string url) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("voiceCallCapCoro", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + + LLSD postData; + postData["method"] = "call"; + postData["session-id"] = mSessionID; + + LL_INFOS("Voice", "voiceCallCapCoro") << "Generic POST for " << url << LL_ENDL; + + LLSD result = httpAdapter->postAndYield(httpRequest, url, postData); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + LLVoiceChannel* channelp = LLVoiceChannel::getChannelByID(mSessionID); + if (!channelp) + { + LL_WARNS("Voice") << "Unable to retrieve channel with Id = " << mSessionID << LL_ENDL; + return; + } + + if (!status) + { + if (status == LLCore::HttpStatus(HTTP_FORBIDDEN)) + { + //403 == no ability + LLNotificationsUtil::add( + "VoiceNotAllowed", + channelp->getNotifyArgs()); + } + else + { + LLNotificationsUtil::add( + "VoiceCallGenericError", + channelp->getNotifyArgs()); + } + channelp->deactivate(); + return; + } + + result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS); + + LLSD::map_const_iterator iter; + for (iter = result.beginMap(); iter != result.endMap(); ++iter) + { + LL_DEBUGS("Voice") << "LLVoiceCallCapResponder::result got " + << iter->first << LL_ENDL; + } + + channelp->setChannelInfo( + result["voice_credentials"]["channel_uri"].asString(), + result["voice_credentials"]["channel_credentials"].asString()); + +} + + // // LLVoiceChannelProximal // diff --git a/indra/newview/llvoicechannel.h b/indra/newview/llvoicechannel.h index fed44974fd..ef15b2c79e 100755 --- a/indra/newview/llvoicechannel.h +++ b/indra/newview/llvoicechannel.h @@ -29,6 +29,8 @@ #include "llhandle.h" #include "llvoiceclient.h" +#include "lleventcoro.h" +#include "llcoros.h" class LLPanel; @@ -157,6 +159,8 @@ protected: virtual void setState(EState state); private: + void voiceCallCapCoro(std::string url); + U32 mRetries; BOOL mIsRetrying; }; diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index a6a7a35b03..f50ffdeae7 100755 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -64,6 +64,8 @@ #include "llviewernetwork.h" #include "llnotificationsutil.h" +#include "llcorehttputil.h" + #include "stringize.h" // for base64 decoding @@ -122,66 +124,6 @@ static int scale_speaker_volume(float volume) } -class LLVivoxVoiceAccountProvisionResponder : - public LLHTTPClient::Responder -{ - LOG_CLASS(LLVivoxVoiceAccountProvisionResponder); -public: - LLVivoxVoiceAccountProvisionResponder(int retries) - { - mRetries = retries; - } - -private: - /* virtual */ void httpFailure() - { - LL_WARNS("Voice") << "ProvisionVoiceAccountRequest returned an error, " - << ( (mRetries > 0) ? "retrying" : "too many retries (giving up)" ) - << " " << dumpResponse() << LL_ENDL; - - if ( mRetries > 0 ) - { - LLVivoxVoiceClient::getInstance()->requestVoiceAccountProvision(mRetries - 1); - } - else - { - LLVivoxVoiceClient::getInstance()->giveUp(); - } - } - - /* virtual */ void httpSuccess() - { - std::string voice_sip_uri_hostname; - std::string voice_account_server_uri; - - LL_DEBUGS("Voice") << "ProvisionVoiceAccountRequest response:" << dumpResponse() << LL_ENDL; - - const LLSD& content = getContent(); - if (!content.isMap()) - { - failureResult(HTTP_INTERNAL_ERROR, "Malformed response contents", content); - return; - } - if(content.has("voice_sip_uri_hostname")) - voice_sip_uri_hostname = content["voice_sip_uri_hostname"].asString(); - - // this key is actually misnamed -- it will be an entire URI, not just a hostname. - if(content.has("voice_account_server_name")) - voice_account_server_uri = content["voice_account_server_name"].asString(); - - LLVivoxVoiceClient::getInstance()->login( - content["username"].asString(), - content["password"].asString(), - voice_sip_uri_hostname, - voice_account_server_uri); - } - -private: - int mRetries; -}; - - - /////////////////////////////////////////////////////////////////////////////////////////////// class LLVivoxVoiceClientMuteListObserver : public LLMuteListObserver @@ -194,59 +136,6 @@ static LLVivoxVoiceClientMuteListObserver mutelist_listener; static bool sMuteListListener_listening = false; /////////////////////////////////////////////////////////////////////////////////////////////// - -class LLVivoxVoiceClientCapResponder : public LLHTTPClient::Responder -{ - LOG_CLASS(LLVivoxVoiceClientCapResponder); -public: - LLVivoxVoiceClientCapResponder(LLVivoxVoiceClient::state requesting_state) : mRequestingState(requesting_state) {}; - -private: - // called with bad status codes - /* virtual */ void httpFailure(); - /* virtual */ void httpSuccess(); - - LLVivoxVoiceClient::state mRequestingState; // state -}; - -void LLVivoxVoiceClientCapResponder::httpFailure() -{ - LL_WARNS("Voice") << dumpResponse() << LL_ENDL; - LLVivoxVoiceClient::getInstance()->sessionTerminate(); -} - -void LLVivoxVoiceClientCapResponder::httpSuccess() -{ - LLSD::map_const_iterator iter; - - LL_DEBUGS("Voice") << "ParcelVoiceInfoRequest response:" << dumpResponse() << LL_ENDL; - - std::string uri; - std::string credentials; - - const LLSD& content = getContent(); - if ( content.has("voice_credentials") ) - { - LLSD voice_credentials = content["voice_credentials"]; - if ( voice_credentials.has("channel_uri") ) - { - uri = voice_credentials["channel_uri"].asString(); - } - if ( voice_credentials.has("channel_credentials") ) - { - credentials = - voice_credentials["channel_credentials"].asString(); - } - } - - // set the spatial channel. If no voice credentials or uri are - // available, then we simply drop out of voice spatially. - if(LLVivoxVoiceClient::getInstance()->parcelVoiceInfoReceived(mRequestingState)) - { - LLVivoxVoiceClient::getInstance()->setSpatialChannel(uri, credentials); - } -} - static LLProcessPtr sGatewayPtr; static bool isGatewayRunning() @@ -556,16 +445,51 @@ void LLVivoxVoiceClient::requestVoiceAccountProvision(S32 retries) if ( !url.empty() ) { - LLHTTPClient::post( - url, - LLSD(), - new LLVivoxVoiceAccountProvisionResponder(retries)); - + LLCoros::instance().launch("LLVivoxVoiceClient::voiceAccountProvisionCoro", + boost::bind(&LLVivoxVoiceClient::voiceAccountProvisionCoro, this, url, retries)); setState(stateConnectorStart); } } } +void LLVivoxVoiceClient::voiceAccountProvisionCoro(std::string url, S32 retries) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("voiceAccountProvision", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpOptions::ptr_t httpOpts = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions); + + httpOpts->setRetries(retries); + + LLSD result = httpAdapter->postAndYield(httpRequest, url, LLSD(), httpOpts); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) + { + LL_WARNS("Voice") << "Unable to provision voice account." << LL_ENDL; + giveUp(); + return; + } + + std::string voice_sip_uri_hostname; + std::string voice_account_server_uri; + + //LL_DEBUGS("Voice") << "ProvisionVoiceAccountRequest response:" << dumpResponse() << LL_ENDL; + + if (result.has("voice_sip_uri_hostname")) + voice_sip_uri_hostname = result["voice_sip_uri_hostname"].asString(); + + // this key is actually misnamed -- it will be an entire URI, not just a hostname. + if (result.has("voice_account_server_name")) + voice_account_server_uri = result["voice_account_server_name"].asString(); + + login(result["username"].asString(), result["password"].asString(), + voice_sip_uri_hostname, voice_account_server_uri); +} + void LLVivoxVoiceClient::login( const std::string& account_name, const std::string& password, @@ -4003,14 +3927,60 @@ bool LLVivoxVoiceClient::requestParcelVoiceInfo() LLSD data; LL_DEBUGS("Voice") << "sending ParcelVoiceInfoRequest (" << mCurrentRegionName << ", " << mCurrentParcelLocalID << ")" << LL_ENDL; - LLHTTPClient::post( - url, - data, - new LLVivoxVoiceClientCapResponder(getState())); + LLCoros::instance().launch("LLVivoxVoiceClient::parcelVoiceInfoRequestCoro", + boost::bind(&LLVivoxVoiceClient::parcelVoiceInfoRequestCoro, this, url)); return true; } } +void LLVivoxVoiceClient::parcelVoiceInfoRequestCoro(std::string url) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("parcelVoiceInfoRequest", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + state requestingState = getState(); + + LLSD result = httpAdapter->postAndYield(httpRequest, url, LLSD()); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) + { + LL_WARNS("Voice") << "No voice on parcel" << LL_ENDL; + sessionTerminate(); + return; + } + + std::string uri; + std::string credentials; + + if (result.has("voice_credentials")) + { + LLSD voice_credentials = result["voice_credentials"]; + if (voice_credentials.has("channel_uri")) + { + uri = voice_credentials["channel_uri"].asString(); + } + if (voice_credentials.has("channel_credentials")) + { + credentials = + voice_credentials["channel_credentials"].asString(); + } + } + + LL_INFOS("Voice") << "Voice URI is " << uri << LL_ENDL; + + // set the spatial channel. If no voice credentials or uri are + // available, then we simply drop out of voice spatially. + if (parcelVoiceInfoReceived(requestingState)) + { + setSpatialChannel(uri, credentials); + } + +} + void LLVivoxVoiceClient::switchChannel( std::string uri, bool spatial, diff --git a/indra/newview/llvoicevivox.h b/indra/newview/llvoicevivox.h index a4ec9f2a69..b12ed80e41 100755 --- a/indra/newview/llvoicevivox.h +++ b/indra/newview/llvoicevivox.h @@ -37,6 +37,9 @@ class LLVivoxProtocolParser; #include "llframetimer.h" #include "llviewerregion.h" #include "llcallingcard.h" // for LLFriendObserver +#include "lleventcoro.h" +#include "llcoros.h" +#include <queue> #ifdef LL_USESYSTEMLIBS # include "expat.h" @@ -46,7 +49,6 @@ class LLVivoxProtocolParser; #include "llvoiceclient.h" class LLAvatarName; -class LLVivoxVoiceAccountProvisionResponder; class LLVivoxVoiceClientMuteListObserver; @@ -251,7 +253,6 @@ protected: ////////////////////// // Vivox Specific definitions - friend class LLVivoxVoiceAccountProvisionResponder; friend class LLVivoxVoiceClientMuteListObserver; friend class LLVivoxVoiceClientFriendsObserver; @@ -635,6 +636,10 @@ protected: void accountGetTemplateFontsResponse(int statusCode, const std::string &statusString); private: + + void voiceAccountProvisionCoro(std::string url, S32 retries); + void parcelVoiceInfoRequestCoro(std::string url); + LLVoiceVersionInfo mVoiceVersion; /// Clean up objects created during a voice session. diff --git a/indra/newview/llwebprofile.cpp b/indra/newview/llwebprofile.cpp index ddb7f7bfce..2033a5f36a 100755 --- a/indra/newview/llwebprofile.cpp +++ b/indra/newview/llwebprofile.cpp @@ -30,14 +30,17 @@ // libs #include "llbufferstream.h" -#include "llhttpclient.h" #include "llimagepng.h" #include "llplugincookiestore.h" +#include "llsdserialize.h" + // newview #include "llpanelprofile.h" // for getProfileURL(). FIXME: move the method to LLAvatarActions #include "llviewermedia.h" // FIXME: don't use LLViewerMedia internals +#include "llcorehttputil.h" + // third-party #include "reader.h" // JSON @@ -55,139 +58,6 @@ */ /////////////////////////////////////////////////////////////////////////////// -// LLWebProfileResponders::ConfigResponder - -class LLWebProfileResponders::ConfigResponder : public LLHTTPClient::Responder -{ - LOG_CLASS(LLWebProfileResponders::ConfigResponder); - -public: - ConfigResponder(LLPointer<LLImageFormatted> imagep) - : mImagep(imagep) - { - } - - // *TODO: Check for 'application/json' content type, and parse json at the base class. - /*virtual*/ void completedRaw( - const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer) - { - LLBufferStream istr(channels, buffer.get()); - std::stringstream strstrm; - strstrm << istr.rdbuf(); - const std::string body = strstrm.str(); - - if (getStatus() != HTTP_OK) - { - LL_WARNS() << "Failed to get upload config " << dumpResponse() << LL_ENDL; - LLWebProfile::reportImageUploadStatus(false); - return; - } - - Json::Value root; - Json::Reader reader; - if (!reader.parse(body, root)) - { - LL_WARNS() << "Failed to parse upload config: " << reader.getFormatedErrorMessages() << LL_ENDL; - LLWebProfile::reportImageUploadStatus(false); - return; - } - - // *TODO: 404 = not supported by the grid - // *TODO: increase timeout or handle 499 Expired - - // Convert config to LLSD. - const Json::Value data = root["data"]; - const std::string upload_url = root["url"].asString(); - LLSD config; - config["acl"] = data["acl"].asString(); - config["AWSAccessKeyId"] = data["AWSAccessKeyId"].asString(); - config["Content-Type"] = data["Content-Type"].asString(); - config["key"] = data["key"].asString(); - config["policy"] = data["policy"].asString(); - config["success_action_redirect"] = data["success_action_redirect"].asString(); - config["signature"] = data["signature"].asString(); - config["add_loc"] = data.get("add_loc", "0").asString(); - config["caption"] = data.get("caption", "").asString(); - - // Do the actual image upload using the configuration. - LL_DEBUGS("Snapshots") << "Got upload config, POSTing image to " << upload_url << ", config=[" << config << "]" << LL_ENDL; - LLWebProfile::post(mImagep, config, upload_url); - } - -private: - LLPointer<LLImageFormatted> mImagep; -}; - -/////////////////////////////////////////////////////////////////////////////// -// LLWebProfilePostImageRedirectResponder -class LLWebProfileResponders::PostImageRedirectResponder : public LLHTTPClient::Responder -{ - LOG_CLASS(LLWebProfileResponders::PostImageRedirectResponder); - -public: - /*virtual*/ void completedRaw( - const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer) - { - if (getStatus() != HTTP_OK) - { - LL_WARNS() << "Failed to upload image " << dumpResponse() << LL_ENDL; - LLWebProfile::reportImageUploadStatus(false); - return; - } - - LLBufferStream istr(channels, buffer.get()); - std::stringstream strstrm; - strstrm << istr.rdbuf(); - const std::string body = strstrm.str(); - LL_INFOS() << "Image uploaded." << LL_ENDL; - LL_DEBUGS("Snapshots") << "Uploading image succeeded. Response: [" << body << "]" << LL_ENDL; - LLWebProfile::reportImageUploadStatus(true); - } -}; - - -/////////////////////////////////////////////////////////////////////////////// -// LLWebProfileResponders::PostImageResponder -class LLWebProfileResponders::PostImageResponder : public LLHTTPClient::Responder -{ - LOG_CLASS(LLWebProfileResponders::PostImageResponder); - -public: - /*virtual*/ void completedRaw(const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer) - { - // Viewer seems to fail to follow a 303 redirect on POST request - // (URLRequest Error: 65, Send failed since rewinding of the data stream failed). - // Handle it manually. - if (getStatus() == HTTP_SEE_OTHER) - { - LLSD headers = LLViewerMedia::getHeaders(); - headers[HTTP_OUT_HEADER_COOKIE] = LLWebProfile::getAuthCookie(); - const std::string& redir_url = getResponseHeader(HTTP_IN_HEADER_LOCATION); - if (redir_url.empty()) - { - LL_WARNS() << "Received empty redirection URL " << dumpResponse() << LL_ENDL; - LL_DEBUGS("Snapshots") << "[headers:" << getResponseHeaders() << "]" << LL_ENDL; - LLWebProfile::reportImageUploadStatus(false); - } - else - { - LL_DEBUGS("Snapshots") << "Got redirection URL: " << redir_url << LL_ENDL; - LLHTTPClient::get(redir_url, new LLWebProfileResponders::PostImageRedirectResponder, headers); - } - } - else - { - LL_WARNS() << "Unexpected POST response " << dumpResponse() << LL_ENDL; - LL_DEBUGS("Snapshots") << "[headers:" << getResponseHeaders() << "]" << LL_ENDL; - LLWebProfile::reportImageUploadStatus(false); - } - } -}; - -/////////////////////////////////////////////////////////////////////////////// // LLWebProfile std::string LLWebProfile::sAuthCookie; @@ -196,15 +66,9 @@ LLWebProfile::status_callback_t LLWebProfile::mStatusCallback; // static void LLWebProfile::uploadImage(LLPointer<LLImageFormatted> image, const std::string& caption, bool add_location) { - // Get upload configuration data. - std::string config_url(getProfileURL(LLStringUtil::null) + "snapshots/s3_upload_config"); - config_url += "?caption=" + LLURI::escape(caption); - config_url += "&add_loc=" + std::string(add_location ? "1" : "0"); - - LL_DEBUGS("Snapshots") << "Requesting " << config_url << LL_ENDL; - LLSD headers = LLViewerMedia::getHeaders(); - headers[HTTP_OUT_HEADER_COOKIE] = getAuthCookie(); - LLHTTPClient::get(config_url, new LLWebProfileResponders::ConfigResponder(image), headers); + LLCoros::instance().launch("LLWebProfile::uploadImageCoro", + boost::bind(&LLWebProfile::uploadImageCoro, image, caption, add_location)); + } // static @@ -214,74 +78,178 @@ void LLWebProfile::setAuthCookie(const std::string& cookie) sAuthCookie = cookie; } -// static -void LLWebProfile::post(LLPointer<LLImageFormatted> image, const LLSD& config, const std::string& url) + +/*static*/ +LLCore::HttpHeaders::ptr_t LLWebProfile::buildDefaultHeaders() { - if (dynamic_cast<LLImagePNG*>(image.get()) == 0) - { - LL_WARNS() << "Image to upload is not a PNG" << LL_ENDL; - llassert(dynamic_cast<LLImagePNG*>(image.get()) != 0); - return; - } + LLCore::HttpHeaders::ptr_t httpHeaders(new LLCore::HttpHeaders); + LLSD headers = LLViewerMedia::getHeaders(); - const std::string boundary = "----------------------------0123abcdefab"; + for (LLSD::map_iterator it = headers.beginMap(); it != headers.endMap(); ++it) + { + httpHeaders->append((*it).first, (*it).second.asStringRef()); + } - LLSD headers = LLViewerMedia::getHeaders(); - headers[HTTP_OUT_HEADER_COOKIE] = getAuthCookie(); - headers[HTTP_OUT_HEADER_CONTENT_TYPE] = "multipart/form-data; boundary=" + boundary; + return httpHeaders; +} - std::ostringstream body; - // *NOTE: The order seems to matter. - body << "--" << boundary << "\r\n" - << "Content-Disposition: form-data; name=\"key\"\r\n\r\n" - << config["key"].asString() << "\r\n"; +/*static*/ +void LLWebProfile::uploadImageCoro(LLPointer<LLImageFormatted> image, std::string caption, bool addLocation) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("genericPostCoro", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); + LLCore::HttpHeaders::ptr_t httpHeaders; - body << "--" << boundary << "\r\n" - << "Content-Disposition: form-data; name=\"AWSAccessKeyId\"\r\n\r\n" - << config["AWSAccessKeyId"].asString() << "\r\n"; + if (dynamic_cast<LLImagePNG*>(image.get()) == 0) + { + LL_WARNS() << "Image to upload is not a PNG" << LL_ENDL; + llassert(dynamic_cast<LLImagePNG*>(image.get()) != 0); + return; + } - body << "--" << boundary << "\r\n" - << "Content-Disposition: form-data; name=\"acl\"\r\n\r\n" - << config["acl"].asString() << "\r\n"; + httpOpts->setWantHeaders(true); + httpOpts->setFollowRedirects(false); - body << "--" << boundary << "\r\n" - << "Content-Disposition: form-data; name=\"Content-Type\"\r\n\r\n" - << config["Content-Type"].asString() << "\r\n"; + // Get upload configuration data. + std::string configUrl(getProfileURL(std::string()) + "snapshots/s3_upload_config"); + configUrl += "?caption=" + LLURI::escape(caption); + configUrl += "&add_loc=" + std::string(addLocation ? "1" : "0"); - body << "--" << boundary << "\r\n" - << "Content-Disposition: form-data; name=\"policy\"\r\n\r\n" - << config["policy"].asString() << "\r\n"; + LL_DEBUGS("Snapshots") << "Requesting " << configUrl << LL_ENDL; - body << "--" << boundary << "\r\n" - << "Content-Disposition: form-data; name=\"signature\"\r\n\r\n" - << config["signature"].asString() << "\r\n"; + httpHeaders = buildDefaultHeaders(); + httpHeaders->append(HTTP_OUT_HEADER_COOKIE, getAuthCookie()); - body << "--" << boundary << "\r\n" - << "Content-Disposition: form-data; name=\"success_action_redirect\"\r\n\r\n" - << config["success_action_redirect"].asString() << "\r\n"; + LLSD result = httpAdapter->getJsonAndYield(httpRequest, configUrl, httpOpts, httpHeaders); - body << "--" << boundary << "\r\n" - << "Content-Disposition: form-data; name=\"file\"; filename=\"snapshot.png\"\r\n" - << "Content-Type: image/png\r\n\r\n"; + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); - // Insert the image data. - // *FIX: Treating this as a string will probably screw it up ... - U8* image_data = image->getData(); - for (S32 i = 0; i < image->getDataSize(); ++i) - { - body << image_data[i]; - } + if (!status) + { + LL_WARNS("Snapshots") << "Failed to get image upload config" << LL_ENDL; + LLWebProfile::reportImageUploadStatus(false); + return; + } + + // Ready to build our image post body. + + const LLSD &data = result["data"]; + const std::string &uploadUrl = result["url"].asStringRef(); + const std::string boundary = "----------------------------0123abcdefab"; + + // a new set of headers. + httpHeaders = LLWebProfile::buildDefaultHeaders(); + httpHeaders->append(HTTP_OUT_HEADER_COOKIE, getAuthCookie()); + httpHeaders->remove(HTTP_OUT_HEADER_CONTENT_TYPE); + httpHeaders->append(HTTP_OUT_HEADER_CONTENT_TYPE, "multipart/form-data; boundary=" + boundary); + + LLCore::BufferArray::ptr_t body = LLWebProfile::buildPostData(data, image, boundary); + + result = httpAdapter->postAndYield(httpRequest, uploadUrl, body, httpOpts, httpHeaders); + + body.reset(); + httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); - body << "\r\n--" << boundary << "--\r\n"; + if (!status && (status != LLCore::HttpStatus(HTTP_SEE_OTHER))) + { + LL_WARNS("Snapshots") << "Failed to upload image data." << LL_ENDL; + LLWebProfile::reportImageUploadStatus(false); + return; + } - // postRaw() takes ownership of the buffer and releases it later. - size_t size = body.str().size(); - U8 *data = new U8[size]; - memcpy(data, body.str().data(), size); + LLSD resultHeaders = httpResults[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_HEADERS]; - // Send request, successful upload will trigger posting metadata. - LLHTTPClient::postRaw(url, data, size, new LLWebProfileResponders::PostImageResponder(), headers); + httpHeaders = LLWebProfile::buildDefaultHeaders(); + httpHeaders->append(HTTP_OUT_HEADER_COOKIE, getAuthCookie()); + + const std::string& redirUrl = resultHeaders[HTTP_IN_HEADER_LOCATION].asStringRef(); + + if (redirUrl.empty()) + { + LL_WARNS("Snapshots") << "Received empty redirection URL in post image." << LL_ENDL; + LLWebProfile::reportImageUploadStatus(false); + } + + LL_DEBUGS("Snapshots") << "Got redirection URL: " << redirUrl << LL_ENDL; + + result = httpAdapter->getRawAndYield(httpRequest, redirUrl, httpOpts, httpHeaders); + + httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (status != LLCore::HttpStatus(HTTP_OK)) + { + LL_WARNS("Snapshots") << "Failed to upload image." << LL_ENDL; + LLWebProfile::reportImageUploadStatus(false); + return; + } + + //LLSD raw = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_RAW]; + + LL_INFOS("Snapshots") << "Image uploaded." << LL_ENDL; + //LL_DEBUGS("Snapshots") << "Uploading image succeeded. Response: [" << raw.asString() << "]" << LL_ENDL; + LLWebProfile::reportImageUploadStatus(true); + + +} + +/*static*/ +LLCore::BufferArray::ptr_t LLWebProfile::buildPostData(const LLSD &data, LLPointer<LLImageFormatted> &image, const std::string &boundary) +{ + LLCore::BufferArray::ptr_t body(new LLCore::BufferArray); + LLCore::BufferArrayStream bas(body.get()); + + // *NOTE: The order seems to matter. + bas << "--" << boundary << "\r\n" + << "Content-Disposition: form-data; name=\"key\"\r\n\r\n" + << data["key"].asString() << "\r\n"; + + bas << "--" << boundary << "\r\n" + << "Content-Disposition: form-data; name=\"AWSAccessKeyId\"\r\n\r\n" + << data["AWSAccessKeyId"].asString() << "\r\n"; + + bas << "--" << boundary << "\r\n" + << "Content-Disposition: form-data; name=\"acl\"\r\n\r\n" + << data["acl"].asString() << "\r\n"; + + bas << "--" << boundary << "\r\n" + << "Content-Disposition: form-data; name=\"Content-Type\"\r\n\r\n" + << data["Content-Type"].asString() << "\r\n"; + + bas << "--" << boundary << "\r\n" + << "Content-Disposition: form-data; name=\"policy\"\r\n\r\n" + << data["policy"].asString() << "\r\n"; + + bas << "--" << boundary << "\r\n" + << "Content-Disposition: form-data; name=\"signature\"\r\n\r\n" + << data["signature"].asString() << "\r\n"; + + bas << "--" << boundary << "\r\n" + << "Content-Disposition: form-data; name=\"success_action_redirect\"\r\n\r\n" + << data["success_action_redirect"].asString() << "\r\n"; + + bas << "--" << boundary << "\r\n" + << "Content-Disposition: form-data; name=\"file\"; filename=\"snapshot.png\"\r\n" + << "Content-Type: image/png\r\n\r\n"; + + // Insert the image data. + //char *datap = (char *)(image->getData()); + //bas.write(datap, image->getDataSize()); + U8* image_data = image->getData(); + for (S32 i = 0; i < image->getDataSize(); ++i) + { + bas << image_data[i]; + } + + bas << "\r\n--" << boundary << "--\r\n"; + + return body; } // static diff --git a/indra/newview/llwebprofile.h b/indra/newview/llwebprofile.h index 10279bffac..6227e00afe 100755 --- a/indra/newview/llwebprofile.h +++ b/indra/newview/llwebprofile.h @@ -28,6 +28,10 @@ #define LL_LLWEBPROFILE_H #include "llimage.h" +#include "lleventcoro.h" +#include "llcoros.h" +#include "httpheaders.h" +#include "bufferarray.h" namespace LLWebProfileResponders { @@ -54,11 +58,11 @@ public: static void setImageUploadResultCallback(status_callback_t cb) { mStatusCallback = cb; } private: - friend class LLWebProfileResponders::ConfigResponder; - friend class LLWebProfileResponders::PostImageResponder; - friend class LLWebProfileResponders::PostImageRedirectResponder; + static LLCore::HttpHeaders::ptr_t buildDefaultHeaders(); + + static void uploadImageCoro(LLPointer<LLImageFormatted> image, std::string caption, bool add_location); + static LLCore::BufferArray::ptr_t buildPostData(const LLSD &data, LLPointer<LLImageFormatted> &image, const std::string &boundary); - static void post(LLPointer<LLImageFormatted> image, const LLSD& config, const std::string& url); static void reportImageUploadStatus(bool ok); static std::string getAuthCookie(); diff --git a/indra/newview/llwlhandlers.cpp b/indra/newview/llwlhandlers.cpp index 3bedfbe502..ff15afa598 100755 --- a/indra/newview/llwlhandlers.cpp +++ b/indra/newview/llwlhandlers.cpp @@ -32,6 +32,7 @@ #include "llviewerregion.h" #include "llenvmanager.h" #include "llnotificationsutil.h" +#include "llcorehttputil.h" /**** * LLEnvironmentRequest @@ -81,55 +82,62 @@ bool LLEnvironmentRequest::doRequest() return false; } - LL_INFOS("WindlightCaps") << "Requesting region windlight settings via " << url << LL_ENDL; - LLHTTPClient::get(url, new LLEnvironmentRequestResponder()); - return true; -} - -/**** - * LLEnvironmentRequestResponder - ****/ -int LLEnvironmentRequestResponder::sCount = 0; // init to 0 + std::string coroname = + LLCoros::instance().launch("LLEnvironmentRequest::environmentRequestCoro", + boost::bind(&LLEnvironmentRequest::environmentRequestCoro, url)); -LLEnvironmentRequestResponder::LLEnvironmentRequestResponder() -{ - mID = ++sCount; + LL_INFOS("WindlightCaps") << "Requesting region windlight settings via " << url << LL_ENDL; + return true; } -/*virtual*/ void LLEnvironmentRequestResponder::httpSuccess() -{ - const LLSD& unvalidated_content = getContent(); - LL_INFOS("WindlightCaps") << "Received region windlight settings" << LL_ENDL; - if (mID != sCount) - { - LL_INFOS("WindlightCaps") << "Got superseded by another responder; ignoring..." << LL_ENDL; - return; - } - - LLUUID regionId; - if( gAgent.getRegion() ) - { - regionId = gAgent.getRegion()->getRegionID(); - } - - if (unvalidated_content[0]["regionID"].asUUID() != regionId ) - { - LL_WARNS("WindlightCaps") << "Not in the region from where this data was received (wanting " - << regionId << " but got " << unvalidated_content[0]["regionID"].asUUID() - << ") - ignoring..." << LL_ENDL; - return; - } +S32 LLEnvironmentRequest::sLastRequest = 0; - LLEnvManagerNew::getInstance()->onRegionSettingsResponse(unvalidated_content); -} -/*virtual*/ -void LLEnvironmentRequestResponder::httpFailure() +//static +void LLEnvironmentRequest::environmentRequestCoro(std::string url) { - LL_WARNS("WindlightCaps") << "Got an error, not using region windlight... " - << dumpResponse() << LL_ENDL; - LLEnvManagerNew::getInstance()->onRegionSettingsResponse(LLSD()); + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + S32 requestId = ++LLEnvironmentRequest::sLastRequest; + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("EnvironmentRequest", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + + LLSD result = httpAdapter->getAndYield(httpRequest, url); + + if (requestId != LLEnvironmentRequest::sLastRequest) + { + LL_INFOS("WindlightCaps") << "Got superseded by another responder; ignoring..." << LL_ENDL; + return; + } + + LLSD httpResults = result["http_result"]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + if (!status) + { + LL_WARNS("WindlightCaps") << "Got an error, not using region windlight... " << LL_ENDL; + LLEnvManagerNew::getInstance()->onRegionSettingsResponse(LLSD()); + return; + } + result = result["content"]; + LL_INFOS("WindlightCaps") << "Received region windlight settings" << LL_ENDL; + + LLUUID regionId; + if (gAgent.getRegion()) + { + regionId = gAgent.getRegion()->getRegionID(); + } + + if (result[0]["regionID"].asUUID() != regionId) + { + LL_WARNS("WindlightCaps") << "Not in the region from where this data was received (wanting " + << regionId << " but got " << result[0]["regionID"].asUUID() + << ") - ignoring..." << LL_ENDL; + return; + } + + LLEnvManagerNew::getInstance()->onRegionSettingsResponse(result); } + /**** * LLEnvironmentApply ****/ @@ -161,53 +169,86 @@ bool LLEnvironmentApply::initiateRequest(const LLSD& content) return false; } - LL_INFOS("WindlightCaps") << "Sending windlight settings to " << url << LL_ENDL; - LL_DEBUGS("WindlightCaps") << "content: " << content << LL_ENDL; - LLHTTPClient::post(url, content, new LLEnvironmentApplyResponder()); + LL_INFOS("WindlightCaps") << "Sending windlight settings to " << url << LL_ENDL; + LL_DEBUGS("WindlightCaps") << "content: " << content << LL_ENDL; + + std::string coroname = + LLCoros::instance().launch("LLEnvironmentApply::environmentApplyCoro", + boost::bind(&LLEnvironmentApply::environmentApplyCoro, url, content)); return true; } -/**** - * LLEnvironmentApplyResponder - ****/ -/*virtual*/ void LLEnvironmentApplyResponder::httpSuccess() -{ - const LLSD& content = getContent(); - if (!content.isMap() || !content.has("regionID")) - { - failureResult(HTTP_INTERNAL_ERROR, "Malformed response contents", content); - return; - } - if (content["regionID"].asUUID() != gAgent.getRegion()->getRegionID()) - { - LL_WARNS("WindlightCaps") << "No longer in the region where data was sent (currently " - << gAgent.getRegion()->getRegionID() << ", reply is from " << content["regionID"].asUUID() - << "); ignoring..." << LL_ENDL; - return; - } - else if (content["success"].asBoolean()) - { - LL_DEBUGS("WindlightCaps") << "Success in applying windlight settings to region " << content["regionID"].asUUID() << LL_ENDL; - LLEnvManagerNew::instance().onRegionSettingsApplyResponse(true); - } - else - { - LL_WARNS("WindlightCaps") << "Region couldn't apply windlight settings! " << dumpResponse() << LL_ENDL; - LLSD args(LLSD::emptyMap()); - args["FAIL_REASON"] = content["fail_reason"].asString(); - LLNotificationsUtil::add("WLRegionApplyFail", args); - LLEnvManagerNew::instance().onRegionSettingsApplyResponse(false); - } -} -/*virtual*/ -void LLEnvironmentApplyResponder::httpFailure() +void LLEnvironmentApply::environmentApplyCoro(std::string url, LLSD content) { - LL_WARNS("WindlightCaps") << "Couldn't apply windlight settings to region! " - << dumpResponse() << LL_ENDL; - - LLSD args(LLSD::emptyMap()); - std::stringstream msg; - msg << getReason() << " (Code " << getStatus() << ")"; - args["FAIL_REASON"] = msg.str(); - LLNotificationsUtil::add("WLRegionApplyFail", args); + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("EnvironmentApply", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + + LLSD result = httpAdapter->postAndYield(httpRequest, url, content); + + LLSD notify; // for error reporting. If there is something to report to user this will be defined. + /* + * Expecting reply from sim in form of: + * { + * regionID : uuid, + * messageID: uuid, + * success : true + * } + * or + * { + * regionID : uuid, + * success : false, + * fail_reason : string + * } + */ + + do // while false. + { // Breaks from loop in the case of an error. + + LLSD httpResults = result["http_result"]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + if (!status) + { + LL_WARNS("WindlightCaps") << "Couldn't apply windlight settings to region! " << LL_ENDL; + + std::stringstream msg; + msg << status.toString() << " (Code " << status.toTerseString() << ")"; + notify = LLSD::emptyMap(); + notify["FAIL_REASON"] = msg.str(); + break; + } + + if (!result.has("regionID")) + { + notify = LLSD::emptyMap(); + notify["FAIL_REASON"] = "Missing regionID, malformed response"; + break; + } + else if (result["regionID"].asUUID() != gAgent.getRegion()->getRegionID()) + { + // note that there is no report to the user in this failure case. + LL_WARNS("WindlightCaps") << "No longer in the region where data was sent (currently " + << gAgent.getRegion()->getRegionID() << ", reply is from " << result["regionID"].asUUID() + << "); ignoring..." << LL_ENDL; + break; + } + else if (!result["success"].asBoolean()) + { + LL_WARNS("WindlightCaps") << "Region couldn't apply windlight settings! " << LL_ENDL; + notify = LLSD::emptyMap(); + notify["FAIL_REASON"] = result["fail_reason"].asString(); + break; + } + + LL_DEBUGS("WindlightCaps") << "Success in applying windlight settings to region " << result["regionID"].asUUID() << LL_ENDL; + LLEnvManagerNew::instance().onRegionSettingsApplyResponse(true); + + } while (false); + + if (!notify.isUndefined()) + { + LLNotificationsUtil::add("WLRegionApplyFail", notify); + LLEnvManagerNew::instance().onRegionSettingsApplyResponse(false); + } } diff --git a/indra/newview/llwlhandlers.h b/indra/newview/llwlhandlers.h index 089c799da7..eb2bbf9553 100755 --- a/indra/newview/llwlhandlers.h +++ b/indra/newview/llwlhandlers.h @@ -28,7 +28,7 @@ #define LL_LLWLHANDLERS_H #include "llviewerprecompiledheaders.h" -#include "llhttpclient.h" +#include "llcoros.h" class LLEnvironmentRequest { @@ -40,21 +40,10 @@ public: private: static void onRegionCapsReceived(const LLUUID& region_id); static bool doRequest(); -}; - -class LLEnvironmentRequestResponder: public LLHTTPClient::Responder -{ - LOG_CLASS(LLEnvironmentRequestResponder); -private: - /* virtual */ void httpSuccess(); - /* virtual */ void httpFailure(); -private: - friend class LLEnvironmentRequest; + static void environmentRequestCoro(std::string url); - LLEnvironmentRequestResponder(); - static int sCount; - int mID; + static S32 sLastRequest; }; class LLEnvironmentApply @@ -67,35 +56,8 @@ public: private: static clock_t sLastUpdate; static clock_t UPDATE_WAIT_SECONDS; -}; -class LLEnvironmentApplyResponder: public LLHTTPClient::Responder -{ - LOG_CLASS(LLEnvironmentApplyResponder); -private: - /* - * Expecting reply from sim in form of: - * { - * regionID : uuid, - * messageID: uuid, - * success : true - * } - * or - * { - * regionID : uuid, - * success : false, - * fail_reason : string - * } - */ - /* virtual */ void httpSuccess(); - - // non-2xx errors only - /* virtual */ void httpFailure(); - -private: - friend class LLEnvironmentApply; - - LLEnvironmentApplyResponder() {} + static void environmentApplyCoro(std::string url, LLSD content); }; #endif // LL_LLWLHANDLERS_H diff --git a/indra/newview/llxmlrpctransaction.cpp b/indra/newview/llxmlrpctransaction.cpp index c12c2cc24c..5828aee7fc 100755 --- a/indra/newview/llxmlrpctransaction.cpp +++ b/indra/newview/llxmlrpctransaction.cpp @@ -35,6 +35,11 @@ #include "llxmlrpclistener.h" #include "llcurl.h" +#include "httpcommon.h" +#include "httprequest.h" +#include "httpoptions.h" +#include "httpheaders.h" +#include "bufferarray.h" #include "llviewercontrol.h" // Have to include these last to avoid queue redefinition! @@ -43,6 +48,13 @@ #include "llappviewer.h" #include "lltrans.h" +#include "boost/move/unique_ptr.hpp" + +namespace boost +{ + using ::boost::movelib::unique_ptr; // move unique_ptr into the boost namespace. +} + // Static instance of LLXMLRPCListener declared here so that every time we // bring in this code, we instantiate a listener. If we put the static // instance of LLXMLRPCListener into llxmlrpclistener.cpp, the linker would @@ -155,55 +167,158 @@ XMLRPC_VALUE LLXMLRPCValue::getValue() const } +class LLXMLRPCTransaction::Handler : public LLCore::HttpHandler +{ +public: + Handler(LLCore::HttpRequest::ptr_t &request, LLXMLRPCTransaction::Impl *impl); + virtual ~Handler(); + + virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response); + + typedef boost::unique_ptr<LLXMLRPCTransaction::Handler> ptr_t; + +private: + + LLXMLRPCTransaction::Impl *mImpl; + LLCore::HttpRequest::ptr_t mRequest; +}; + class LLXMLRPCTransaction::Impl { public: typedef LLXMLRPCTransaction::EStatus EStatus; - LLCurlEasyRequest* mCurlRequest; + LLCore::HttpRequest::ptr_t mHttpRequest; + + + EStatus mStatus; + CURLcode mCurlCode; + std::string mStatusMessage; + std::string mStatusURI; + LLCore::HttpResponse::TransferStats::ptr_t mTransferStats; + Handler::ptr_t mHandler; + LLCore::HttpHandle mPostH; - EStatus mStatus; - CURLcode mCurlCode; - std::string mStatusMessage; - std::string mStatusURI; - LLCurl::TransferInfo mTransferInfo; - std::string mURI; - char* mRequestText; - int mRequestTextSize; - + std::string mProxyAddress; std::string mResponseText; XMLRPC_REQUEST mResponse; std::string mCertStore; LLPointer<LLCertificate> mErrorCert; - + Impl(const std::string& uri, XMLRPC_REQUEST request, bool useGzip); Impl(const std::string& uri, - const std::string& method, LLXMLRPCValue params, bool useGzip); + const std::string& method, LLXMLRPCValue params, bool useGzip); ~Impl(); - + bool process(); - - void setStatus(EStatus code, - const std::string& message = "", const std::string& uri = ""); - void setCurlStatus(CURLcode); + + void setStatus(EStatus code, const std::string& message = "", const std::string& uri = ""); + void setHttpStatus(const LLCore::HttpStatus &status); private: void init(XMLRPC_REQUEST request, bool useGzip); - static int _sslCertVerifyCallback(X509_STORE_CTX *ctx, void *param); - static CURLcode _sslCtxFunction(CURL * curl, void *sslctx, void *param); - static size_t curlDownloadCallback( - char* data, size_t size, size_t nmemb, void* user_data); }; +LLXMLRPCTransaction::Handler::Handler(LLCore::HttpRequest::ptr_t &request, + LLXMLRPCTransaction::Impl *impl) : + mImpl(impl), + mRequest(request) +{ +} + +LLXMLRPCTransaction::Handler::~Handler() +{ +} + +void LLXMLRPCTransaction::Handler::onCompleted(LLCore::HttpHandle handle, + LLCore::HttpResponse * response) +{ + LLCore::HttpStatus status = response->getStatus(); + + if (!status) + { + if ((status.toULong() != CURLE_SSL_PEER_CERTIFICATE) && + (status.toULong() != CURLE_SSL_CACERT)) + { + // if we have a curl error that's not already been handled + // (a non cert error), then generate the error message as + // appropriate + mImpl->setHttpStatus(status); + LLCertificate *errordata = static_cast<LLCertificate *>(status.getErrorData()); + + if (errordata) + { + mImpl->mErrorCert = LLPointer<LLCertificate>(errordata); + status.setErrorData(NULL); + errordata->unref(); + } + + LL_WARNS() << "LLXMLRPCTransaction error " + << status.toHex() << ": " << status.toString() << LL_ENDL; + LL_WARNS() << "LLXMLRPCTransaction request URI: " + << mImpl->mURI << LL_ENDL; + } + + return; + } + + mImpl->setStatus(LLXMLRPCTransaction::StatusComplete); + mImpl->mTransferStats = response->getTransferStats(); + + // the contents of a buffer array are potentially noncontiguous, so we + // will need to copy them into an contiguous block of memory for XMLRPC. + LLCore::BufferArray *body = response->getBody(); + char * bodydata = new char[body->size()]; + + body->read(0, bodydata, body->size()); + + mImpl->mResponse = XMLRPC_REQUEST_FromXML(bodydata, body->size(), 0); + + delete[] bodydata; + + bool hasError = false; + bool hasFault = false; + int faultCode = 0; + std::string faultString; + + LLXMLRPCValue error(XMLRPC_RequestGetError(mImpl->mResponse)); + if (error.isValid()) + { + hasError = true; + faultCode = error["faultCode"].asInt(); + faultString = error["faultString"].asString(); + } + else if (XMLRPC_ResponseIsFault(mImpl->mResponse)) + { + hasFault = true; + faultCode = XMLRPC_GetResponseFaultCode(mImpl->mResponse); + faultString = XMLRPC_GetResponseFaultString(mImpl->mResponse); + } + + if (hasError || hasFault) + { + mImpl->setStatus(LLXMLRPCTransaction::StatusXMLRPCError); + + LL_WARNS() << "LLXMLRPCTransaction XMLRPC " + << (hasError ? "error " : "fault ") + << faultCode << ": " + << faultString << LL_ENDL; + LL_WARNS() << "LLXMLRPCTransaction request URI: " + << mImpl->mURI << LL_ENDL; + } + +} + +//========================================================================= + LLXMLRPCTransaction::Impl::Impl(const std::string& uri, XMLRPC_REQUEST request, bool useGzip) - : mCurlRequest(0), + : mHttpRequest(), mStatus(LLXMLRPCTransaction::StatusNotStarted), mURI(uri), - mRequestText(0), mResponse(0) { init(request, useGzip); @@ -212,10 +327,9 @@ LLXMLRPCTransaction::Impl::Impl(const std::string& uri, LLXMLRPCTransaction::Impl::Impl(const std::string& uri, const std::string& method, LLXMLRPCValue params, bool useGzip) - : mCurlRequest(0), + : mHttpRequest(), mStatus(LLXMLRPCTransaction::StatusNotStarted), mURI(uri), - mRequestText(0), mResponse(0) { XMLRPC_REQUEST request = XMLRPC_RequestNew(); @@ -231,127 +345,53 @@ LLXMLRPCTransaction::Impl::Impl(const std::string& uri, XMLRPC_RequestFree(request, 1); } -// _sslCertVerifyCallback -// callback called when a cert verification is requested. -// calls SECAPI to validate the context -int LLXMLRPCTransaction::Impl::_sslCertVerifyCallback(X509_STORE_CTX *ctx, void *param) +void LLXMLRPCTransaction::Impl::init(XMLRPC_REQUEST request, bool useGzip) { - LLXMLRPCTransaction::Impl *transaction = (LLXMLRPCTransaction::Impl *)param; - LLPointer<LLCertificateStore> store = gSecAPIHandler->getCertificateStore(transaction->mCertStore); - LLPointer<LLCertificateChain> chain = gSecAPIHandler->getCertificateChain(ctx); - LLSD validation_params = LLSD::emptyMap(); - LLURI uri(transaction->mURI); - validation_params[CERT_HOSTNAME] = uri.hostName(); - try - { - // don't validate hostname. Let libcurl do it instead. That way, it'll handle redirects - store->validate(VALIDATION_POLICY_SSL & (~VALIDATION_POLICY_HOSTNAME), chain, validation_params); - } - catch (LLCertValidationTrustException& cert_exception) - { - // this exception is is handled differently than the general cert - // exceptions, as we allow the user to actually add the certificate - // for trust. - // therefore we pass back a different error code - // NOTE: We're currently 'wired' to pass around CURL error codes. This is - // somewhat clumsy, as we may run into errors that do not map directly to curl - // error codes. Should be refactored with login refactoring, perhaps. - transaction->mCurlCode = CURLE_SSL_CACERT; - // set the status directly. set curl status generates error messages and we want - // to use the fixed ones from the exceptions - transaction->setStatus(StatusCURLError, cert_exception.getMessage(), std::string()); - // We should probably have a more generic way of passing information - // back to the error handlers. - transaction->mErrorCert = cert_exception.getCert(); - return 0; - } - catch (LLCertException& cert_exception) - { - transaction->mCurlCode = CURLE_SSL_PEER_CERTIFICATE; - // set the status directly. set curl status generates error messages and we want - // to use the fixed ones from the exceptions - transaction->setStatus(StatusCURLError, cert_exception.getMessage(), std::string()); - transaction->mErrorCert = cert_exception.getCert(); - return 0; - } - catch (...) - { - // any other odd error, we just handle as a connect error. - transaction->mCurlCode = CURLE_SSL_CONNECT_ERROR; - transaction->setCurlStatus(CURLE_SSL_CONNECT_ERROR); - return 0; - } - return 1; -} + LLCore::HttpOptions::ptr_t httpOpts; + LLCore::HttpHeaders::ptr_t httpHeaders; -// _sslCtxFunction -// Callback function called when an SSL Context is created via CURL -// used to configure the context for custom cert validate(<, <#const & xs#>, <#T * #>, <#long #>)tion -// based on SECAPI - -CURLcode LLXMLRPCTransaction::Impl::_sslCtxFunction(CURL * curl, void *sslctx, void *param) -{ - SSL_CTX * ctx = (SSL_CTX *) sslctx; - // disable any default verification for server certs - SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL); - // set the verification callback. - SSL_CTX_set_cert_verify_callback(ctx, _sslCertVerifyCallback, param); - // the calls are void - return CURLE_OK; - -} -void LLXMLRPCTransaction::Impl::init(XMLRPC_REQUEST request, bool useGzip) -{ - if (!mCurlRequest) + if (!mHttpRequest) { - mCurlRequest = new LLCurlEasyRequest(); + mHttpRequest = LLCore::HttpRequest::ptr_t(new LLCore::HttpRequest); } - if(!mCurlRequest->isValid()) - { - LL_WARNS() << "mCurlRequest is invalid." << LL_ENDL ; - delete mCurlRequest ; - mCurlRequest = NULL ; - return ; - } + // LLRefCounted starts with a 1 ref, so don't add a ref in the smart pointer + httpOpts = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()); - mErrorCert = NULL; + httpOpts->setTimeout(40L); -// mCurlRequest->setopt(CURLOPT_VERBOSE, 1); // useful for debugging - mCurlRequest->setopt(CURLOPT_NOSIGNAL, 1); - mCurlRequest->setWriteCallback(&curlDownloadCallback, (void*)this); - BOOL vefifySSLCert = !gSavedSettings.getBOOL("NoVerifySSLCert"); + bool vefifySSLCert = !gSavedSettings.getBOOL("NoVerifySSLCert"); mCertStore = gSavedSettings.getString("CertStore"); - mCurlRequest->setopt(CURLOPT_SSL_VERIFYPEER, vefifySSLCert); - mCurlRequest->setopt(CURLOPT_SSL_VERIFYHOST, vefifySSLCert ? 2 : 0); - // Be a little impatient about establishing connections. - mCurlRequest->setopt(CURLOPT_CONNECTTIMEOUT, 40L); - mCurlRequest->setSSLCtxCallback(_sslCtxFunction, (void *)this); - /* Setting the DNS cache timeout to -1 disables it completely. - This might help with bug #503 */ - mCurlRequest->setopt(CURLOPT_DNS_CACHE_TIMEOUT, -1); + httpOpts->setSSLVerifyPeer( vefifySSLCert ); + httpOpts->setSSLVerifyHost( vefifySSLCert ? 2 : 0); - mCurlRequest->slist_append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_TEXT_XML); + // LLRefCounted starts with a 1 ref, so don't add a ref in the smart pointer + httpHeaders = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders()); - if (useGzip) - { - mCurlRequest->setoptString(CURLOPT_ENCODING, ""); - } + httpHeaders->append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_TEXT_XML); + + ///* Setting the DNS cache timeout to -1 disables it completely. + //This might help with bug #503 */ + //httpOpts->setDNSCacheTimeout(-1); + + LLCore::BufferArray::ptr_t body = LLCore::BufferArray::ptr_t(new LLCore::BufferArray()); + + // TODO: See if there is a way to serialize to a preallocated buffer I'm + // not fond of the copy here. + int requestSize(0); + char * requestText = XMLRPC_REQUEST_ToXML(request, &requestSize); + + body->append(requestText, requestSize); - mRequestText = XMLRPC_REQUEST_ToXML(request, &mRequestTextSize); - if (mRequestText) - { - mCurlRequest->setoptString(CURLOPT_POSTFIELDS, mRequestText); - mCurlRequest->setopt(CURLOPT_POSTFIELDSIZE, mRequestTextSize); - } - else - { - setStatus(StatusOtherError); - } + XMLRPC_Free(requestText); + + mHandler = LLXMLRPCTransaction::Handler::ptr_t(new Handler( mHttpRequest, this )); + + mPostH = mHttpRequest->requestPost(LLCore::HttpRequest::DEFAULT_POLICY_ID, 0, + mURI, body.get(), httpOpts, httpHeaders, mHandler.get()); - mCurlRequest->sendRequest(mURI); } @@ -361,28 +401,17 @@ LLXMLRPCTransaction::Impl::~Impl() { XMLRPC_RequestFree(mResponse, 1); } - - if (mRequestText) - { - XMLRPC_Free(mRequestText); - } - - delete mCurlRequest; - mCurlRequest = NULL ; } bool LLXMLRPCTransaction::Impl::process() { - if(!mCurlRequest || !mCurlRequest->isValid()) + if (!mPostH || !mHttpRequest) { - LL_WARNS() << "transaction failed." << LL_ENDL ; - - delete mCurlRequest ; - mCurlRequest = NULL ; - return true ; //failed, quit. + LL_WARNS() << "transaction failed." << LL_ENDL; + return true; //failed, quit. } - switch(mStatus) + switch (mStatus) { case LLXMLRPCTransaction::StatusComplete: case LLXMLRPCTransaction::StatusCURLError: @@ -391,93 +420,25 @@ bool LLXMLRPCTransaction::Impl::process() { return true; } - + case LLXMLRPCTransaction::StatusNotStarted: { setStatus(LLXMLRPCTransaction::StatusStarted); break; } - + default: - { - // continue onward - } - } - - if(!mCurlRequest->wait()) - { - return false ; + break; } - while(1) - { - CURLcode result; - bool newmsg = mCurlRequest->getResult(&result, &mTransferInfo); - if (newmsg) - { - if (result != CURLE_OK) - { - if ((result != CURLE_SSL_PEER_CERTIFICATE) && - (result != CURLE_SSL_CACERT)) - { - // if we have a curl error that's not already been handled - // (a non cert error), then generate the error message as - // appropriate - setCurlStatus(result); - - LL_WARNS() << "LLXMLRPCTransaction CURL error " - << mCurlCode << ": " << mCurlRequest->getErrorString() << LL_ENDL; - LL_WARNS() << "LLXMLRPCTransaction request URI: " - << mURI << LL_ENDL; - } - - return true; - } - - setStatus(LLXMLRPCTransaction::StatusComplete); + LLCore::HttpStatus status = mHttpRequest->update(0); - mResponse = XMLRPC_REQUEST_FromXML( - mResponseText.data(), mResponseText.size(), NULL); - - bool hasError = false; - bool hasFault = false; - int faultCode = 0; - std::string faultString; - - LLXMLRPCValue error(XMLRPC_RequestGetError(mResponse)); - if (error.isValid()) - { - hasError = true; - faultCode = error["faultCode"].asInt(); - faultString = error["faultString"].asString(); - } - else if (XMLRPC_ResponseIsFault(mResponse)) - { - hasFault = true; - faultCode = XMLRPC_GetResponseFaultCode(mResponse); - faultString = XMLRPC_GetResponseFaultString(mResponse); - } - - if (hasError || hasFault) - { - setStatus(LLXMLRPCTransaction::StatusXMLRPCError); - - LL_WARNS() << "LLXMLRPCTransaction XMLRPC " - << (hasError ? "error " : "fault ") - << faultCode << ": " - << faultString << LL_ENDL; - LL_WARNS() << "LLXMLRPCTransaction request URI: " - << mURI << LL_ENDL; - } - - return true; - } - else - { - break; // done - } + status = mHttpRequest->getStatus(); + if (!status) + { + return false; } - + return false; } @@ -516,64 +477,51 @@ void LLXMLRPCTransaction::Impl::setStatus(EStatus status, } } -void LLXMLRPCTransaction::Impl::setCurlStatus(CURLcode code) +void LLXMLRPCTransaction::Impl::setHttpStatus(const LLCore::HttpStatus &status) { + CURLcode code = static_cast<CURLcode>(status.toULong()); std::string message; std::string uri = "http://secondlife.com/community/support.php"; - + LLURI failuri(mURI); + + switch (code) { - case CURLE_COULDNT_RESOLVE_HOST: - message = - "DNS could not resolve the host name.\n" - "Please verify that you can connect to the www.secondlife.com\n" - "web site. If you can, but continue to receive this error,\n" - "please go to the support section and report this problem."; - break; - - case CURLE_SSL_PEER_CERTIFICATE: - message = - "The login server couldn't verify itself via SSL.\n" - "If you continue to receive this error, please go\n" - "to the Support section of the SecondLife.com web site\n" - "and report the problem."; - break; - - case CURLE_SSL_CACERT: - case CURLE_SSL_CONNECT_ERROR: - message = - "Often this means that your computer\'s clock is set incorrectly.\n" - "Please go to Control Panels and make sure the time and date\n" - "are set correctly.\n" - "Also check that your network and firewall are set up correctly.\n" - "If you continue to receive this error, please go\n" - "to the Support section of the SecondLife.com web site\n" - "and report the problem."; - break; - - default: - break; + case CURLE_COULDNT_RESOLVE_HOST: + message = + std::string("DNS could not resolve the host name(") + failuri.hostName() + ").\n" + "Please verify that you can connect to the www.secondlife.com\n" + "web site. If you can, but continue to receive this error,\n" + "please go to the support section and report this problem."; + break; + + case CURLE_SSL_PEER_CERTIFICATE: + message = + "The login server couldn't verify itself via SSL.\n" + "If you continue to receive this error, please go\n" + "to the Support section of the SecondLife.com web site\n" + "and report the problem."; + break; + + case CURLE_SSL_CACERT: + case CURLE_SSL_CONNECT_ERROR: + message = + "Often this means that your computer\'s clock is set incorrectly.\n" + "Please go to Control Panels and make sure the time and date\n" + "are set correctly.\n" + "Also check that your network and firewall are set up correctly.\n" + "If you continue to receive this error, please go\n" + "to the Support section of the SecondLife.com web site\n" + "and report the problem."; + break; + + default: + break; } - + mCurlCode = code; setStatus(StatusCURLError, message, uri); -} - -size_t LLXMLRPCTransaction::Impl::curlDownloadCallback( - char* data, size_t size, size_t nmemb, void* user_data) -{ - Impl& impl(*(Impl*)user_data); - - size_t n = size * nmemb; - impl.mResponseText.append(data, n); - - if (impl.mStatus == LLXMLRPCTransaction::StatusStarted) - { - impl.setStatus(LLXMLRPCTransaction::StatusDownloading); - } - - return n; } @@ -645,11 +593,11 @@ F64 LLXMLRPCTransaction::transferRate() return 0.0L; } - double rate_bits_per_sec = impl.mTransferInfo.mSpeedDownload * 8.0; + double rate_bits_per_sec = impl.mTransferStats->mSpeedDownload * 8.0; LL_INFOS("AppInit") << "Buffer size: " << impl.mResponseText.size() << " B" << LL_ENDL; - LL_DEBUGS("AppInit") << "Transfer size: " << impl.mTransferInfo.mSizeDownload << " B" << LL_ENDL; - LL_DEBUGS("AppInit") << "Transfer time: " << impl.mTransferInfo.mTotalTime << " s" << LL_ENDL; + LL_DEBUGS("AppInit") << "Transfer size: " << impl.mTransferStats->mSizeDownload << " B" << LL_ENDL; + LL_DEBUGS("AppInit") << "Transfer time: " << impl.mTransferStats->mTotalTime << " s" << LL_ENDL; LL_INFOS("AppInit") << "Transfer rate: " << rate_bits_per_sec / 1000.0 << " Kb/s" << LL_ENDL; return rate_bits_per_sec; diff --git a/indra/newview/llxmlrpctransaction.h b/indra/newview/llxmlrpctransaction.h index f2589c7f41..3a1c9c82b7 100755 --- a/indra/newview/llxmlrpctransaction.h +++ b/indra/newview/llxmlrpctransaction.h @@ -81,7 +81,7 @@ private: class LLXMLRPCTransaction - // an asynchronous request and respones via XML-RPC + // an asynchronous request and responses via XML-RPC { public: LLXMLRPCTransaction(const std::string& uri, @@ -127,7 +127,9 @@ public: // only valid if StsatusComplete, otherwise 0.0 private: + class Handler; class Impl; + Impl& impl; }; diff --git a/indra/newview/tests/llcapabilitylistener_test.cpp b/indra/newview/tests/llcapabilitylistener_test.cpp deleted file mode 100755 index bde991a01e..0000000000 --- a/indra/newview/tests/llcapabilitylistener_test.cpp +++ /dev/null @@ -1,271 +0,0 @@ -/** - * @file llcapabilitylistener_test.cpp - * @author Nat Goodspeed - * @date 2008-12-31 - * @brief Test for llcapabilitylistener.cpp. - * - * $LicenseInfo:firstyear=2008&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -// Precompiled header -#include "../llviewerprecompiledheaders.h" -// Own header -#include "../llcapabilitylistener.h" -// STL headers -#include <stdexcept> -#include <map> -#include <vector> -// std headers -// external library headers -#include "boost/bind.hpp" -// other Linden headers -#include "../test/lltut.h" -#include "../llcapabilityprovider.h" -#include "lluuid.h" -#include "tests/networkio.h" -#include "tests/commtest.h" -#include "tests/wrapllerrs.h" -#include "message.h" -#include "stringize.h" - -#if defined(LL_WINDOWS) -#pragma warning(disable: 4355) // using 'this' in base-class ctor initializer expr -#endif - -/***************************************************************************** -* TestCapabilityProvider -*****************************************************************************/ -struct TestCapabilityProvider: public LLCapabilityProvider -{ - TestCapabilityProvider(const LLHost& host): - mHost(host) - {} - - std::string getCapability(const std::string& cap) const - { - CapMap::const_iterator found = mCaps.find(cap); - if (found != mCaps.end()) - return found->second; - // normal LLViewerRegion lookup failure mode - return ""; - } - void setCapability(const std::string& cap, const std::string& url) - { - mCaps[cap] = url; - } - const LLHost& getHost() const { return mHost; } - std::string getDescription() const { return "TestCapabilityProvider"; } - - LLHost mHost; - typedef std::map<std::string, std::string> CapMap; - CapMap mCaps; -}; - -/***************************************************************************** -* Dummy LLMessageSystem methods -*****************************************************************************/ -/*==========================================================================*| -// This doesn't work because we're already linking in llmessage.a, and we get -// duplicate-symbol errors from the linker. Perhaps if I wanted to go through -// the exercise of providing dummy versions of every single symbol defined in -// message.o -- maybe some day. -typedef std::vector< std::pair<std::string, std::string> > StringPairVector; -StringPairVector call_history; - -S32 LLMessageSystem::sendReliable(const LLHost& host) -{ - call_history.push_back(StringPairVector::value_type("sendReliable", stringize(host))); - return 0; -} -|*==========================================================================*/ - -/***************************************************************************** -* TUT -*****************************************************************************/ -namespace tut -{ - struct llcapears_data: public commtest_data - { - TestCapabilityProvider provider; - LLCapabilityListener regionListener; - LLEventPump& regionPump; - - llcapears_data(): - provider(host), - regionListener("testCapabilityListener", NULL, provider, LLUUID(), LLUUID()), - regionPump(regionListener.getCapAPI()) - { - LLCurl::initClass(); - provider.setCapability("good", server + "capability-test"); - provider.setCapability("fail", server + "fail"); - } - }; - typedef test_group<llcapears_data> llcapears_group; - typedef llcapears_group::object llcapears_object; - llcapears_group llsdmgr("llcapabilitylistener"); - - template<> template<> - void llcapears_object::test<1>() - { - LLSD request, body; - body["data"] = "yes"; - request["payload"] = body; - request["reply"] = replyPump.getName(); - request["error"] = errorPump.getName(); - std::string threw; - try - { - WrapLLErrs capture; - regionPump.post(request); - } - catch (const WrapLLErrs::FatalException& e) - { - threw = e.what(); - } - ensure_contains("missing capability name", threw, "without 'message' key"); - } - - template<> template<> - void llcapears_object::test<2>() - { - LLSD request, body; - body["data"] = "yes"; - request["message"] = "good"; - request["payload"] = body; - request["reply"] = replyPump.getName(); - request["error"] = errorPump.getName(); - regionPump.post(request); - ensure("got response", netio.pump()); - ensure("success response", success); - ensure_equals(result["reply"].asString(), "success"); - - body["status"] = 499; - body["reason"] = "custom error message"; - request["message"] = "fail"; - request["payload"] = body; - regionPump.post(request); - ensure("got response", netio.pump()); - ensure("failure response", ! success); - ensure_equals(result["status"].asInteger(), body["status"].asInteger()); - ensure_equals(result["reason"].asString(), body["reason"].asString()); - } - - template<> template<> - void llcapears_object::test<3>() - { - LLSD request, body; - body["data"] = "yes"; - request["message"] = "unknown"; - request["payload"] = body; - request["reply"] = replyPump.getName(); - request["error"] = errorPump.getName(); - std::string threw; - try - { - WrapLLErrs capture; - regionPump.post(request); - } - catch (const WrapLLErrs::FatalException& e) - { - threw = e.what(); - } - ensure_contains("bad capability name", threw, "unsupported capability"); - } - - struct TestMapper: public LLCapabilityListener::CapabilityMapper - { - // Instantiator gets to specify whether mapper expects a reply. - // I'd really like to be able to test CapabilityMapper::buildMessage() - // functionality, too, but -- even though LLCapabilityListener accepts - // the LLMessageSystem* that it passes to CapabilityMapper -- - // LLMessageSystem::sendReliable(const LLHost&) isn't virtual, so it's - // not helpful to pass a subclass instance. I suspect that making any - // LLMessageSystem methods virtual would provoke howls of outrage, - // given how heavily it's used. Nor can I just provide a local - // definition of LLMessageSystem::sendReliable(const LLHost&) because - // we're already linking in the rest of message.o via llmessage.a, and - // that produces duplicate-symbol link errors. - TestMapper(const std::string& replyMessage = std::string()): - LLCapabilityListener::CapabilityMapper("test", replyMessage) - {} - virtual void buildMessage(LLMessageSystem* msg, - const LLUUID& agentID, - const LLUUID& sessionID, - const std::string& capabilityName, - const LLSD& payload) const - { - msg->newMessageFast(_PREHASH_SetStartLocationRequest); - msg->nextBlockFast( _PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, agentID); - msg->addUUIDFast(_PREHASH_SessionID, sessionID); - msg->nextBlockFast( _PREHASH_StartLocationData); - // corrected by sim - msg->addStringFast(_PREHASH_SimName, ""); - msg->addU32Fast(_PREHASH_LocationID, payload["HomeLocation"]["LocationId"].asInteger()); -/*==========================================================================*| - msg->addVector3Fast(_PREHASH_LocationPos, - ll_vector3_from_sdmap(payload["HomeLocation"]["LocationPos"])); - msg->addVector3Fast(_PREHASH_LocationLookAt, - ll_vector3_from_sdmap(payload["HomeLocation"]["LocationLookAt"])); -|*==========================================================================*/ - } - }; - - template<> template<> - void llcapears_object::test<4>() - { - TestMapper testMapper("WantReply"); - LLSD request, body; - body["data"] = "yes"; - request["message"] = "test"; - request["payload"] = body; - request["reply"] = replyPump.getName(); - request["error"] = errorPump.getName(); - std::string threw; - try - { - WrapLLErrs capture; - regionPump.post(request); - } - catch (const WrapLLErrs::FatalException& e) - { - threw = e.what(); - } - ensure_contains("capability mapper wants reply", threw, "unimplemented support for reply message"); - } - - template<> template<> - void llcapears_object::test<5>() - { - TestMapper testMapper; - std::string threw; - try - { - TestMapper testMapper2; - } - catch (const std::runtime_error& e) - { - threw = e.what(); - } - ensure_contains("no dup cap mapper", threw, "DupCapMapper"); - } -} diff --git a/indra/newview/tests/llhttpretrypolicy_test.cpp b/indra/newview/tests/llhttpretrypolicy_test.cpp index 25e6de46d9..8bd6cc2690 100755 --- a/indra/newview/tests/llhttpretrypolicy_test.cpp +++ b/indra/newview/tests/llhttpretrypolicy_test.cpp @@ -285,10 +285,10 @@ void RetryPolicyTestObject::test<7>() ensure_approximately_equals_range("header 2", seconds_to_wait, 7.0F, 2.0F); LLCore::HttpResponse *response; - LLCore::HttpHeaders *headers; + LLCore::HttpHeaders::ptr_t headers; response = new LLCore::HttpResponse(); - headers = new LLCore::HttpHeaders(); + headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders()); response->setStatus(503); response->setHeaders(headers); headers->append(HTTP_IN_HEADER_RETRY_AFTER, std::string("600")); @@ -299,7 +299,7 @@ void RetryPolicyTestObject::test<7>() response->release(); response = new LLCore::HttpResponse(); - headers = new LLCore::HttpHeaders(); + headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders()); response->setStatus(503); response->setHeaders(headers); time(&nowseconds); diff --git a/indra/newview/tests/llmediadataclient_test.cpp b/indra/newview/tests/llmediadataclient_test.cpp index 6f57daf151..61120686e4 100755 --- a/indra/newview/tests/llmediadataclient_test.cpp +++ b/indra/newview/tests/llmediadataclient_test.cpp @@ -106,7 +106,7 @@ const char *DATA = _DATA(VALID_OBJECT_ID,"1.0","true"); LLSD *gPostRecords = NULL; F64 gMinimumInterestLevel = (F64)0.0; - +#if 0 // stubs: void LLHTTPClient::post( const std::string& url, @@ -140,6 +140,7 @@ void LLHTTPClient::post( } responder->successResult(result); } +#endif const F32 HTTP_REQUEST_EXPIRY_SECS = 60.0f; diff --git a/indra/newview/tests/llremoteparcelrequest_test.cpp b/indra/newview/tests/llremoteparcelrequest_test.cpp index c49b0350e9..ea5014a59c 100755 --- a/indra/newview/tests/llremoteparcelrequest_test.cpp +++ b/indra/newview/tests/llremoteparcelrequest_test.cpp @@ -27,6 +27,7 @@ #include "linden_common.h" #include "../test/lltut.h" +#if 0 #include "../llremoteparcelrequest.h" @@ -134,3 +135,4 @@ namespace tut processor.processParcelInfoReply(gMessageSystem, NULL); } } +#endif diff --git a/indra/viewer_components/login/lllogin.cpp b/indra/viewer_components/login/lllogin.cpp index b8408a6fb4..88415ff11a 100755 --- a/indra/viewer_components/login/lllogin.cpp +++ b/indra/viewer_components/login/lllogin.cpp @@ -107,9 +107,8 @@ private: } // In a coroutine's top-level function args, do NOT NOT NOT accept - // references (const or otherwise) to anything but the self argument! Pass - // by value only! - void login_(LLCoros::self& self, std::string uri, LLSD credentials); + // references (const or otherwise) to anything! Pass by value only! + void login_(std::string uri, LLSD credentials); LLEventStream mPump; LLSD mAuthResponse, mValidAuthResponse; @@ -123,11 +122,11 @@ void LLLogin::Impl::connect(const std::string& uri, const LLSD& login_params) // its first wait; at that point, return here. std::string coroname = LLCoros::instance().launch("LLLogin::Impl::login_", - boost::bind(&Impl::login_, this, _1, uri, login_params)); + boost::bind(&Impl::login_, this, uri, login_params)); LL_DEBUGS("LLLogin") << " connected with uri '" << uri << "', login_params " << login_params << LL_ENDL; } -void LLLogin::Impl::login_(LLCoros::self& self, std::string uri, LLSD login_params) +void LLLogin::Impl::login_(std::string uri, LLSD login_params) { try { @@ -137,7 +136,7 @@ void LLLogin::Impl::login_(LLCoros::self& self, std::string uri, LLSD login_para //{ // printable_params["params"]["passwd"] = "*******"; //} - LL_DEBUGS("LLLogin") << "Entering coroutine " << LLCoros::instance().getName(self) + LL_DEBUGS("LLLogin") << "Entering coroutine " << LLCoros::instance().getName() << " with uri '" << uri << "', parameters " << printable_params << LL_ENDL; // Arriving in SRVRequest state @@ -176,7 +175,7 @@ void LLLogin::Impl::login_(LLCoros::self& self, std::string uri, LLSD login_para request["op"] = "rewriteURI"; request["uri"] = uri; request["reply"] = replyPump.getName(); - rewrittenURIs = postAndWait(self, request, srv_pump_name, filter); + rewrittenURIs = llcoro::postAndWait(request, srv_pump_name, filter); // EXP-772: If rewrittenURIs fail, try original URI as a fallback. rewrittenURIs.append(uri); } // we no longer need the filter @@ -222,10 +221,10 @@ void LLLogin::Impl::login_(LLCoros::self& self, std::string uri, LLSD login_para // returns. Subsequent responses, of course, must be awaited // without posting again. for (mAuthResponse = validateResponse(loginReplyPump.getName(), - postAndWait(self, request, xmlrpcPump, loginReplyPump, "reply")); + llcoro::postAndWait(request, xmlrpcPump, loginReplyPump, "reply")); mAuthResponse["status"].asString() == "Downloading"; mAuthResponse = validateResponse(loginReplyPump.getName(), - waitForEventOn(self, loginReplyPump))) + llcoro::waitForEventOn(loginReplyPump))) { // Still Downloading -- send progress update. sendProgressEvent("offline", "downloading"); diff --git a/indra/viewer_components/updater/CMakeLists.txt b/indra/viewer_components/updater/CMakeLists.txt index 61fd4220e0..0d44334276 100755 --- a/indra/viewer_components/updater/CMakeLists.txt +++ b/indra/viewer_components/updater/CMakeLists.txt @@ -6,15 +6,18 @@ include(00-Common) if(LL_TESTS) include(LLAddBuildTest) endif(LL_TESTS) +include(Boost) include(CMakeCopyIfDifferent) include(CURL) include(LLCommon) +include(LLCoreHttp) include(LLMessage) include(LLPlugin) include(LLVFS) include_directories( ${LLCOMMON_INCLUDE_DIRS} + ${LLCOREHTTP_INCLUDE_DIRS} ${LLMESSAGE_INCLUDE_DIRS} ${LLPLUGIN_INCLUDE_DIRS} ${LLVFS_INCLUDE_DIRS} @@ -59,6 +62,7 @@ add_library(llupdaterservice target_link_libraries(llupdaterservice ${LLCOMMON_LIBRARIES} + ${LLCOREHTTP_LIBRARIES} ${LLMESSAGE_LIBRARIES} ${LLPLUGIN_LIBRARIES} ${LLVFS_LIBRARIES} @@ -69,14 +73,16 @@ if(LL_TESTS) llupdaterservice.cpp ) + +set_source_files_properties( + llupdaterservice.cpp + PROPERTIES + LL_TEST_ADDITIONAL_LIBRARIES "${BOOST_SYSTEM_LIBRARY}" # *NOTE:Mani - I was trying to use the preprocessor seam to mock out -# llifstream (and other) llcommon classes. I didn't work +# llifstream (and other) llcommon classes. It didn't work # because of the windows declspec(dllimport)attribute. -#set_source_files_properties( -# llupdaterservice.cpp -# PROPERTIES -# LL_TEST_ADDITIONAL_CFLAGS "-Dllifstream=llus_mock_llifstream" -# ) +# LL_TEST_ADDITIONAL_CFLAGS "-Dllifstream=llus_mock_llifstream" + ) LL_ADD_PROJECT_UNIT_TESTS(llupdaterservice "${llupdater_service_TEST_SOURCE_FILES}") endif(LL_TESTS) diff --git a/indra/viewer_components/updater/llupdatechecker.cpp b/indra/viewer_components/updater/llupdatechecker.cpp index 8da4f88905..e889f83aa9 100755 --- a/indra/viewer_components/updater/llupdatechecker.cpp +++ b/indra/viewer_components/updater/llupdatechecker.cpp @@ -26,10 +26,10 @@ #include "linden_common.h" #include <stdexcept> #include <boost/format.hpp> -#include "llhttpclient.h" #include "llsd.h" #include "llupdatechecker.h" #include "lluri.h" +#include "llcorehttputil.h" #if LL_DARWIN #include <CoreServices/CoreServices.h> #endif @@ -53,15 +53,12 @@ public: // LLUpdateChecker //----------------------------------------------------------------------------- - - LLUpdateChecker::LLUpdateChecker(LLUpdateChecker::Client & client): mImplementation(new LLUpdateChecker::Implementation(client)) { ; // No op. } - void LLUpdateChecker::checkVersion(std::string const & urlBase, std::string const & channel, std::string const & version, @@ -74,11 +71,8 @@ void LLUpdateChecker::checkVersion(std::string const & urlBase, } - // LLUpdateChecker::Implementation //----------------------------------------------------------------------------- - - const char * LLUpdateChecker::Implementation::sProtocolVersion = "v1.1"; @@ -121,57 +115,58 @@ void LLUpdateChecker::Implementation::checkVersion(std::string const & urlBase, std::string checkUrl = buildUrl(urlBase, channel, version, platform, platform_version, uniqueid, willing_to_test); LL_INFOS("UpdaterService") << "checking for updates at " << checkUrl << LL_ENDL; - - mHttpClient.get(checkUrl, this); - } - else - { - LL_WARNS("UpdaterService") << "attempting to restart a check when one is in progress; ignored" << LL_ENDL; - } -} -void LLUpdateChecker::Implementation::httpCompleted() -{ - mInProgress = false; + LLCoros::instance().launch("LLUpdateChecker::Implementation::checkVersionCoro", + boost::bind(&Implementation::checkVersionCoro, this, checkUrl)); - S32 status = getStatus(); - const LLSD& content = getContent(); - const std::string& reason = getReason(); - if(status != 200) - { - std::string server_error; - if ( content.has("error_code") ) - { - server_error += content["error_code"].asString(); - } - if ( content.has("error_text") ) - { - server_error += server_error.empty() ? "" : ": "; - server_error += content["error_text"].asString(); - } - - LL_WARNS("UpdaterService") << "response error " << status - << " " << reason - << " (" << server_error << ")" - << LL_ENDL; - mClient.error(reason); } else { - mClient.response(content); + LL_WARNS("UpdaterService") << "attempting to restart a check when one is in progress; ignored" << LL_ENDL; } } - -void LLUpdateChecker::Implementation::httpFailure() +void LLUpdateChecker::Implementation::checkVersionCoro(std::string url) { - const std::string& reason = getReason(); - mInProgress = false; - LL_WARNS("UpdaterService") << "update check failed; " << reason << LL_ENDL; - mClient.error(reason); + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("checkVersionCoro", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + + LL_INFOS("checkVersionCoro") << "Getting update information from " << url << LL_ENDL; + + LLSD result = httpAdapter->getAndYield(httpRequest, url); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + mInProgress = false; + + if (status != LLCore::HttpStatus(HTTP_OK)) + { + std::string server_error; + if (result.has("error_code")) + { + server_error += result["error_code"].asString(); + } + if (result.has("error_text")) + { + server_error += server_error.empty() ? "" : ": "; + server_error += result["error_text"].asString(); + } + + LL_WARNS("UpdaterService") << "response error " << status.getStatus() + << " " << status.toString() + << " (" << server_error << ")" + << LL_ENDL; + mClient.error(status.toString()); + return; + } + + result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS); + mClient.response(result); } - std::string LLUpdateChecker::Implementation::buildUrl(std::string const & urlBase, std::string const & channel, std::string const & version, diff --git a/indra/viewer_components/updater/llupdatechecker.h b/indra/viewer_components/updater/llupdatechecker.h index 3163a6d53c..d10ea4cf42 100755 --- a/indra/viewer_components/updater/llupdatechecker.h +++ b/indra/viewer_components/updater/llupdatechecker.h @@ -30,61 +30,27 @@ #include <boost/shared_ptr.hpp> #include "llmd5.h" -#include "llhttpclient.h" +#include "lleventcoro.h" +#include "llcoros.h" // // Implements asynchronous checking for updates. // class LLUpdateChecker { public: - class Client; - class Implementation: public LLHTTPClient::Responder - { - public: - Implementation(Client & client); - ~Implementation(); - void checkVersion(std::string const & urlBase, - std::string const & channel, - std::string const & version, - std::string const & platform, - std::string const & platform_version, - unsigned char uniqueid[MD5HEX_STR_SIZE], - bool willing_to_test - ); - - protected: - // Responder: - virtual void httpCompleted(); - virtual void httpFailure(); - - private: - static const char * sLegacyProtocolVersion; - static const char * sProtocolVersion; - const char* mProtocol; - - Client & mClient; - LLHTTPClient mHttpClient; - bool mInProgress; - std::string mVersion; - std::string mUrlBase; - std::string mChannel; - std::string mPlatform; - std::string mPlatformVersion; - unsigned char mUniqueId[MD5HEX_STR_SIZE]; - bool mWillingToTest; - - std::string buildUrl(std::string const & urlBase, - std::string const & channel, - std::string const & version, - std::string const & platform, - std::string const & platform_version, - unsigned char uniqueid[MD5HEX_STR_SIZE], - bool willing_to_test); - - LOG_CLASS(LLUpdateChecker::Implementation); - }; + // + // The client interface implemented by a requestor checking for an update. + // + class Client + { + public: + // An error occurred while checking for an update. + virtual void error(std::string const & message) = 0; + + // A successful response was received from the viewer version manager + virtual void response(LLSD const & content) = 0; + }; - // An exception that may be raised on check errors. class CheckError; @@ -100,25 +66,54 @@ public: bool willing_to_test); private: - LLPointer<Implementation> mImplementation; -}; + class Implementation + { + public: + typedef boost::shared_ptr<Implementation> ptr_t; + Implementation(Client & client); + ~Implementation(); + void checkVersion(std::string const & urlBase, + std::string const & channel, + std::string const & version, + std::string const & platform, + std::string const & platform_version, + unsigned char uniqueid[MD5HEX_STR_SIZE], + bool willing_to_test + ); -class LLURI; // From lluri.h + private: + static const char * sLegacyProtocolVersion; + static const char * sProtocolVersion; + const char* mProtocol; -// -// The client interface implemented by a requestor checking for an update. -// -class LLUpdateChecker::Client -{ -public: - // An error occurred while checking for an update. - virtual void error(std::string const & message) = 0; - - // A successful response was received from the viewer version manager - virtual void response(LLSD const & content) = 0; -}; + Client & mClient; + bool mInProgress; + std::string mVersion; + std::string mUrlBase; + std::string mChannel; + std::string mPlatform; + std::string mPlatformVersion; + unsigned char mUniqueId[MD5HEX_STR_SIZE]; + bool mWillingToTest; + + std::string buildUrl(std::string const & urlBase, + std::string const & channel, + std::string const & version, + std::string const & platform, + std::string const & platform_version, + unsigned char uniqueid[MD5HEX_STR_SIZE], + bool willing_to_test); + + void checkVersionCoro(std::string url); + LOG_CLASS(LLUpdateChecker::Implementation); + }; + + + Implementation::ptr_t mImplementation; + //LLPointer<Implementation> mImplementation; +}; #endif diff --git a/indra/win_crash_logger/CMakeLists.txt b/indra/win_crash_logger/CMakeLists.txt index c6070020db..144d037a31 100755 --- a/indra/win_crash_logger/CMakeLists.txt +++ b/indra/win_crash_logger/CMakeLists.txt @@ -4,6 +4,7 @@ project(win_crash_logger) include(00-Common) include(LLCommon) +include(LLCoreHttp) include(LLCrashLogger) include(LLMath) include(LLMessage) @@ -13,8 +14,10 @@ include(LLXML) include(Linking) include(LLSharedLibs) include(GoogleBreakpad) +include(Boost) include_directories( + ${LLCOREHTTP_INCLUDE_DIRS} ${LLCOMMON_INCLUDE_DIRS} ${LLCRASHLOGGER_INCLUDE_DIRS} ${LLMATH_INCLUDE_DIRS} @@ -77,7 +80,10 @@ target_link_libraries(windows-crash-logger ${LLXML_LIBRARIES} ${LLMESSAGE_LIBRARIES} ${LLMATH_LIBRARIES} + ${LLCOREHTTP_LIBRARIES} ${LLCOMMON_LIBRARIES} + ${BOOST_CONTEXT_LIBRARY} + ${BOOST_COROUTINE_LIBRARY} ${WINDOWS_LIBRARIES} ${DXGUID_LIBRARY} ${GOOGLE_PERFTOOLS_LIBRARIES} |