diff options
author | Oz Linden <oz@lindenlab.com> | 2016-04-04 15:53:09 -0400 |
---|---|---|
committer | Oz Linden <oz@lindenlab.com> | 2016-04-04 15:53:09 -0400 |
commit | 9be58e915a6c69de280ccabd3019e9ac40beed26 (patch) | |
tree | ad6f8f1f754a865afb761d0f17744cc7e1ef8b74 /indra | |
parent | ab46c9226bd9048fa218f54bc8668594401529e7 (diff) | |
parent | 18928ea6c6f2830a0d45ec412c915eceff1b76b0 (diff) |
merge with 4.0.3-release
Diffstat (limited to 'indra')
359 files changed, 20312 insertions, 31091 deletions
diff --git a/indra/cmake/00-COMPILE-LINK-RUN.txt b/indra/cmake/00-COMPILE-LINK-RUN.txt index 49b899c50d..162b22865c 100644 --- a/indra/cmake/00-COMPILE-LINK-RUN.txt +++ b/indra/cmake/00-COMPILE-LINK-RUN.txt @@ -51,7 +51,6 @@ Compilation WINVER=0x0501 " " _WIN32_WINNT=0x0501 " " LL_OS_DRAGDROP_ENABLED=1 " " - CARES_STATICLIB " " LIB_NDOF=1 " " ---------------------------------------------------------------------------- @@ -109,7 +108,6 @@ Compilation DEBUG_INFO=1 LL_DARWIN=1 " " LL_OS_DRAGDROP_ENABLED=1 " " - CARES_STATICLIB " " LIB_NDOF=1 " " ---------------------------------------------------------------------------- diff --git a/indra/cmake/Boost.cmake b/indra/cmake/Boost.cmake index 25e54b7cbd..180a84dbcf 100644 --- a/indra/cmake/Boost.cmake +++ b/indra/cmake/Boost.cmake @@ -123,3 +123,9 @@ else (USESYSTEMLIBS) debug boost_thread-mt-d) endif (WINDOWS) endif (USESYSTEMLIBS) + +if (LINUX) + set(BOOST_SYSTEM_LIBRARY ${BOOST_SYSTEM_LIBRARY} rt) + set(BOOST_THREAD_LIBRARY ${BOOST_THREAD_LIBRARY} rt) +endif (LINUX) + diff --git a/indra/cmake/CARes.cmake b/indra/cmake/CARes.cmake deleted file mode 100644 index baa55aa49d..0000000000 --- a/indra/cmake/CARes.cmake +++ /dev/null @@ -1,21 +0,0 @@ -# -*- cmake -*- -include(Linking) -include(Prebuilt) - -set(CARES_FIND_QUIETLY ON) -set(CARES_FIND_REQUIRED ON) - -if (USESYSTEMLIBS) - include(FindCARes) -else (USESYSTEMLIBS) - use_prebuilt_binary(ares) - add_definitions("-DCARES_STATICLIB") - if (WINDOWS) - set(CARES_LIBRARIES areslib) - elseif (DARWIN) - set(CARES_LIBRARIES cares) - else (WINDOWS) - set(CARES_LIBRARIES cares) - endif (WINDOWS) - set(CARES_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/include/ares) -endif (USESYSTEMLIBS) diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt index 88e64ffc97..6dc8e3dfbf 100644 --- a/indra/cmake/CMakeLists.txt +++ b/indra/cmake/CMakeLists.txt @@ -13,7 +13,7 @@ set(cmake_SOURCE_FILES BerkeleyDB.cmake Boost.cmake BuildVersion.cmake - CARes.cmake + CEFPlugin.cmake CEFPlugin.cmake CMakeCopyIfDifferent.cmake ConfigurePkgConfig.cmake @@ -28,7 +28,6 @@ set(cmake_SOURCE_FILES FindAPR.cmake FindAutobuild.cmake FindBerkeleyDB.cmake - FindCARes.cmake FindFMODEX.cmake FindGLH.cmake FindGoogleBreakpad.cmake @@ -59,7 +58,6 @@ set(cmake_SOURCE_FILES JsonCpp.cmake LLAddBuildTest.cmake LLAppearance.cmake - LLAppearanceUtility.cmake LLAudio.cmake LLCharacter.cmake LLCommon.cmake diff --git a/indra/cmake/Copy3rdPartyLibs.cmake b/indra/cmake/Copy3rdPartyLibs.cmake index a6fd756c88..70d85b864c 100644 --- a/indra/cmake/Copy3rdPartyLibs.cmake +++ b/indra/cmake/Copy3rdPartyLibs.cmake @@ -22,10 +22,8 @@ if(WINDOWS) SLVoice.exe ca-bundle.crt libsndfile-1.dll - vivoxplatform.dll vivoxsdk.dll ortp.dll - zlib1.dll vivoxoal.dll ) diff --git a/indra/cmake/FindCARes.cmake b/indra/cmake/FindCARes.cmake deleted file mode 100644 index 1ed5b32913..0000000000 --- a/indra/cmake/FindCARes.cmake +++ /dev/null @@ -1,48 +0,0 @@ -# -*- cmake -*- - -# - Find c-ares -# Find the c-ares includes and library -# This module defines -# CARES_INCLUDE_DIR, where to find ares.h, etc. -# CARES_LIBRARIES, the libraries needed to use c-ares. -# CARES_FOUND, If false, do not try to use c-ares. -# also defined, but not for general use are -# CARES_LIBRARY, where to find the c-ares library. - -FIND_PATH(CARES_INCLUDE_DIR ares.h -/usr/local/include -/usr/include -) - -SET(CARES_NAMES ${CARES_NAMES} cares) -FIND_LIBRARY(CARES_LIBRARY - NAMES ${CARES_NAMES} - PATHS /usr/lib /usr/local/lib - ) - -IF (CARES_LIBRARY AND CARES_INCLUDE_DIR) - SET(CARES_LIBRARIES ${CARES_LIBRARY}) - SET(CARES_FOUND "YES") -ELSE (CARES_LIBRARY AND CARES_INCLUDE_DIR) - SET(CARES_FOUND "NO") -ENDIF (CARES_LIBRARY AND CARES_INCLUDE_DIR) - - -IF (CARES_FOUND) - IF (NOT CARES_FIND_QUIETLY) - MESSAGE(STATUS "Found c-ares: ${CARES_LIBRARIES}") - ENDIF (NOT CARES_FIND_QUIETLY) -ELSE (CARES_FOUND) - IF (CARES_FIND_REQUIRED) - MESSAGE(FATAL_ERROR "Could not find c-ares library") - ENDIF (CARES_FIND_REQUIRED) -ENDIF (CARES_FOUND) - -# Deprecated declarations. -SET (NATIVE_CARES_INCLUDE_PATH ${CARES_INCLUDE_DIR} ) -GET_FILENAME_COMPONENT (NATIVE_CARES_LIB_PATH ${CARES_LIBRARY} PATH) - -MARK_AS_ADVANCED( - CARES_LIBRARY - CARES_INCLUDE_DIR - ) diff --git a/indra/cmake/LLAddBuildTest.cmake b/indra/cmake/LLAddBuildTest.cmake index ac5c5c6a2a..db8b95dbe2 100644 --- a/indra/cmake/LLAddBuildTest.cmake +++ b/indra/cmake/LLAddBuildTest.cmake @@ -35,6 +35,7 @@ INCLUDE(GoogleMock) ${APRUTIL_LIBRARIES} ${APR_LIBRARIES} llcommon + llcorehttp ) IF(NOT "${project}" STREQUAL "llmath") # add llmath as a dep unless the tested module *is* llmath! @@ -49,6 +50,9 @@ INCLUDE(GoogleMock) ${GOOGLEMOCK_INCLUDE_DIRS} ) SET(alltest_LIBRARIES + ${BOOST_COROUTINE_LIBRARY} + ${BOOST_CONTEXT_LIBRARY} + ${BOOST_SYSTEM_LIBRARY} ${GOOGLEMOCK_LIBRARIES} ${PTHREAD_LIBRARY} ${WINDOWS_LIBRARIES} @@ -191,6 +195,9 @@ FUNCTION(LL_ADD_INTEGRATION_TEST SET(libraries ${library_dependencies} + ${BOOST_COROUTINE_LIBRARY} + ${BOOST_CONTEXT_LIBRARY} + ${BOOST_SYSTEM_LIBRARY} ${GOOGLEMOCK_LIBRARIES} ${PTHREAD_LIBRARY} ) diff --git a/indra/cmake/LLAppearance.cmake b/indra/cmake/LLAppearance.cmake index bd3795a526..ae265d07e3 100644 --- a/indra/cmake/LLAppearance.cmake +++ b/indra/cmake/LLAppearance.cmake @@ -1,6 +1,9 @@ # -*- cmake -*- include(Variables) +include(Boost) +include(LLMessage) +include(LLCoreHttp) set(LLAPPEARANCE_INCLUDE_DIRS ${LIBS_OPEN_DIR}/llappearance @@ -12,6 +15,13 @@ if (BUILD_HEADLESS) ) endif (BUILD_HEADLESS) -set(LLAPPEARANCE_LIBRARIES llappearance) +set(LLAPPEARANCE_LIBRARIES llappearance + llmessage + llcorehttp + ${BOOST_COROUTINE_LIBRARY} + ${BOOST_CONTEXT_LIBRARY} + ${BOOST_SYSTEM_LIBRARY} + ) + diff --git a/indra/cmake/LLAppearanceUtility.cmake b/indra/cmake/LLAppearanceUtility.cmake index 709b91c134..28b49bf75f 100644 --- a/indra/cmake/LLAppearanceUtility.cmake +++ b/indra/cmake/LLAppearanceUtility.cmake @@ -1,5 +1,6 @@ # -*- cmake -*- include(Prebuilt) +include(Boost) # Linux proprietary build only if (INSTALL_PROPRIETARY) @@ -10,3 +11,4 @@ if (INSTALL_PROPRIETARY) endif (LINUX) endif (INSTALL_PROPRIETARY) + diff --git a/indra/cmake/LLCommon.cmake b/indra/cmake/LLCommon.cmake index b52556a73e..b50b4bcdb2 100644 --- a/indra/cmake/LLCommon.cmake +++ b/indra/cmake/LLCommon.cmake @@ -19,9 +19,19 @@ if (LINUX) # In order to support using ld.gold on linux, we need to explicitely # specify all libraries that llcommon uses. # llcommon uses `clock_gettime' which is provided by librt on linux. - set(LLCOMMON_LIBRARIES llcommon rt) + set(LLCOMMON_LIBRARIES llcommon + ${BOOST_COROUTINE_LIBRARY} + ${BOOST_CONTEXT_LIBRARY} + ${BOOST_THREAD_LIBRARY} + ${BOOST_SYSTEM_LIBRARY} + rt + ) else (LINUX) - set(LLCOMMON_LIBRARIES llcommon) + set(LLCOMMON_LIBRARIES llcommon + ${BOOST_COROUTINE_LIBRARY} + ${BOOST_CONTEXT_LIBRARY} + ${BOOST_THREAD_LIBRARY} + ${BOOST_SYSTEM_LIBRARY} ) endif (LINUX) # add_definitions(${TCMALLOC_FLAG}) diff --git a/indra/cmake/LLCoreHttp.cmake b/indra/cmake/LLCoreHttp.cmake index 61e4b23d98..379ae207de 100644 --- a/indra/cmake/LLCoreHttp.cmake +++ b/indra/cmake/LLCoreHttp.cmake @@ -1,16 +1,17 @@ # -*- cmake -*- -include(CARes) include(CURL) include(OpenSSL) include(Boost) set(LLCOREHTTP_INCLUDE_DIRS ${LIBS_OPEN_DIR}/llcorehttp - ${CARES_INCLUDE_DIRS} ${CURL_INCLUDE_DIRS} ${OPENSSL_INCLUDE_DIRS} ${BOOST_INCLUDE_DIRS} ) -set(LLCOREHTTP_LIBRARIES llcorehttp) +set(LLCOREHTTP_LIBRARIES llcorehttp + ${BOOST_COROUTINE_LIBRARY} + ${BOOST_CONTEXT_LIBRARY} + ${BOOST_SYSTEM_LIBRARY}) diff --git a/indra/cmake/LLMessage.cmake b/indra/cmake/LLMessage.cmake index 0143d04fd7..7be53ec0ec 100644 --- a/indra/cmake/LLMessage.cmake +++ b/indra/cmake/LLMessage.cmake @@ -1,13 +1,11 @@ # -*- cmake -*- -include(CARes) include(CURL) include(OpenSSL) include(XmlRpcEpi) set(LLMESSAGE_INCLUDE_DIRS ${LIBS_OPEN_DIR}/llmessage - ${CARES_INCLUDE_DIRS} ${CURL_INCLUDE_DIRS} ${OPENSSL_INCLUDE_DIRS} ) diff --git a/indra/linux_crash_logger/CMakeLists.txt b/indra/linux_crash_logger/CMakeLists.txt index c0fc1b2be0..029096df37 100644 --- 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_COROUTINE_LIBRARY} + ${BOOST_CONTEXT_LIBRARY} ${UI_LIBRARIES} ${DB_LIBRARIES} ${FREETYPE_LIBRARIES} + ${LIBRT_LIBRARY} ) add_custom_target(linux-crash-logger-target ALL diff --git a/indra/llappearance/CMakeLists.txt b/indra/llappearance/CMakeLists.txt index 0dbd58b7cd..20eb4678dd 100644 --- a/indra/llappearance/CMakeLists.txt +++ b/indra/llappearance/CMakeLists.txt @@ -9,6 +9,7 @@ include(LLImage) include(LLInventory) include(LLMath) include(LLMessage) +include(LLCoreHttp) include(LLRender) include(LLVFS) include(LLWindow) @@ -86,6 +87,8 @@ target_link_libraries(llappearance ${LLMATH_LIBRARIES} ${LLXML_LIBRARIES} ${LLMATH_LIBRARIES} + ${LLMESSAGE_LIBRARIES} + ${LLCOREHTTP_LIBRARIES} ${LLCOMMON_LIBRARIES} ) @@ -101,6 +104,8 @@ if (BUILD_HEADLESS) ${LLMATH_LIBRARIES} ${LLXML_LIBRARIES} ${LLMATH_LIBRARIES} + ${LLMESSAGE_LIBRARIES} + ${LLCOREHTTP_LIBRARIES} ${LLCOMMON_LIBRARIES} ) endif (BUILD_HEADLESS) diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 5863310162..907dbab8f8 100644 --- 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} @@ -86,6 +88,7 @@ set(llcommon_SOURCE_FILES llrefcount.cpp llrun.cpp llsd.cpp + llsdjson.cpp llsdparam.cpp llsdserialize.cpp llsdserialize_xml.cpp @@ -195,6 +198,7 @@ set(llcommon_HEADER_FILES llrefcount.h llsafehandle.h llsd.h + llsdjson.h llsdparam.h llsdserialize.h llsdserialize_xml.h @@ -262,10 +266,14 @@ target_link_libraries( ${APRUTIL_LIBRARIES} ${APR_LIBRARIES} ${EXPAT_LIBRARIES} + ${JSONCPP_LIBRARIES} ${ZLIB_LIBRARIES} ${WINDOWS_LIBRARIES} + ${BOOST_COROUTINE_LIBRARY} + ${BOOST_CONTEXT_LIBRARY} ${BOOST_PROGRAM_OPTIONS_LIBRARY} ${BOOST_REGEX_LIBRARY} + ${BOOST_SYSTEM_LIBRARY} ${GOOGLE_PERFTOOLS_LIBRARIES} ${URIPARSER_LIBRARIES} ) @@ -286,7 +294,14 @@ if (LL_TESTS) LL_ADD_PROJECT_UNIT_TESTS(llcommon "${llcommon_TEST_SOURCE_FILES}") #set(TEST_DEBUG on) - set(test_libs llcommon ${LLCOMMON_LIBRARIES} ${WINDOWS_LIBRARIES} ${GOOGLEMOCK_LIBRARIES}) + set(test_libs llcommon + ${LLCOMMON_LIBRARIES} + ${WINDOWS_LIBRARIES} + ${GOOGLEMOCK_LIBRARIES} + ${BOOST_COROUTINE_LIBRARY} + ${BOOST_CONTEXT_LIBRARY} + ${BOOST_THREAD_LIBRARY} + ${BOOST_SYSTEM_LIBRARY}) LL_ADD_INTEGRATION_TEST(commonmisc "" "${test_libs}") LL_ADD_INTEGRATION_TEST(bitpack "" "${test_libs}") LL_ADD_INTEGRATION_TEST(llbase64 "" "${test_libs}") @@ -308,7 +323,7 @@ if (LL_TESTS) LL_ADD_INTEGRATION_TEST(llunits "" "${test_libs}") LL_ADD_INTEGRATION_TEST(stringize "" "${test_libs}") LL_ADD_INTEGRATION_TEST(lleventdispatcher "" "${test_libs}") - LL_ADD_INTEGRATION_TEST(lleventcoro "" "${test_libs};${BOOST_CONTEXT_LIBRARY};${BOOST_THREAD_LIBRARY};${BOOST_COROUTINE_LIBRARY};${BOOST_SYSTEM_LIBRARY}") + LL_ADD_INTEGRATION_TEST(lleventcoro "" "${test_libs}") LL_ADD_INTEGRATION_TEST(llprocess "" "${test_libs}") LL_ADD_INTEGRATION_TEST(llleap "" "${test_libs}") LL_ADD_INTEGRATION_TEST(llstreamqueue "" "${test_libs}") diff --git a/indra/llcommon/fix_macros.h b/indra/llcommon/fix_macros.h index ef959decff..43c09c54bc 100644 --- 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 100644 --- 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..d16bf0160b 100644 --- a/indra/llcommon/llcoros.cpp +++ b/indra/llcommon/llcoros.cpp @@ -39,6 +39,62 @@ #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::CoroData& LLCoros::get_CoroData(const std::string& caller) +{ + CoroData* current = sCurrentCoro.get(); + if (! current) + { + LL_ERRS("LLCoros") << "Calling " << caller << " from non-coroutine context!" << LL_ENDL; + } + return *current; +} + +//static +LLCoros::coro::self& LLCoros::get_self() +{ + return *get_CoroData("get_self()").mSelf; +} + +//static +void LLCoros::set_consuming(bool consuming) +{ + get_CoroData("set_consuming()").mConsuming = consuming; +} + +//static +bool LLCoros::get_consuming() +{ + return get_CoroData("get_consuming()").mConsuming; +} + +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 @@ -53,14 +109,33 @@ LLCoros::LLCoros(): bool LLCoros::cleanup(const LLSD&) { + static std::string previousName; + static int previousCount = 0; // Walk the mCoros map, checking and removing completed coroutines. for (CoroMap::iterator mi(mCoros.begin()), mend(mCoros.end()); mi != mend; ) { // 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; + if (previousName != mi->first) + { + previousName = mi->first; + previousCount = 1; + } + else + { + ++previousCount; + } + + if ((previousCount < 5) || !(previousCount % 50)) + { + if (previousCount < 5) + LL_INFOS("LLCoros") << "LLCoros: cleaning up coroutine " << mi->first << LL_ENDL; + else + LL_INFOS("LLCoros") << "LLCoros: cleaning up coroutine " << mi->first << "("<< previousCount << ")" << 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. @@ -78,6 +153,9 @@ bool LLCoros::cleanup(const LLSD&) std::string LLCoros::generateDistinctName(const std::string& prefix) const { + static std::string previousName; + static int previousCount = 0; + // Allowing empty name would make getName()'s not-found return ambiguous. if (prefix.empty()) { @@ -94,7 +172,25 @@ std::string LLCoros::generateDistinctName(const std::string& prefix) const { if (mCoros.find(name) == mCoros.end()) { - LL_INFOS("LLCoros") << "LLCoros: launching coroutine " << name << LL_ENDL; + if (previousName != name) + { + previousName = name; + previousCount = 1; + } + else + { + ++previousCount; + } + + if ((previousCount < 5) || !(previousCount % 50)) + { + if (previousCount < 5) + LL_INFOS("LLCoros") << "LLCoros: launching coroutine " << name << LL_ENDL; + else + LL_INFOS("LLCoros") << "LLCoros: launching coroutine " << name << "(" << previousCount << ")" << LL_ENDL; + + } + return name; } } @@ -114,20 +210,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 +227,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 +252,35 @@ 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), + // don't consume events unless specifically directed + mConsuming(false), + 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..39316ed0e6 100644 --- a/indra/llcommon/llcoros.h +++ b/indra/llcommon/llcoros.h @@ -30,14 +30,20 @@ #define LL_LLCOROS_H #include <boost/dcoroutine/coroutine.hpp> +#include <boost/dcoroutine/future.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 +86,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 +100,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 +138,150 @@ 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(); + + /** + * Most coroutines, most of the time, don't "consume" the events for which + * they're suspending. This way, an arbitrary number of listeners (whether + * coroutines or simple callbacks) can be registered on a particular + * LLEventPump, every listener responding to each of the events on that + * LLEventPump. But a particular coroutine can assert that it will consume + * each event for which it suspends. (See also llcoro::postAndSuspend(), + * llcoro::VoidListener) + */ + static void set_consuming(bool consuming); + static bool get_consuming(); + + /** + * Please do NOT directly use boost::dcoroutines::future! It is essential + * to maintain the "current" coroutine at every context switch. This + * Future wraps the essential boost::dcoroutines::future functionality + * with that maintenance. + */ + template <typename T> + class Future; + 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); + static CoroData& get_CoroData(const std::string& caller); 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; + // set_consuming() state + bool mConsuming; + // 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 + +template <typename T> +class LLCoros::Future +{ + typedef boost::dcoroutines::future<T> dfuture; + +public: + Future(): + mFuture(get_self()) + {} + + typedef typename boost::dcoroutines::make_callback_result<dfuture>::type callback_t; + + callback_t make_callback() + { + return boost::dcoroutines::make_callback(mFuture); + } + +#ifndef LL_LINUX + explicit +#endif + operator bool() const + { + return bool(mFuture); + } + + bool operator!() const + { + return ! mFuture; + } + + T get() + { + // instantiate Suspending to manage the "current" coroutine + llcoro::Suspending suspended; + return *mFuture; + } + +private: + dfuture mFuture; }; #endif /* ! defined(LL_LLCOROS_H) */ diff --git a/indra/llcommon/llerror.h b/indra/llcommon/llerror.h index 73544cb914..3beef65723 100644 --- 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..578a2b62c8 100644 --- a/indra/llcommon/lleventcoro.cpp +++ b/indra/llcommon/lleventcoro.cpp @@ -38,34 +38,60 @@ #include "llsdserialize.h" #include "llerror.h" #include "llcoros.h" +#include "llmake.h" -std::string LLEventDetail::listenerNameForCoroImpl(const void* self_id) +#include "lleventfilter.h" + +namespace { - // First, if this coroutine was launched by LLCoros::launch(), find that name. - std::string name(LLCoros::instance().getNameByID(self_id)); + +/** + * suspendUntilEventOn() 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 suspendUntilEventOn() 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 suspendUntilEventOn() 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 postAndSuspend()'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 +144,185 @@ void LLEventDetail::storeToLLSDPath(LLSD& dest, const LLSD& rawPath, const LLSD& *pdest = value; } +/// For LLCoros::Future<LLSD>::make_callback(), the callback has a signature +/// like void callback(LLSD), which isn't a valid LLEventPump listener: such +/// listeners must return bool. +template <typename LISTENER> +class FutureListener +{ +public: + // FutureListener is instantiated on the coroutine stack: the stack, in + // other words, that wants to suspend. + FutureListener(const LISTENER& listener): + mListener(listener), + // Capture the suspending coroutine's flag as a consuming or + // non-consuming listener. + mConsume(LLCoros::get_consuming()) + {} + + // operator()() is called on the main stack: the stack on which the + // expected event is fired. + bool operator()(const LLSD& event) + { + mListener(event); + // tell upstream LLEventPump whether listener consumed + return mConsume; + } + +protected: + LISTENER mListener; + bool mConsume; +}; + +} // anonymous + +void llcoro::suspend() +{ + // 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 suspend. + suspendUntilEventOn("mainloop"); +} + +void llcoro::suspendUntilTimeout(float seconds) +{ + LLEventTimeout timeout; + + timeout.eventAfter(seconds, LLSD()); + llcoro::suspendUntilEventOn(timeout); +} + +LLSD llcoro::postAndSuspend(const LLSD& event, const LLEventPumpOrPumpName& requestPump, + const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath) +{ + // declare the future + LLCoros::Future<LLSD> future; + // 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, + llmake<FutureListener>(future.make_callback()))); + // 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") << "postAndSuspend(): 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") << "postAndSuspend(): coroutine " << listenerName + << " about to wait on LLEventPump " << replyPump.getPump().getName() + << LL_ENDL; + // calling get() on the future makes us wait for it + LLSD value(future.get()); + LL_DEBUGS("lleventcoro") << "postAndSuspend(): coroutine " << listenerName + << " resuming with " << value << LL_ENDL; + // returning should disconnect the connection + return value; +} + +namespace +{ + +/** + * This helper is specifically for postAndSuspend2(). 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 + * FutureListener for the purpose. The basic idea is that we construct a + * distinct instance of FutureListener2 -- binding different instance data -- + * for each of the pumps. Then, when a pump delivers an LLSD value to either + * FutureListener2, it can combine that LLSD with its discriminator to feed + * the future object. + * + * DISCRIM is a template argument so we can use llmake() rather than + * having to write our own argument-deducing helper function. + */ +template <typename LISTENER, typename DISCRIM> +class FutureListener2: public FutureListener<LISTENER> +{ + typedef FutureListener<LISTENER> super; + +public: + // instantiated on coroutine stack: the stack about to suspend + FutureListener2(const LISTENER& listener, DISCRIM discriminator): + super(listener), + mDiscrim(discriminator) + {} + + // called on main stack: the stack on which event is fired + bool operator()(const LLSD& event) + { + // our future object is defined to accept LLEventWithID + super::mListener(LLEventWithID(event, mDiscrim)); + // tell LLEventPump whether or not event was consumed + return super::mConsume; + } + +private: + const DISCRIM mDiscrim; +}; + +} // anonymous + +namespace llcoro +{ + +LLEventWithID postAndSuspend2(const LLSD& event, + const LLEventPumpOrPumpName& requestPump, + const LLEventPumpOrPumpName& replyPump0, + const LLEventPumpOrPumpName& replyPump1, + const LLSD& replyPump0NamePath, + const LLSD& replyPump1NamePath) +{ + // declare the future + LLCoros::Future<LLEventWithID> future; + // 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", + llmake<FutureListener2>(future.make_callback(), 0))); + LLTempBoundListener connection1( + replyPump1.getPump().listen( + name + "b", + llmake<FutureListener2>(future.make_callback(), 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") << "postAndSuspend2(): coroutine " << name + << " posting to " << requestPump.getPump().getName() + << ": " << modevent << LL_ENDL; + requestPump.getPump().post(modevent); + } + LL_DEBUGS("lleventcoro") << "postAndSuspend2(): coroutine " << name + << " about to wait on LLEventPumps " << replyPump0.getPump().getName() + << ", " << replyPump1.getPump().getName() << LL_ENDL; + // calling get() on the future makes us wait for it + LLEventWithID value(future.get()); + LL_DEBUGS("lleventcoro") << "postAndSuspend(): 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 +349,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..2105faf861 100644 --- a/indra/llcommon/lleventcoro.h +++ b/indra/llcommon/lleventcoro.h @@ -29,11 +29,10 @@ #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> +#include <utility> // std::pair #include "llevents.h" #include "llerror.h" @@ -74,111 +73,46 @@ private: boost::optional<LLEventPump&> mPump; }; -/// 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 +namespace llcoro { -public: - LLVoidListener(const LISTENER& listener): - mListener(listener) - {} - bool operator()(const LLSD& event) - { - mListener(event); - // don't swallow the event, let other listeners see it - return false; - } -private: - LISTENER mListener; -}; - -/// LLVoidListener helper function to infer the type of the LISTENER -template <typename LISTENER> -LLVoidListener<LISTENER> voidlistener(const LISTENER& listener) -{ - return LLVoidListener<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()); - } +/** + * 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 suspend(); - /** - * 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 at least the specified number of seconds + */ +void suspendUntilTimeout(float seconds); /** - * Post specified LLSD event on the specified LLEventPump, then wait for a + * Post specified LLSD event on the specified LLEventPump, then suspend for a * response on specified other LLEventPump. This is more than mere * convenience: the difference between this function and the sequence * @code * requestPump.post(myEvent); - * LLSD reply = waitForEventOn(self, replyPump); + * LLSD reply = suspendUntilEventOn(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 * sequence above, the running coroutine isn't even listening on @a replyPump - * until <tt>requestPump.post()</tt> returns and @c waitForEventOn() is + * until <tt>requestPump.post()</tt> returns and @c suspendUntilEventOn() is * entered. Therefore, the coroutine completely misses an immediate reply - * event, making it wait indefinitely. + * event, making it suspend indefinitely. * - * By contrast, postAndWait() listens on the @a replyPump @em before posting + * By contrast, postAndSuspend() listens on the @a replyPump @em before posting * the specified LLSD event on the specified @a requestPump. * - * @param self The @c self object passed into a coroutine * @param event LLSD data to be posted on @a requestPump * @param requestPump an LLEventPump on which to post @a event. Pass either * the LLEventPump& or its string name. However, if you pass a * default-constructed @c LLEventPumpOrPumpName, we skip the post() call. - * @param replyPump an LLEventPump on which postAndWait() will listen for a + * @param replyPump an LLEventPump on which postAndSuspend() will listen for a * reply. Pass either the LLEventPump& or its string name. The calling - * coroutine will wait until that reply arrives. (If you're concerned about a + * coroutine will suspend until that reply arrives. (If you're concerned about a * reply that might not arrive, please see also LLEventTimeout.) * @param replyPumpNamePath specifies the location within @a event in which to * store <tt>replyPump.getName()</tt>. This is a strictly optional convenience @@ -201,101 +135,29 @@ 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 postAndSuspend(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 suspendUntilEventOn(const LLEventPumpOrPumpName& pump) { - // This is now a convenience wrapper for postAndWait(). - return postAndWait(self, LLSD(), LLEventPumpOrPumpName(), pump); + // This is now a convenience wrapper for postAndSuspend(). + return postAndSuspend(LLSD(), LLEventPumpOrPumpName(), pump); } -/// return type for two-pump variant of waitForEventOn() +} // namespace llcoro + +/// return type for two-pump variant of suspendUntilEventOn() 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. - * Otherwise, it closely resembles postAndWait(); please see the documentation + * Otherwise, it closely resembles postAndSuspend(); please see the documentation * for that function for detailed parameter info. * * While we could have implemented the single-pump variant in terms of this @@ -310,81 +172,41 @@ namespace LLEventDetail * the index of the pump on which it arrived (0 or 1). * * @note - * I'd have preferred to overload the name postAndWait() for both signatures. + * I'd have preferred to overload the name postAndSuspend() for both signatures. * But consider the following ambiguous call: * @code - * postAndWait(self, LLSD(), requestPump, replyPump, "someString"); + * postAndSuspend(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 * function). * - * It seems less burdensome to write postAndWait2() than to write either + * It seems less burdensome to write postAndSuspend2() than to write either * LLSD("someString") or LLEventOrPumpName("someString"). */ -template <typename SELF> -LLEventWithID postAndWait2(SELF& self, const LLSD& event, +LLEventWithID postAndSuspend2(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) +suspendUntilEventOn(const LLEventPumpOrPumpName& pump0, const LLEventPumpOrPumpName& pump1) { - // This is now a convenience wrapper for postAndWait2(). - return postAndWait2(self, LLSD(), LLEventPumpOrPumpName(), pump0, pump1); + // This is now a convenience wrapper for postAndSuspend2(). + return postAndSuspend2(LLSD(), LLEventPumpOrPumpName(), pump0, pump1); } /** - * Helper for the two-pump variant of waitForEventOn(), e.g.: + * Helper for the two-pump variant of suspendUntilEventOn(), e.g.: * * @code - * LLSD reply = errorException(waitForEventOn(self, replyPump, errorPump), + * LLSD reply = errorException(suspendUntilEventOn(replyPump, errorPump), * "error response from login.cgi"); * @endcode * @@ -400,6 +222,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 +244,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 @@ -437,7 +266,7 @@ LL_COMMON_API LLSD errorLog(const LLEventWithID& result, const std::string& desc * 2. Provide its actual name to the event API in question as the name of the * reply LLEventPump. * 3. Initiate the request to the event API. - * 4. Call your LLEventTempStream's wait() method to wait for the reply. + * 4. Call your LLEventTempStream's suspend() method to suspend for the reply. * 5. Let the LLCoroEventPump go out of scope. */ class LL_COMMON_API LLCoroEventPump @@ -454,26 +283,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 suspend() { - return waitForEventOn(self, mPump); + return llcoro::suspendUntilEventOn(mPump); } - template <typename SELF> - LLSD postAndWait(SELF& self, const LLSD& event, const LLEventPumpOrPumpName& requestPump, + LLSD postAndSuspend(const LLSD& event, const LLEventPumpOrPumpName& requestPump, const LLSD& replyPumpNamePath=LLSD()) { - return ::postAndWait(self, event, requestPump, mPump, replyPumpNamePath); + return llcoro::postAndSuspend(event, requestPump, mPump, replyPumpNamePath); } private: @@ -509,57 +328,51 @@ public: /// request pump 1 LLEventPump& getPump1() { return mPump1; } - /// waitForEventOn(self, either of our two LLEventPumps) - template <typename SELF> - LLEventWithID wait(SELF& self) + /// suspendUntilEventOn(either of our two LLEventPumps) + LLEventWithID suspend() { - return waitForEventOn(self, mPump0, mPump1); + return llcoro::suspendUntilEventOn(mPump0, mPump1); } - /// errorException(wait(self)) - template <typename SELF> - LLSD waitWithException(SELF& self) + /// errorException(suspend()) + LLSD suspendWithException() { - return errorException(wait(self), std::string("Error event on ") + getName1()); + return llcoro::errorException(suspend(), std::string("Error event on ") + getName1()); } - /// errorLog(wait(self)) - template <typename SELF> - LLSD waitWithLog(SELF& self) + /// errorLog(suspend()) + LLSD suspendWithLog() { - return errorLog(wait(self), std::string("Error event on ") + getName1()); + return llcoro::errorLog(suspend(), std::string("Error event on ") + getName1()); } - template <typename SELF> - LLEventWithID postAndWait(SELF& self, const LLSD& event, + LLEventWithID postAndSuspend(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::postAndSuspend2(event, requestPump, mPump0, mPump1, + replyPump0NamePath, replyPump1NamePath); } - template <typename SELF> - LLSD postAndWaitWithException(SELF& self, const LLSD& event, + LLSD postAndSuspendWithException(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(postAndSuspend(event, requestPump, + replyPump0NamePath, replyPump1NamePath), + std::string("Error event on ") + getName1()); } - template <typename SELF> - LLSD postAndWaitWithLog(SELF& self, const LLSD& event, + LLSD postAndSuspendWithLog(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(postAndSuspend(event, requestPump, + replyPump0NamePath, replyPump1NamePath), + std::string("Error event on ") + getName1()); } private: diff --git a/indra/llcommon/llevents.cpp b/indra/llcommon/llevents.cpp index 1c928b3db8..645c29d770 100644 --- a/indra/llcommon/llevents.cpp +++ b/indra/llcommon/llevents.cpp @@ -132,6 +132,17 @@ LLEventPump& LLEventPumps::obtain(const std::string& name) return *newInstance; } +bool LLEventPumps::post(const std::string&name, const LLSD&message) +{ + PumpMap::iterator found = mPumpMap.find(name); + + if (found == mPumpMap.end()) + return false; + + return (*found).second->post(message); +} + + void LLEventPumps::flush() { // Flush every known LLEventPump instance. Leave it up to each instance to @@ -497,6 +508,43 @@ bool LLEventStream::post(const LLSD& event) } /***************************************************************************** + * LLEventMailDrop + *****************************************************************************/ +bool LLEventMailDrop::post(const LLSD& event) +{ + bool posted = false; + + if (!mSignal->empty()) + posted = LLEventStream::post(event); + + if (!posted) + { // if the event was not handled we will save it for later so that it can + // be posted to any future listeners when they attach. + mEventHistory.push_back(event); + } + + return posted; +} + +LLBoundListener LLEventMailDrop::listen_impl(const std::string& name, + const LLEventListener& listener, + const NameList& after, + const NameList& before) +{ + if (!mEventHistory.empty()) + { + if (listener(mEventHistory.front())) + { + mEventHistory.pop_front(); + } + } + + return LLEventStream::listen_impl(name, listener, after, before); + +} + + +/***************************************************************************** * LLEventQueue *****************************************************************************/ bool LLEventQueue::post(const LLSD& event) diff --git a/indra/llcommon/llevents.h b/indra/llcommon/llevents.h index 0cbd1da32d..6175329a9d 100644 --- a/indra/llcommon/llevents.h +++ b/indra/llcommon/llevents.h @@ -217,6 +217,18 @@ public: * an instance without conferring @em ownership. */ LLEventPump& obtain(const std::string& name); + + /** + * Find the named LLEventPump instance. If it exists post the message to it. + * If the pump does not exist, do nothing. + * + * returns the result of the LLEventPump::post. If no pump exists returns false. + * + * This is syntactically similar to LLEventPumps::instance().post(name, message), + * however if the pump does not already exist it will not be created. + */ + bool post(const std::string&, const LLSD&); + /** * Flush all known LLEventPump instances */ @@ -496,7 +508,7 @@ public: // at the boost::bind object itself before that happens. return LLEventDetail::visit_and_connect(name, listener, - boost::bind(&LLEventPump::listen_impl, + boost::bind(&LLEventPump::listen_invoke, this, name, _1, @@ -541,13 +553,23 @@ private: virtual void reset(); + + private: - virtual LLBoundListener listen_impl(const std::string& name, const LLEventListener&, - const NameList& after, - const NameList& before); + LLBoundListener listen_invoke(const std::string& name, const LLEventListener& listener, + const NameList& after, + const NameList& before) + { + return this->listen_impl(name, listener, after, before); + } + std::string mName; protected: + virtual LLBoundListener listen_impl(const std::string& name, const LLEventListener&, + const NameList& after, + const NameList& before); + /// implement the dispatching boost::shared_ptr<LLStandardSignal> mSignal; @@ -586,10 +608,39 @@ public: }; /***************************************************************************** + * LLEventMailDrop + *****************************************************************************/ +/** + * LLEventMailDrop is a specialization of LLEventStream. Events are posted normally, + * however if no listeners return that they have handled the event it is placed in + * a queue. Subsequent attaching listeners will receive stored events from the queue + * until a listener indicates that the event has been handled. In order to receive + * multiple events from a mail drop the listener must disconnect and reconnect. + */ +class LL_COMMON_API LLEventMailDrop : public LLEventStream +{ +public: + LLEventMailDrop(const std::string& name, bool tweak = false) : LLEventStream(name, tweak) {} + virtual ~LLEventMailDrop() {} + + /// Post an event to all listeners + virtual bool post(const LLSD& event); + +protected: + virtual LLBoundListener listen_impl(const std::string& name, const LLEventListener&, + const NameList& after, + const NameList& before); + +private: + typedef std::list<LLSD> EventList; + EventList mEventHistory; +}; + +/***************************************************************************** * LLEventQueue *****************************************************************************/ /** - * LLEventQueue isa LLEventPump whose post() method defers calling registered + * LLEventQueue is a LLEventPump whose post() method defers calling registered * listeners until flush() is called. */ class LL_COMMON_API LLEventQueue: public LLEventPump diff --git a/indra/llcommon/llmake.h b/indra/llcommon/llmake.h new file mode 100644 index 0000000000..9a662a0640 --- /dev/null +++ b/indra/llcommon/llmake.h @@ -0,0 +1,63 @@ +/** + * @file llmake.h + * @author Nat Goodspeed + * @date 2015-12-18 + * @brief Generic llmake<Template>(arg) function to instantiate + * Template<decltype(arg)>(arg). + * + * Many of our class templates have an accompanying helper function to + * make an instance with arguments of arbitrary type. llmake() + * eliminates the need to declare a new helper function for every such + * class template. + * + * also relevant: + * + * Template parameter deduction for constructors + * http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0091r0.html + * + * https://github.com/viboes/std-make + * + * but obviously we're not there yet. + * + * $LicenseInfo:firstyear=2015&license=viewerlgpl$ + * Copyright (c) 2015, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_LLMAKE_H) +#define LL_LLMAKE_H + +/*==========================================================================*| +// When we allow ourselves to compile with C++11 features enabled, this form +// should generically handle an arbitrary number of arguments. + +template <template<typename...> class CLASS_TEMPLATE, typename... ARGS> +CLASS_TEMPLATE<ARGS...> llmake(ARGS && ... args) +{ + return CLASS_TEMPLATE<ARGS...>(std::forward<ARGS>(args)...); +} +|*==========================================================================*/ + +// As of 2015-12-18, this is what we'll use instead. Add explicit overloads +// for different numbers of template parameters as use cases arise. + +/** + * Usage: llmake<SomeTemplate>(arg) + * + * Deduces the type T of 'arg' and returns an instance of SomeTemplate<T> + * initialized with 'arg'. Assumes a constructor accepting T (by value, + * reference or whatever). + */ +template <template<typename> class CLASS_TEMPLATE, typename ARG1> +CLASS_TEMPLATE<ARG1> llmake(const ARG1& arg1) +{ + return CLASS_TEMPLATE<ARG1>(arg1); +} + +template <template<typename, typename> class CLASS_TEMPLATE, typename ARG1, typename ARG2> +CLASS_TEMPLATE<ARG1, ARG2> llmake(const ARG1& arg1, const ARG2& arg2) +{ + return CLASS_TEMPLATE<ARG1, ARG2>(arg1, arg2); +} + +#endif /* ! defined(LL_LLMAKE_H) */ diff --git a/indra/llcommon/llprocess.cpp b/indra/llcommon/llprocess.cpp index e0aa30cc1a..44f56daf2d 100644 --- a/indra/llcommon/llprocess.cpp +++ b/indra/llcommon/llprocess.cpp @@ -710,7 +710,7 @@ LLProcess::LLProcess(const LLSDOrParams& params): // Tie the lifespan of this child process to the lifespan of our APR // pool: on destruction of the pool, forcibly kill the process. Tell - // APR to try SIGTERM and wait 3 seconds. If that didn't work, use + // APR to try SIGTERM and suspend 3 seconds. If that didn't work, use // SIGKILL. apr_pool_note_subprocess(gAPRPoolp, &mProcess, APR_KILL_AFTER_TIMEOUT); |*==========================================================================*/ @@ -989,9 +989,9 @@ void LLProcess::handle_status(int reason, int status) // wi->rv = apr_proc_wait(wi->child, &wi->rc, &wi->why, APR_NOWAIT); // It's just wrong to call apr_proc_wait() here. The only way APR knows to // call us with APR_OC_REASON_DEATH is that it's already reaped this child - // process, so calling wait() will only produce "huh?" from the OS. We + // process, so calling suspend() will only produce "huh?" from the OS. We // must rely on the status param passed in, which unfortunately comes - // straight from the OS wait() call, which means we have to decode it by + // straight from the OS suspend() call, which means we have to decode it by // hand. mStatus = interpret_status(status); LL_INFOS("LLProcess") << getStatusString() << LL_ENDL; diff --git a/indra/llcommon/llrefcount.h b/indra/llcommon/llrefcount.h index 3836a9b5fb..1107973569 100644 --- 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..8caaaee534 --- /dev/null +++ b/indra/llcommon/llsdjson.cpp @@ -0,0 +1,126 @@ +/** + * @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; +} + +//========================================================================= +Json::Value LlsdToJson(const LLSD &val) +{ + Json::Value result; + + switch (val.type()) + { + case LLSD::TypeUndefined: + result = Json::Value::null; + break; + case LLSD::TypeBoolean: + result = Json::Value(static_cast<bool>(val.asBoolean())); + break; + case LLSD::TypeInteger: + result = Json::Value(static_cast<int>(val.asInteger())); + break; + case LLSD::TypeReal: + result = Json::Value(static_cast<double>(val.asReal())); + break; + case LLSD::TypeURI: + case LLSD::TypeDate: + case LLSD::TypeUUID: + case LLSD::TypeString: + result = Json::Value(val.asString()); + break; + case LLSD::TypeMap: + result = Json::Value(Json::objectValue); + for (LLSD::map_const_iterator it = val.beginMap(); it != val.endMap(); ++it) + { + result[it->first] = LlsdToJson(it->second); + } + break; + case LLSD::TypeArray: + result = Json::Value(Json::arrayValue); + for (LLSD::array_const_iterator it = val.beginArray(); it != val.endArray(); ++it) + { + result.append(LlsdToJson(*it)); + } + break; + case LLSD::TypeBinary: + default: + LL_ERRS("LlsdToJson") << "Unsupported conversion to JSON from LLSD type (" << val.type() << ")." << LL_ENDL; + break; + } + + return result; +} diff --git a/indra/llcommon/llsdjson.h b/indra/llcommon/llsdjson.h new file mode 100644 index 0000000000..2be7112404 --- /dev/null +++ b/indra/llcommon/llsdjson.h @@ -0,0 +1,77 @@ +/** + * @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); + +/// Convert an LLSD object into Parsed JSON object maintaining member names and +/// array indexs. +/// +/// Types are converted as follows: +/// LLSD Type | JSON Type +/// --------------+---------------- +/// TypeUndefined | null +/// TypeBoolean | boolean +/// TypeInteger | integer +/// TypeReal | real/numeric +/// TypeString | string +/// TypeURI | string +/// TypeDate | string +/// TypeUUID | string +/// TypeMap | object +/// TypeArray | array +/// TypeBinary | unsupported +Json::Value LlsdToJson(const LLSD &val); + +#endif // LL_LLSDJSON_H diff --git a/indra/llcommon/llstring.h b/indra/llcommon/llstring.h index 0177f48bf5..393f6d7a8c 100644 --- 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..a459d17fb8 100644 --- a/indra/llcommon/tests/lleventcoro_test.cpp +++ b/indra/llcommon/tests/lleventcoro_test.cpp @@ -59,17 +59,17 @@ // 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. #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 <boost/shared_ptr.hpp> #include "linden_common.h" @@ -82,9 +82,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,13 +124,10 @@ 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 -/// waitForEventOn(). +/// distinguishes postAndSuspend() from calling post(), then calling +/// suspendUntilEventOn(). class ImmediateAPI { public: @@ -162,306 +162,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,54 +212,122 @@ 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, stringdata; + LLSD result, errordata; + int which; + + // reinit vars at the start of each test + void clear() + { + replyName.clear(); + errorName.clear(); + threw.clear(); + stringdata.clear(); + result = LLSD(); + errordata = LLSD(); + which = 0; + } + + void explicit_wait(boost::shared_ptr<LLCoros::Future<std::string>::callback_t>& cbp) + { + BEGIN + { + // The point of this test is to verify / illustrate suspending a + // coroutine for something other than an LLEventPump. In other + // words, this shows how to adapt to any async operation that + // provides a callback-style notification (and prove that it + // works). + + LLCoros::Future<std::string> future; + // get the callback from that future + LLCoros::Future<std::string>::callback_t callback(future.make_callback()); + + // Perhaps we would send a request to a remote server and arrange + // for 'callback' to be called on response. Of course that might + // involve an adapter object from the actual callback signature to + // the signature of 'callback' -- in this case, void(std::string). + // For test purposes, instead of handing 'callback' (or the + // adapter) off to some I/O subsystem, we'll just pass it back to + // our caller. + cbp.reset(new LLCoros::Future<std::string>::callback_t(callback)); + + ensure("Not yet", ! future); + // calling get() on the future causes us to suspend + debug("about to suspend"); + stringdata = future.get(); + ensure("Got it", bool(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)); - // Start the coroutine - coro(std::nothrow); - // When the coroutine waits for the event pump, it returns here. - debug("about to send"); - // Satisfy the wait. - LLEventPumps::instance().obtain("source").post("received"); - // Now wait for the coroutine to complete. - ensure_done(coro); + boost::shared_ptr<LLCoros::Future<std::string>::callback_t> respond; + LLCoros::instance().launch("test<2>", + boost::bind(explicit_wait, boost::ref(respond))); + // When the coroutine waits for the future, it returns here. + debug("about to respond"); + // Now we're the I/O subsystem delivering a result. This immediately + // transfers control back to the coroutine. + (*respond)("received"); // ensure the coroutine ran and woke up again with the intended result - ensure_equals(result.asString(), "received"); + ensure_equals(stringdata, "received"); + } + + void waitForEventOn1() + { + BEGIN + { + result = suspendUntilEventOn("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 = suspendUntilEventOn("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 +336,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.suspend(); + } + 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.suspend()); + 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 +402,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.suspendWithException(); + } + 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.suspendWithException(); + 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.suspendWithLog(); + } + 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.suspendWithLog(); + 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 = postAndSuspend(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 = ::postAndSuspend2(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 = ::postAndSuspend2(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.postAndSuspend(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.postAndSuspend(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.postAndSuspend(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.postAndSuspendWithException(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.postAndSuspendWithException( + 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.postAndSuspendWithLog(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.postAndSuspendWithLog( + 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 ed61012bdb..0bb0348d26 100644 --- a/indra/llcorehttp/CMakeLists.txt +++ b/indra/llcorehttp/CMakeLists.txt @@ -5,7 +5,6 @@ project(llcorehttp) include(00-Common) include(GoogleMock) include(CURL) -include(CARes) include(OpenSSL) include(ZLIB) include(LLCoreHttp) @@ -26,6 +25,7 @@ set(llcorehttp_SOURCE_FILES bufferarray.cpp bufferstream.cpp httpcommon.cpp + llhttpconstants.cpp httpheaders.cpp httpoptions.cpp httprequest.cpp @@ -51,6 +51,7 @@ set(llcorehttp_HEADER_FILES bufferarray.h bufferstream.h httpcommon.h + llhttpconstants.h httphandler.h httpheaders.h httpoptions.h @@ -89,7 +90,6 @@ add_library (llcorehttp ${llcorehttp_SOURCE_FILES}) target_link_libraries( llcorehttp ${CURL_LIBRARIES} - ${CARES_LIBRARIES} ${OPENSSL_LIBRARIES} ${CRYPTO_LIBRARIES} ${BOOST_THREAD_LIBRARY} @@ -127,7 +127,6 @@ if (LL_TESTS) ${LLCOMMON_LIBRARIES} ${GOOGLEMOCK_LIBRARIES} ${CURL_LIBRARIES} - ${CARES_LIBRARIES} ${OPENSSL_LIBRARIES} ${CRYPTO_LIBRARIES} ${BOOST_SYSTEM_LIBRARY} @@ -196,7 +195,6 @@ endif (DARWIN) ${LLCOMMON_LIBRARIES} ${GOOGLEMOCK_LIBRARIES} ${CURL_LIBRARIES} - ${CARES_LIBRARIES} ${OPENSSL_LIBRARIES} ${CRYPTO_LIBRARIES} ${BOOST_SYSTEM_LIBRARY} diff --git a/indra/llcorehttp/_httpinternal.h b/indra/llcorehttp/_httpinternal.h index a2a60ca056..79c89d6c92 100644 --- 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..c25e01a318 100644 --- a/indra/llcorehttp/_httplibcurl.cpp +++ b/indra/llcorehttp/_httplibcurl.cpp @@ -71,11 +71,10 @@ void HttpLibcurl::shutdown() { while (! mActiveOps.empty()) { - HttpOpRequest * op(* mActiveOps.begin()); + HttpOpRequest::ptr_t op(* mActiveOps.begin()); mActiveOps.erase(mActiveOps.begin()); cancelRequest(op); - op->release(); } if (mMultiHandles) @@ -204,7 +203,7 @@ HttpService::ELoopSpeed HttpLibcurl::processTransport() // Caller has provided us with a ref count on op. -void HttpLibcurl::addOp(HttpOpRequest * op) +void HttpLibcurl::addOp(const HttpOpRequest::ptr_t &op) { llassert_always(op->mReqPolicy < mPolicyCount); llassert_always(mMultiHandles[op->mReqPolicy] != NULL); @@ -235,21 +234,21 @@ void HttpLibcurl::addOp(HttpOpRequest * op) HttpPolicy & policy(mService->getPolicy()); LL_INFOS(LOG_CORE) << "TRACE, ToActiveQueue, Handle: " - << static_cast<HttpHandle>(op) - << ", Actives: " << mActiveOps.size() - << ", Readies: " << policy.getReadyCount(op->mReqPolicy) - << LL_ENDL; + << op->getHandle() + << ", Actives: " << mActiveOps.size() + << ", Readies: " << policy.getReadyCount(op->mReqPolicy) + << LL_ENDL; } } // Implements the transport part of any cancel operation. // See if the handle is an active operation and if so, -// use the more complicated transport-based cancelation +// use the more complicated transport-based cancellation // method to kill the request. bool HttpLibcurl::cancel(HttpHandle handle) { - HttpOpRequest * op(static_cast<HttpOpRequest *>(handle)); + HttpOpRequest::ptr_t op = HttpOpRequest::fromHandle<HttpOpRequest>(handle); active_set_t::iterator it(mActiveOps.find(op)); if (mActiveOps.end() == it) { @@ -262,7 +261,6 @@ bool HttpLibcurl::cancel(HttpHandle handle) // Drop references mActiveOps.erase(it); --mActiveHandles[op->mReqPolicy]; - op->release(); return true; } @@ -273,7 +271,7 @@ bool HttpLibcurl::cancel(HttpHandle handle) // remove the op from the active list and release the op *after* // calling this method. It must be called first to deliver the // op to the reply queue with refcount intact. -void HttpLibcurl::cancelRequest(HttpOpRequest * op) +void HttpLibcurl::cancelRequest(const HttpOpRequest::ptr_t &op) { // Deactivate request op->mCurlActive = false; @@ -287,7 +285,7 @@ void HttpLibcurl::cancelRequest(HttpOpRequest * op) if (op->mTracing > HTTP_TRACE_OFF) { LL_INFOS(LOG_CORE) << "TRACE, RequestCanceled, Handle: " - << static_cast<HttpHandle>(op) + << op->getHandle() << ", Status: " << op->mStatus.toTerseString() << LL_ENDL; } @@ -301,8 +299,23 @@ void HttpLibcurl::cancelRequest(HttpOpRequest * op) // Keep them synchronized as necessary. bool HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode status) { - HttpOpRequest * op(NULL); - curl_easy_getinfo(handle, CURLINFO_PRIVATE, &op); + HttpHandle ophandle(NULL); + + CURLcode ccode(CURLE_OK); + + ccode = curl_easy_getinfo(handle, CURLINFO_PRIVATE, &ophandle); + if (ccode) + { + LL_WARNS(LOG_CORE) << "libcurl error: " << ccode << " Unable to retrieve operation handle from CURL handle" << LL_ENDL; + return false; + } + HttpOpRequest::ptr_t op(HttpOpRequest::fromHandle<HttpOpRequest>(ophandle)); + + if (!op) + { + LL_WARNS() << "Unable to locate operation by handle. May have expired!" << LL_ENDL; + return false; + } if (handle != op->mCurlHandle || ! op->mCurlActive) { @@ -331,42 +344,72 @@ bool HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode { op->mStatus = HttpStatus(HttpStatus::EXT_CURL_EASY, status); } - if (op->mStatus) - { - int http_status(HTTP_OK); - - curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &http_status); - if (http_status >= 100 && http_status <= 999) - { - char * cont_type(NULL); - curl_easy_getinfo(handle, CURLINFO_CONTENT_TYPE, &cont_type); - if (cont_type) - { - op->mReplyConType = cont_type; - } - op->mStatus = HttpStatus(http_status); - } - else - { - LL_WARNS(LOG_CORE) << "Invalid HTTP response code (" - << http_status << ") received from server." - << LL_ENDL; - op->mStatus = HttpStatus(HttpStatus::LLCORE, HE_INVALID_HTTP_STATUS); - } + if (op->mStatus) + { + int http_status(HTTP_OK); + + if (handle) + { + ccode = curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &http_status); + if (ccode == CURLE_OK) + { + if (http_status >= 100 && http_status <= 999) + { + char * cont_type(NULL); + ccode = curl_easy_getinfo(handle, CURLINFO_CONTENT_TYPE, &cont_type); + if (ccode == CURLE_OK) + { + if (cont_type) + { + op->mReplyConType = cont_type; + } + } + else + { + LL_WARNS(LOG_CORE) << "CURL error:" << ccode << " Attempting to get content type." << LL_ENDL; + } + op->mStatus = HttpStatus(http_status); + } + else + { + LL_WARNS(LOG_CORE) << "Invalid HTTP response code (" + << http_status << ") received from server." + << LL_ENDL; + op->mStatus = HttpStatus(HttpStatus::LLCORE, HE_INVALID_HTTP_STATUS); + } + } + else + { + op->mStatus = HttpStatus(HttpStatus::LLCORE, HE_INVALID_HTTP_STATUS); + } + } + else + { + LL_WARNS(LOG_CORE) << "Attempt to retrieve status from NULL handle!" << LL_ENDL; + } } - // Detach from multi and recycle handle - curl_multi_remove_handle(multi_handle, handle); - mHandleCache.freeHandle(op->mCurlHandle); - op->mCurlHandle = NULL; + if (multi_handle && handle) + { + // Detach from multi and recycle handle + curl_multi_remove_handle(multi_handle, handle); + mHandleCache.freeHandle(op->mCurlHandle); + } + else + { + LL_WARNS(LOG_CORE) << "Curl multi_handle or handle is NULL on remove! multi:" + << std::hex << multi_handle << " h:" << std::hex << handle << std::dec << LL_ENDL; + } + + op->mCurlHandle = NULL; // Tracing if (op->mTracing > HTTP_TRACE_OFF) { LL_INFOS(LOG_CORE) << "TRACE, RequestComplete, Handle: " - << static_cast<HttpHandle>(op) - << ", Status: " << op->mStatus.toTerseString() - << LL_ENDL; + << op->getHandle() + << ", Status: " << op->mStatus.toTerseString() + << LL_ENDL; } // Dispatch to next stage @@ -554,7 +597,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/_httplibcurl.h b/indra/llcorehttp/_httplibcurl.h index ffc24c63a8..a71eae59c0 100644 --- a/indra/llcorehttp/_httplibcurl.h +++ b/indra/llcorehttp/_httplibcurl.h @@ -65,6 +65,8 @@ private: void operator=(const HttpLibcurl &); // Not defined public: + typedef boost::shared_ptr<HttpOpRequest> opReqPtr_t; + /// Give cycles to libcurl to run active requests. Completed /// operations (successful or failed) will be retried or handed /// over to the reply queue as final responses. @@ -80,7 +82,7 @@ public: /// request. (No additional references will be added.) /// /// Threading: called by worker thread. - void addOp(HttpOpRequest * op); + void addOp(const opReqPtr_t & op); /// One-time call to set the number of policy classes to be /// serviced and to create the resources for each. Value @@ -148,10 +150,10 @@ protected: /// Invoked to cancel an active request, mainly during shutdown /// and destroy. - void cancelRequest(HttpOpRequest * op); + void cancelRequest(const opReqPtr_t &op); protected: - typedef std::set<HttpOpRequest *> active_set_t; + typedef std::set<opReqPtr_t> active_set_t; /// Simple request handle cache for libcurl. /// diff --git a/indra/llcorehttp/_httpopcancel.h b/indra/llcorehttp/_httpopcancel.h index 336dfdc573..86944eb159 100644 --- a/indra/llcorehttp/_httpopcancel.h +++ b/indra/llcorehttp/_httpopcancel.h @@ -56,13 +56,8 @@ public: /// be canceled. HttpOpCancel(HttpHandle handle); -protected: virtual ~HttpOpCancel(); // Use release() -private: - HttpOpCancel(const HttpOpCancel &); // Not defined - void operator=(const HttpOpCancel &); // Not defined - public: virtual void stageFromRequest(HttpService *); diff --git a/indra/llcorehttp/_httpoperation.cpp b/indra/llcorehttp/_httpoperation.cpp index fefe561f80..3fc4e28910 100644 --- a/indra/llcorehttp/_httpoperation.cpp +++ b/indra/llcorehttp/_httpoperation.cpp @@ -53,15 +53,18 @@ namespace LLCore // ================================== // HttpOperation // ================================== - - -HttpOperation::HttpOperation() - : LLCoreInt::RefCounted(true), - mReplyQueue(NULL), - mUserHandler(NULL), - mReqPolicy(HttpRequest::DEFAULT_POLICY_ID), - mReqPriority(0U), - mTracing(HTTP_TRACE_OFF) +/*static*/ +HttpOperation::handleMap_t HttpOperation::mHandleMap; +LLCoreInt::HttpMutex HttpOperation::mOpMutex; + +HttpOperation::HttpOperation(): + boost::enable_shared_from_this<HttpOperation>(), + mReplyQueue(), + mUserHandler(), + mReqPolicy(HttpRequest::DEFAULT_POLICY_ID), + mReqPriority(0U), + mTracing(HTTP_TRACE_OFF), + mMyHandle(LLCORE_HTTP_HANDLE_INVALID) { mMetricCreated = totalTime(); } @@ -69,30 +72,17 @@ HttpOperation::HttpOperation() HttpOperation::~HttpOperation() { - setReplyPath(NULL, NULL); + destroyHandle(); + mReplyQueue.reset(); + mUserHandler.reset(); } -void HttpOperation::setReplyPath(HttpReplyQueue * reply_queue, - HttpHandler * user_handler) +void HttpOperation::setReplyPath(HttpReplyQueue::ptr_t reply_queue, + HttpHandler::ptr_t user_handler) { - if (reply_queue != mReplyQueue) - { - if (mReplyQueue) - { - mReplyQueue->release(); - } - - if (reply_queue) - { - reply_queue->addRef(); - } - - mReplyQueue = reply_queue; - } - - // Not refcounted - mUserHandler = user_handler; + mReplyQueue.swap(reply_queue); + mUserHandler.swap(user_handler); } @@ -134,7 +124,7 @@ void HttpOperation::visitNotifier(HttpRequest *) HttpResponse * response = new HttpResponse(); response->setStatus(mStatus); - mUserHandler->onCompleted(static_cast<HttpHandle>(this), response); + mUserHandler->onCompleted(getHandle(), response); response->release(); } @@ -148,20 +138,83 @@ HttpStatus HttpOperation::cancel() return status; } +// Handle methods +HttpHandle HttpOperation::getHandle() +{ + if (mMyHandle == LLCORE_HTTP_HANDLE_INVALID) + return createHandle(); + + return mMyHandle; +} + +HttpHandle HttpOperation::createHandle() +{ + HttpHandle handle = static_cast<HttpHandle>(this); + + { + LLCoreInt::HttpScopedLock lock(mOpMutex); + + mHandleMap[handle] = shared_from_this(); + mMyHandle = handle; + } + + return mMyHandle; +} + +void HttpOperation::destroyHandle() +{ + if (mMyHandle == LLCORE_HTTP_HANDLE_INVALID) + return; + { + LLCoreInt::HttpScopedLock lock(mOpMutex); + + handleMap_t::iterator it = mHandleMap.find(mMyHandle); + if (it != mHandleMap.end()) + mHandleMap.erase(it); + } +} + +/*static*/ +HttpOperation::ptr_t HttpOperation::findByHandle(HttpHandle handle) +{ + wptr_t weak; + + if (!handle) + return ptr_t(); + + { + LLCoreInt::HttpScopedLock lock(mOpMutex); + + handleMap_t::iterator it = mHandleMap.find(handle); + if (it == mHandleMap.end()) + { + LL_WARNS("LLCore::HTTP") << "Could not find operation for handle " << handle << LL_ENDL; + return ptr_t(); + } + + weak = (*it).second; + } + + if (!weak.expired()) + return weak.lock(); + + return ptr_t(); +} + void HttpOperation::addAsReply() { if (mTracing > HTTP_TRACE_OFF) { LL_INFOS(LOG_CORE) << "TRACE, ToReplyQueue, Handle: " - << static_cast<HttpHandle>(this) + << getHandle() << LL_ENDL; } if (mReplyQueue) { - addRef(); - mReplyQueue->addOp(this); + HttpOperation::ptr_t op = shared_from_this(); + mReplyQueue->addOp(op); } } @@ -244,11 +297,8 @@ void HttpOpSpin::stageFromRequest(HttpService * service) else { ms_sleep(1); // backoff interlock plumbing a bit - this->addRef(); - if (! service->getRequestQueue().addOp(this)) - { - this->release(); - } + HttpOperation::ptr_t opptr = shared_from_this(); + service->getRequestQueue().addOp(opptr); } } diff --git a/indra/llcorehttp/_httpoperation.h b/indra/llcorehttp/_httpoperation.h index 937a61187d..1a75921c09 100644 --- a/indra/llcorehttp/_httpoperation.h +++ b/indra/llcorehttp/_httpoperation.h @@ -30,8 +30,7 @@ #include "httpcommon.h" #include "httprequest.h" -#include "_refcounted.h" - +#include "_mutex.h" namespace LLCore { @@ -69,19 +68,20 @@ class HttpService; /// via queue-like interfaces that are thread compatible /// and those interfaces establish the access rules. -class HttpOperation : public LLCoreInt::RefCounted +class HttpOperation : private boost::noncopyable, + public boost::enable_shared_from_this<HttpOperation> { public: + typedef boost::shared_ptr<HttpOperation> ptr_t; + typedef boost::weak_ptr<HttpOperation> wptr_t; + typedef boost::shared_ptr<HttpReplyQueue> HttpReplyQueuePtr_t; + /// Threading: called by consumer thread. HttpOperation(); -protected: /// Threading: called by any thread. virtual ~HttpOperation(); // Use release() -private: - HttpOperation(const HttpOperation &); // Not defined - void operator=(const HttpOperation &); // Not defined public: /// Register a reply queue and a handler for completion notifications. @@ -110,8 +110,8 @@ public: /// /// Threading: called by consumer thread. /// - void setReplyPath(HttpReplyQueue * reply_queue, - HttpHandler * handler); + void setReplyPath(HttpReplyQueuePtr_t reply_queue, + HttpHandler::ptr_t handler); /// The three possible staging steps in an operation's lifecycle. /// Asynchronous requests like HTTP operations move from the @@ -152,6 +152,18 @@ public: /// Threading: called by worker thread. /// virtual HttpStatus cancel(); + + /// Retrieves a unique handle for this operation. + HttpHandle getHandle(); + + template< class OPT > + static boost::shared_ptr< OPT > fromHandle(HttpHandle handle) + { + ptr_t ptr = findByHandle(handle); + if (!ptr) + return boost::shared_ptr< OPT >(); + return boost::dynamic_pointer_cast< OPT >(ptr); + } protected: /// Delivers request to reply queue on completion. After this @@ -163,8 +175,8 @@ protected: void addAsReply(); protected: - HttpReplyQueue * mReplyQueue; // Have refcount - HttpHandler * mUserHandler; // Naked pointer + HttpReplyQueuePtr_t mReplyQueue; + HttpHandler::ptr_t mUserHandler; public: // Request Data @@ -177,6 +189,21 @@ public: // Tracing, debug and metrics HttpTime mMetricCreated; int mTracing; + +private: + typedef std::map<HttpHandle, wptr_t> handleMap_t; + + HttpHandle createHandle(); + void destroyHandle(); + HttpHandle mMyHandle; + + static handleMap_t mHandleMap; + static LLCoreInt::HttpMutex mOpMutex; + +protected: + static ptr_t findByHandle(HttpHandle handle); + + }; // end class HttpOperation @@ -195,7 +222,6 @@ class HttpOpStop : public HttpOperation public: HttpOpStop(); -protected: virtual ~HttpOpStop(); private: @@ -218,7 +244,6 @@ class HttpOpNull : public HttpOperation public: HttpOpNull(); -protected: virtual ~HttpOpNull(); private: @@ -241,7 +266,6 @@ public: // 1 does a soft spin continuously requeuing itself HttpOpSpin(int mode); -protected: virtual ~HttpOpSpin(); private: diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp index b9632a7921..db57869a1b 100644 --- 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,25 +182,20 @@ HttpOpRequest::~HttpOpRequest() mReplyBody = NULL; } - if (mReplyHeaders) - { - mReplyHeaders->release(); - mReplyHeaders = NULL; - } } void HttpOpRequest::stageFromRequest(HttpService * service) { - addRef(); - service->getPolicy().addOp(this); // transfers refcount + HttpOpRequest::ptr_t self(boost::dynamic_pointer_cast<HttpOpRequest>(shared_from_this())); + service->getPolicy().addOp(self); // transfers refcount } void HttpOpRequest::stageFromReady(HttpService * service) { - addRef(); - service->getTransport().addOp(this); // transfers refcount + HttpOpRequest::ptr_t self(boost::dynamic_pointer_cast<HttpOpRequest>(shared_from_this())); + service->getTransport().addOp(self); // transfers refcount } @@ -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,12 +253,27 @@ void HttpOpRequest::visitNotifier(HttpRequest * request) response->setContentType(mReplyConType); response->setRetries(mPolicyRetries, mPolicy503Retries); - mUserHandler->onCompleted(static_cast<HttpHandle>(this), response); + 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(this->getHandle(), response); response->release(); } } +// /*static*/ +// HttpOpRequest::ptr_t HttpOpRequest::fromHandle(HttpHandle handle) +// { +// +// return boost::dynamic_pointer_cast<HttpOpRequest>((static_cast<HttpOpRequest *>(handle))->shared_from_this()); +// } + HttpStatus HttpOpRequest::cancel() { @@ -287,8 +288,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 +303,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 +323,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 +337,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 +347,65 @@ 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(); +} + + +HttpStatus HttpOpRequest::setupMove(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_MOVE; + + 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 +418,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 +468,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 @@ -447,38 +495,74 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) check_curl_easy_code(code, CURLOPT_NOPROGRESS); code = curl_easy_setopt(mCurlHandle, CURLOPT_URL, mReqURL.c_str()); check_curl_easy_code(code, CURLOPT_URL); - code = curl_easy_setopt(mCurlHandle, CURLOPT_PRIVATE, this); + code = curl_easy_setopt(mCurlHandle, CURLOPT_PRIVATE, getHandle()); check_curl_easy_code(code, CURLOPT_PRIVATE); 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); check_curl_easy_code(code, CURLOPT_WRITEFUNCTION); - code = curl_easy_setopt(mCurlHandle, CURLOPT_WRITEDATA, this); + code = curl_easy_setopt(mCurlHandle, CURLOPT_WRITEDATA, getHandle()); check_curl_easy_code(code, CURLOPT_WRITEDATA); code = curl_easy_setopt(mCurlHandle, CURLOPT_READFUNCTION, readCallback); check_curl_easy_code(code, CURLOPT_READFUNCTION); - code = curl_easy_setopt(mCurlHandle, CURLOPT_READDATA, this); + code = curl_easy_setopt(mCurlHandle, CURLOPT_READDATA, getHandle()); 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, getHandle()); + 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, getHandle()); + 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 +593,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 +614,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 +632,25 @@ 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; + + case HOR_MOVE: + code = curl_easy_setopt(mCurlHandle, CURLOPT_CUSTOMREQUEST, "MOVE"); + 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 +658,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) { @@ -650,7 +750,11 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) xfer_timeout *= 2L; } // *DEBUG: Enable following override for timeout handling and "[curl:bugs] #1420" tests - // xfer_timeout = 1L; + //if (cpolicy.mPipelining) + //{ + // xfer_timeout = 1L; + // timeout = 1L; + //} code = curl_easy_setopt(mCurlHandle, CURLOPT_TIMEOUT, xfer_timeout); check_curl_easy_code(code, CURLOPT_TIMEOUT); code = curl_easy_setopt(mCurlHandle, CURLOPT_CONNECTTIMEOUT, timeout); @@ -683,7 +787,7 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) size_t HttpOpRequest::writeCallback(void * data, size_t size, size_t nmemb, void * userdata) { - HttpOpRequest * op(static_cast<HttpOpRequest *>(userdata)); + HttpOpRequest::ptr_t op(HttpOpRequest::fromHandle<HttpOpRequest>(userdata)); if (! op->mReplyBody) { @@ -697,7 +801,7 @@ size_t HttpOpRequest::writeCallback(void * data, size_t size, size_t nmemb, void size_t HttpOpRequest::readCallback(void * data, size_t size, size_t nmemb, void * userdata) { - HttpOpRequest * op(static_cast<HttpOpRequest *>(userdata)); + HttpOpRequest::ptr_t op(HttpOpRequest::fromHandle<HttpOpRequest>(userdata)); if (! op->mReqBody) { @@ -723,6 +827,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::ptr_t op(HttpOpRequest::fromHandle<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) { @@ -731,7 +866,7 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi static const char con_ran_line[] = "content-range"; static const char con_retry_line[] = "retry-after"; - HttpOpRequest * op(static_cast<HttpOpRequest *>(userdata)); + HttpOpRequest::ptr_t op(HttpOpRequest::fromHandle<HttpOpRequest>(userdata)); const size_t hdr_size(size * nmemb); const char * hdr_data(static_cast<const char *>(data)); // Not null terminated @@ -817,7 +952,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,9 +1008,38 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi } +CURLcode HttpOpRequest::curlSslCtxCallback(CURL *curl, void *sslctx, void *userdata) +{ + HttpOpRequest::ptr_t op(HttpOpRequest::fromHandle<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::ptr_t op(HttpOpRequest::fromHandle<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)); + HttpOpRequest::ptr_t op(HttpOpRequest::fromHandle<HttpOpRequest>(userdata)); std::string safe_line; std::string tag; @@ -955,7 +1119,7 @@ int HttpOpRequest::debugCallback(CURL * handle, curl_infotype info, char * buffe if (logit) { LL_INFOS(LOG_CORE) << "TRACE, LibcurlDebug, Handle: " - << static_cast<HttpHandle>(op) + << op->getHandle() << ", Type: " << tag << ", Data: " << safe_line << LL_ENDL; diff --git a/indra/llcorehttp/_httpoprequest.h b/indra/llcorehttp/_httpoprequest.h index 2f628b5aba..dbcc57d0fd 100644 --- 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 @@ -63,9 +66,10 @@ class HttpOptions; class HttpOpRequest : public HttpOperation { public: + typedef boost::shared_ptr<HttpOpRequest> ptr_t; + HttpOpRequest(); -protected: virtual ~HttpOpRequest(); // Use release() private: @@ -77,7 +81,11 @@ public: { HOR_GET, HOR_POST, - HOR_PUT + HOR_PUT, + HOR_DELETE, + HOR_PATCH, + HOR_COPY, + HOR_MOVE }; virtual void stageFromRequest(HttpService *); @@ -98,32 +106,57 @@ 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); + 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); - // Internal method used to setup the libcurl options for a request. + 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); + + HttpStatus setupMove(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 +174,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 +183,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 +196,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 +205,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 +223,7 @@ public: off_t mReplyOffset; size_t mReplyLength; size_t mReplyFullLength; - HttpHeaders * mReplyHeaders; + HttpHeaders::ptr_t mReplyHeaders; std::string mReplyConType; int mReplyRetryAfter; @@ -215,7 +254,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/_httpopsetget.h b/indra/llcorehttp/_httpopsetget.h index a1e76dd429..eabd41e79f 100644 --- a/indra/llcorehttp/_httpopsetget.h +++ b/indra/llcorehttp/_httpopsetget.h @@ -53,9 +53,10 @@ namespace LLCore class HttpOpSetGet : public HttpOperation { public: + typedef boost::shared_ptr<HttpOpSetGet> ptr_t; + HttpOpSetGet(); -protected: virtual ~HttpOpSetGet(); // Use release() private: diff --git a/indra/llcorehttp/_httpopsetpriority.h b/indra/llcorehttp/_httpopsetpriority.h index 31706b737c..43e2aa081b 100644 --- a/indra/llcorehttp/_httpopsetpriority.h +++ b/indra/llcorehttp/_httpopsetpriority.h @@ -51,7 +51,6 @@ class HttpOpSetPriority : public HttpOperation public: HttpOpSetPriority(HttpHandle handle, HttpRequest::priority_t priority); -protected: virtual ~HttpOpSetPriority(); private: diff --git a/indra/llcorehttp/_httppolicy.cpp b/indra/llcorehttp/_httppolicy.cpp index e5d6321401..b2709b53ec 100644 --- a/indra/llcorehttp/_httppolicy.cpp +++ b/indra/llcorehttp/_httppolicy.cpp @@ -116,21 +116,19 @@ void HttpPolicy::shutdown() HttpRetryQueue & retryq(state.mRetryQueue); while (! retryq.empty()) { - HttpOpRequest * op(retryq.top()); + HttpOpRequest::ptr_t op(retryq.top()); retryq.pop(); op->cancel(); - op->release(); } HttpReadyQueue & readyq(state.mReadyQueue); while (! readyq.empty()) { - HttpOpRequest * op(readyq.top()); + HttpOpRequest::ptr_t op(readyq.top()); readyq.pop(); op->cancel(); - op->release(); } } } @@ -141,7 +139,7 @@ void HttpPolicy::start() } -void HttpPolicy::addOp(HttpOpRequest * op) +void HttpPolicy::addOp(const HttpOpRequest::ptr_t &op) { const int policy_class(op->mReqPolicy); @@ -151,7 +149,7 @@ void HttpPolicy::addOp(HttpOpRequest * op) } -void HttpPolicy::retryOp(HttpOpRequest * op) +void HttpPolicy::retryOp(const HttpOpRequest::ptr_t &op) { static const HttpTime retry_deltas[] = { @@ -180,7 +178,7 @@ void HttpPolicy::retryOp(HttpOpRequest * op) { ++op->mPolicy503Retries; } - LL_DEBUGS(LOG_CORE) << "HTTP request " << static_cast<HttpHandle>(op) + LL_DEBUGS(LOG_CORE) << "HTTP request " << op->getHandle() << " retry " << op->mPolicyRetries << " scheduled in " << (delta / HttpTime(1000)) << " mS (" << (external_delta ? "external" : "internal") @@ -189,10 +187,10 @@ void HttpPolicy::retryOp(HttpOpRequest * op) if (op->mTracing > HTTP_TRACE_OFF) { LL_INFOS(LOG_CORE) << "TRACE, ToRetryQueue, Handle: " - << static_cast<HttpHandle>(op) - << ", Delta: " << (delta / HttpTime(1000)) - << ", Retries: " << op->mPolicyRetries - << LL_ENDL; + << op->getHandle() + << ", Delta: " << (delta / HttpTime(1000)) + << ", Retries: " << op->mPolicyRetries + << LL_ENDL; } mClasses[policy_class]->mRetryQueue.push(op); } @@ -264,14 +262,14 @@ HttpService::ELoopSpeed HttpPolicy::processReadyQueue() // First see if we have any retries... while (needed > 0 && ! retryq.empty()) { - HttpOpRequest * op(retryq.top()); + HttpOpRequest::ptr_t op(retryq.top()); if (op->mPolicyRetryAt > now) break; retryq.pop(); op->stageFromReady(mService); - op->release(); + op.reset(); ++state.mRequestCount; --needed; @@ -296,11 +294,11 @@ HttpService::ELoopSpeed HttpPolicy::processReadyQueue() // Now go on to the new requests... while (needed > 0 && ! readyq.empty()) { - HttpOpRequest * op(readyq.top()); + HttpOpRequest::ptr_t op(readyq.top()); readyq.pop(); op->stageFromReady(mService); - op->release(); + op.reset(); ++state.mRequestCount; --needed; @@ -351,9 +349,9 @@ bool HttpPolicy::changePriority(HttpHandle handle, HttpRequest::priority_t prior { HttpReadyQueue::container_type::iterator cur(iter++); - if (static_cast<HttpHandle>(*cur) == handle) + if ((*cur)->getHandle() == handle) { - HttpOpRequest * op(*cur); + HttpOpRequest::ptr_t op(*cur); c.erase(cur); // All iterators are now invalidated op->mReqPriority = priority; state.mReadyQueue.push(op); // Re-insert using adapter class @@ -378,12 +376,11 @@ bool HttpPolicy::cancel(HttpHandle handle) { HttpRetryQueue::container_type::iterator cur(iter++); - if (static_cast<HttpHandle>(*cur) == handle) + if ((*cur)->getHandle() == handle) { - HttpOpRequest * op(*cur); + HttpOpRequest::ptr_t op(*cur); c1.erase(cur); // All iterators are now invalidated op->cancel(); - op->release(); return true; } } @@ -394,12 +391,11 @@ bool HttpPolicy::cancel(HttpHandle handle) { HttpReadyQueue::container_type::iterator cur(iter++); - if (static_cast<HttpHandle>(*cur) == handle) + if ((*cur)->getHandle() == handle) { - HttpOpRequest * op(*cur); + HttpOpRequest::ptr_t op(*cur); c2.erase(cur); // All iterators are now invalidated op->cancel(); - op->release(); return true; } } @@ -409,7 +405,7 @@ bool HttpPolicy::cancel(HttpHandle handle) } -bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op) +bool HttpPolicy::stageAfterCompletion(const HttpOpRequest::ptr_t &op) { // Retry or finalize if (! op->mStatus) @@ -420,7 +416,7 @@ bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op) #if 0 if (op->mStatus == HttpStatus(HttpStatus::EXT_CURL_EASY, CURLE_OPERATION_TIMEDOUT)) { - LL_WARNS(LOG_CORE) << "HTTP request " << static_cast<HttpHandle>(op) + LL_WARNS(LOG_CORE) << "HTTP request " << op->getHandle() << " timed out." << LL_ENDL; } @@ -438,7 +434,7 @@ bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op) // This op is done, finalize it delivering it to the reply queue... if (! op->mStatus) { - LL_WARNS(LOG_CORE) << "HTTP request " << static_cast<HttpHandle>(op) + LL_WARNS(LOG_CORE) << "HTTP request " << op->getHandle() << " failed after " << op->mPolicyRetries << " retries. Reason: " << op->mStatus.toString() << " (" << op->mStatus.toTerseString() << ")" @@ -446,13 +442,12 @@ bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op) } else if (op->mPolicyRetries) { - LL_DEBUGS(LOG_CORE) << "HTTP request " << static_cast<HttpHandle>(op) + LL_DEBUGS(LOG_CORE) << "HTTP request " << op->getHandle() << " succeeded on retry " << op->mPolicyRetries << "." << LL_ENDL; } op->stageFromActive(mService); - op->release(); return false; // not active } diff --git a/indra/llcorehttp/_httppolicy.h b/indra/llcorehttp/_httppolicy.h index 11cd89bbd1..3c4126e14b 100644 --- a/indra/llcorehttp/_httppolicy.h +++ b/indra/llcorehttp/_httppolicy.h @@ -60,6 +60,8 @@ private: void operator=(const HttpPolicy &); // Not defined public: + typedef boost::shared_ptr<HttpOpRequest> opReqPtr_t; + /// Threading: called by init thread. HttpRequest::policy_t createPolicyClass(); @@ -96,7 +98,7 @@ public: /// from queue. /// /// Threading: called by worker thread - void addOp(HttpOpRequest *); + void addOp(const opReqPtr_t &); /// Similar to addOp, used when a caller wants to retry a /// request that has failed. It's placed on a special retry @@ -106,7 +108,7 @@ public: /// order. /// /// Threading: called by worker thread - void retryOp(HttpOpRequest *); + void retryOp(const opReqPtr_t &); /// Attempt to change the priority of an earlier request. /// Request that Shadows HttpService's method @@ -130,7 +132,7 @@ public: /// sent on to the reply queue. /// /// Threading: called by worker thread - bool stageAfterCompletion(HttpOpRequest * op); + bool stageAfterCompletion(const opReqPtr_t &op); /// Get a reference to global policy options. Caller is expected /// to do context checks like no setting once running. These diff --git a/indra/llcorehttp/_httppolicyglobal.cpp b/indra/llcorehttp/_httppolicyglobal.cpp index 1dc95f3dce..3d0df96ade 100644 --- 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 100644 --- 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/_httpreadyqueue.h b/indra/llcorehttp/_httpreadyqueue.h index 5f19a9c5f9..7418988ec1 100644 --- a/indra/llcorehttp/_httpreadyqueue.h +++ b/indra/llcorehttp/_httpreadyqueue.h @@ -56,12 +56,12 @@ namespace LLCore #if LLCORE_HTTP_READY_QUEUE_IGNORES_PRIORITY -typedef std::deque<HttpOpRequest *> HttpReadyQueueBase; +typedef std::deque<HttpOpRequest::ptr_t> HttpReadyQueueBase; #else -typedef std::priority_queue<HttpOpRequest *, - std::deque<HttpOpRequest *>, +typedef std::priority_queue<HttpOpRequest::ptr_t, + std::deque<HttpOpRequest::ptr_t>, LLCore::HttpOpRequestCompare> HttpReadyQueueBase; #endif // LLCORE_HTTP_READY_QUEUE_IGNORES_PRIORITY diff --git a/indra/llcorehttp/_httpreplyqueue.cpp b/indra/llcorehttp/_httpreplyqueue.cpp index 558b7bdee9..2b138f3ad5 100644 --- a/indra/llcorehttp/_httpreplyqueue.cpp +++ b/indra/llcorehttp/_httpreplyqueue.cpp @@ -39,23 +39,17 @@ namespace LLCore HttpReplyQueue::HttpReplyQueue() - : RefCounted(true) { } HttpReplyQueue::~HttpReplyQueue() { - while (! mQueue.empty()) - { - HttpOperation * op = mQueue.back(); - mQueue.pop_back(); - op->release(); - } + mQueue.clear(); } -void HttpReplyQueue::addOp(HttpOperation * op) +void HttpReplyQueue::addOp(const HttpReplyQueue::opPtr_t &op) { { HttpScopedLock lock(mQueueMutex); @@ -66,15 +60,15 @@ void HttpReplyQueue::addOp(HttpOperation * op) } -HttpOperation * HttpReplyQueue::fetchOp() +HttpReplyQueue::opPtr_t HttpReplyQueue::fetchOp() { - HttpOperation * result(NULL); + HttpOperation::ptr_t result; { HttpScopedLock lock(mQueueMutex); if (mQueue.empty()) - return NULL; + return opPtr_t(); result = mQueue.front(); mQueue.erase(mQueue.begin()); @@ -98,9 +92,6 @@ void HttpReplyQueue::fetchAll(OpContainer & ops) mQueue.swap(ops); } } - - // Caller also acquires the reference counts on each op. - return; } diff --git a/indra/llcorehttp/_httpreplyqueue.h b/indra/llcorehttp/_httpreplyqueue.h index 4220a09a3b..0e39e22dde 100644 --- a/indra/llcorehttp/_httpreplyqueue.h +++ b/indra/llcorehttp/_httpreplyqueue.h @@ -58,21 +58,19 @@ class HttpOperation; /// will be coded anyway so it shouldn't be too much of a /// burden. -class HttpReplyQueue : public LLCoreInt::RefCounted +class HttpReplyQueue : private boost::noncopyable { -public: - /// Caller acquires a Refcount on construction - HttpReplyQueue(); -protected: - virtual ~HttpReplyQueue(); // Use release() +public: + typedef boost::shared_ptr<HttpOperation> opPtr_t; + typedef boost::shared_ptr<HttpReplyQueue> ptr_t; -private: - HttpReplyQueue(const HttpReplyQueue &); // Not defined - void operator=(const HttpReplyQueue &); // Not defined + HttpReplyQueue(); + virtual ~HttpReplyQueue(); public: - typedef std::vector<HttpOperation *> OpContainer; + + typedef std::vector< opPtr_t > OpContainer; /// Insert an object at the back of the reply queue. /// @@ -80,7 +78,7 @@ public: /// through the queue. /// /// Threading: callable by any thread. - void addOp(HttpOperation * op); + void addOp(const opPtr_t &op); /// Fetch an operation from the head of the queue. Returns /// NULL if none exists. @@ -88,7 +86,7 @@ public: /// Caller acquires reference count on returned operation. /// /// Threading: callable by any thread. - HttpOperation * fetchOp(); + opPtr_t fetchOp(); /// Caller acquires reference count on each returned operation /// @@ -96,6 +94,7 @@ public: void fetchAll(OpContainer & ops); protected: + OpContainer mQueue; LLCoreInt::HttpMutex mQueueMutex; LLCoreInt::HttpConditionVariable mQueueCV; diff --git a/indra/llcorehttp/_httprequestqueue.cpp b/indra/llcorehttp/_httprequestqueue.cpp index c16966d078..c6f4ad789f 100644 --- a/indra/llcorehttp/_httprequestqueue.cpp +++ b/indra/llcorehttp/_httprequestqueue.cpp @@ -47,12 +47,7 @@ HttpRequestQueue::HttpRequestQueue() HttpRequestQueue::~HttpRequestQueue() { - while (! mQueue.empty()) - { - HttpOperation * op = mQueue.back(); - mQueue.pop_back(); - op->release(); - } + mQueue.clear(); } @@ -73,7 +68,7 @@ void HttpRequestQueue::term() } -HttpStatus HttpRequestQueue::addOp(HttpOperation * op) +HttpStatus HttpRequestQueue::addOp(const HttpRequestQueue::opPtr_t &op) { bool wake(false); { @@ -95,9 +90,9 @@ HttpStatus HttpRequestQueue::addOp(HttpOperation * op) } -HttpOperation * HttpRequestQueue::fetchOp(bool wait) +HttpRequestQueue::opPtr_t HttpRequestQueue::fetchOp(bool wait) { - HttpOperation * result(NULL); + HttpOperation::ptr_t result; { HttpScopedLock lock(mQueueMutex); @@ -105,7 +100,7 @@ HttpOperation * HttpRequestQueue::fetchOp(bool wait) while (mQueue.empty()) { if (! wait || mQueueStopped) - return NULL; + return HttpOperation::ptr_t(); mQueueCV.wait(lock); } diff --git a/indra/llcorehttp/_httprequestqueue.h b/indra/llcorehttp/_httprequestqueue.h index c9c52b7233..3c3d134b07 100644 --- a/indra/llcorehttp/_httprequestqueue.h +++ b/indra/llcorehttp/_httprequestqueue.h @@ -61,6 +61,8 @@ private: void operator=(const HttpRequestQueue &); // Not defined public: + typedef boost::shared_ptr<HttpOperation> opPtr_t; + static void init(); static void term(); @@ -71,7 +73,7 @@ public: } public: - typedef std::vector<HttpOperation *> OpContainer; + typedef std::vector<opPtr_t> OpContainer; /// Insert an object at the back of the request queue. /// @@ -83,7 +85,7 @@ public: /// an explicit release() call. /// /// Threading: callable by any thread. - HttpStatus addOp(HttpOperation * op); + HttpStatus addOp(const opPtr_t &op); /// Return the operation on the front of the queue. If /// the queue is empty and @wait is false, call returns @@ -95,7 +97,7 @@ public: /// Caller acquires reference count any returned operation /// /// Threading: callable by any thread. - HttpOperation * fetchOp(bool wait); + opPtr_t fetchOp(bool wait); /// Return all queued requests to caller. The @ops argument /// should be empty when called and will be swap()'d with diff --git a/indra/llcorehttp/_httpretryqueue.h b/indra/llcorehttp/_httpretryqueue.h index 745adec09d..5d8c529cff 100644 --- a/indra/llcorehttp/_httpretryqueue.h +++ b/indra/llcorehttp/_httpretryqueue.h @@ -49,15 +49,15 @@ namespace LLCore struct HttpOpRetryCompare { - bool operator()(const HttpOpRequest * lhs, const HttpOpRequest * rhs) + bool operator()(const HttpOpRequest::ptr_t &lhs, const HttpOpRequest::ptr_t &rhs) { return lhs->mPolicyRetryAt < rhs->mPolicyRetryAt; } }; -typedef std::priority_queue<HttpOpRequest *, - std::deque<HttpOpRequest *>, +typedef std::priority_queue<HttpOpRequest::ptr_t, + std::deque<HttpOpRequest::ptr_t>, LLCore::HttpOpRetryCompare> HttpRetryQueueBase; class HttpRetryQueue : public HttpRetryQueueBase diff --git a/indra/llcorehttp/_httpservice.cpp b/indra/llcorehttp/_httpservice.cpp index c673e1be1d..6c39fdc61b 100644 --- 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); @@ -262,14 +263,13 @@ void HttpService::shutdown() // Cancel requests already on the request queue HttpRequestQueue::OpContainer ops; mRequestQueue->fetchAll(false, ops); - while (! ops.empty()) - { - HttpOperation * op(ops.front()); - ops.erase(ops.begin()); - op->cancel(); - op->release(); - } + for (HttpRequestQueue::OpContainer::iterator it = ops.begin(); + it != ops.end(); ++it) + { + (*it)->cancel(); + } + ops.clear(); // Shutdown transport canceling requests, freeing resources mTransport->shutdown(); @@ -323,7 +323,7 @@ HttpService::ELoopSpeed HttpService::processRequestQueue(ELoopSpeed loop) mRequestQueue->fetchAll(wait_for_req, ops); while (! ops.empty()) { - HttpOperation * op(ops.front()); + HttpOperation::ptr_t op(ops.front()); ops.erase(ops.begin()); // Process operation @@ -337,7 +337,7 @@ HttpService::ELoopSpeed HttpService::processRequestQueue(ELoopSpeed loop) if (op->mTracing > HTTP_TRACE_OFF) { LL_INFOS(LOG_CORE) << "TRACE, FromRequestQueue, Handle: " - << static_cast<HttpHandle>(op) + << op->getHandle() << LL_ENDL; } @@ -346,7 +346,7 @@ HttpService::ELoopSpeed HttpService::processRequestQueue(ELoopSpeed loop) } // Done with operation - op->release(); + op.reset(); } // Queue emptied, allow polling loop to sleep @@ -413,6 +413,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 +517,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 100644 --- 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 100644 --- 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 100644 --- 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..b91aaf0593 100644 --- 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); @@ -361,10 +361,9 @@ int main(int argc, char** argv) << std::endl; // Clean up - hr->requestStopThread(NULL); + hr->requestStopThread(LLCore::HttpHandler::ptr_t()); ms_sleep(1000); - opt->release(); - opt = NULL; + opt.reset(); delete hr; LLCore::HttpRequest::destroyService(); term_curl(); @@ -427,22 +426,22 @@ 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; - } } +namespace +{ + void NoOpDeletor(LLCore::HttpHandler *) + { /*NoOp*/ } +} -bool WorkingSet::reload(LLCore::HttpRequest * hr, LLCore::HttpOptions * opt) +bool WorkingSet::reload(LLCore::HttpRequest * hr, LLCore::HttpOptions::ptr_t & opt) { if (mRequestLowWater <= mHandles.size()) { @@ -470,11 +469,11 @@ bool WorkingSet::reload(LLCore::HttpRequest * hr, LLCore::HttpOptions * opt) LLCore::HttpHandle handle; if (offset || length) { - handle = hr->requestGetByteRange(0, 0, buffer, offset, length, opt, mHeaders, this); + handle = hr->requestGetByteRange(0, 0, buffer, offset, length, opt, mHeaders, LLCore::HttpHandler::ptr_t(this, NoOpDeletor)); } else { - handle = hr->requestGet(0, 0, buffer, opt, mHeaders, this); + handle = hr->requestGet(0, 0, buffer, opt, mHeaders, LLCore::HttpHandler::ptr_t(this, NoOpDeletor)); } if (! handle) { diff --git a/indra/llcorehttp/httpcommon.cpp b/indra/llcorehttp/httpcommon.cpp index 7907e958a4..c423047bb0 100644 --- a/indra/llcorehttp/httpcommon.cpp +++ b/indra/llcorehttp/httpcommon.cpp @@ -23,12 +23,24 @@ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ +#if LL_WINDOWS +#define SAFE_SSL 1 +#elif LL_DARWIN +#define SAFE_SSL 1 +#else +#define SAFE_SSL 1 +#endif +#include "linden_common.h" // Modifies curl/curl.h interfaces #include "httpcommon.h" - +#include "llmutex.h" +#include "llthread.h" #include <curl/curl.h> #include <string> #include <sstream> +#if SAFE_SSL +#include <openssl/crypto.h> +#endif namespace LLCore @@ -42,7 +54,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 +143,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 +178,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 +198,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 +218,7 @@ std::string HttpStatus::toTerseString() const if (isHttpStatus()) { result << "Http_"; - error_value = mType; + error_value = getType(); } else { @@ -244,7 +260,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 @@ -259,5 +275,168 @@ bool HttpStatus::isRetryable() const *this == inv_cont_range); // Short data read disagrees with content-range } -} // end namespace LLCore +namespace LLHttp +{ +namespace +{ +typedef boost::shared_ptr<LLMutex> LLMutex_ptr; +std::vector<LLMutex_ptr> sSSLMutex; + +CURL *getCurlTemplateHandle() +{ + static CURL *curlpTemplateHandle = NULL; + + if (curlpTemplateHandle == NULL) + { // Late creation of the template curl handle + curlpTemplateHandle = curl_easy_init(); + if (curlpTemplateHandle == NULL) + { + LL_WARNS() << "curl error calling curl_easy_init()" << LL_ENDL; + } + else + { + CURLcode result = curl_easy_setopt(curlpTemplateHandle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); + check_curl_code(result, CURLOPT_IPRESOLVE); + result = curl_easy_setopt(curlpTemplateHandle, CURLOPT_NOSIGNAL, 1); + check_curl_code(result, CURLOPT_NOSIGNAL); + result = curl_easy_setopt(curlpTemplateHandle, CURLOPT_NOPROGRESS, 1); + check_curl_code(result, CURLOPT_NOPROGRESS); + result = curl_easy_setopt(curlpTemplateHandle, CURLOPT_ENCODING, ""); + check_curl_code(result, CURLOPT_ENCODING); + result = curl_easy_setopt(curlpTemplateHandle, CURLOPT_AUTOREFERER, 1); + check_curl_code(result, CURLOPT_AUTOREFERER); + result = curl_easy_setopt(curlpTemplateHandle, CURLOPT_FOLLOWLOCATION, 1); + check_curl_code(result, CURLOPT_FOLLOWLOCATION); + result = curl_easy_setopt(curlpTemplateHandle, CURLOPT_SSL_VERIFYPEER, 1); + check_curl_code(result, CURLOPT_SSL_VERIFYPEER); + result = curl_easy_setopt(curlpTemplateHandle, CURLOPT_SSL_VERIFYHOST, 0); + check_curl_code(result, CURLOPT_SSL_VERIFYHOST); + + // 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. + result = curl_easy_setopt(curlpTemplateHandle, CURLOPT_DNS_CACHE_TIMEOUT, 15); + check_curl_code(result, CURLOPT_DNS_CACHE_TIMEOUT); + } + } + + return curlpTemplateHandle; +} + +LLMutex *getCurlMutex() +{ + static LLMutex* sHandleMutexp = NULL; + + if (!sHandleMutexp) + { + sHandleMutexp = new LLMutex(NULL); + } + + return sHandleMutexp; +} + +void deallocateEasyCurl(CURL *curlp) +{ + LLMutexLock lock(getCurlMutex()); + + curl_easy_cleanup(curlp); +} + + +#if SAFE_SSL +//static +void ssl_locking_callback(int mode, int type, const char *file, int line) +{ + if (type >= sSSLMutex.size()) + { + LL_WARNS() << "Attempt to get unknown MUTEX in SSL Lock." << LL_ENDL; + } + + if (mode & CRYPTO_LOCK) + { + sSSLMutex[type]->lock(); + } + else + { + sSSLMutex[type]->unlock(); + } +} + +//static +unsigned long ssl_thread_id(void) +{ + return LLThread::currentID(); +} +#endif + +} + +void initialize() +{ + // Do not change this "unless you are familiar with and mean to control + // internal operations of libcurl" + // - http://curl.haxx.se/libcurl/c/curl_global_init.html + CURLcode code = curl_global_init(CURL_GLOBAL_ALL); + + check_curl_code(code, CURL_GLOBAL_ALL); + +#if SAFE_SSL + S32 mutex_count = CRYPTO_num_locks(); + for (S32 i = 0; i < mutex_count; i++) + { + sSSLMutex.push_back(LLMutex_ptr(new LLMutex(NULL))); + } + CRYPTO_set_id_callback(&ssl_thread_id); + CRYPTO_set_locking_callback(&ssl_locking_callback); +#endif + +} + + +void cleanup() +{ +#if SAFE_SSL + CRYPTO_set_id_callback(NULL); + CRYPTO_set_locking_callback(NULL); + sSSLMutex.clear(); +#endif + + curl_global_cleanup(); +} + + +CURL_ptr createEasyHandle() +{ + LLMutexLock lock(getCurlMutex()); + + CURL* handle = curl_easy_duphandle(getCurlTemplateHandle()); + + return CURL_ptr(handle, &deallocateEasyCurl); +} + +std::string getCURLVersion() +{ + return std::string(curl_version()); +} + +void check_curl_code(CURLcode code, int curl_setopt_option) +{ + if (CURLE_OK != code) + { + // Comment from old llcurl code which may no longer apply: + // + // linux appears to throw a curl error once per session for a bad initialization + // at a pretty random time (when enabling cookies). + LL_WARNS() << "libcurl error detected: " << curl_easy_strerror(code) + << ", curl_easy_setopt option: " << curl_setopt_option + << LL_ENDL; + } + +} + +} +} // end namespace LLCore diff --git a/indra/llcorehttp/httpcommon.h b/indra/llcorehttp/httpcommon.h index 9601f94125..b2db01d038 100644 --- a/indra/llcorehttp/httpcommon.h +++ b/indra/llcorehttp/httpcommon.h @@ -188,9 +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> - +#include <curl/curl.h> namespace LLCore { @@ -206,6 +209,7 @@ namespace LLCore /// becomes invalid and may be recycled for other queued requests. typedef void * HttpHandle; + #define LLCORE_HTTP_HANDLE_INVALID (NULL) /// For internal scheduling and metrics, we use a microsecond @@ -286,52 +290,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 +354,7 @@ struct HttpStatus /// operator bool() const { - return 0 == mStatus; + return 0 == mDetails->mStatus; } /// Inverse of previous operator. @@ -347,14 +362,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 +410,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,9 +418,94 @@ 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 +/// A namespace for several free methods and low level utilities. +namespace LLHttp +{ + typedef boost::shared_ptr<CURL> CURL_ptr; + + void initialize(); + void cleanup(); + + CURL_ptr createEasyHandle(); + std::string getCURLVersion(); + + void check_curl_code(CURLcode code, int curl_setopt_option); +} + } // end namespace LLCore #endif // _LLCORE_HTTP_COMMON_H_ diff --git a/indra/llcorehttp/httphandler.h b/indra/llcorehttp/httphandler.h index 9171e4e7b9..65e043f5d3 100644 --- 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,16 @@ 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: + typedef boost::shared_ptr<HttpHandler> ptr_t; + typedef boost::weak_ptr<HttpHandler> wptr_t; + 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 100644 --- 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 100644 --- 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 100644 --- 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 100644 --- 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..e09f0c3b18 100644 --- a/indra/llcorehttp/httprequest.cpp +++ b/indra/llcorehttp/httprequest.cpp @@ -55,13 +55,13 @@ namespace LLCore HttpRequest::HttpRequest() - : mReplyQueue(NULL), + : mReplyQueue(), mRequestQueue(NULL) { mRequestQueue = HttpRequestQueue::instanceOf(); mRequestQueue->addRef(); - mReplyQueue = new HttpReplyQueue(); + mReplyQueue.reset( new HttpReplyQueue() ); } @@ -73,11 +73,7 @@ HttpRequest::~HttpRequest() mRequestQueue = NULL; } - if (mReplyQueue) - { - mReplyQueue->release(); - mReplyQueue = NULL; - } + mReplyQueue.reset(); } @@ -117,60 +113,59 @@ 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) + long value, HttpHandler::ptr_t handler) { HttpStatus status; - HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); - HttpOpSetGet * op = new HttpOpSetGet(); + HttpOpSetGet::ptr_t op(new HttpOpSetGet()); if (! (status = op->setupSet(opt, pclass, value))) { - op->release(); mLastReqStatus = status; - return handle; + return LLCORE_HTTP_HANDLE_INVALID; } op->setReplyPath(mReplyQueue, handler); if (! (status = mRequestQueue->addOp(op))) // transfers refcount { - op->release(); mLastReqStatus = status; - return handle; + return LLCORE_HTTP_HANDLE_INVALID; } mLastReqStatus = status; - handle = static_cast<HttpHandle>(op); - - return handle; + return op->getHandle(); } HttpHandle HttpRequest::setPolicyOption(EPolicyOption opt, policy_t pclass, - const std::string & value, HttpHandler * handler) + const std::string & value, HttpHandler::ptr_t handler) { HttpStatus status; - HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); - HttpOpSetGet * op = new HttpOpSetGet(); + HttpOpSetGet::ptr_t op (new HttpOpSetGet()); if (! (status = op->setupSet(opt, pclass, value))) { - op->release(); mLastReqStatus = status; - return handle; + return LLCORE_HTTP_HANDLE_INVALID; } op->setReplyPath(mReplyQueue, handler); if (! (status = mRequestQueue->addOp(op))) // transfers refcount { - op->release(); mLastReqStatus = status; - return handle; + return LLCORE_HTTP_HANDLE_INVALID; } mLastReqStatus = status; - handle = static_cast<HttpHandle>(op); - - return handle; + return op->getHandle(); } @@ -188,32 +183,27 @@ HttpStatus HttpRequest::getStatus() const HttpHandle HttpRequest::requestGet(policy_t policy_id, priority_t priority, const std::string & url, - HttpOptions * options, - HttpHeaders * headers, - HttpHandler * user_handler) + const HttpOptions::ptr_t & options, + const HttpHeaders::ptr_t & headers, + HttpHandler::ptr_t user_handler) { HttpStatus status; - HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); - HttpOpRequest * op = new HttpOpRequest(); + HttpOpRequest::ptr_t op(new HttpOpRequest()); if (! (status = op->setupGet(policy_id, priority, url, options, headers))) { - op->release(); mLastReqStatus = status; - return handle; + return LLCORE_HTTP_HANDLE_INVALID; } op->setReplyPath(mReplyQueue, user_handler); if (! (status = mRequestQueue->addOp(op))) // transfers refcount { - op->release(); mLastReqStatus = status; - return handle; + return LLCORE_HTTP_HANDLE_INVALID; } mLastReqStatus = status; - handle = static_cast<HttpHandle>(op); - - return handle; + return op->getHandle(); } @@ -222,32 +212,27 @@ HttpHandle HttpRequest::requestGetByteRange(policy_t policy_id, const std::string & url, size_t offset, size_t len, - HttpOptions * options, - HttpHeaders * headers, - HttpHandler * user_handler) + const HttpOptions::ptr_t & options, + const HttpHeaders::ptr_t & headers, + HttpHandler::ptr_t user_handler) { HttpStatus status; - HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); - HttpOpRequest * op = new HttpOpRequest(); + HttpOpRequest::ptr_t op(new HttpOpRequest()); if (! (status = op->setupGetByteRange(policy_id, priority, url, offset, len, options, headers))) { - op->release(); mLastReqStatus = status; - return handle; + return LLCORE_HTTP_HANDLE_INVALID; } op->setReplyPath(mReplyQueue, user_handler); if (! (status = mRequestQueue->addOp(op))) // transfers refcount { - op->release(); mLastReqStatus = status; - return handle; + return LLCORE_HTTP_HANDLE_INVALID; } mLastReqStatus = status; - handle = static_cast<HttpHandle>(op); - - return handle; + return op->getHandle(); } @@ -255,32 +240,27 @@ HttpHandle HttpRequest::requestPost(policy_t policy_id, priority_t priority, const std::string & url, BufferArray * body, - HttpOptions * options, - HttpHeaders * headers, - HttpHandler * user_handler) + const HttpOptions::ptr_t & options, + const HttpHeaders::ptr_t & headers, + HttpHandler::ptr_t user_handler) { HttpStatus status; - HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); - HttpOpRequest * op = new HttpOpRequest(); + HttpOpRequest::ptr_t op(new HttpOpRequest()); if (! (status = op->setupPost(policy_id, priority, url, body, options, headers))) { - op->release(); mLastReqStatus = status; - return handle; + return LLCORE_HTTP_HANDLE_INVALID; } op->setReplyPath(mReplyQueue, user_handler); if (! (status = mRequestQueue->addOp(op))) // transfers refcount { - op->release(); mLastReqStatus = status; - return handle; + return LLCORE_HTTP_HANDLE_INVALID; } mLastReqStatus = status; - handle = static_cast<HttpHandle>(op); - - return handle; + return op->getHandle(); } @@ -288,59 +268,156 @@ HttpHandle HttpRequest::requestPut(policy_t policy_id, priority_t priority, const std::string & url, BufferArray * body, - HttpOptions * options, - HttpHeaders * headers, - HttpHandler * user_handler) + const HttpOptions::ptr_t & options, + const HttpHeaders::ptr_t & headers, + HttpHandler::ptr_t user_handler) { HttpStatus status; - HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); - HttpOpRequest * op = new HttpOpRequest(); + HttpOpRequest::ptr_t op (new HttpOpRequest()); if (! (status = op->setupPut(policy_id, priority, url, body, options, headers))) { - op->release(); mLastReqStatus = status; - return handle; + return LLCORE_HTTP_HANDLE_INVALID; } op->setReplyPath(mReplyQueue, user_handler); if (! (status = mRequestQueue->addOp(op))) // transfers refcount { - op->release(); mLastReqStatus = status; - return handle; + return LLCORE_HTTP_HANDLE_INVALID; } mLastReqStatus = status; - handle = static_cast<HttpHandle>(op); - - return handle; + return op->getHandle(); } +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::ptr_t user_handler) +{ + HttpStatus status; + + HttpOpRequest::ptr_t op(new HttpOpRequest()); + if (!(status = op->setupDelete(policy_id, priority, url, options, headers))) + { + mLastReqStatus = status; + return LLCORE_HTTP_HANDLE_INVALID; + } + op->setReplyPath(mReplyQueue, user_handler); + if (!(status = mRequestQueue->addOp(op))) // transfers refcount + { + mLastReqStatus = status; + return LLCORE_HTTP_HANDLE_INVALID; + } + + mLastReqStatus = status; + return op->getHandle(); +} + +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::ptr_t user_handler) +{ + HttpStatus status; + + HttpOpRequest::ptr_t op (new HttpOpRequest()); + if (!(status = op->setupPatch(policy_id, priority, url, body, options, headers))) + { + mLastReqStatus = status; + return LLCORE_HTTP_HANDLE_INVALID; + } + op->setReplyPath(mReplyQueue, user_handler); + if (!(status = mRequestQueue->addOp(op))) // transfers refcount + { + mLastReqStatus = status; + return LLCORE_HTTP_HANDLE_INVALID; + } + + mLastReqStatus = status; + return op->getHandle(); +} -HttpHandle HttpRequest::requestNoOp(HttpHandler * user_handler) +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::ptr_t user_handler) +{ + HttpStatus status; + + HttpOpRequest::ptr_t op(new HttpOpRequest()); + if (!(status = op->setupCopy(policy_id, priority, url, options, headers))) + { + mLastReqStatus = status; + return LLCORE_HTTP_HANDLE_INVALID; + } + op->setReplyPath(mReplyQueue, user_handler); + if (!(status = mRequestQueue->addOp(op))) // transfers refcount + { + mLastReqStatus = status; + return LLCORE_HTTP_HANDLE_INVALID; + } + + mLastReqStatus = status; + return op->getHandle(); + +} + +HttpHandle HttpRequest::requestMove(policy_t policy_id, + priority_t priority, + const std::string & url, + const HttpOptions::ptr_t & options, + const HttpHeaders::ptr_t & headers, + HttpHandler::ptr_t user_handler) +{ + HttpStatus status; + + HttpOpRequest::ptr_t op (new HttpOpRequest()); + if (!(status = op->setupMove(policy_id, priority, url, options, headers))) + { + mLastReqStatus = status; + return LLCORE_HTTP_HANDLE_INVALID; + } + op->setReplyPath(mReplyQueue, user_handler); + if (!(status = mRequestQueue->addOp(op))) // transfers refcount + { + mLastReqStatus = status; + return LLCORE_HTTP_HANDLE_INVALID; + } + + mLastReqStatus = status; + return op->getHandle(); +} + + +HttpHandle HttpRequest::requestNoOp(HttpHandler::ptr_t user_handler) { HttpStatus status; - HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); - HttpOpNull * op = new HttpOpNull(); + HttpOperation::ptr_t op (new HttpOpNull()); op->setReplyPath(mReplyQueue, user_handler); if (! (status = mRequestQueue->addOp(op))) // transfers refcount { - op->release(); mLastReqStatus = status; - return handle; + return LLCORE_HTTP_HANDLE_INVALID; } mLastReqStatus = status; - handle = static_cast<HttpHandle>(op); - - return handle; + return op->getHandle(); } HttpStatus HttpRequest::update(long usecs) { - HttpOperation * op(NULL); + HttpOperation::ptr_t op; if (usecs) { @@ -351,7 +428,7 @@ HttpStatus HttpRequest::update(long usecs) op->visitNotifier(this); // We're done with the operation - op->release(); + op.reset(); } } else @@ -366,13 +443,13 @@ HttpStatus HttpRequest::update(long usecs) ++iter) { // Swap op pointer for NULL; - op = *iter; *iter = NULL; + op.reset(); + op.swap(*iter); // Process operation op->visitNotifier(this); // We're done with the operation - op->release(); } } } @@ -387,46 +464,38 @@ HttpStatus HttpRequest::update(long usecs) // Request Management Methods // ==================================== -HttpHandle HttpRequest::requestCancel(HttpHandle request, HttpHandler * user_handler) +HttpHandle HttpRequest::requestCancel(HttpHandle request, HttpHandler::ptr_t user_handler) { HttpStatus status; - HttpHandle ret_handle(LLCORE_HTTP_HANDLE_INVALID); - HttpOpCancel * op = new HttpOpCancel(request); + HttpOperation::ptr_t op(new HttpOpCancel(request)); op->setReplyPath(mReplyQueue, user_handler); if (! (status = mRequestQueue->addOp(op))) // transfers refcount { - op->release(); mLastReqStatus = status; - return ret_handle; + return LLCORE_HTTP_HANDLE_INVALID; } mLastReqStatus = status; - ret_handle = static_cast<HttpHandle>(op); - - return ret_handle; + return op->getHandle(); } HttpHandle HttpRequest::requestSetPriority(HttpHandle request, priority_t priority, - HttpHandler * handler) + HttpHandler::ptr_t handler) { HttpStatus status; - HttpHandle ret_handle(LLCORE_HTTP_HANDLE_INVALID); - HttpOpSetPriority * op = new HttpOpSetPriority(request, priority); + HttpOperation::ptr_t op (new HttpOpSetPriority(request, priority)); op->setReplyPath(mReplyQueue, handler); if (! (status = mRequestQueue->addOp(op))) // transfers refcount { - op->release(); mLastReqStatus = status; - return ret_handle; + return LLCORE_HTTP_HANDLE_INVALID; } mLastReqStatus = status; - ret_handle = static_cast<HttpHandle>(op); - - return ret_handle; + return op->getHandle(); } @@ -475,22 +544,21 @@ HttpStatus HttpRequest::startThread() } -HttpHandle HttpRequest::requestStopThread(HttpHandler * user_handler) +HttpHandle HttpRequest::requestStopThread(HttpHandler::ptr_t user_handler) { HttpStatus status; HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); - HttpOpStop * op = new HttpOpStop(); + HttpOperation::ptr_t op(new HttpOpStop()); 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); + handle = op->getHandle(); return handle; } @@ -501,17 +569,16 @@ HttpHandle HttpRequest::requestSpin(int mode) HttpStatus status; HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); - HttpOpSpin * op = new HttpOpSpin(mode); - op->setReplyPath(mReplyQueue, NULL); + HttpOperation::ptr_t op(new HttpOpSpin(mode)); + op->setReplyPath(mReplyQueue, HttpHandler::ptr_t()); if (! (status = mRequestQueue->addOp(op))) // transfers refcount { - op->release(); mLastReqStatus = status; return handle; } mLastReqStatus = status; - handle = static_cast<HttpHandle>(op); + handle = op->getHandle(); return handle; } diff --git a/indra/llcorehttp/httprequest.h b/indra/llcorehttp/httprequest.h index 7f23723b0b..17cfdcd7b6 100644 --- 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 &, const HttpHandler::ptr_t &, 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 @@ -256,9 +270,9 @@ public: /// @return Handle of dynamic request. Use @see getStatus() if /// the returned handle is invalid. HttpHandle setPolicyOption(EPolicyOption opt, policy_t pclass, long value, - HttpHandler * handler); + HttpHandler::ptr_t handler); HttpHandle setPolicyOption(EPolicyOption opt, policy_t pclass, const std::string & value, - HttpHandler * handler); + HttpHandler::ptr_t handler); /// @} @@ -334,9 +348,9 @@ public: HttpHandle requestGet(policy_t policy_id, priority_t priority, const std::string & url, - HttpOptions * options, - HttpHeaders * headers, - HttpHandler * handler); + const HttpOptions::ptr_t & options, + const HttpHeaders::ptr_t & headers, + HttpHandler::ptr_t handler); /// Queue a full HTTP GET request to be issued with a 'Range' header. @@ -377,9 +391,9 @@ public: const std::string & url, size_t offset, size_t len, - HttpOptions * options, - HttpHeaders * headers, - HttpHandler * handler); + const HttpOptions::ptr_t & options, + const HttpHeaders::ptr_t & headers, + HttpHandler::ptr_t handler); /// Queue a full HTTP POST. Query arguments and body may @@ -418,9 +432,9 @@ public: priority_t priority, const std::string & url, BufferArray * body, - HttpOptions * options, - HttpHeaders * headers, - HttpHandler * handler); + const HttpOptions::ptr_t & options, + const HttpHeaders::ptr_t & headers, + HttpHandler::ptr_t handler); /// Queue a full HTTP PUT. Query arguments and body may @@ -459,12 +473,92 @@ public: priority_t priority, const std::string & url, BufferArray * body, - HttpOptions * options, - HttpHeaders * headers, - HttpHandler * handler); - - - /// Queue a NoOp request. + const HttpOptions::ptr_t & options, + const HttpHeaders::ptr_t & headers, + HttpHandler::ptr_t handler); + + + /// Queue a full HTTP DELETE. 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::ptr_t user_handler); + + /// Queue a full HTTP PATCH. 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::ptr_t user_handler); + + /// Queue a full HTTP COPY. 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::ptr_t user_handler); + + /// Queue a full HTTP MOVE. 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 requestMove(policy_t policy_id, + priority_t priority, + const std::string & url, + const HttpOptions::ptr_t & options, + const HttpHeaders::ptr_t & headers, + HttpHandler::ptr_t 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. @@ -472,7 +566,7 @@ public: /// @param handler @see requestGet() /// @return " /// - HttpHandle requestNoOp(HttpHandler * handler); + HttpHandle requestNoOp(HttpHandler::ptr_t handler); /// While all the heavy work is done by the worker thread, notifications /// must be performed in the context of the application thread. These @@ -497,7 +591,7 @@ public: /// /// @{ - HttpHandle requestCancel(HttpHandle request, HttpHandler *); + HttpHandle requestCancel(HttpHandle request, HttpHandler::ptr_t); /// Request that a previously-issued request be reprioritized. /// The status of whether the change itself succeeded arrives @@ -509,7 +603,7 @@ public: /// @param handler @see requestGet() /// @return " /// - HttpHandle requestSetPriority(HttpHandle request, priority_t priority, HttpHandler * handler); + HttpHandle requestSetPriority(HttpHandle request, priority_t priority, HttpHandler::ptr_t handler); /// @} @@ -547,7 +641,7 @@ public: /// As the request cannot be cancelled, the handle /// is generally not useful. /// - HttpHandle requestStopThread(HttpHandler * handler); + HttpHandle requestStopThread(HttpHandler::ptr_t handler); /// Queue a Spin request. /// DEBUG/TESTING ONLY. This puts the worker into a CPU spin for @@ -561,14 +655,15 @@ public: /// @} protected: - void generateNotification(HttpOperation * op); private: + typedef boost::shared_ptr<HttpReplyQueue> HttpReplyQueuePtr_t; + /// @name InstanceData /// /// @{ HttpStatus mLastReqStatus; - HttpReplyQueue * mReplyQueue; + HttpReplyQueuePtr_t mReplyQueue; HttpRequestQueue * mRequestQueue; /// @} diff --git a/indra/llcorehttp/httpresponse.cpp b/indra/llcorehttp/httpresponse.cpp index c974395b0a..f5ad2ebd47 100644 --- 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 100644 --- 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/llmessage/llhttpconstants.cpp b/indra/llcorehttp/llhttpconstants.cpp index 32f76f0d70..71d4f19408 100644..100755 --- a/indra/llmessage/llhttpconstants.cpp +++ b/indra/llcorehttp/llhttpconstants.cpp @@ -133,94 +133,3 @@ const std::string HTTP_VERB_MOVE("MOVE"); const std::string HTTP_VERB_OPTIONS("OPTIONS"); const std::string HTTP_VERB_PATCH("PATCH"); const std::string HTTP_VERB_COPY("COPY"); - -const std::string& httpMethodAsVerb(EHTTPMethod method) -{ - static const std::string VERBS[] = - { - HTTP_VERB_INVALID, - HTTP_VERB_HEAD, - HTTP_VERB_GET, - HTTP_VERB_PUT, - HTTP_VERB_POST, - HTTP_VERB_DELETE, - HTTP_VERB_MOVE, - HTTP_VERB_OPTIONS, - HTTP_VERB_PATCH, - HTTP_VERB_COPY - }; - if(((S32)method <=0) || ((S32)method >= HTTP_METHOD_COUNT)) - { - return VERBS[0]; - } - return VERBS[method]; -} - -bool isHttpInformationalStatus(S32 status) -{ - // Check for status 1xx. - return((100 <= status) && (status < 200)); -} - -bool isHttpGoodStatus(S32 status) -{ - // Check for status 2xx. - return((200 <= status) && (status < 300)); -} - -bool isHttpRedirectStatus(S32 status) -{ - // Check for status 3xx. - return((300 <= status) && (status < 400)); -} - -bool isHttpClientErrorStatus(S32 status) -{ - // Status 499 is sometimes used for re-interpreted status 2xx errors - // based on body content. Treat these as potentially retryable 'server' status errors, - // since we do not have enough context to know if this will always fail. - if (HTTP_INTERNAL_ERROR == status) return false; - - // Check for status 5xx. - return((400 <= status) && (status < 500)); -} - -bool isHttpServerErrorStatus(S32 status) -{ - // Status 499 is sometimes used for re-interpreted status 2xx errors. - // Allow retry of these, since we don't have enough information in this - // context to know if this will always fail. - if (HTTP_INTERNAL_ERROR == status) return true; - - // Check for status 5xx. - return((500 <= status) && (status < 600)); -} - -// Parses 'Retry-After' header contents and returns seconds until retry should occur. -bool getSecondsUntilRetryAfter(const std::string& retry_after, F32& seconds_to_wait) -{ - // *TODO: This needs testing! Not in use yet. - // Examples of Retry-After headers: - // Retry-After: Fri, 31 Dec 1999 23:59:59 GMT - // Retry-After: 120 - - // Check for number of seconds version, first: - char* end = 0; - // Parse as double - double seconds = std::strtod(retry_after.c_str(), &end); - if ( end != 0 && *end == 0 ) - { - // Successful parse - seconds_to_wait = (F32) seconds; - return true; - } - - // Parse rfc1123 date. - time_t date = curl_getdate(retry_after.c_str(), NULL ); - if (-1 == date) return false; - - seconds_to_wait = (F64)date - LLTimer::getTotalSeconds(); - - return true; -} - diff --git a/indra/llmessage/llhttpconstants.h b/indra/llcorehttp/llhttpconstants.h index d6bcbd3c19..121448854e 100644..100755 --- a/indra/llmessage/llhttpconstants.h +++ b/indra/llcorehttp/llhttpconstants.h @@ -116,13 +116,6 @@ enum EHTTPMethod HTTP_METHOD_COUNT }; -const std::string& httpMethodAsVerb(EHTTPMethod method); -bool isHttpInformationalStatus(S32 status); -bool isHttpGoodStatus(S32 status); -bool isHttpRedirectStatus(S32 status); -bool isHttpClientErrorStatus(S32 status); -bool isHttpServerErrorStatus(S32 status); - // Parses 'Retry-After' header contents and returns seconds until retry should occur. bool getSecondsUntilRetryAfter(const std::string& retry_after, F32& seconds_to_wait); diff --git a/indra/llcorehttp/tests/llcorehttp_test.cpp b/indra/llcorehttp/tests/llcorehttp_test.cpp index e863ddd13f..bef762f5ce 100644 --- a/indra/llcorehttp/tests/llcorehttp_test.cpp +++ b/indra/llcorehttp/tests/llcorehttp_test.cpp @@ -160,7 +160,7 @@ void stop_thread(LLCore::HttpRequest * req) { if (req) { - req->requestStopThread(NULL); + req->requestStopThread(LLCore::HttpHandler::ptr_t()); int count = 0; int limit = 10; diff --git a/indra/llcorehttp/tests/test_httpheaders.hpp b/indra/llcorehttp/tests/test_httpheaders.hpp index 668c36dc66..c05f1d9429 100644 --- 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_httpoperation.hpp b/indra/llcorehttp/tests/test_httpoperation.hpp index 17b1a96878..e7df2337de 100644 --- a/indra/llcorehttp/tests/test_httpoperation.hpp +++ b/indra/llcorehttp/tests/test_httpoperation.hpp @@ -76,12 +76,12 @@ namespace tut mMemTotal = GetMemTotal(); // create a new ref counted object with an implicit reference - HttpOpNull * op = new HttpOpNull(); - ensure(op->getRefCount() == 1); + HttpOperation::ptr_t op (new HttpOpNull()); + ensure(op.use_count() == 1); ensure(mMemTotal < GetMemTotal()); // release the implicit reference, causing the object to be released - op->release(); + op.reset(); // make sure we didn't leak any memory ensure(mMemTotal == GetMemTotal()); @@ -96,26 +96,24 @@ namespace tut mMemTotal = GetMemTotal(); // Get some handlers - TestHandler * h1 = new TestHandler(); + LLCore::HttpHandler::ptr_t h1 (new TestHandler()); // create a new ref counted object with an implicit reference - HttpOpNull * op = new HttpOpNull(); + HttpOperation::ptr_t op (new HttpOpNull()); // Add the handlers - op->setReplyPath(NULL, h1); + op->setReplyPath(LLCore::HttpOperation::HttpReplyQueuePtr_t(), h1); // Check ref count - ensure(op->getRefCount() == 1); + ensure(op.unique() == 1); // release the reference, releasing the operation but // not the handlers. - op->release(); - op = NULL; + op.reset(); ensure(mMemTotal != GetMemTotal()); // release the handlers - delete h1; - h1 = NULL; + h1.reset(); ensure(mMemTotal == GetMemTotal()); } diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp index 43f7e36da5..463e55dd7e 100644 --- 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()) @@ -247,7 +247,7 @@ void HttpRequestTestObjectType::test<2>() ensure("Memory being used", mMemTotal < GetMemTotal()); // Issue a NoOp - HttpHandle handle = req->requestNoOp(NULL); + HttpHandle handle = req->requestNoOp(LLCore::HttpHandler::ptr_t()); ensure("Request issued", handle != LLCORE_HTTP_HANDLE_INVALID); // release the request object @@ -275,6 +275,10 @@ void HttpRequestTestObjectType::test<2>() } } +namespace +{ + void NoOpDeletor(LLCore::HttpHandler *) { } +} template <> template <> void HttpRequestTestObjectType::test<3>() @@ -287,7 +291,8 @@ void HttpRequestTestObjectType::test<3>() // references to it after completion of this method. // Create before memory record as the string copy will bump numbers. TestHandler2 handler(this, "handler"); - + LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor); + // record the total amount of dynamically allocated memory mMemTotal = GetMemTotal(); mHandlerCalls = 0; @@ -296,9 +301,8 @@ void HttpRequestTestObjectType::test<3>() try { - // Get singletons created - HttpRequest::createService(); + HttpRequest::createService(); // Start threading early so that thread memory is invariant // over the test. @@ -309,7 +313,7 @@ void HttpRequestTestObjectType::test<3>() ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); // Issue a NoOp - HttpHandle handle = req->requestNoOp(&handler); + HttpHandle handle = req->requestNoOp(handlerp); ensure("Valid handle returned for first request", handle != LLCORE_HTTP_HANDLE_INVALID); // Run the notification pump. @@ -324,7 +328,7 @@ void HttpRequestTestObjectType::test<3>() ensure("One handler invocation for request", mHandlerCalls == 1); // Okay, request a shutdown of the servicing thread - handle = req->requestStopThread(&handler); + handle = req->requestStopThread(handlerp); ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); // Run the notification pump again @@ -378,7 +382,10 @@ void HttpRequestTestObjectType::test<4>() // references to it after completion of this method. TestHandler2 handler1(this, "handler1"); TestHandler2 handler2(this, "handler2"); - + + LLCore::HttpHandler::ptr_t handler1p(&handler1, NoOpDeletor); + LLCore::HttpHandler::ptr_t handler2p(&handler2, NoOpDeletor); + // record the total amount of dynamically allocated memory mMemTotal = GetMemTotal(); mHandlerCalls = 0; @@ -388,7 +395,7 @@ void HttpRequestTestObjectType::test<4>() try { - + // Get singletons created HttpRequest::createService(); @@ -402,11 +409,11 @@ void HttpRequestTestObjectType::test<4>() ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); // Issue some NoOps - HttpHandle handle = req1->requestNoOp(&handler1); + HttpHandle handle = req1->requestNoOp(handler1p); ensure("Valid handle returned for first request", handle != LLCORE_HTTP_HANDLE_INVALID); handler1.mExpectHandle = handle; - handle = req2->requestNoOp(&handler2); + handle = req2->requestNoOp(handler2p); ensure("Valid handle returned for first request", handle != LLCORE_HTTP_HANDLE_INVALID); handler2.mExpectHandle = handle; @@ -423,7 +430,7 @@ void HttpRequestTestObjectType::test<4>() ensure("One handler invocation for request", mHandlerCalls == 2); // Okay, request a shutdown of the servicing thread - handle = req2->requestStopThread(&handler2); + handle = req2->requestStopThread(handler2p); ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); handler2.mExpectHandle = handle; @@ -482,7 +489,8 @@ void HttpRequestTestObjectType::test<5>() // references to it after completion of this method. // Create before memory record as the string copy will bump numbers. TestHandler2 handler(this, "handler"); - + LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor); + // record the total amount of dynamically allocated memory mMemTotal = GetMemTotal(); mHandlerCalls = 0; @@ -491,7 +499,6 @@ void HttpRequestTestObjectType::test<5>() try { - // Get singletons created HttpRequest::createService(); @@ -508,7 +515,7 @@ void HttpRequestTestObjectType::test<5>() ensure("Valid handle returned for spin request", handle != LLCORE_HTTP_HANDLE_INVALID); // Issue a NoOp - handle = req->requestNoOp(&handler); + handle = req->requestNoOp(handlerp); ensure("Valid handle returned for no-op request", handle != LLCORE_HTTP_HANDLE_INVALID); // Run the notification pump. @@ -567,7 +574,8 @@ void HttpRequestTestObjectType::test<6>() try { - + LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor); + // Get singletons created HttpRequest::createService(); @@ -584,7 +592,7 @@ void HttpRequestTestObjectType::test<6>() ensure("Valid handle returned for spin request", handle != LLCORE_HTTP_HANDLE_INVALID); // Issue a NoOp - handle = req->requestNoOp(&handler); + handle = req->requestNoOp(handlerp); ensure("Valid handle returned for no-op request", handle != LLCORE_HTTP_HANDLE_INVALID); // Run the notification pump. @@ -632,17 +640,19 @@ void HttpRequestTestObjectType::test<7>() // references to it after completion of this method. // Create before memory record as the string copy will bump numbers. TestHandler2 handler(this, "handler"); - + + LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor); + // record the total amount of dynamically allocated memory mMemTotal = GetMemTotal(); mHandlerCalls = 0; HttpRequest * req = NULL; - HttpOptions * opts = NULL; + HttpOptions::ptr_t opts; try { - // Get singletons created + // Get singletons created HttpRequest::createService(); // Start threading early so that thread memory is invariant @@ -653,7 +663,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,8 +674,8 @@ void HttpRequestTestObjectType::test<7>() 0, 0, opts, - NULL, - &handler); + HttpHeaders::ptr_t(), + handlerp); ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); // Run the notification pump. @@ -681,7 +691,7 @@ void HttpRequestTestObjectType::test<7>() // Okay, request a shutdown of the servicing thread mStatus = HttpStatus(); - handle = req->requestStopThread(&handler); + handle = req->requestStopThread(handlerp); ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); // Run the notification pump again @@ -705,8 +715,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 +737,7 @@ void HttpRequestTestObjectType::test<7>() catch (...) { stop_thread(req); - if (opts) - { - opts->release(); - opts = NULL; - } + opts.reset(); delete req; HttpRequest::destroyService(); throw; @@ -754,7 +759,8 @@ void HttpRequestTestObjectType::test<8>() // references to it after completion of this method. // Create before memory record as the string copy will bump numbers. TestHandler2 handler(this, "handler"); - + LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor); + // record the total amount of dynamically allocated memory mMemTotal = GetMemTotal(); mHandlerCalls = 0; @@ -763,7 +769,7 @@ void HttpRequestTestObjectType::test<8>() try { - // Get singletons created + // Get singletons created HttpRequest::createService(); // Start threading early so that thread memory is invariant @@ -779,9 +785,9 @@ void HttpRequestTestObjectType::test<8>() HttpHandle handle = req->requestGet(HttpRequest::DEFAULT_POLICY_ID, 0U, url_base, - NULL, - NULL, - &handler); + HttpOptions::ptr_t(), + HttpHeaders::ptr_t(), + handlerp); ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); // Run the notification pump. @@ -797,7 +803,7 @@ void HttpRequestTestObjectType::test<8>() // Okay, request a shutdown of the servicing thread mStatus = HttpStatus(); - handle = req->requestStopThread(&handler); + handle = req->requestStopThread(handlerp); ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); // Run the notification pump again @@ -862,7 +868,8 @@ void HttpRequestTestObjectType::test<9>() // references to it after completion of this method. // Create before memory record as the string copy will bump numbers. TestHandler2 handler(this, "handler"); - + LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor); + // record the total amount of dynamically allocated memory mMemTotal = GetMemTotal(); mHandlerCalls = 0; @@ -871,7 +878,7 @@ void HttpRequestTestObjectType::test<9>() try { - // Get singletons created + // Get singletons created HttpRequest::createService(); // Start threading early so that thread memory is invariant @@ -889,9 +896,9 @@ void HttpRequestTestObjectType::test<9>() url_base, 0, 0, - NULL, - NULL, - &handler); + HttpOptions::ptr_t(), + HttpHeaders::ptr_t(), + handlerp); ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); // Run the notification pump. @@ -907,7 +914,7 @@ void HttpRequestTestObjectType::test<9>() // Okay, request a shutdown of the servicing thread mStatus = HttpStatus(); - handle = req->requestStopThread(&handler); + handle = req->requestStopThread(handlerp); ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); // Run the notification pump again @@ -972,7 +979,8 @@ void HttpRequestTestObjectType::test<10>() // references to it after completion of this method. // Create before memory record as the string copy will bump numbers. TestHandler2 handler(this, "handler"); - + LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor); + // record the total amount of dynamically allocated memory mMemTotal = GetMemTotal(); mHandlerCalls = 0; @@ -982,7 +990,7 @@ void HttpRequestTestObjectType::test<10>() try { - // Get singletons created + // Get singletons created HttpRequest::createService(); // Start threading early so that thread memory is invariant @@ -1001,9 +1009,9 @@ void HttpRequestTestObjectType::test<10>() 0U, url_base, body, - NULL, - NULL, - &handler); + HttpOptions::ptr_t(), + HttpHeaders::ptr_t(), + handlerp); ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); // Run the notification pump. @@ -1019,7 +1027,7 @@ void HttpRequestTestObjectType::test<10>() // Okay, request a shutdown of the servicing thread mStatus = HttpStatus(); - handle = req->requestStopThread(&handler); + handle = req->requestStopThread(handlerp); ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); // Run the notification pump again @@ -1090,7 +1098,8 @@ void HttpRequestTestObjectType::test<11>() // references to it after completion of this method. // Create before memory record as the string copy will bump numbers. TestHandler2 handler(this, "handler"); - + LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor); + // record the total amount of dynamically allocated memory mMemTotal = GetMemTotal(); mHandlerCalls = 0; @@ -1100,7 +1109,7 @@ void HttpRequestTestObjectType::test<11>() try { - // Get singletons created + // Get singletons created HttpRequest::createService(); // Start threading early so that thread memory is invariant @@ -1119,9 +1128,9 @@ void HttpRequestTestObjectType::test<11>() 0U, url_base, body, - NULL, - NULL, - &handler); + HttpOptions::ptr_t(), + HttpHeaders::ptr_t(), + handlerp); ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); // Run the notification pump. @@ -1137,7 +1146,7 @@ void HttpRequestTestObjectType::test<11>() // Okay, request a shutdown of the servicing thread mStatus = HttpStatus(); - handle = req->requestStopThread(&handler); + handle = req->requestStopThread(handlerp); ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); // Run the notification pump again @@ -1209,7 +1218,8 @@ void HttpRequestTestObjectType::test<12>() // references to it after completion of this method. // Create before memory record as the string copy will bump numbers. TestHandler2 handler(this, "handler"); - + LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor); + // record the total amount of dynamically allocated memory mMemTotal = GetMemTotal(); mHandlerCalls = 0; @@ -1218,7 +1228,7 @@ void HttpRequestTestObjectType::test<12>() try { - // Get singletons created + // Get singletons created HttpRequest::createService(); // Enable tracing @@ -1239,9 +1249,9 @@ void HttpRequestTestObjectType::test<12>() url_base, 0, 0, - NULL, - NULL, - &handler); + HttpOptions::ptr_t(), + HttpHeaders::ptr_t(), + handlerp); ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); // Run the notification pump. @@ -1257,7 +1267,7 @@ void HttpRequestTestObjectType::test<12>() // Okay, request a shutdown of the servicing thread mStatus = HttpStatus(); - handle = req->requestStopThread(&handler); + handle = req->requestStopThread(handlerp); ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); // Run the notification pump again @@ -1326,17 +1336,18 @@ void HttpRequestTestObjectType::test<13>() // Create before memory record as the string copy will bump numbers. TestHandler2 handler(this, "handler"); handler.mHeadersRequired.reserve(20); // Avoid memory leak test failure - + LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor); + // record the total amount of dynamically allocated memory mMemTotal = GetMemTotal(); mHandlerCalls = 0; HttpRequest * req = NULL; - HttpOptions * opts = NULL; + HttpOptions::ptr_t opts; try { - // Get singletons created + // Get singletons created HttpRequest::createService(); // Enable tracing @@ -1350,7 +1361,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 +1375,12 @@ void HttpRequestTestObjectType::test<13>() 0, 0, opts, - NULL, - &handler); + HttpHeaders::ptr_t(), + handlerp); 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); @@ -1386,7 +1396,7 @@ void HttpRequestTestObjectType::test<13>() // Okay, request a shutdown of the servicing thread mStatus = HttpStatus(); handler.mHeadersRequired.clear(); - handle = req->requestStopThread(&handler); + handle = req->requestStopThread(handlerp); ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); // Run the notification pump again @@ -1430,11 +1440,7 @@ void HttpRequestTestObjectType::test<13>() catch (...) { stop_thread(req); - if (opts) - { - opts->release(); - opts = NULL; - } + opts.reset(); delete req; HttpRequest::destroyService(); throw; @@ -1453,18 +1459,19 @@ void HttpRequestTestObjectType::test<14>() // references to it after completion of this method. // Create before memory record as the string copy will bump numbers. TestHandler2 handler(this, "handler"); - std::string url_base(get_base_url() + "/sleep/"); // path to a 30-second sleep + LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor); + std::string url_base(get_base_url() + "/sleep/"); // path to a 30-second sleep // record the total amount of dynamically allocated memory mMemTotal = GetMemTotal(); mHandlerCalls = 0; HttpRequest * req = NULL; - HttpOptions * opts = NULL; + HttpOptions::ptr_t opts; try { - // Get singletons created + // Get singletons created HttpRequest::createService(); // Start threading early so that thread memory is invariant @@ -1475,7 +1482,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 +1494,8 @@ void HttpRequestTestObjectType::test<14>() 0, 0, opts, - NULL, - &handler); + HttpHeaders::ptr_t(), + handlerp); ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); // Run the notification pump. @@ -1504,7 +1511,7 @@ void HttpRequestTestObjectType::test<14>() // Okay, request a shutdown of the servicing thread mStatus = HttpStatus(); - handle = req->requestStopThread(&handler); + handle = req->requestStopThread(handlerp); ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); // Run the notification pump again @@ -1528,8 +1535,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 +1558,7 @@ void HttpRequestTestObjectType::test<14>() catch (...) { stop_thread(req); - if (opts) - { - opts->release(); - opts = NULL; - } + opts.reset(); delete req; HttpRequest::destroyService(); throw; @@ -1578,6 +1580,7 @@ void HttpRequestTestObjectType::test<15>() // references to it after completion of this method. // Create before memory record as the string copy will bump numbers. TestHandler2 handler(this, "handler"); + LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor); // Load and clear the string setting to preload std::string object // for memory return tests. @@ -1592,7 +1595,7 @@ void HttpRequestTestObjectType::test<15>() try { - // Get singletons created + // Get singletons created HttpRequest::createService(); // Start threading early so that thread memory is invariant @@ -1609,9 +1612,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(), + handlerp); ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); // Run the notification pump. @@ -1628,7 +1631,7 @@ void HttpRequestTestObjectType::test<15>() // Okay, request a shutdown of the servicing thread mStatus = HttpStatus(); handler.mCheckContentType.clear(); - handle = req->requestStopThread(&handler); + handle = req->requestStopThread(handlerp); ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); // Run the notification pump again @@ -1697,18 +1700,19 @@ void HttpRequestTestObjectType::test<16>() // references to it after completion of this method. // Create before memory record as the string copy will bump numbers. TestHandler2 handler(this, "handler"); + LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor); // record the total amount of dynamically allocated memory mMemTotal = GetMemTotal(); mHandlerCalls = 0; HttpRequest * req = NULL; - HttpOptions * options = NULL; - HttpHeaders * headers = NULL; + HttpOptions::ptr_t options; + HttpHeaders::ptr_t headers; try { - // Get singletons created + // Get singletons created HttpRequest::createService(); // Start threading early so that thread memory is invariant @@ -1719,7 +1723,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,8 +1780,8 @@ void HttpRequestTestObjectType::test<16>() 0U, url_base + "reflect/", options, - NULL, - &handler); + HttpHeaders::ptr_t(), + handlerp); ensure("Valid handle returned for get request", handle != LLCORE_HTTP_HANDLE_INVALID); // Run the notification pump. @@ -1792,7 +1796,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); @@ -1854,7 +1858,7 @@ void HttpRequestTestObjectType::test<16>() 47, options, headers, - &handler); + handlerp); ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); // Run the notification pump. @@ -1873,7 +1877,7 @@ void HttpRequestTestObjectType::test<16>() mStatus = HttpStatus(); handler.mHeadersRequired.clear(); handler.mHeadersDisallowed.clear(); - handle = req->requestStopThread(&handler); + handle = req->requestStopThread(handlerp); ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); // Run the notification pump again @@ -1897,17 +1901,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 +1914,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; @@ -1954,19 +1942,20 @@ void HttpRequestTestObjectType::test<17>() // references to it after completion of this method. // Create before memory record as the string copy will bump numbers. TestHandler2 handler(this, "handler"); + LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor); // record the total amount of dynamically allocated memory mMemTotal = GetMemTotal(); mHandlerCalls = 0; HttpRequest * req = NULL; - HttpOptions * options = NULL; - HttpHeaders * headers = NULL; + HttpOptions::ptr_t options; + HttpHeaders::ptr_t headers; BufferArray * ba = NULL; try { - // Get singletons created + // Get singletons created HttpRequest::createService(); // Start threading early so that thread memory is invariant @@ -1977,7 +1966,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,8 +2038,8 @@ void HttpRequestTestObjectType::test<17>() url_base + "reflect/", ba, options, - NULL, - &handler); + HttpHeaders::ptr_t(), + handlerp); ensure("Valid handle returned for get request", handle != LLCORE_HTTP_HANDLE_INVALID); ba->release(); ba = NULL; @@ -2071,7 +2060,7 @@ void HttpRequestTestObjectType::test<17>() mStatus = HttpStatus(); handler.mHeadersRequired.clear(); handler.mHeadersDisallowed.clear(); - handle = req->requestStopThread(&handler); + handle = req->requestStopThread(handlerp); ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); // Run the notification pump again @@ -2095,17 +2084,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 +2102,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; } @@ -2157,19 +2130,20 @@ void HttpRequestTestObjectType::test<18>() // references to it after completion of this method. // Create before memory record as the string copy will bump numbers. TestHandler2 handler(this, "handler"); + LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor); // record the total amount of dynamically allocated memory mMemTotal = GetMemTotal(); mHandlerCalls = 0; HttpRequest * req = NULL; - HttpOptions * options = NULL; - HttpHeaders * headers = NULL; + HttpOptions::ptr_t options; + HttpHeaders::ptr_t headers; BufferArray * ba = NULL; try { - // Get singletons created + // Get singletons created HttpRequest::createService(); // Start threading early so that thread memory is invariant @@ -2180,7 +2154,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,8 +2227,8 @@ void HttpRequestTestObjectType::test<18>() url_base + "reflect/", ba, options, - NULL, - &handler); + HttpHeaders::ptr_t(), + handlerp); ensure("Valid handle returned for get request", handle != LLCORE_HTTP_HANDLE_INVALID); ba->release(); ba = NULL; @@ -2275,7 +2249,7 @@ void HttpRequestTestObjectType::test<18>() mStatus = HttpStatus(); handler.mHeadersRequired.clear(); handler.mHeadersDisallowed.clear(); - handle = req->requestStopThread(&handler); + handle = req->requestStopThread(handlerp); ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); // Run the notification pump again @@ -2299,17 +2273,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 +2291,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; } @@ -2361,18 +2319,19 @@ void HttpRequestTestObjectType::test<19>() // references to it after completion of this method. // Create before memory record as the string copy will bump numbers. TestHandler2 handler(this, "handler"); + LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor); // record the total amount of dynamically allocated memory mMemTotal = GetMemTotal(); mHandlerCalls = 0; HttpRequest * req = NULL; - HttpOptions * options = NULL; - HttpHeaders * headers = NULL; + HttpOptions::ptr_t options; + HttpHeaders::ptr_t headers; try { - // Get singletons created + // Get singletons created HttpRequest::createService(); // Start threading early so that thread memory is invariant @@ -2383,11 +2342,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"); @@ -2460,7 +2419,7 @@ void HttpRequestTestObjectType::test<19>() url_base + "reflect/", options, headers, - &handler); + handlerp); ensure("Valid handle returned for get request", handle != LLCORE_HTTP_HANDLE_INVALID); // Run the notification pump. @@ -2478,7 +2437,7 @@ void HttpRequestTestObjectType::test<19>() mStatus = HttpStatus(); handler.mHeadersRequired.clear(); handler.mHeadersDisallowed.clear(); - handle = req->requestStopThread(&handler); + handle = req->requestStopThread(handlerp); ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); // Run the notification pump again @@ -2502,17 +2461,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 +2474,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; @@ -2559,19 +2502,21 @@ void HttpRequestTestObjectType::test<20>() // references to it after completion of this method. // Create before memory record as the string copy will bump numbers. TestHandler2 handler(this, "handler"); + LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor); // record the total amount of dynamically allocated memory mMemTotal = GetMemTotal(); mHandlerCalls = 0; HttpRequest * req = NULL; - HttpOptions * options = NULL; - HttpHeaders * headers = NULL; + HttpOptions::ptr_t options; + HttpHeaders::ptr_t headers; BufferArray * ba = NULL; try { - // Get singletons created + + // Get singletons created HttpRequest::createService(); // Start threading early so that thread memory is invariant @@ -2582,11 +2527,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"); @@ -2675,7 +2620,7 @@ void HttpRequestTestObjectType::test<20>() ba, options, headers, - &handler); + handlerp); ensure("Valid handle returned for get request", handle != LLCORE_HTTP_HANDLE_INVALID); ba->release(); ba = NULL; @@ -2696,7 +2641,7 @@ void HttpRequestTestObjectType::test<20>() mStatus = HttpStatus(); handler.mHeadersRequired.clear(); handler.mHeadersDisallowed.clear(); - handle = req->requestStopThread(&handler); + handle = req->requestStopThread(handlerp); ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); // Run the notification pump again @@ -2720,17 +2665,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 +2683,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; @@ -2782,19 +2710,20 @@ void HttpRequestTestObjectType::test<21>() // references to it after completion of this method. // Create before memory record as the string copy will bump numbers. TestHandler2 handler(this, "handler"); + LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor); // record the total amount of dynamically allocated memory mMemTotal = GetMemTotal(); mHandlerCalls = 0; HttpRequest * req = NULL; - HttpOptions * options = NULL; - HttpHeaders * headers = NULL; + HttpOptions::ptr_t options; + HttpHeaders::ptr_t headers; BufferArray * ba = NULL; try { - // Get singletons created + // Get singletons created HttpRequest::createService(); // Start threading early so that thread memory is invariant @@ -2805,11 +2734,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"); @@ -2892,7 +2821,7 @@ void HttpRequestTestObjectType::test<21>() ba, options, headers, - &handler); + handlerp); ensure("Valid handle returned for get request", handle != LLCORE_HTTP_HANDLE_INVALID); ba->release(); ba = NULL; @@ -2913,7 +2842,7 @@ void HttpRequestTestObjectType::test<21>() mStatus = HttpStatus(); handler.mHeadersRequired.clear(); handler.mHeadersDisallowed.clear(); - handle = req->requestStopThread(&handler); + handle = req->requestStopThread(handlerp); ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); // Run the notification pump again @@ -2937,17 +2866,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 +2884,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; @@ -2995,18 +2907,19 @@ void HttpRequestTestObjectType::test<22>() // references to it after completion of this method. // Create before memory record as the string copy will bump numbers. TestHandler2 handler(this, "handler"); - + LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor); + // record the total amount of dynamically allocated memory mMemTotal = GetMemTotal(); mHandlerCalls = 0; - HttpOptions * options = NULL; + HttpOptions::ptr_t options; HttpRequest * req = NULL; try { - // options set - options = new HttpOptions(); + // options set + options = HttpOptions::ptr_t(new HttpOptions()); options->setRetries(1); // Partial_File is retryable and can timeout in here // Get singletons created @@ -3035,8 +2948,8 @@ void HttpRequestTestObjectType::test<22>() 0, 25, options, - NULL, - &handler); + HttpHeaders::ptr_t(), + handlerp); ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); } @@ -3067,8 +2980,8 @@ void HttpRequestTestObjectType::test<22>() 0, 25, options, - NULL, - &handler); + HttpHeaders::ptr_t(), + handlerp); ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); } @@ -3099,8 +3012,8 @@ void HttpRequestTestObjectType::test<22>() 0, 25, options, - NULL, - &handler); + HttpHeaders::ptr_t(), + handlerp); ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); } @@ -3120,7 +3033,7 @@ void HttpRequestTestObjectType::test<22>() // ====================================== mStatus = HttpStatus(); mHandlerCalls = 0; - HttpHandle handle = req->requestStopThread(&handler); + HttpHandle handle = req->requestStopThread(handlerp); ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); // Run the notification pump again @@ -3144,11 +3057,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; @@ -3191,18 +3100,19 @@ void HttpRequestTestObjectType::test<23>() // references to it after completion of this method. // Create before memory record as the string copy will bump numbers. TestHandler2 handler(this, "handler"); - std::string url_base(get_base_url() + "/503/"); // path to 503 generators + LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor); + std::string url_base(get_base_url() + "/503/"); // path to 503 generators // record the total amount of dynamically allocated memory mMemTotal = GetMemTotal(); mHandlerCalls = 0; HttpRequest * req = NULL; - HttpOptions * opts = NULL; + HttpOptions::ptr_t opts; try { - // Get singletons created + // Get singletons created HttpRequest::createService(); // Start threading early so that thread memory is invariant @@ -3213,7 +3123,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 +3140,8 @@ void HttpRequestTestObjectType::test<23>() 0, 0, opts, - NULL, - &handler); + HttpHeaders::ptr_t(), + handlerp); std::ostringstream testtag; testtag << "Valid handle returned for 503 request #" << i; @@ -3253,7 +3163,7 @@ void HttpRequestTestObjectType::test<23>() // Okay, request a shutdown of the servicing thread mStatus = HttpStatus(); mHandlerCalls = 0; - HttpHandle handle = req->requestStopThread(&handler); + HttpHandle handle = req->requestStopThread(handlerp); ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); // Run the notification pump again @@ -3277,8 +3187,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 +3208,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_httprequestqueue.hpp b/indra/llcorehttp/tests/test_httprequestqueue.hpp index 1de2d8f9ab..ef4ce0479b 100644 --- a/indra/llcorehttp/tests/test_httprequestqueue.hpp +++ b/indra/llcorehttp/tests/test_httprequestqueue.hpp @@ -113,16 +113,16 @@ void HttpRequestqueueTestObjectType::test<3>() HttpRequestQueue * rq = HttpRequestQueue::instanceOf(); - HttpOperation * op = new HttpOpNull(); + HttpOperation::ptr_t op(new HttpOpNull()); rq->addOp(op); // transfer my refcount op = rq->fetchOp(true); // Potentially hangs the test on failure - ensure("One goes in, one comes out", NULL != op); - op->release(); + ensure("One goes in, one comes out", static_cast<bool>(op)); + op.reset(); op = rq->fetchOp(false); - ensure("Better not be two of them", NULL == op); + ensure("Better not be two of them", !op); // release the singleton, hold on to the object HttpRequestQueue::term(); @@ -144,13 +144,13 @@ void HttpRequestqueueTestObjectType::test<4>() HttpRequestQueue * rq = HttpRequestQueue::instanceOf(); - HttpOperation * op = new HttpOpNull(); + HttpOperation::ptr_t op (new HttpOpNull()); rq->addOp(op); // transfer my refcount - op = new HttpOpNull(); + op.reset(new HttpOpNull()); rq->addOp(op); // transfer my refcount - op = new HttpOpNull(); + op.reset(new HttpOpNull()); rq->addOp(op); // transfer my refcount { @@ -159,8 +159,9 @@ void HttpRequestqueueTestObjectType::test<4>() ensure("Three go in, three come out", 3 == ops.size()); op = rq->fetchOp(false); - ensure("Better not be any more of them", NULL == op); - + ensure("Better not be any more of them", !op); + op.reset(); + // release the singleton, hold on to the object HttpRequestQueue::term(); @@ -168,12 +169,13 @@ void HttpRequestqueueTestObjectType::test<4>() ensure(mMemTotal < GetMemTotal()); // Release them - while (! ops.empty()) - { - HttpOperation * op = ops.front(); - ops.erase(ops.begin()); - op->release(); - } + ops.clear(); +// while (! ops.empty()) +// { +// HttpOperation * op = ops.front(); +// ops.erase(ops.begin()); +// op->release(); +// } } // Should be clean diff --git a/indra/llcorehttp/tests/test_httpstatus.hpp b/indra/llcorehttp/tests/test_httpstatus.hpp index 0b379836c9..4502d32fe1 100644 --- 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 100644 --- 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..bc9cbc9cf1 100644 --- 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), @@ -207,11 +214,13 @@ void LLCrashLogger::gatherFiles() mFileMap["SettingsXml"] = mDebugLog["SettingsFilename"].asString(); if(mDebugLog.has("CAFilename")) { - LLCurl::setCAFile(mDebugLog["CAFilename"].asString()); + LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_CA_FILE, + LLCore::HttpRequest::GLOBAL_POLICY_ID, mDebugLog["CAFilename"].asString(), NULL); } else { - LLCurl::setCAFile(gDirUtilp->getCAFile()); + LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_CA_FILE, + LLCore::HttpRequest::GLOBAL_POLICY_ID, gDirUtilp->getCAFile(), NULL); } LL_INFOS() << "Using log file from debug log " << mFileMap["SecondLifeLog"] << LL_ENDL; @@ -220,7 +229,8 @@ void LLCrashLogger::gatherFiles() else { // Figure out the filename of the second life log - LLCurl::setCAFile(gDirUtilp->getCAFile()); + LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_CA_FILE, + LLCore::HttpRequest::GLOBAL_POLICY_ID, gDirUtilp->getCAFile(), NULL); mFileMap["SecondLifeLog"] = gDirUtilp->getExpandedFilename(LL_PATH_DUMP,"SecondLife.log"); mFileMap["SettingsXml"] = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS,"settings.xml"); @@ -389,19 +399,38 @@ 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) + + LLCore::HttpHandle handle = LLCoreHttpUtil::requestPostWithLLSD(httpRequest.get(), LLCore::HttpRequest::DEFAULT_POLICY_ID, 0, + host, data, httpOpts, LLCore::HttpHeaders::ptr_t(), LLCore::HttpHandler::ptr_t(new LLCrashLoggerHandler)); + + if (handle == LLCORE_HTTP_HANDLE_INVALID) + { + LLCore::HttpStatus status = httpRequest->getStatus(); + LL_WARNS("CRASHREPORT") << "Request POST failed to " << host << " with status of [" << + status.getType() << "]\"" << status.toString() << "\"" << LL_ENDL; + return false; + } + + while(!gBreak) { updateApplication(); // No new message, just pump the IO + httpRequest->update(0L); } if(gSent) { return gSent; } + + LL_WARNS("CRASHREPORT") << "Failed to send crash report to \"" << host << "\"" << LL_ENDL; } return gSent; } @@ -510,14 +539,12 @@ bool LLCrashLogger::sendCrashLogs() void LLCrashLogger::updateApplication(const std::string& message) { - gServicePump->pump(); - gServicePump->callback(); if (!message.empty()) LL_INFOS() << message << LL_ENDL; } bool LLCrashLogger::init() { - LLCurl::initClass(false); + LLCore::LLHttp::initialize(); // We assume that all the logs we're looking for reside on the current drive gDirUtilp->initAppDirs("SecondLife"); @@ -576,16 +603,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 100644 --- 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/llinventory/CMakeLists.txt b/indra/llinventory/CMakeLists.txt index 0a1f93bd80..68dd00d880 100644 --- a/indra/llinventory/CMakeLists.txt +++ b/indra/llinventory/CMakeLists.txt @@ -4,6 +4,7 @@ project(llinventory) include(00-Common) include(LLCommon) +include(LLCoreHttp) include(LLMath) include(LLMessage) include(LLVFS) @@ -71,7 +72,7 @@ if (LL_TESTS) LL_ADD_PROJECT_UNIT_TESTS(llinventory "${llinventory_TEST_SOURCE_FILES}") #set(TEST_DEBUG on) - set(test_libs llinventory ${LLMESSAGE_LIBRARIES} ${LLVFS_LIBRARIES} ${LLMATH_LIBRARIES} ${LLCOMMON_LIBRARIES} ${WINDOWS_LIBRARIES}) + set(test_libs llinventory ${LLMESSAGE_LIBRARIES} ${LLVFS_LIBRARIES} ${LLCOREHTTP_LIBRARIES} ${LLMATH_LIBRARIES} ${LLCOMMON_LIBRARIES} ${WINDOWS_LIBRARIES}) LL_ADD_INTEGRATION_TEST(inventorymisc "" "${test_libs}") LL_ADD_INTEGRATION_TEST(llparcel "" "${test_libs}") endif (LL_TESTS) diff --git a/indra/llkdu/llimagej2ckdu.cpp b/indra/llkdu/llimagej2ckdu.cpp index 6a8959517d..282c859e9e 100644 --- a/indra/llkdu/llimagej2ckdu.cpp +++ b/indra/llkdu/llimagej2ckdu.cpp @@ -1034,7 +1034,7 @@ all necessary level shifting, type conversion, rounding and truncation. */ val = (kdu_int32)(sp->fval*scale16); val = (val+128)>>8; // May be faster than true rounding val += 128; - if (val & ((-1)<<8)) + if (val & ((0xffffffffU)<<8)) { val = (val < 0 ? 0 : 255); } @@ -1052,7 +1052,7 @@ all necessary level shifting, type conversion, rounding and truncation. */ val = sp->ival; val = (val+offset)>>downshift; val += 128; - if (val & ((-1)<<8)) + if (val & ((0xffffffffU)<<8)) { val = (val < 0 ? 0 : 255); } @@ -1075,7 +1075,7 @@ all necessary level shifting, type conversion, rounding and truncation. */ val += (1<<(KDU_FIX_POINT-8))>>1; val >>= (KDU_FIX_POINT-8); val += 128; - if (val & ((-1)<<8)) + if (val & ((0xffffffffU)<<8)) { val = (val < 0 ? 0 : 255); } @@ -1094,7 +1094,7 @@ all necessary level shifting, type conversion, rounding and truncation. */ val = (val+offset)>>downshift; val <<= upshift; val += 128; - if (val & ((-1)<<8)) + if (val & ((0xffffffffU)<<8)) { val = (val < 0 ? 0 : 256 - (1<<upshift)); } @@ -1116,7 +1116,7 @@ all necessary level shifting, type conversion, rounding and truncation. */ val = sp->ival; val = (val+offset)>>downshift; val += 128; - if (val & ((-1)<<8)) + if (val & ((0xffffffffU)<<8)) { val = (val < 0 ? 0 : 255); } @@ -1132,7 +1132,7 @@ all necessary level shifting, type conversion, rounding and truncation. */ val = sp->ival; val <<= upshift; val += 128; - if (val & ((-1)<<8)) + if (val & ((0xffffffffU)<<8)) { val = (val < 0 ? 0 : 256 - (1<<upshift)); } diff --git a/indra/llmessage/CMakeLists.txt b/indra/llmessage/CMakeLists.txt index 0a308fbf10..87bec60d95 100644 --- 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,11 +24,10 @@ include_directories( ${LLMATH_INCLUDE_DIRS} ${LLMESSAGE_INCLUDE_DIRS} ${LLVFS_INCLUDE_DIRS} + ${JSONCPP_INCLUDE_DIR} ) set(llmessage_SOURCE_FILES - llares.cpp - llareslistener.cpp llassetstorage.cpp llavatarname.cpp llavatarnamecache.cpp @@ -38,19 +38,15 @@ set(llmessage_SOURCE_FILES llchainio.cpp llcircuit.cpp llclassifiedflags.cpp + llcoproceduremanager.cpp llcorehttputil.cpp - llcurl.cpp lldatapacker.cpp lldispatcher.cpp llexperiencecache.cpp llfiltersd2xmlrpc.cpp llhost.cpp - llhttpassetstorage.cpp - llhttpclient.cpp - llhttpclientadapter.cpp - llhttpconstants.cpp llhttpnode.cpp - llhttpsender.cpp + llhttpsdhandler.cpp llinstantmessage.cpp lliobuffer.cpp lliohttpserver.cpp @@ -74,11 +70,8 @@ set(llmessage_SOURCE_FILES llpumpio.cpp llsdappservices.cpp llsdhttpserver.cpp - llsdmessage.cpp llsdmessagebuilder.cpp llsdmessagereader.cpp - llsdrpcclient.cpp - llsdrpcserver.cpp llservicebuilder.cpp llservice.cpp llstoredmessage.cpp @@ -92,7 +85,6 @@ set(llmessage_SOURCE_FILES lltransfertargetfile.cpp lltransfertargetvfile.cpp lltrustedmessageservice.cpp - llurlrequest.cpp lluseroperation.cpp llxfer.cpp llxfer_file.cpp @@ -115,8 +107,6 @@ set(llmessage_SOURCE_FILES set(llmessage_HEADER_FILES CMakeLists.txt - llares.h - llareslistener.h llassetstorage.h llavatarname.h llavatarnamecache.h @@ -128,8 +118,8 @@ set(llmessage_HEADER_FILES llcipher.h llcircuit.h llclassifiedflags.h + llcoproceduremanager.h llcorehttputil.h - llcurl.h lldatapacker.h lldbstrings.h lldispatcher.h @@ -139,14 +129,9 @@ set(llmessage_HEADER_FILES llfiltersd2xmlrpc.h llfollowcamparams.h llhost.h - llhttpassetstorage.h - llhttpclient.h - llhttpclientinterface.h - llhttpclientadapter.h - llhttpconstants.h llhttpnode.h llhttpnodeadapter.h - llhttpsender.h + llhttpsdhandler.h llinstantmessage.h llinvite.h lliobuffer.h @@ -176,11 +161,8 @@ set(llmessage_HEADER_FILES llregionhandle.h llsdappservices.h llsdhttpserver.h - llsdmessage.h llsdmessagebuilder.h llsdmessagereader.h - llsdrpcclient.h - llsdrpcserver.h llservice.h llservicebuilder.h llstoredmessage.h @@ -196,7 +178,6 @@ set(llmessage_HEADER_FILES lltransfertargetfile.h lltransfertargetvfile.h lltrustedmessageservice.h - llurlrequest.h lluseroperation.h llvehicleparams.h llxfer.h @@ -222,17 +203,41 @@ set_source_files_properties(${llmessage_HEADER_FILES} list(APPEND llmessage_SOURCE_FILES ${llmessage_HEADER_FILES}) add_library (llmessage ${llmessage_SOURCE_FILES}) + +if (LINUX) target_link_libraries( llmessage ${CURL_LIBRARIES} ${LLCOMMON_LIBRARIES} - ${LLVFS_LIBRARES} + ${LLVFS_LIBRARIES} ${LLMATH_LIBRARIES} - ${CARES_LIBRARIES} + ${JSONCPP_LIBRARIES} ${OPENSSL_LIBRARIES} ${CRYPTO_LIBRARIES} ${XMLRPCEPI_LIBRARIES} + ${LLCOREHTTP_LIBRARIES} + ${BOOST_COROUTINE_LIBRARY} + ${BOOST_CONTEXT_LIBRARY} + ${BOOST_SYSTEM_LIBRARY} + rt ) +else (LINUX) +target_link_libraries( + llmessage + ${CURL_LIBRARIES} + ${LLCOMMON_LIBRARIES} + ${LLVFS_LIBRARIES} + ${LLMATH_LIBRARIES} + ${JSONCPP_LIBRARIES} + ${OPENSSL_LIBRARIES} + ${CRYPTO_LIBRARIES} + ${XMLRPCEPI_LIBRARIES} + ${LLCOREHTTP_LIBRARIES} + ${BOOST_COROUTINE_LIBRARY} + ${BOOST_CONTEXT_LIBRARY} + ${BOOST_SYSTEM_LIBRARY} + ) +endif(LINUX) # tests if (LL_TESTS) @@ -243,36 +248,42 @@ if (LL_TESTS) ) LL_ADD_PROJECT_UNIT_TESTS(llmessage "${llmessage_TEST_SOURCE_FILES}") + # set(TEST_DEBUG on) + +if (LINUX) set(test_libs + ${WINDOWS_LIBRARIES} + ${LLVFS_LIBRARIES} + ${LLMATH_LIBRARIES} ${CURL_LIBRARIES} + ${LLCOMMON_LIBRARIES} ${LLMESSAGE_LIBRARIES} + ${LLCOREHTTP_LIBRARIES} + ${JSONCPP_LIBRARIES} + ${BOOST_COROUTINE_LIBRARY} + ${BOOST_CONTEXT_LIBRARY} + rt + ${GOOGLEMOCK_LIBRARIES} + ) +else (LINUX) + set(test_libs ${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} ) +endif(LINUX) - 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/llares.cpp b/indra/llmessage/llares.cpp deleted file mode 100644 index 9f90ae1544..0000000000 --- a/indra/llmessage/llares.cpp +++ /dev/null @@ -1,839 +0,0 @@ -/** - * @file llares.cpp - * @author Bryan O'Sullivan - * @date 2007-08-15 - * @brief Wrapper for asynchronous DNS lookups. - * - * $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 "linden_common.h" -#include "llares.h" - -#include <ares_dns.h> -#include <ares_version.h> - -#include "apr_portable.h" -#include "apr_network_io.h" -#include "apr_poll.h" - -#include "llapr.h" -#include "llareslistener.h" - -#if defined(LL_WINDOWS) -#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally -# define ns_c_in 1 -# define NS_HFIXEDSZ 12 /* #/bytes of fixed data in header */ -# define NS_QFIXEDSZ 4 /* #/bytes of fixed data in query */ -# define NS_RRFIXEDSZ 10 /* #/bytes of fixed data in r record */ -#else -# include <arpa/nameser.h> -#endif - -LLAres::HostResponder::~HostResponder() -{ -} - -void LLAres::HostResponder::hostResult(const hostent *ent) -{ - LL_INFOS() << "LLAres::HostResponder::hostResult not implemented" << LL_ENDL; -} - -void LLAres::HostResponder::hostError(int code) -{ - LL_INFOS() << "LLAres::HostResponder::hostError " << code << ": " - << LLAres::strerror(code) << LL_ENDL; -} - -LLAres::NameInfoResponder::~NameInfoResponder() -{ -} - -void LLAres::NameInfoResponder::nameInfoResult(const char *node, - const char *service) -{ - LL_INFOS() << "LLAres::NameInfoResponder::nameInfoResult not implemented" - << LL_ENDL; -} - -void LLAres::NameInfoResponder::nameInfoError(int code) -{ - LL_INFOS() << "LLAres::NameInfoResponder::nameInfoError " << code << ": " - << LLAres::strerror(code) << LL_ENDL; -} - -LLAres::QueryResponder::~QueryResponder() -{ -} - -void LLAres::QueryResponder::queryResult(const char *buf, size_t len) -{ - LL_INFOS() << "LLAres::QueryResponder::queryResult not implemented" - << LL_ENDL; -} - -void LLAres::QueryResponder::queryError(int code) -{ - LL_INFOS() << "LLAres::QueryResponder::queryError " << code << ": " - << LLAres::strerror(code) << LL_ENDL; -} - -LLAres::LLAres() : - chan_(NULL), - mInitSuccess(false) -{ - if (ares_library_init( ARES_LIB_INIT_ALL ) != ARES_SUCCESS || - ares_init(&chan_) != ARES_SUCCESS) - { - LL_WARNS() << "Could not succesfully initialize ares!" << LL_ENDL; - return; - } - - mListener = boost::shared_ptr< LLAresListener >(new LLAresListener(this)); - - mInitSuccess = true; -} - -LLAres::~LLAres() -{ - ares_destroy(chan_); - ares_library_cleanup(); -} - -void LLAres::cancel() -{ - ares_cancel(chan_); -} - -static void host_callback_1_5(void *arg, int status, int timeouts, - struct hostent *ent) -{ - LLPointer<LLAres::HostResponder> *resp = - (LLPointer<LLAres::HostResponder> *) arg; - - if (status == ARES_SUCCESS) - { - (*resp)->hostResult(ent); - } else { - (*resp)->hostError(status); - } - - delete resp; -} - -#if ARES_VERSION_MAJOR == 1 && ARES_VERSION_MINOR == 4 -static void host_callback(void *arg, int status, struct hostent *ent) -{ - host_callback_1_5(arg, status, 0, ent); -} -#else -# define host_callback host_callback_1_5 -#endif - -void LLAres::getHostByName(const char *name, HostResponder *resp, - int family) -{ - ares_gethostbyname(chan_, name, family, host_callback, - new LLPointer<LLAres::HostResponder>(resp)); -} - -void LLAres::getSrvRecords(const std::string &name, SrvResponder *resp) -{ - search(name, RES_SRV, resp); -} - -void LLAres::rewriteURI(const std::string &uri, UriRewriteResponder *resp) -{ - if (resp && uri.size()) - { - LLURI* pURI = new LLURI(uri); - - resp->mUri = *pURI; - - delete pURI; - - if (!resp->mUri.scheme().size() || !resp->mUri.hostName().size()) - { - return; - } - - //LL_INFOS() << "LLAres::rewriteURI (" << uri << ") search: '" << "_" + resp->mUri.scheme() + "._tcp." + resp->mUri.hostName() << "'" << LL_ENDL; - - search("_" + resp->mUri.scheme() + "._tcp." + resp->mUri.hostName(), RES_SRV, resp); - - - } -} - -LLQueryResponder::LLQueryResponder() - : LLAres::QueryResponder(), - mResult(ARES_ENODATA), - mType(RES_INVALID) -{ -} - -int LLQueryResponder::parseRR(const char *buf, size_t len, const char *&pos, - LLPointer<LLDnsRecord> &r) -{ - std::string rrname; - size_t enclen; - int ret; - - // RR name. - - ret = LLAres::expandName(pos, buf, len, rrname, enclen); - if (ret != ARES_SUCCESS) - { - return ret; - } - - pos += enclen; - - if (pos + NS_RRFIXEDSZ > buf + len) - { - return ARES_EBADRESP; - } - - int rrtype = DNS_RR_TYPE(pos); - int rrclass = DNS_RR_CLASS(pos); - int rrttl = DNS_RR_TTL(pos); - int rrlen = DNS_RR_LEN(pos); - - if (rrclass != ns_c_in) - { - return ARES_EBADRESP; - } - - pos += NS_RRFIXEDSZ; - - if (pos + rrlen > buf + len) - { - return ARES_EBADRESP; - } - - switch (rrtype) - { - case RES_A: - r = new LLARecord(rrname, rrttl); - break; - case RES_NS: - r = new LLNsRecord(rrname, rrttl); - break; - case RES_CNAME: - r = new LLCnameRecord(rrname, rrttl); - break; - case RES_PTR: - r = new LLPtrRecord(rrname, rrttl); - break; - case RES_AAAA: - r = new LLAaaaRecord(rrname, rrttl); - break; - case RES_SRV: - r = new LLSrvRecord(rrname, rrttl); - break; - default: - LL_INFOS() << "LLQueryResponder::parseRR got unknown RR type " << rrtype - << LL_ENDL; - return ARES_EBADRESP; - } - - ret = r->parse(buf, len, pos, rrlen); - - if (ret == ARES_SUCCESS) - { - pos += rrlen; - } else { - r = NULL; - } - - return ret; -} - -int LLQueryResponder::parseSection(const char *buf, size_t len, - size_t count, const char *&pos, - dns_rrs_t &rrs) -{ - int ret = ARES_SUCCESS; - - for (size_t i = 0; i < count; i++) - { - LLPointer<LLDnsRecord> r; - ret = parseRR(buf, len, pos, r); - if (ret != ARES_SUCCESS) - { - break; - } - rrs.push_back(r); - } - - return ret; -} - -void LLQueryResponder::queryResult(const char *buf, size_t len) -{ - const char *pos = buf; - int qdcount = DNS_HEADER_QDCOUNT(pos); - int ancount = DNS_HEADER_ANCOUNT(pos); - int nscount = DNS_HEADER_NSCOUNT(pos); - int arcount = DNS_HEADER_ARCOUNT(pos); - int ret; - - if (qdcount == 0 || ancount + nscount + arcount == 0) - { - ret = ARES_ENODATA; - goto bail; - } - - pos += NS_HFIXEDSZ; - - for (int i = 0; i < qdcount; i++) - { - std::string ignore; - size_t enclen; - - ret = LLAres::expandName(pos, buf, len, i == 0 ? mQuery : ignore, - enclen); - if (ret != ARES_SUCCESS) - { - goto bail; - } - - pos += enclen; - - if (i == 0) - { - int t = DNS_QUESTION_TYPE(pos); - switch (t) - { - case RES_A: - case RES_NS: - case RES_CNAME: - case RES_PTR: - case RES_AAAA: - case RES_SRV: - mType = (LLResType) t; - break; - default: - LL_INFOS() << "Cannot grok query type " << t << LL_ENDL; - ret = ARES_EBADQUERY; - goto bail; - } - } - - pos += NS_QFIXEDSZ; - if (pos > buf + len) - { - ret = ARES_EBADRESP; - goto bail; - } - } - - ret = parseSection(buf, len, ancount, pos, mAnswers); - if (ret != ARES_SUCCESS) - { - goto bail; - } - - ret = parseSection(buf, len, nscount, pos, mAuthorities); - if (ret != ARES_SUCCESS) - { - goto bail; - } - - ret = parseSection(buf, len, arcount, pos, mAdditional); - -bail: - mResult = ret; - if (mResult == ARES_SUCCESS) - { - querySuccess(); - } else { - queryError(mResult); - } -} - -void LLQueryResponder::querySuccess() -{ - LL_INFOS() << "LLQueryResponder::queryResult not implemented" << LL_ENDL; -} - -void LLAres::SrvResponder::querySuccess() -{ - if (mType == RES_SRV) - { - srvResult(mAnswers); - } else { - srvError(ARES_EBADRESP); - } -} - -void LLAres::SrvResponder::queryError(int code) -{ - srvError(code); -} - -void LLAres::SrvResponder::srvResult(const dns_rrs_t &ents) -{ - LL_INFOS() << "LLAres::SrvResponder::srvResult not implemented" << LL_ENDL; - - for (size_t i = 0; i < ents.size(); i++) - { - const LLSrvRecord *s = (const LLSrvRecord *) ents[i].get(); - - LL_INFOS() << "[" << i << "] " << s->host() << ":" << s->port() - << " priority " << s->priority() - << " weight " << s->weight() - << LL_ENDL; - } -} - -void LLAres::SrvResponder::srvError(int code) -{ - LL_INFOS() << "LLAres::SrvResponder::srvError " << code << ": " - << LLAres::strerror(code) << LL_ENDL; -} - -static void nameinfo_callback_1_5(void *arg, int status, int timeouts, - char *node, char *service) -{ - LLPointer<LLAres::NameInfoResponder> *resp = - (LLPointer<LLAres::NameInfoResponder> *) arg; - - if (status == ARES_SUCCESS) - { - (*resp)->nameInfoResult(node, service); - } else { - (*resp)->nameInfoError(status); - } - - delete resp; -} - -#if ARES_VERSION_MAJOR == 1 && ARES_VERSION_MINOR == 4 -static void nameinfo_callback(void *arg, int status, char *node, char *service) -{ - nameinfo_callback_1_5(arg, status, 0, node, service); -} -#else -# define nameinfo_callback nameinfo_callback_1_5 -#endif - -void LLAres::getNameInfo(const struct sockaddr &sa, socklen_t salen, int flags, - NameInfoResponder *resp) -{ - ares_getnameinfo(chan_, &sa, salen, flags, nameinfo_callback, - new LLPointer<NameInfoResponder>(resp)); -} - -static void search_callback_1_5(void *arg, int status, int timeouts, - unsigned char *abuf, int alen) -{ - LLPointer<LLAres::QueryResponder> *resp = - (LLPointer<LLAres::QueryResponder> *) arg; - - if (status == ARES_SUCCESS) - { - (*resp)->queryResult((const char *) abuf, alen); - } else { - (*resp)->queryError(status); - } - - delete resp; -} - -#if ARES_VERSION_MAJOR == 1 && ARES_VERSION_MINOR == 4 -static void search_callback(void *arg, int status, unsigned char *abuf, - int alen) -{ - search_callback_1_5(arg, status, 0, abuf, alen); -} -#else -# define search_callback search_callback_1_5 -#endif - -void LLAres::search(const std::string &query, LLResType type, - QueryResponder *resp) -{ - ares_search(chan_, query.c_str(), ns_c_in, type, search_callback, - new LLPointer<QueryResponder>(resp)); -} - -bool LLAres::process(U64 timeout) -{ - if (!gAPRPoolp) - { - ll_init_apr(); - } - - ares_socket_t socks[ARES_GETSOCK_MAXNUM]; - apr_pollfd_t aprFds[ARES_GETSOCK_MAXNUM]; - apr_int32_t nsds = 0; - int nactive = 0; - int bitmask; - - bitmask = ares_getsock(chan_, socks, ARES_GETSOCK_MAXNUM); - - if (bitmask == 0) - { - return nsds > 0; - } - - apr_status_t status; - LLAPRPool pool; - status = pool.getStatus() ; - ll_apr_assert_status(status); - - for (int i = 0; i < ARES_GETSOCK_MAXNUM; i++) - { - if (ARES_GETSOCK_READABLE(bitmask, i)) - { - aprFds[nactive].reqevents = APR_POLLIN | APR_POLLERR; - } - else if (ARES_GETSOCK_WRITABLE(bitmask, i)) - { - aprFds[nactive].reqevents = APR_POLLOUT | APR_POLLERR; - } else { - continue; - } - - apr_socket_t *aprSock = NULL; - - status = apr_os_sock_put(&aprSock, (apr_os_sock_t *) &socks[i], pool.getAPRPool()); - if (status != APR_SUCCESS) - { - ll_apr_warn_status(status); - return nsds > 0; - } - - aprFds[nactive].desc.s = aprSock; - aprFds[nactive].desc_type = APR_POLL_SOCKET; - aprFds[nactive].p = pool.getAPRPool(); - aprFds[nactive].rtnevents = 0; - aprFds[nactive].client_data = &socks[i]; - - nactive++; - } - - if (nactive > 0) - { - status = apr_poll(aprFds, nactive, &nsds, timeout); - - if (status != APR_SUCCESS && status != APR_TIMEUP) - { - ll_apr_warn_status(status); - } - - for (int i = 0; i < nactive; i++) - { - int evts = aprFds[i].rtnevents; - int ifd = (evts & (APR_POLLIN | APR_POLLERR)) - ? *((int *) aprFds[i].client_data) : ARES_SOCKET_BAD; - int ofd = (evts & (APR_POLLOUT | APR_POLLERR)) - ? *((int *) aprFds[i].client_data) : ARES_SOCKET_BAD; - - ares_process_fd(chan_, ifd, ofd); - } - } - - return nsds > 0; -} - -bool LLAres::processAll() -{ - bool anyProcessed = false, ret; - - do { - timeval tv; - - ret = ares_timeout(chan_, NULL, &tv) != NULL; - - if (ret) - { - ret = process(tv.tv_sec * 1000000LL + tv.tv_usec); - anyProcessed |= ret; - } - } while (ret); - - return anyProcessed; -} - -int LLAres::expandName(const char *encoded, const char *abuf, size_t alen, - std::string &s, size_t &enclen) -{ - char *t; - int ret; - long e; - - ret = ares_expand_name((const unsigned char *) encoded, - (const unsigned char *) abuf, alen, &t, &e); - if (ret == ARES_SUCCESS) - { - s.assign(t); - enclen = e; - ares_free_string(t); - } - return ret; -} - -const char *LLAres::strerror(int code) -{ - return ares_strerror(code); -} - -LLAres *gAres; - -LLAres *ll_init_ares() -{ - if (gAres == NULL) - { - gAres = new LLAres(); - } - return gAres; -} - -void ll_cleanup_ares() -{ - if (gAres != NULL) - { - delete gAres; - gAres = NULL; - } -} - -LLDnsRecord::LLDnsRecord(LLResType type, const std::string &name, - unsigned ttl) - : LLRefCount(), - mType(type), - mName(name), - mTTL(ttl) -{ -} - -LLHostRecord::LLHostRecord(LLResType type, const std::string &name, - unsigned ttl) - : LLDnsRecord(type, name, ttl) -{ -} - -int LLHostRecord::parse(const char *buf, size_t len, const char *pos, - size_t rrlen) -{ - int ret; - - ret = LLAres::expandName(pos, buf, len, mHost); - if (ret != ARES_SUCCESS) - { - goto bail; - } - - ret = ARES_SUCCESS; - -bail: - return ret; -} - -LLCnameRecord::LLCnameRecord(const std::string &name, unsigned ttl) - : LLHostRecord(RES_CNAME, name, ttl) -{ -} - -LLPtrRecord::LLPtrRecord(const std::string &name, unsigned ttl) - : LLHostRecord(RES_PTR, name, ttl) -{ -} - -LLAddrRecord::LLAddrRecord(LLResType type, const std::string &name, - unsigned ttl) - : LLDnsRecord(type, name, ttl), - - mSize(0) -{ -} - -LLARecord::LLARecord(const std::string &name, unsigned ttl) - : LLAddrRecord(RES_A, name, ttl) -{ -} - -int LLARecord::parse(const char *buf, size_t len, const char *pos, - size_t rrlen) -{ - int ret; - - if (rrlen != sizeof(mSA.sin.sin_addr.s_addr)) - { - ret = ARES_EBADRESP; - goto bail; - } - - memset(&mSA, 0, sizeof(mSA)); - memcpy(&mSA.sin.sin_addr.s_addr, pos, rrlen); - mSA.sin.sin_family = AF_INET6; - mSize = sizeof(mSA.sin); - - ret = ARES_SUCCESS; - -bail: - return ret; -} - -LLAaaaRecord::LLAaaaRecord(const std::string &name, unsigned ttl) - : LLAddrRecord(RES_AAAA, name, ttl) -{ -} - -int LLAaaaRecord::parse(const char *buf, size_t len, const char *pos, - size_t rrlen) -{ - int ret; - - if (rrlen != sizeof(mSA.sin6.sin6_addr)) - { - ret = ARES_EBADRESP; - goto bail; - } - - memset(&mSA, 0, sizeof(mSA)); - memcpy(&mSA.sin6.sin6_addr.s6_addr, pos, rrlen); - mSA.sin6.sin6_family = AF_INET6; - mSize = sizeof(mSA.sin6); - - ret = ARES_SUCCESS; - -bail: - return ret; -} - -LLSrvRecord::LLSrvRecord(const std::string &name, unsigned ttl) - : LLHostRecord(RES_SRV, name, ttl), - - mPriority(0), - mWeight(0), - mPort(0) -{ -} - -int LLSrvRecord::parse(const char *buf, size_t len, const char *pos, - size_t rrlen) -{ - int ret; - - if (rrlen < 6) - { - ret = ARES_EBADRESP; - goto bail; - } - - memcpy(&mPriority, pos, 2); - memcpy(&mWeight, pos + 2, 2); - memcpy(&mPort, pos + 4, 2); - - mPriority = ntohs(mPriority); - mWeight = ntohs(mWeight); - mPort = ntohs(mPort); - - ret = LLHostRecord::parse(buf, len, pos + 6, rrlen - 6); - -bail: - return ret; -} - -LLNsRecord::LLNsRecord(const std::string &name, unsigned ttl) - : LLHostRecord(RES_NS, name, ttl) -{ -} - -void LLAres::UriRewriteResponder::queryError(int code) -{ - std::vector<std::string> uris; - uris.push_back(mUri.asString()); - rewriteResult(uris); -} - -void LLAres::UriRewriteResponder::querySuccess() -{ - std::vector<std::string> uris; - - if (mType != RES_SRV) - { - goto bail; - } - - for (size_t i = 0; i < mAnswers.size(); i++) - { - const LLSrvRecord *r = (const LLSrvRecord *) mAnswers[i].get(); - - if (r->type() == RES_SRV) - { - // Check the domain in the response to ensure that it's - // the same as the domain in the request, so that bad guys - // can't forge responses that point to their own login - // servers with their own certificates. - - // Hard-coding the domain to check here is a bit of a - // hack. Hoist it to an outer caller if anyone ever needs - // this functionality on other domains. - - static const std::string domain(".lindenlab.com"); - const std::string &host = r->host(); - - std::string::size_type s = host.find(domain) + domain.length(); - - if (s != host.length() && s != host.length() - 1) - { - continue; - } - - LLURI uri(mUri.scheme(), - mUri.userName(), - mUri.password(), - r->host(), - mUri.defaultPort() ? r->port() : mUri.hostPort(), - mUri.escapedPath(), - mUri.escapedQuery()); - uris.push_back(uri.asString()); - } - } - - if (!uris.empty()) - { - goto done; - } - -bail: - uris.push_back(mUri.asString()); - -done: - rewriteResult(uris); -} - -void LLAres::UriRewriteResponder::rewriteResult( - const std::vector<std::string> &uris) -{ - LL_INFOS() << "LLAres::UriRewriteResponder::rewriteResult not implemented" - << LL_ENDL; - - for (size_t i = 0; i < uris.size(); i++) - { - LL_INFOS() << "[" << i << "] " << uris[i] << LL_ENDL; - } -} diff --git a/indra/llmessage/llares.h b/indra/llmessage/llares.h deleted file mode 100644 index c727363b60..0000000000 --- a/indra/llmessage/llares.h +++ /dev/null @@ -1,583 +0,0 @@ -/** - * @file llares.h - * @author Bryan O'Sullivan - * @date 2007-08-15 - * @brief Wrapper for asynchronous DNS lookups. - * - * $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_LLARES_H -#define LL_LLARES_H - -#ifdef LL_WINDOWS -// ares.h is broken on windows in that it depends on types defined in ws2tcpip.h -// we need to include them first to work around it, but the headers issue warnings -# pragma warning(push) -# pragma warning(disable:4996) -# include <winsock2.h> -# include <ws2tcpip.h> -# pragma warning(pop) -#endif - -#ifdef LL_USESYSTEMLIBS -# include <ares.h> -#else -# include <ares/ares.h> -#endif - -#include "llpointer.h" -#include "llrefcount.h" -#include "lluri.h" - -#include <boost/shared_ptr.hpp> - -class LLQueryResponder; -class LLAresListener; - -/** - * @brief Supported DNS RR types. - */ -enum LLResType -{ - RES_INVALID = 0, /**< Cookie. */ - RES_A = 1, /**< "A" record. IPv4 address. */ - RES_NS = 2, /**< "NS" record. Authoritative server. */ - RES_CNAME = 5, /**< "CNAME" record. Canonical name. */ - RES_PTR = 12, /**< "PTR" record. Domain name pointer. */ - RES_AAAA = 28, /**< "AAAA" record. IPv6 Address. */ - RES_SRV = 33, /**< "SRV" record. Server Selection. */ - RES_MAX = 65536 /**< Sentinel; RR types are 16 bits wide. */ -}; - -/** - * @class LLDnsRecord - * @brief Base class for all DNS RR types. - */ -class LLDnsRecord : public LLRefCount -{ -protected: - friend class LLQueryResponder; - - LLResType mType; - std::string mName; - unsigned mTTL; - - virtual int parse(const char *buf, size_t len, const char *pos, - size_t rrlen) = 0; - - LLDnsRecord(LLResType type, const std::string &name, unsigned ttl); - -public: - /** - * @brief Record name. - */ - const std::string &name() const { return mName; } - - /** - * @brief Time-to-live value, in seconds. - */ - unsigned ttl() const { return mTTL; } - - /** - * @brief RR type. - */ - LLResType type() const { return mType; } -}; - -/** - * @class LLAddrRecord - * @brief Base class for address-related RRs. - */ -class LLAddrRecord : public LLDnsRecord -{ -protected: - friend class LLQueryResponder; - - LLAddrRecord(LLResType type, const std::string &name, unsigned ttl); - - union - { - sockaddr sa; - sockaddr_in sin; - sockaddr_in6 sin6; - } mSA; - - socklen_t mSize; - -public: - /** - * @brief Generic socket address. - */ - const sockaddr &addr() const { return mSA.sa; } - - /** - * @brief Size of the socket structure. - */ - socklen_t size() const { return mSize; } -}; - -/** - * @class LLARecord - * @brief A RR, for IPv4 addresses. - */ -class LLARecord : public LLAddrRecord -{ -protected: - friend class LLQueryResponder; - - LLARecord(const std::string &name, unsigned ttl); - - int parse(const char *buf, size_t len, const char *pos, size_t rrlen); - -public: - /** - * @brief Socket address. - */ - const sockaddr_in &addr_in() const { return mSA.sin; } -}; - -/** - * @class LLAaaaRecord - * @brief AAAA RR, for IPv6 addresses. - */ -class LLAaaaRecord : public LLAddrRecord -{ -protected: - friend class LLQueryResponder; - - LLAaaaRecord(const std::string &name, unsigned ttl); - - int parse(const char *buf, size_t len, const char *pos, size_t rrlen); - -public: - /** - * @brief Socket address. - */ - const sockaddr_in6 &addr_in6() const { return mSA.sin6; } -}; - -/** - * @class LLHostRecord - * @brief Base class for host-related RRs. - */ -class LLHostRecord : public LLDnsRecord -{ -protected: - LLHostRecord(LLResType type, const std::string &name, unsigned ttl); - - int parse(const char *buf, size_t len, const char *pos, size_t rrlen); - - std::string mHost; - -public: - /** - * @brief Host name. - */ - const std::string &host() const { return mHost; } -}; - -/** - * @class LLCnameRecord - * @brief CNAME RR. - */ -class LLCnameRecord : public LLHostRecord -{ -protected: - friend class LLQueryResponder; - - LLCnameRecord(const std::string &name, unsigned ttl); -}; - -/** - * @class LLPtrRecord - * @brief PTR RR. - */ -class LLPtrRecord : public LLHostRecord -{ -protected: - friend class LLQueryResponder; - - LLPtrRecord(const std::string &name, unsigned ttl); -}; - -/** - * @class LLSrvRecord - * @brief SRV RR. - */ -class LLSrvRecord : public LLHostRecord -{ -protected: - U16 mPriority; - U16 mWeight; - U16 mPort; - - int parse(const char *buf, size_t len, const char *pos, size_t rrlen); - -public: - LLSrvRecord(const std::string &name, unsigned ttl); - - /** - * @brief Service priority. - */ - U16 priority() const { return mPriority; } - - /** - * @brief Service weight. - */ - U16 weight() const { return mWeight; } - - /** - * @brief Port number of service. - */ - U16 port() const { return mPort; } - - /** - * @brief Functor for sorting SRV records by priority. - */ - struct ComparePriorityLowest - { - bool operator()(const LLSrvRecord& lhs, const LLSrvRecord& rhs) - { - return lhs.mPriority < rhs.mPriority; - } - }; -}; - -/** - * @class LLNsRecord - * @brief NS RR. - */ -class LLNsRecord : public LLHostRecord -{ -public: - LLNsRecord(const std::string &name, unsigned ttl); -}; - -class LLQueryResponder; - -/** - * @class LLAres - * @brief Asynchronous address resolver. - */ -class LLAres -{ -public: - /** - * @class HostResponder - * @brief Base class for responding to hostname lookups. - * @see LLAres::getHostByName - */ - class HostResponder : public LLRefCount - { - public: - virtual ~HostResponder(); - - virtual void hostResult(const hostent *ent); - virtual void hostError(int code); - }; - - /** - * @class NameInfoResponder - * @brief Base class for responding to address lookups. - * @see LLAres::getNameInfo - */ - class NameInfoResponder : public LLRefCount - { - public: - virtual ~NameInfoResponder(); - - virtual void nameInfoResult(const char *node, const char *service); - virtual void nameInfoError(int code); - }; - - /** - * @class QueryResponder - * @brief Base class for responding to custom searches. - * @see LLAres::search - */ - class QueryResponder : public LLRefCount - { - public: - virtual ~QueryResponder(); - - virtual void queryResult(const char *buf, size_t len); - virtual void queryError(int code); - }; - - class SrvResponder; - class UriRewriteResponder; - - LLAres(); - - ~LLAres(); - - /** - * Cancel all outstanding requests. The error methods of the - * corresponding responders will be called, with ARES_ETIMEOUT. - */ - void cancel(); - - /** - * Look up the address of a host. - * - * @param name name of host to look up - * @param resp responder to call with result - * @param family AF_INET for IPv4 addresses, AF_INET6 for IPv6 - */ - void getHostByName(const std::string &name, HostResponder *resp, - int family = AF_INET) { - getHostByName(name.c_str(), resp, family); - } - - /** - * Look up the address of a host. - * - * @param name name of host to look up - * @param resp responder to call with result - * @param family AF_INET for IPv4 addresses, AF_INET6 for IPv6 - */ - void getHostByName(const char *name, HostResponder *resp, - int family = PF_INET); - - /** - * Look up the name associated with a socket address. - * - * @param sa socket address to look up - * @param salen size of socket address - * @param flags flags to use - * @param resp responder to call with result - */ - void getNameInfo(const struct sockaddr &sa, socklen_t salen, int flags, - NameInfoResponder *resp); - - /** - * Look up SRV (service location) records for a service name. - * - * @param name service name (e.g. "_https._tcp.login.agni.lindenlab.com") - * @param resp responder to call with result - */ - void getSrvRecords(const std::string &name, SrvResponder *resp); - - /** - * Rewrite a URI, using SRV (service location) records for its - * protocol if available. If no SRV records are published, the - * existing URI is handed to the responder. - * - * @param uri URI to rewrite - * @param resp responder to call with result - */ - void rewriteURI(const std::string &uri, - UriRewriteResponder *resp); - - /** - * Start a custom search. - * - * @param query query to make - * @param type type of query to perform - * @param resp responder to call with result - */ - void search(const std::string &query, LLResType type, - QueryResponder *resp); - - /** - * Process any outstanding queries. This method takes an optional - * timeout parameter (specified in microseconds). If provided, it - * will block the calling thread for that length of time to await - * possible responses. A timeout of zero will return immediately - * if there are no responses or timeouts to process. - * - * @param timeoutUsecs number of microseconds to block before timing out - * @return whether any responses were processed - */ - bool process(U64 timeoutUsecs = 0); - - /** - * Process all outstanding queries, blocking the calling thread - * until all have either been responded to or timed out. - * - * @return whether any responses were processed - */ - bool processAll(); - - /** - * Expand a DNS-encoded compressed string into a normal string. - * - * @param encoded the encoded name (null-terminated) - * @param abuf the response buffer in which the string is embedded - * @param alen the length of the response buffer - * @param s the string into which to place the result - * @return ARES_SUCCESS on success, otherwise an error indicator - */ - static int expandName(const char *encoded, const char *abuf, size_t alen, - std::string &s) { - size_t ignore; - return expandName(encoded, abuf, alen, s, ignore); - } - - static int expandName(const char *encoded, const char *abuf, size_t alen, - std::string &s, size_t &enclen); - - /** - * Return a string describing an error code. - */ - static const char *strerror(int code); - - bool isInitialized(void) { return mInitSuccess; } - -protected: - ares_channel chan_; - bool mInitSuccess; - // boost::scoped_ptr would actually fit the requirement better, but it - // can't handle incomplete types as boost::shared_ptr can. - boost::shared_ptr<LLAresListener> mListener; -}; - -/** - * An ordered collection of DNS resource records. - */ -typedef std::vector<LLPointer<LLDnsRecord> > dns_rrs_t; - -/** - * @class LLQueryResponder - * @brief Base class for friendly handling of DNS query responses. - * - * This class parses a DNS response and represents it in a friendly - * manner. - * - * @see LLDnsRecord - * @see LLARecord - * @see LLNsRecord - * @see LLCnameRecord - * @see LLPtrRecord - * @see LLAaaaRecord - * @see LLSrvRecord - */ -class LLQueryResponder : public LLAres::QueryResponder -{ -protected: - int mResult; - std::string mQuery; - LLResType mType; - - dns_rrs_t mAnswers; - dns_rrs_t mAuthorities; - dns_rrs_t mAdditional; - - /** - * Parse a single RR. - */ - int parseRR(const char *buf, size_t len, const char *&pos, - LLPointer<LLDnsRecord> &r); - /** - * Parse one section of a response. - */ - int parseSection(const char *buf, size_t len, - size_t count, const char *& pos, dns_rrs_t &rrs); - - void queryResult(const char *buf, size_t len); - virtual void querySuccess(); - -public: - LLQueryResponder(); - - /** - * Indicate whether the response could be parsed successfully. - */ - bool valid() const { return mResult == ARES_SUCCESS; } - - /** - * The more detailed result of parsing the response. - */ - int result() const { return mResult; } - - /** - * Return the query embedded in the response. - */ - const std::string &query() const { return mQuery; } - - /** - * Return the contents of the "answers" section of the response. - */ - const dns_rrs_t &answers() const { return mAnswers; } - - /** - * Return the contents of the "authorities" section of the - * response. - */ - const dns_rrs_t &authorities() const { return mAuthorities; } - - /** - * Return the contents of the "additional records" section of the - * response. - */ - const dns_rrs_t &additional() const { return mAdditional; } -}; - -/** - * @class LLAres::SrvResponder - * @brief Class for handling SRV query responses. - */ -class LLAres::SrvResponder : public LLQueryResponder -{ -public: - friend void LLAres::getSrvRecords(const std::string &name, - SrvResponder *resp); - void querySuccess(); - void queryError(int code); - - virtual void srvResult(const dns_rrs_t &ents); - virtual void srvError(int code); -}; - -/** - * @class LLAres::UriRewriteResponder - * @brief Class for handling URI rewrites based on SRV records. - */ -class LLAres::UriRewriteResponder : public LLQueryResponder -{ -protected: - LLURI mUri; - -public: - friend void LLAres::rewriteURI(const std::string &uri, - UriRewriteResponder *resp); - void querySuccess(); - void queryError(int code); - - virtual void rewriteResult(const std::vector<std::string> &uris); -}; - -/** - * Singleton responder. - */ -extern LLAres *gAres; - -/** - * Set up the singleton responder. It's safe to call this more than - * once from within a single thread, but this function is not - * thread safe. - */ -extern LLAres *ll_init_ares(); -extern void ll_cleanup_ares(); - -#endif // LL_LLARES_H diff --git a/indra/llmessage/llareslistener.cpp b/indra/llmessage/llareslistener.cpp deleted file mode 100644 index 3d65906b98..0000000000 --- a/indra/llmessage/llareslistener.cpp +++ /dev/null @@ -1,104 +0,0 @@ -/** - * @file llareslistener.cpp - * @author Nat Goodspeed - * @date 2009-03-18 - * @brief Implementation for llareslistener. - * - * $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 "linden_common.h" -// associated header -#include "llareslistener.h" -// STL headers -// std headers -// external library headers -// other Linden headers -#include "llares.h" -#include "llerror.h" -#include "llevents.h" -#include "llsdutil.h" - -LLAresListener::LLAresListener(LLAres* llares): - LLEventAPI("LLAres", - "LLAres listener to request DNS operations"), - mAres(llares) -{ - // add() every method we want to be able to invoke via this event API. - // Optional last parameter validates expected LLSD request structure. - add("rewriteURI", - "Given [\"uri\"], return on [\"reply\"] an array of alternative URIs.\n" - "On failure, returns an array containing only the original URI, so\n" - "failure case can be processed like success case.", - &LLAresListener::rewriteURI, - LLSD().with("uri", LLSD()).with("reply", LLSD())); -} - -/// This UriRewriteResponder subclass packages returned URIs as an LLSD -/// array to send back to the requester. -class UriRewriteResponder: public LLAres::UriRewriteResponder -{ -public: - /** - * Specify the request, containing the event pump name on which to send - * the reply. - */ - UriRewriteResponder(const LLSD& request): - mReqID(request), - mPumpName(request["reply"]) - {} - - /// Called by base class with results. This is called in both the - /// success and error cases. On error, the calling logic passes the - /// original URI. - virtual void rewriteResult(const std::vector<std::string>& uris) - { - LLSD result; - for (std::vector<std::string>::const_iterator ui(uris.begin()), uend(uris.end()); - ui != uend; ++ui) - { - result.append(*ui); - } - // This call knows enough to avoid trying to insert a map key into an - // LLSD array. It's there so that if, for any reason, we ever decide - // to change the response from array to map, it will Just Start Working. - mReqID.stamp(result); - LLEventPumps::instance().obtain(mPumpName).post(result); - } - -private: - LLReqID mReqID; - const std::string mPumpName; -}; - -void LLAresListener::rewriteURI(const LLSD& data) -{ - if (mAres) - { - mAres->rewriteURI(data["uri"], new UriRewriteResponder(data)); - } - else - { - LL_INFOS() << "LLAresListener::rewriteURI requested without Ares present. Ignoring: " << data << LL_ENDL; - } -} diff --git a/indra/llmessage/llareslistener.h b/indra/llmessage/llareslistener.h deleted file mode 100644 index 780dcdd9c5..0000000000 --- a/indra/llmessage/llareslistener.h +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file llareslistener.h - * @author Nat Goodspeed - * @date 2009-03-18 - * @brief LLEventPump API for LLAres. This header doesn't actually define the - * API; the API is defined by the pump name on which this class - * listens, and by the expected content of LLSD it receives. - * - * $LicenseInfo:firstyear=2009&license=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_LLARESLISTENER_H) -#define LL_LLARESLISTENER_H - -#include "lleventapi.h" - -class LLAres; -class LLSD; - -/// Listen on an LLEventPump with specified name for LLAres request events. -class LLAresListener: public LLEventAPI -{ -public: - /// Bind the LLAres instance to use (e.g. gAres) - LLAresListener(LLAres* llares); - -private: - /// command["op"] == "rewriteURI" - void rewriteURI(const LLSD& data); - - LLAres* mAres; -}; - -#endif /* ! defined(LL_LLARESLISTENER_H) */ diff --git a/indra/llmessage/llassetstorage.cpp b/indra/llmessage/llassetstorage.cpp index cab3073eca..bcf4e52b8f 100644 --- a/indra/llmessage/llassetstorage.cpp +++ b/indra/llmessage/llassetstorage.cpp @@ -306,7 +306,7 @@ LLAssetStorage::LLAssetStorage(LLMessageSystem *msg, LLXferManager *xfer, LLVFS LLAssetStorage::LLAssetStorage(LLMessageSystem *msg, LLXferManager *xfer, LLVFS *vfs, LLVFS *static_vfs) { - _init(msg, xfer, vfs, static_vfs, LLHost::invalid); + _init(msg, xfer, vfs, static_vfs, LLHost()); } @@ -1629,3 +1629,4 @@ void LLAssetStorage::markAssetToxic( const LLUUID& uuid ) } } + diff --git a/indra/llmessage/llassetstorage.h b/indra/llmessage/llassetstorage.h index 8a4d41565f..4be677a4b0 100644 --- a/indra/llmessage/llassetstorage.h +++ b/indra/llmessage/llassetstorage.h @@ -27,7 +27,6 @@ #ifndef LL_LLASSETSTORAGE_H #define LL_LLASSETSTORAGE_H - #include <string> #include "lluuid.h" diff --git a/indra/llmessage/llavatarnamecache.cpp b/indra/llmessage/llavatarnamecache.cpp index 360d239e61..1ca5f58ae2 100644 --- 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.getAndSuspend(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_DEBUGS("AvNameCache") << " getting " << ids << " ids" << LL_ENDL; - LLHTTPClient::get(url, new LLAvatarNameResponder(agent_ids)); + if (!url.empty()) + { + LL_DEBUGS("AvNameCache") << "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 100644 --- 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/llcachename.cpp b/indra/llmessage/llcachename.cpp index daf3e0b4de..66bd85f4e6 100644 --- a/indra/llmessage/llcachename.cpp +++ b/indra/llmessage/llcachename.cpp @@ -259,7 +259,7 @@ LLCacheName::~LLCacheName() } LLCacheName::Impl::Impl(LLMessageSystem* msg) - : mMsg(msg), mUpstreamHost(LLHost::invalid) + : mMsg(msg), mUpstreamHost(LLHost()) { mMsg->setHandlerFuncFast( _PREHASH_UUIDNameRequest, handleUUIDNameRequest, (void**)this); diff --git a/indra/llmessage/llcoproceduremanager.cpp b/indra/llmessage/llcoproceduremanager.cpp new file mode 100644 index 0000000000..f0fe1ab01b --- /dev/null +++ b/indra/llmessage/llcoproceduremanager.cpp @@ -0,0 +1,405 @@ +/** +* @file LLCoprocedurePool.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 "linden_common.h" +#include "llcoproceduremanager.h" +#include <boost/assign.hpp> + +//========================================================================= +// Map of pool sizes for known pools +// *TODO$: When C++11 this can be initialized here as follows: +// = {{"AIS", 25}, {"Upload", 1}} +static std::map<std::string, U32> DefaultPoolSizes = + boost::assign::map_list_of + (std::string("Upload"), 1) + (std::string("AIS"), 25); + +#define DEFAULT_POOL_SIZE 5 + +//========================================================================= +class LLCoprocedurePool: private boost::noncopyable +{ +public: + typedef LLCoprocedureManager::CoProcedure_t CoProcedure_t; + + LLCoprocedurePool(const std::string &name, size_t size); + virtual ~LLCoprocedurePool(); + + /// 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 cancelSuspendedOperation() on the associated HttpAdapter + /// If it has not yet been dequeued it is simply removed from the queue. + bool 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; + + std::string mPoolName; + size_t mPoolSize; + 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); + +}; + +//========================================================================= +LLCoprocedureManager::LLCoprocedureManager() +{ +} + +LLCoprocedureManager::~LLCoprocedureManager() +{ + +} + +LLCoprocedureManager::poolPtr_t LLCoprocedureManager::initializePool(const std::string &poolName) +{ + // Attempt to look up a pool size in the configuration. If found use that + std::string keyName = "PoolSize" + poolName; + int size = 0; + + if (poolName.empty()) + LL_ERRS("CoprocedureManager") << "Poolname must not be empty" << LL_ENDL; + + if (mPropertyQueryFn && !mPropertyQueryFn.empty()) + { + size = mPropertyQueryFn(keyName); + } + + if (size == 0) + { // if not found grab the know default... if there is no known + // default use a reasonable number like 5. + std::map<std::string, U32>::iterator it = DefaultPoolSizes.find(poolName); + if (it == DefaultPoolSizes.end()) + size = DEFAULT_POOL_SIZE; + else + size = (*it).second; + + if (mPropertyDefineFn && !mPropertyDefineFn.empty()) + mPropertyDefineFn(keyName, size, "Coroutine Pool size for " + poolName); + LL_WARNS() << "LLCoprocedureManager: No setting for \"" << keyName << "\" setting pool size to default of " << size << LL_ENDL; + } + + poolPtr_t pool(new LLCoprocedurePool(poolName, size)); + mPoolMap.insert(poolMap_t::value_type(poolName, pool)); + + if (!pool) + LL_ERRS("CoprocedureManager") << "Unable to create pool named \"" << poolName << "\" FATAL!" << LL_ENDL; + return pool; +} + +//------------------------------------------------------------------------- +LLUUID LLCoprocedureManager::enqueueCoprocedure(const std::string &pool, const std::string &name, CoProcedure_t proc) +{ + // Attempt to find the pool and enqueue the procedure. If the pool does + // not exist, create it. + poolPtr_t targetPool; + poolMap_t::iterator it = mPoolMap.find(pool); + + if (it == mPoolMap.end()) + { + targetPool = initializePool(pool); + } + else + { + targetPool = (*it).second; + } + + return targetPool->enqueueCoprocedure(name, proc); +} + +void LLCoprocedureManager::cancelCoprocedure(const LLUUID &id) +{ + for (poolMap_t::const_iterator it = mPoolMap.begin(); it != mPoolMap.end(); ++it) + { + if ((*it).second->cancelCoprocedure(id)) + return; + } + LL_INFOS() << "Coprocedure not found." << LL_ENDL; +} + +void LLCoprocedureManager::shutdown(bool hardShutdown) +{ + for (poolMap_t::const_iterator it = mPoolMap.begin(); it != mPoolMap.end(); ++it) + { + (*it).second->shutdown(hardShutdown); + } + mPoolMap.clear(); +} + +void LLCoprocedureManager::setPropertyMethods(SettingQuery_t queryfn, SettingUpdate_t updatefn) +{ + mPropertyQueryFn = queryfn; + mPropertyDefineFn = updatefn; +} + +//------------------------------------------------------------------------- +size_t LLCoprocedureManager::countPending() const +{ + size_t count = 0; + for (poolMap_t::const_iterator it = mPoolMap.begin(); it != mPoolMap.end(); ++it) + { + count += (*it).second->countPending(); + } + return count; +} + +size_t LLCoprocedureManager::countPending(const std::string &pool) const +{ + poolMap_t::const_iterator it = mPoolMap.find(pool); + + if (it == mPoolMap.end()) + return 0; + return (*it).second->countPending(); +} + +size_t LLCoprocedureManager::countActive() const +{ + size_t count = 0; + for (poolMap_t::const_iterator it = mPoolMap.begin(); it != mPoolMap.end(); ++it) + { + count += (*it).second->countActive(); + } + return count; +} + +size_t LLCoprocedureManager::countActive(const std::string &pool) const +{ + poolMap_t::const_iterator it = mPoolMap.find(pool); + + if (it == mPoolMap.end()) + return 0; + return (*it).second->countActive(); +} + +size_t LLCoprocedureManager::count() const +{ + size_t count = 0; + for (poolMap_t::const_iterator it = mPoolMap.begin(); it != mPoolMap.end(); ++it) + { + count += (*it).second->count(); + } + return count; +} + +size_t LLCoprocedureManager::count(const std::string &pool) const +{ + poolMap_t::const_iterator it = mPoolMap.find(pool); + + if (it == mPoolMap.end()) + return 0; + return (*it).second->count(); +} + +//========================================================================= +LLCoprocedurePool::LLCoprocedurePool(const std::string &poolName, size_t size): + mPoolName(poolName), + mPoolSize(size), + mPendingCoprocs(), + mShutdown(false), + mWakeupTrigger("CoprocedurePool" + poolName, true), + mCoroMapping(), + mHTTPPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID) +{ + for (size_t count = 0; count < mPoolSize; ++count) + { + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter( mPoolName + "Adapter", mHTTPPolicy)); + + std::string pooledCoro = LLCoros::instance().launch("LLCoprocedurePool("+mPoolName+")::coprocedureInvokerCoro", + boost::bind(&LLCoprocedurePool::coprocedureInvokerCoro, this, httpAdapter)); + + mCoroMapping.insert(CoroAdapterMap_t::value_type(pooledCoro, httpAdapter)); + } + + LL_INFOS() << "Created coprocedure pool named \"" << mPoolName << "\" with " << size << " items." << LL_ENDL; + + mWakeupTrigger.post(LLSD()); +} + +LLCoprocedurePool::~LLCoprocedurePool() +{ + shutdown(); +} + +//------------------------------------------------------------------------- +void LLCoprocedurePool::shutdown(bool hardShutdown) +{ + CoroAdapterMap_t::iterator it; + + for (it = mCoroMapping.begin(); it != mCoroMapping.end(); ++it) + { + if (hardShutdown) + { + LLCoros::instance().kill((*it).first); + } + if ((*it).second) + { + (*it).second->cancelSuspendedOperation(); + } + } + + mShutdown = true; + mCoroMapping.clear(); + mPendingCoprocs.clear(); +} + +//------------------------------------------------------------------------- +LLUUID LLCoprocedurePool::enqueueCoprocedure(const std::string &name, LLCoprocedurePool::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() << " in pool \"" << mPoolName << "\"" << LL_ENDL; + + mWakeupTrigger.post(LLSD()); + + return id; +} + +bool LLCoprocedurePool::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() << " in pool \"" << mPoolName << "\"" << LL_ENDL; + (*itActive).second->cancelSuspendedOperation(); + mActiveCoprocs.erase(itActive); + return true; + } + + 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() << " in pool \"" << mPoolName << "\"" << LL_ENDL; + mPendingCoprocs.erase(it); + return true; + } + } + + LL_INFOS() << "Coprocedure with Id=" << id.asString() << " was not found in pool \"" << mPoolName << "\"" << LL_ENDL; + return false; +} + +//------------------------------------------------------------------------- +void LLCoprocedurePool::coprocedureInvokerCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter) +{ + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + + while (!mShutdown) + { + llcoro::suspendUntilEventOn(mWakeupTrigger); + if (mShutdown) + break; + + while (!mPendingCoprocs.empty()) + { + QueuedCoproc::ptr_t coproc = mPendingCoprocs.front(); + mPendingCoprocs.pop_front(); + ActiveCoproc_t::iterator itActive = mActiveCoprocs.insert(ActiveCoproc_t::value_type(coproc->mId, httpAdapter)).first; + + LL_INFOS() << "Dequeued and invoking coprocedure(" << coproc->mName << ") with id=" << coproc->mId.asString() << " in pool \"" << mPoolName << "\"" << 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 << "." << " in pool \"" << mPoolName << "\"" << LL_ENDL; + } + + LL_INFOS() << "Finished coprocedure(" << coproc->mName << ")" << " in pool \"" << mPoolName << "\"" << LL_ENDL; + + mActiveCoprocs.erase(itActive); + } + } +} diff --git a/indra/llmessage/llcoproceduremanager.h b/indra/llmessage/llcoproceduremanager.h new file mode 100644 index 0000000000..497367b80c --- /dev/null +++ b/indra/llmessage/llcoproceduremanager.h @@ -0,0 +1,98 @@ +/** +* @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 LLCoprocedurePool; + +class LLCoprocedureManager : public LLSingleton < LLCoprocedureManager > +{ + friend class LLSingleton < LLCoprocedureManager > ; + +public: + typedef boost::function<U32(const std::string &)> SettingQuery_t; + typedef boost::function<void(const std::string &, U32, const std::string &)> SettingUpdate_t; + + 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 &pool, 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); + + void setPropertyMethods(SettingQuery_t queryfn, SettingUpdate_t updatefn); + + /// Returns the number of coprocedures in the queue awaiting processing. + /// + size_t countPending() const; + size_t countPending(const std::string &pool) const; + + /// Returns the number of coprocedures actively being processed. + /// + size_t countActive() const; + size_t countActive(const std::string &pool) const; + + /// Returns the total number of coprocedures either queued or in active processing. + /// + size_t count() const; + size_t count(const std::string &pool) const; + +private: + + typedef boost::shared_ptr<LLCoprocedurePool> poolPtr_t; + typedef std::map<std::string, poolPtr_t> poolMap_t; + + poolMap_t mPoolMap; + + poolPtr_t initializePool(const std::string &poolName); + + SettingQuery_t mPropertyQueryFn; + SettingUpdate_t mPropertyDefineFn; +}; + +#endif diff --git a/indra/llmessage/llcorehttputil.cpp b/indra/llmessage/llcorehttputil.cpp index ee80b0fd94..9a23ede81b 100644 --- a/indra/llmessage/llcorehttputil.cpp +++ b/indra/llmessage/llcorehttputil.cpp @@ -28,10 +28,18 @@ #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" // JSON +#include "writer.h" // JSON +#include "llvfile.h" +#include "message.h" // for getting the port using namespace LLCore; @@ -39,101 +47,1269 @@ using namespace LLCore; namespace LLCoreHttpUtil { +const F32 HTTP_REQUEST_EXPIRY_SECS = 60.0f; + + +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, + const HttpHandler::ptr_t &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, + const HttpHandler::ptr_t &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, + const HttpHandler::ptr_t &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) + { + bool parseSuccess(false); + result = LLSD::emptyMap(); + LLCore::HttpStatus::type_enum_t errType = status.getType(); + + LL_WARNS() + << "\n--------------------------------------------------------------------------\n" + << " Error[" << status.toTerseString() << "] cannot access url '" << response->getRequestURL() + << "' because " << status.toString() + << "\n--------------------------------------------------------------------------" + << LL_ENDL; + if ((errType >= 400) && (errType < 500)) + { + LLSD body = this->parseBody(response, parseSuccess); + if (!body.isUndefined()) + { + if (!body.isMap()) + { + result[HttpCoroutineAdapter::HTTP_RESULTS_CONTENT] = body; + } + else + { + result = body; + } + } + + } + } + else + { + result = this->handleSuccess(response, status); + } + + buildStatusEntry(response, status, result); + + 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); +#if 1 + // commenting out, but keeping since this can be useful for debugging + 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); + virtual LLSD parseBody(LLCore::HttpResponse *response, bool &success); +}; + +//------------------------------------------------------------------------- +HttpCoroLLSDHandler::HttpCoroLLSDHandler(LLEventStream &reply): + HttpCoroHandler(reply) +{ +} + + +LLSD HttpCoroLLSDHandler::handleSuccess(LLCore::HttpResponse * response, LLCore::HttpStatus &status) +{ + LLSD result; + +// const bool emit_parse_errors = false; + bool success(false); + + result = parseBody(response, success); + +#if 0 + 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."); + } + } +#endif + + if (!success) + { +#if 1 + // 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."); + } +#endif + // 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; +} + +LLSD HttpCoroLLSDHandler::parseBody(LLCore::HttpResponse *response, bool &success) +{ + success = true; + if (response->getBodySize() == 0) + return LLSD(); + + LLSD result; + + if (!LLCoreHttpUtil::responseToLLSD(response, true, result)) + { + success = false; + return LLSD(); + } + + 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); + virtual LLSD parseBody(LLCore::HttpResponse *response, bool &success); +}; + +//------------------------------------------------------------------------- +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; +} + +LLSD HttpCoroRawHandler::parseBody(LLCore::HttpResponse *response, bool &success) +{ + success = true; + return LLSD(); +} + +//======================================================================== +/// 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); + virtual LLSD parseBody(LLCore::HttpResponse *response, bool &success); +}; + +//------------------------------------------------------------------------- +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; +} + +LLSD HttpCoroJSONHandler::parseBody(LLCore::HttpResponse *response, bool &success) +{ + success = true; + BufferArray * body(response->getBody()); + if (!body || !body->size()) + { + return LLSD(); + } + + LLCore::BufferArrayStream bas(body); + Json::Value jsonRoot; + + try + { + bas >> jsonRoot; + } + catch (std::runtime_error e) + { + success = false; + return LLSD(); + } + + // Convert the JSON structure to LLSD + return LlsdFromJson(jsonRoot); +} + +//======================================================================== +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() +{ + cancelSuspendedOperation(); +} + +LLSD HttpCoroutineAdapter::postAndSuspend(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(new HttpCoroLLSDHandler(replyPump)); + + return postAndSuspend_(request, url, body, options, headers, httpHandler); +} + +LLSD HttpCoroutineAdapter::postAndSuspend_(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); + + if (hhandle == LLCORE_HTTP_HANDLE_INVALID) + { + return HttpCoroutineAdapter::buildImmediateErrorResult(request, url); + } + + saveState(hhandle, request, handler); + LLSD results = llcoro::suspendUntilEventOn(handler->getReplyPump()); + cleanState(); + + return results; +} + +LLSD HttpCoroutineAdapter::postAndSuspend(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(new HttpCoroLLSDHandler(replyPump)); + + return postAndSuspend_(request, url, rawbody, options, headers, httpHandler); +} + +LLSD HttpCoroutineAdapter::postRawAndSuspend(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(new HttpCoroRawHandler(replyPump)); + + return postAndSuspend_(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::postFileAndSuspend(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 postAndSuspend(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::postFileAndSuspend(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 postAndSuspend(request, url, fileData, options, headers); +} + +LLSD HttpCoroutineAdapter::postJsonAndSuspend(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(new HttpCoroJSONHandler(replyPump)); + + LLCore::BufferArray::ptr_t rawbody(new LLCore::BufferArray); + + { + LLCore::BufferArrayStream outs(rawbody.get()); + Json::Value root = LlsdToJson(body); + Json::FastWriter writer; + + LL_WARNS("Http::post") << "JSON Generates: \"" << writer.write(root) << "\"" << LL_ENDL; + + outs << writer.write(root); + } + + return postAndSuspend_(request, url, rawbody, options, headers, httpHandler); +} + + +LLSD HttpCoroutineAdapter::postAndSuspend_(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); + + if (hhandle == LLCORE_HTTP_HANDLE_INVALID) + { + return HttpCoroutineAdapter::buildImmediateErrorResult(request, url); + } + + saveState(hhandle, request, handler); + LLSD results = llcoro::suspendUntilEventOn(handler->getReplyPump()); + cleanState(); + + return results; +} + +LLSD HttpCoroutineAdapter::putAndSuspend(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(new HttpCoroLLSDHandler(replyPump)); + + return putAndSuspend_(request, url, body, options, headers, httpHandler); +} + +LLSD HttpCoroutineAdapter::putJsonAndSuspend(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(new HttpCoroJSONHandler(replyPump)); + + LLCore::BufferArray::ptr_t rawbody(new LLCore::BufferArray); + + { + LLCore::BufferArrayStream outs(rawbody.get()); + Json::Value root = LlsdToJson(body); + Json::FastWriter writer; + + LL_WARNS("Http::put") << "JSON Generates: \"" << writer.write(root) << "\"" << LL_ENDL; + outs << writer.write(root); + } + + return putAndSuspend_(request, url, rawbody, options, headers, httpHandler); +} + +LLSD HttpCoroutineAdapter::putAndSuspend_(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); + + if (hhandle == LLCORE_HTTP_HANDLE_INVALID) + { + return HttpCoroutineAdapter::buildImmediateErrorResult(request, url); + } + + saveState(hhandle, request, handler); + LLSD results = llcoro::suspendUntilEventOn(handler->getReplyPump()); + cleanState(); + + return results; +} + +LLSD HttpCoroutineAdapter::putAndSuspend_(LLCore::HttpRequest::ptr_t &request, + const std::string & url, const 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->requestPut(mPolicyId, mPriority, + url, rawbody.get(), options, headers, handler); + + if (hhandle == LLCORE_HTTP_HANDLE_INVALID) + { + return HttpCoroutineAdapter::buildImmediateErrorResult(request, url); + } + + saveState(hhandle, request, handler); + LLSD results = llcoro::suspendUntilEventOn(handler->getReplyPump()); + cleanState(); + + return results; +} + + +LLSD HttpCoroutineAdapter::getAndSuspend(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(new HttpCoroLLSDHandler(replyPump)); + + return getAndSuspend_(request, url, options, headers, httpHandler); +} + +LLSD HttpCoroutineAdapter::getRawAndSuspend(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(new HttpCoroRawHandler(replyPump)); + + return getAndSuspend_(request, url, options, headers, httpHandler); +} + +LLSD HttpCoroutineAdapter::getJsonAndSuspend(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(new HttpCoroJSONHandler(replyPump)); + + return getAndSuspend_(request, url, options, headers, httpHandler); +} + + +LLSD HttpCoroutineAdapter::getAndSuspend_(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); + + if (hhandle == LLCORE_HTTP_HANDLE_INVALID) + { + return HttpCoroutineAdapter::buildImmediateErrorResult(request, url); + } + + saveState(hhandle, request, handler); + LLSD results = llcoro::suspendUntilEventOn(handler->getReplyPump()); + cleanState(); + + return results; +} + + +LLSD HttpCoroutineAdapter::deleteAndSuspend(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(new HttpCoroLLSDHandler(replyPump)); + + return deleteAndSuspend_(request, url, options, headers, httpHandler); +} + +LLSD HttpCoroutineAdapter::deleteJsonAndSuspend(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(new HttpCoroJSONHandler(replyPump)); + + return deleteAndSuspend_(request, url, options, headers, httpHandler); +} + + +LLSD HttpCoroutineAdapter::deleteAndSuspend_(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); + + if (hhandle == LLCORE_HTTP_HANDLE_INVALID) + { + return HttpCoroutineAdapter::buildImmediateErrorResult(request, url); + } + + saveState(hhandle, request, handler); + LLSD results = llcoro::suspendUntilEventOn(handler->getReplyPump()); + cleanState(); + + return results; +} + +LLSD HttpCoroutineAdapter::patchAndSuspend(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(new HttpCoroLLSDHandler(replyPump)); + + return patchAndSuspend_(request, url, body, options, headers, httpHandler); +} + + +LLSD HttpCoroutineAdapter::patchAndSuspend_(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 = requestPatchWithLLSD(request, + mPolicyId, mPriority, url, body, options, headers, + handler); + + if (hhandle == LLCORE_HTTP_HANDLE_INVALID) + { + return HttpCoroutineAdapter::buildImmediateErrorResult(request, url); + } + + saveState(hhandle, request, handler); + LLSD results = llcoro::suspendUntilEventOn(handler->getReplyPump()); + cleanState(); + + return results; +} + +LLSD HttpCoroutineAdapter::copyAndSuspend(LLCore::HttpRequest::ptr_t request, + const std::string & url, const std::string dest, + LLCore::HttpOptions::ptr_t options, LLCore::HttpHeaders::ptr_t headers) +{ + LLEventStream replyPump(mAdapterName + "Reply", true); + HttpCoroHandler::ptr_t httpHandler(new HttpCoroLLSDHandler(replyPump)); + + if (!headers) + headers.reset(new LLCore::HttpHeaders); + headers->append(HTTP_OUT_HEADER_DESTINATION, dest); + + return copyAndSuspend_(request, url, options, headers, httpHandler); +} + + +LLSD HttpCoroutineAdapter::copyAndSuspend_(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->requestCopy(mPolicyId, mPriority, url, + options, headers, handler); + + if (hhandle == LLCORE_HTTP_HANDLE_INVALID) + { + return HttpCoroutineAdapter::buildImmediateErrorResult(request, url); + } + + saveState(hhandle, request, handler); + LLSD results = llcoro::suspendUntilEventOn(handler->getReplyPump()); + cleanState(); + + return results; +} + +LLSD HttpCoroutineAdapter::moveAndSuspend(LLCore::HttpRequest::ptr_t request, + const std::string & url, const std::string dest, + LLCore::HttpOptions::ptr_t options, LLCore::HttpHeaders::ptr_t headers) +{ + LLEventStream replyPump(mAdapterName + "Reply", true); + HttpCoroHandler::ptr_t httpHandler(new HttpCoroLLSDHandler(replyPump)); + + if (!headers) + headers.reset(new LLCore::HttpHeaders); + headers->append(HTTP_OUT_HEADER_DESTINATION, dest); + + return moveAndSuspend_(request, url, options, headers, httpHandler); +} + + +LLSD HttpCoroutineAdapter::moveAndSuspend_(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->requestMove(mPolicyId, mPriority, url, + options, headers, handler); + + if (hhandle == LLCORE_HTTP_HANDLE_INVALID) + { + return HttpCoroutineAdapter::buildImmediateErrorResult(request, url); + } + + saveState(hhandle, request, handler); + LLSD results = llcoro::suspendUntilEventOn(handler->getReplyPump()); + cleanState(); + + 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(HTTP_OUT_HEADER_CONTENT_TYPE)) + { + headers->append(HTTP_OUT_HEADER_CONTENT_TYPE, 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::cancelSuspendedOperation() +{ + 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); + } +} + +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->getAndSuspend(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->postAndSuspend(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..d21f5ff45c 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 @@ -51,6 +57,7 @@ /// namespace LLCoreHttpUtil { + extern const F32 HTTP_REQUEST_EXPIRY_SECS; /// Attempt to convert a response object's contents to LLSD. /// It is expected that the response body will be of non-zero @@ -101,15 +108,573 @@ 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, + const LLCore::HttpHandler::ptr_t &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, + const LLCore::HttpHandler::ptr_t & 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, + const LLCore::HttpHandler::ptr_t &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, + const LLCore::HttpHandler::ptr_t &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::ptr_t 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::ptr_t 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, + const LLCore::HttpHandler::ptr_t &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, + const LLCore::HttpHandler::ptr_t & 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, + const LLCore::HttpHandler::ptr_t &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; + virtual LLSD parseBody(LLCore::HttpResponse *response, bool &success) = 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 postAndSuspend(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 postAndSuspend(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 postAndSuspend(LLCore::HttpRequest::ptr_t &request, + const std::string & url, const LLSD & body, + LLCore::HttpHeaders::ptr_t &headers) + { + return postAndSuspend(request, url, body, + LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), headers); + } + + LLSD postAndSuspend(LLCore::HttpRequest::ptr_t &request, + const std::string & url, LLCore::BufferArray::ptr_t &rawbody, + LLCore::HttpHeaders::ptr_t &headers) + { + return postAndSuspend(request, url, rawbody, + LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), headers); + } + + LLSD postRawAndSuspend(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 postRawAndSuspend(LLCore::HttpRequest::ptr_t &request, + const std::string & url, LLCore::BufferArray::ptr_t &rawbody, + LLCore::HttpHeaders::ptr_t &headers) + { + return postRawAndSuspend(request, url, rawbody, + LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), headers); + } + + LLSD postFileAndSuspend(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 postFileAndSuspend(LLCore::HttpRequest::ptr_t &request, + const std::string & url, std::string fileName, + LLCore::HttpHeaders::ptr_t &headers) + { + return postFileAndSuspend(request, url, fileName, + LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), headers); + } + + + LLSD postFileAndSuspend(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 postFileAndSuspend(LLCore::HttpRequest::ptr_t request, + const std::string & url, LLUUID assetId, LLAssetType::EType assetType, + LLCore::HttpHeaders::ptr_t &headers) + { + return postFileAndSuspend(request, url, assetId, assetType, + LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), headers); + } + + LLSD postJsonAndSuspend(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 postJsonAndSuspend(LLCore::HttpRequest::ptr_t &request, + const std::string & url, const LLSD & body, + LLCore::HttpHeaders::ptr_t &headers) + { + return postJsonAndSuspend(request, url, body, + 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 putAndSuspend(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 putAndSuspend(LLCore::HttpRequest::ptr_t request, + const std::string & url, const LLSD & body, + LLCore::HttpHeaders::ptr_t headers) + { + return putAndSuspend(request, url, body, + LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), headers); + } + + LLSD putJsonAndSuspend(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 putJsonAndSuspend(LLCore::HttpRequest::ptr_t &request, + const std::string & url, const LLSD & body, + LLCore::HttpHeaders::ptr_t &headers) + { + return putJsonAndSuspend(request, url, body, + LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), headers); + } + + /// 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 getAndSuspend(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 getAndSuspend(LLCore::HttpRequest::ptr_t &request, + const std::string & url, LLCore::HttpHeaders::ptr_t &headers) + { + return getAndSuspend(request, url, + LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), + headers); + } + + LLSD getRawAndSuspend(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 getRawAndSuspend(LLCore::HttpRequest::ptr_t &request, + const std::string & url, LLCore::HttpHeaders::ptr_t &headers) + { + return getRawAndSuspend(request, url, + LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), + headers); + } + + /// These methods have the same behavior as @getAndSuspend() however they are + /// expecting the server to return the results formatted in a JSON string. + /// On a successful GET call the JSON results will be converted into LLSD + /// before being returned to the caller. + LLSD getJsonAndSuspend(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 getJsonAndSuspend(LLCore::HttpRequest::ptr_t &request, + const std::string & url, LLCore::HttpHeaders::ptr_t &headers) + { + return getJsonAndSuspend(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 deleteAndSuspend(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 deleteAndSuspend(LLCore::HttpRequest::ptr_t request, + const std::string & url, LLCore::HttpHeaders::ptr_t headers) + { + return deleteAndSuspend(request, url, + LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), + headers); + } + + /// These methods have the same behavior as @deleteAndSuspend() however they are + /// expecting the server to return any results formatted in a JSON string. + /// On a successful DELETE call the JSON results will be converted into LLSD + /// before being returned to the caller. + LLSD deleteJsonAndSuspend(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 deleteJsonAndSuspend(LLCore::HttpRequest::ptr_t request, + const std::string & url, LLCore::HttpHeaders::ptr_t headers) + { + return deleteJsonAndSuspend(request, url, + LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), + headers); + } + + + /// Execute a PATCH 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 patchAndSuspend(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 patchAndSuspend(LLCore::HttpRequest::ptr_t &request, + const std::string & url, const LLSD & body, + LLCore::HttpHeaders::ptr_t &headers) + { + return patchAndSuspend(request, url, body, + LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), headers); + } + + /// Execute a COPY transaction on the supplied URL and yield execution of + /// the coroutine until a result is available. + /// + /// @Note: The destination is passed through the HTTP pipe as a header + /// The header used is defined as: HTTP_OUT_HEADER_DESTINATION("Destination"); + /// + /// @Note: the request's smart pointer is passed by value so that it will + /// not be deallocated during the yield. + LLSD copyAndSuspend(LLCore::HttpRequest::ptr_t request, + const std::string & url, const std::string dest, + 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 copyAndSuspend(LLCore::HttpRequest::ptr_t &request, + const std::string & url, const std::string & dest, + LLCore::HttpHeaders::ptr_t &headers) + { + return copyAndSuspend(request, url, dest, + LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), headers); + } + + /// Execute a MOVE transaction on the supplied URL and yield execution of + /// the coroutine until a result is available. + /// + /// @Note: The destination is passed through the HTTP pipe in the headers. + /// The header used is defined as: HTTP_OUT_HEADER_DESTINATION("Destination"); + /// + /// @Note: the request's smart pointer is passed by value so that it will + /// not be deallocated during the yield. + LLSD moveAndSuspend(LLCore::HttpRequest::ptr_t request, + const std::string & url, const std::string dest, + 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 moveAndSuspend(LLCore::HttpRequest::ptr_t &request, + const std::string & url, const std::string & dest, + LLCore::HttpHeaders::ptr_t &headers) + { + return moveAndSuspend(request, url, dest, + LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), headers); + } + + /// + void cancelSuspendedOperation(); + + 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 postAndSuspend_(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 postAndSuspend_(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 putAndSuspend_(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 putAndSuspend_(LLCore::HttpRequest::ptr_t &request, + const std::string & url, const LLCore::BufferArray::ptr_t & rawbody, + LLCore::HttpOptions::ptr_t &options, LLCore::HttpHeaders::ptr_t &headers, + HttpCoroHandler::ptr_t &handler); + + LLSD getAndSuspend_(LLCore::HttpRequest::ptr_t &request, + const std::string & url, LLCore::HttpOptions::ptr_t &options, + LLCore::HttpHeaders::ptr_t &headers, HttpCoroHandler::ptr_t &handler); + + LLSD deleteAndSuspend_(LLCore::HttpRequest::ptr_t &request, + const std::string & url, LLCore::HttpOptions::ptr_t &options, + LLCore::HttpHeaders::ptr_t &headers, HttpCoroHandler::ptr_t &handler); + + LLSD patchAndSuspend_(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 copyAndSuspend_(LLCore::HttpRequest::ptr_t &request, + const std::string & url, + LLCore::HttpOptions::ptr_t &options, LLCore::HttpHeaders::ptr_t &headers, + HttpCoroHandler::ptr_t &handler); + + LLSD moveAndSuspend_(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 deleted file mode 100644 index 7e9ae8d108..0000000000 --- a/indra/llmessage/llcurl.cpp +++ /dev/null @@ -1,2043 +0,0 @@ -/** - * @file llcurl.cpp - * @author Zero / Donovan - * @date 2006-10-15 - * @brief Implementation of wrapper around libcurl. - * - * $LicenseInfo:firstyear=2006&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010-2013, 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 -#define SAFE_SSL 1 -#elif LL_DARWIN -#define SAFE_SSL 1 -#else -#define SAFE_SSL 1 -#endif - -#include "linden_common.h" - -#include "llcurl.h" - -#include <algorithm> -#include <iomanip> -#include <curl/curl.h> -#if SAFE_SSL -#include <openssl/crypto.h> -#endif - -#include "llbufferstream.h" -#include "llproxy.h" -#include "llsdserialize.h" -#include "llstl.h" -#include "llstring.h" -#include "llthread.h" -#include "lltimer.h" - -////////////////////////////////////////////////////////////////////////////// -/* - The trick to getting curl to do keep-alives is to reuse the - same easy handle for the requests. It appears that curl - keeps a pool of connections alive for each easy handle, but - doesn't share them between easy handles. Therefore it is - important to keep a pool of easy handles and reuse them, - rather than create and destroy them with each request. This - code does this. - - Furthermore, it would behoove us to keep track of which - hosts an easy handle was used for and pick an easy handle - that matches the next request. This code does not current - do this. - */ - -////////////////////////////////////////////////////////////////////////////// - -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; - -// DEBUG // -S32 gCurlEasyCount = 0; -S32 gCurlMultiCount = 0; - -////////////////////////////////////////////////////////////////////////////// - -//static -std::vector<LLMutex*> LLCurl::sSSLMutex; -std::string LLCurl::sCAPath; -std::string LLCurl::sCAFile; -LLCurlThread* LLCurl::sCurlThread = NULL ; -LLMutex* LLCurl::sHandleMutexp = NULL ; -S32 LLCurl::sTotalHandles = 0 ; -bool LLCurl::sNotQuitting = true; -F32 LLCurl::sCurlRequestTimeOut = 120.f; //seonds -S32 LLCurl::sMaxHandles = 256; //max number of handles, (multi handles and easy handles combined). -CURL* LLCurl::sCurlTemplateStandardHandle = NULL; - -void check_curl_code(CURLcode code) -{ - if (code != CURLE_OK) - { - // linux appears to throw a curl error once per session for a bad initialization - // at a pretty random time (when enabling cookies). - LL_WARNS("curl") << "curl error detected: " << curl_easy_strerror(code) << LL_ENDL; - } -} - -void check_curl_multi_code(CURLMcode code) -{ - if (code != CURLM_OK) - { - // linux appears to throw a curl error once per session for a bad initialization - // at a pretty random time (when enabling cookies). - LL_WARNS("curl") << "curl multi error detected: " << curl_multi_strerror(code) << LL_ENDL; - } -} - -//static -void LLCurl::setCAPath(const std::string& path) -{ - sCAPath = path; -} - -//static -void LLCurl::setCAFile(const std::string& file) -{ - sCAFile = file; -} - -//static -std::string LLCurl::getVersionString() -{ - return std::string(curl_version()); -} - -////////////////////////////////////////////////////////////////////////////// - -LLCurl::Responder::Responder() - : mHTTPMethod(HTTP_INVALID), mStatus(HTTP_INTERNAL_ERROR) -{ -} - -LLCurl::Responder::~Responder() -{ - LL_CHECK_MEMORY -} - -// virtual -void LLCurl::Responder::httpFailure() -{ - LL_WARNS("curl") << dumpResponse() << LL_ENDL; -} - -std::string LLCurl::Responder::dumpResponse() const -{ - std::ostringstream s; - s << "[" << httpMethodAsVerb(mHTTPMethod) << ":" << mURL << "] " - << "[status:" << mStatus << "] " - << "[reason:" << mReason << "] "; - - if (mResponseHeaders.has(HTTP_IN_HEADER_CONTENT_TYPE)) - { - s << "[content-type:" << mResponseHeaders[HTTP_IN_HEADER_CONTENT_TYPE] << "] "; - } - - s << "[content:" << mContent << "]"; - - return s.str(); -} - -// virtual -void LLCurl::Responder::httpSuccess() -{ -} - -void LLCurl::Responder::setURL(const std::string& url) -{ - mURL = url; -} - -const std::string& LLCurl::Responder::getURL() -{ - return mURL; -} - -void LLCurl::Responder::successResult(const LLSD& content) -{ - setResult(HTTP_OK, "", content); - httpSuccess(); -} - -void LLCurl::Responder::failureResult(S32 status, const std::string& reason, const LLSD& content /* = LLSD() */) -{ - setResult(status, reason, content); - httpFailure(); -} - -void LLCurl::Responder::completeResult(S32 status, const std::string& reason, const LLSD& content /* = LLSD() */) -{ - setResult(status, reason, content); - httpCompleted(); -} - -void LLCurl::Responder::setResult(S32 status, const std::string& reason, const LLSD& content /* = LLSD() */) -{ - mStatus = status; - mReason = reason; - mContent = content; -} - -void LLCurl::Responder::setHTTPMethod(EHTTPMethod method) -{ - mHTTPMethod = method; -} - -void LLCurl::Responder::setResponseHeader(const std::string& header, const std::string& value) -{ - mResponseHeaders[header] = value; -} - -const std::string& LLCurl::Responder::getResponseHeader(const std::string& header) const -{ - if (mResponseHeaders.has(header)) - { - return mResponseHeaders[header].asStringRef(); - } - static const std::string empty; - return empty; -} - -bool LLCurl::Responder::hasResponseHeader(const std::string& header) const -{ - if (mResponseHeaders.has(header)) return true; - return false; -} - -// virtual -void LLCurl::Responder::completedRaw( - const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer) -{ - LLBufferStream istr(channels, buffer.get()); - const bool emit_parse_errors = false; - - std::string debug_body("(empty)"); - bool parsed=true; - if (EOF == istr.peek()) - { - parsed=false; - } - // Try to parse body as llsd, no matter what 'content-type' says. - else if (LLSDParser::PARSE_FAILURE == LLSDSerialize::fromXML(mContent, istr, emit_parse_errors)) - { - parsed=false; - char body[1025]; - body[1024] = '\0'; - istr.seekg(0, std::ios::beg); - istr.get(body,1024); - if (strlen(body) > 0) - { - mContent = body; - debug_body = body; - } - } - - // Only emit a warning if we failed to parse when 'content-type' == 'application/llsd+xml' - if (!parsed && (HTTP_CONTENT_LLSD_XML == getResponseHeader(HTTP_IN_HEADER_CONTENT_TYPE))) - { - LL_WARNS() << "Failed to deserialize . " << mURL << " [status:" << mStatus << "] " - << "(" << mReason << ") body: " << debug_body << LL_ENDL; - } - - httpCompleted(); -} - -// virtual -void LLCurl::Responder::httpCompleted() -{ - if (isGoodStatus()) - { - httpSuccess(); - } - else - { - httpFailure(); - } -} - -////////////////////////////////////////////////////////////////////////////// - -std::set<CURL*> LLCurl::Easy::sFreeHandles; -std::set<CURL*> LLCurl::Easy::sActiveHandles; -LLMutex* LLCurl::Easy::sHandleMutexp = NULL ; - -//static -CURL* LLCurl::Easy::allocEasyHandle() -{ - llassert(LLCurl::getCurlThread()) ; - - CURL* ret = NULL; - - LLMutexLock lock(sHandleMutexp) ; - - if (sFreeHandles.empty()) - { - ret = LLCurl::newEasyHandle(); - } - else - { - ret = *(sFreeHandles.begin()); - sFreeHandles.erase(ret); - curl_easy_reset(ret); - } - - if (ret) - { - sActiveHandles.insert(ret); - } - - return ret; -} - -//static -void LLCurl::Easy::releaseEasyHandle(CURL* handle) -{ - static const S32 MAX_NUM_FREE_HANDLES = 32 ; - - if (!handle) - { - return ; //handle allocation failed. - //LL_ERRS() << "handle cannot be NULL!" << LL_ENDL; - } - - LLMutexLock lock(sHandleMutexp) ; - if (sActiveHandles.find(handle) != sActiveHandles.end()) - { - LL_CHECK_MEMORY - sActiveHandles.erase(handle); - LL_CHECK_MEMORY - if(sFreeHandles.size() < MAX_NUM_FREE_HANDLES) - { - sFreeHandles.insert(handle); - LL_CHECK_MEMORY - } - else - { - LLCurl::deleteEasyHandle(handle) ; - LL_CHECK_MEMORY - } - } - else - { - LL_ERRS() << "Invalid handle." << LL_ENDL; - } -} - -//static -void LLCurl::Easy::deleteAllActiveHandles() -{ - LLMutexLock lock(sHandleMutexp) ; - LL_CHECK_MEMORY - for (std::set<CURL*>::iterator activeHandle = sActiveHandles.begin(); activeHandle != sActiveHandles.end(); ++activeHandle) - { - CURL* curlHandle = *activeHandle; - LLCurl::deleteEasyHandle(curlHandle); - LL_CHECK_MEMORY - } - - sFreeHandles.clear(); -} - -//static -void LLCurl::Easy::deleteAllFreeHandles() -{ - LLMutexLock lock(sHandleMutexp) ; - LL_CHECK_MEMORY - for (std::set<CURL*>::iterator freeHandle = sFreeHandles.begin(); freeHandle != sFreeHandles.end(); ++freeHandle) - { - CURL* curlHandle = *freeHandle; - LLCurl::deleteEasyHandle(curlHandle); - LL_CHECK_MEMORY - } - - sFreeHandles.clear(); -} - -LLCurl::Easy::Easy() - : mHeaders(NULL), - mCurlEasyHandle(NULL) -{ - mErrorBuffer[0] = 0; -} - -LLCurl::Easy* LLCurl::Easy::getEasy() -{ - Easy* easy = new Easy(); - easy->mCurlEasyHandle = allocEasyHandle(); - - if (!easy->mCurlEasyHandle) - { - // this can happen if we have too many open files (fails in c-ares/ares_init.c) - LL_WARNS("curl") << "allocEasyHandle() returned NULL! Easy handles: " - << gCurlEasyCount << " Multi handles: " << gCurlMultiCount << LL_ENDL; - delete easy; - return NULL; - } - - // Enable a brief cache period for now. This was zero for the longest time - // which caused some routers grief and generated unneeded traffic. For the - // threaded resolver, we're using system resolution libraries and non-zero values - // are preferred. The c-ares resolver is another matter and it might not - // track server changes as well. - CURLcode result = curl_easy_setopt(easy->mCurlEasyHandle, CURLOPT_DNS_CACHE_TIMEOUT, 15); - check_curl_code(result); - result = curl_easy_setopt(easy->mCurlEasyHandle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); - check_curl_code(result); - - ++gCurlEasyCount; - return easy; -} - -LLCurl::Easy::~Easy() -{ - releaseEasyHandle(mCurlEasyHandle); - --gCurlEasyCount; - curl_slist_free_all(mHeaders); - LL_CHECK_MEMORY - for_each(mStrings.begin(), mStrings.end(), DeletePointerArray()); - LL_CHECK_MEMORY - if (mResponder && LLCurl::sNotQuitting) //aborted - { - // HTTP_REQUEST_TIME_OUT, timeout, abort - // *TODO: This looks like improper use of the 408 status code. - // See: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.9 - // This status code should be returned by the *server* when: - // "The client did not produce a request within the time that the server was prepared to wait." - mResponder->setResult(HTTP_REQUEST_TIME_OUT, "Request timeout, aborted."); - mResponder->completedRaw(mChannels, mOutput); - LL_CHECK_MEMORY - } - mResponder = NULL; -} - -void LLCurl::Easy::resetState() -{ - curl_easy_reset(mCurlEasyHandle); - - if (mHeaders) - { - curl_slist_free_all(mHeaders); - mHeaders = NULL; - } - - mRequest.str(""); - mRequest.clear(); - - mOutput.reset(); - - mInput.str(""); - mInput.clear(); - - mErrorBuffer[0] = 0; - - mHeaderOutput.str(""); - mHeaderOutput.clear(); -} - -void LLCurl::Easy::setErrorBuffer() -{ - setopt(CURLOPT_ERRORBUFFER, &mErrorBuffer); -} - -const char* LLCurl::Easy::getErrorBuffer() -{ - return mErrorBuffer; -} - -void LLCurl::Easy::setCA() -{ - if (!sCAPath.empty()) - { - setoptString(CURLOPT_CAPATH, sCAPath); - } - if (!sCAFile.empty()) - { - setoptString(CURLOPT_CAINFO, sCAFile); - } -} - -void LLCurl::Easy::setHeaders() -{ - setopt(CURLOPT_HTTPHEADER, mHeaders); -} - -void LLCurl::Easy::getTransferInfo(LLCurl::TransferInfo* info) -{ - check_curl_code(curl_easy_getinfo(mCurlEasyHandle, CURLINFO_SIZE_DOWNLOAD, &info->mSizeDownload)); - check_curl_code(curl_easy_getinfo(mCurlEasyHandle, CURLINFO_TOTAL_TIME, &info->mTotalTime)); - check_curl_code(curl_easy_getinfo(mCurlEasyHandle, CURLINFO_SPEED_DOWNLOAD, &info->mSpeedDownload)); -} - -S32 LLCurl::Easy::report(CURLcode code) -{ - S32 responseCode = 0; - std::string responseReason; - - if (code == CURLE_OK) - { - check_curl_code(curl_easy_getinfo(mCurlEasyHandle, CURLINFO_RESPONSE_CODE, &responseCode)); - //*TODO: get reason from first line of mHeaderOutput - } - else - { - responseCode = HTTP_INTERNAL_ERROR; - responseReason = strerror(code) + " : " + mErrorBuffer; - setopt(CURLOPT_FRESH_CONNECT, TRUE); - } - - if (mResponder) - { - mResponder->setResult(responseCode, responseReason); - mResponder->completedRaw(mChannels, mOutput); - mResponder = NULL; - } - - resetState(); - return responseCode; -} - -// Note: these all assume the caller tracks the value (i.e. keeps it persistant) -void LLCurl::Easy::setopt(CURLoption option, S32 value) -{ - CURLcode result = curl_easy_setopt(mCurlEasyHandle, option, value); - check_curl_code(result); -} - -void LLCurl::Easy::setopt(CURLoption option, void* value) -{ - CURLcode result = curl_easy_setopt(mCurlEasyHandle, option, value); - check_curl_code(result); -} - -void LLCurl::Easy::setopt(CURLoption option, char* value) -{ - CURLcode result = curl_easy_setopt(mCurlEasyHandle, option, value); - check_curl_code(result); -} - -// Note: this copies the string so that the caller does not have to keep it around -void LLCurl::Easy::setoptString(CURLoption option, const std::string& value) -{ - char* tstring = new char[value.length()+1]; - strcpy(tstring, value.c_str()); - mStrings.push_back(tstring); - CURLcode result = curl_easy_setopt(mCurlEasyHandle, option, tstring); - check_curl_code(result); -} - -void LLCurl::Easy::slist_append(const std::string& header, const std::string& value) -{ - std::string pair(header); - if (value.empty()) - { - pair += ":"; - } - else - { - pair += ": "; - pair += value; - } - slist_append(pair.c_str()); -} - -void LLCurl::Easy::slist_append(const char* str) -{ - if (str) - { - mHeaders = curl_slist_append(mHeaders, str); - if (!mHeaders) - { - LL_WARNS() << "curl_slist_append() call returned NULL appending " << str << LL_ENDL; - } - } -} - -size_t curlReadCallback(char* data, size_t size, size_t nmemb, void* user_data) -{ - LLCurl::Easy* easy = (LLCurl::Easy*)user_data; - - S32 n = size * nmemb; - S32 startpos = (S32)easy->getInput().tellg(); - easy->getInput().seekg(0, std::ios::end); - S32 endpos = (S32)easy->getInput().tellg(); - easy->getInput().seekg(startpos, std::ios::beg); - S32 maxn = endpos - startpos; - n = llmin(n, maxn); - easy->getInput().read((char*)data, n); - - return n; -} - -size_t curlWriteCallback(char* data, size_t size, size_t nmemb, void* user_data) -{ - LLCurl::Easy* easy = (LLCurl::Easy*)user_data; - - S32 n = size * nmemb; - easy->getOutput()->append(easy->getChannels().in(), (const U8*)data, n); - - return n; -} - -size_t curlHeaderCallback(void* data, size_t size, size_t nmemb, void* user_data) -{ - LLCurl::Easy* easy = (LLCurl::Easy*)user_data; - - size_t n = size * nmemb; - easy->getHeaderOutput().write((const char*)data, n); - - return n; -} - -void LLCurl::Easy::prepRequest(const std::string& url, - const std::vector<std::string>& headers, - ResponderPtr responder, S32 time_out, bool post) -{ - resetState(); - - if (post) setoptString(CURLOPT_ENCODING, ""); - - //setopt(CURLOPT_VERBOSE, 1); // useful for debugging - setopt(CURLOPT_NOSIGNAL, 1); - setopt(CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); - - // Set the CURL options for either Socks or HTTP proxy - LLProxy::getInstance()->applyProxySettings(this); - - mOutput.reset(new LLBufferArray); - mOutput->setThreaded(true); - setopt(CURLOPT_WRITEFUNCTION, (void*)&curlWriteCallback); - setopt(CURLOPT_WRITEDATA, (void*)this); - - setopt(CURLOPT_READFUNCTION, (void*)&curlReadCallback); - setopt(CURLOPT_READDATA, (void*)this); - - setopt(CURLOPT_HEADERFUNCTION, (void*)&curlHeaderCallback); - setopt(CURLOPT_HEADERDATA, (void*)this); - - // Allow up to five redirects - if (responder && responder->followRedir()) - { - setopt(CURLOPT_FOLLOWLOCATION, 1); - setopt(CURLOPT_MAXREDIRS, MAX_REDIRECTS); - } - - setErrorBuffer(); - setCA(); - - setopt(CURLOPT_SSL_VERIFYPEER, true); - - //don't verify host name so urls with scrubbed host names will work (improves DNS performance) - setopt(CURLOPT_SSL_VERIFYHOST, 0); - setopt(CURLOPT_TIMEOUT, llmax(time_out, CURL_REQUEST_TIMEOUT)); - setopt(CURLOPT_CONNECTTIMEOUT, CURL_CONNECT_TIMEOUT); - - setoptString(CURLOPT_URL, url); - - mResponder = responder; - - if (!post) - { - // *TODO: Should this be set to 'Keep-Alive' ? - slist_append(HTTP_OUT_HEADER_CONNECTION, "keep-alive"); - slist_append(HTTP_OUT_HEADER_KEEP_ALIVE, "300"); - // Accept and other headers - for (std::vector<std::string>::const_iterator iter = headers.begin(); - iter != headers.end(); ++iter) - { - slist_append((*iter).c_str()); - } - } -} - -//////////////////////////////////////////////////////////////////////////// -LLCurl::Multi::Multi(F32 idle_time_out) - : mQueued(0), - mErrorCount(0), - mState(STATE_READY), - mDead(FALSE), - mValid(TRUE), - mMutexp(NULL), - mDeletionMutexp(NULL), - mEasyMutexp(NULL) -{ - mCurlMultiHandle = LLCurl::newMultiHandle(); - if (!mCurlMultiHandle) - { - LL_WARNS() << "curl_multi_init() returned NULL! Easy handles: " << gCurlEasyCount << " Multi handles: " << gCurlMultiCount << LL_ENDL; - mCurlMultiHandle = LLCurl::newMultiHandle(); - } - - //llassert_always(mCurlMultiHandle); - - if(mCurlMultiHandle) - { - if(LLCurl::getCurlThread()->getThreaded()) - { - mMutexp = new LLMutex(NULL) ; - mDeletionMutexp = new LLMutex(NULL) ; - mEasyMutexp = new LLMutex(NULL) ; - } - LLCurl::getCurlThread()->addMulti(this) ; - - mIdleTimeOut = idle_time_out ; - if(mIdleTimeOut < LLCurl::sCurlRequestTimeOut) - { - mIdleTimeOut = LLCurl::sCurlRequestTimeOut ; - } - - ++gCurlMultiCount; -} -} - -LLCurl::Multi::~Multi() -{ - cleanup(true) ; - - delete mDeletionMutexp ; - mDeletionMutexp = NULL ; -} - -void LLCurl::Multi::cleanup(bool deleted) -{ - if(!mCurlMultiHandle) - { - return ; //nothing to clean. - } - llassert_always(deleted || !mValid) ; - - LLMutexLock lock(mDeletionMutexp); - - - // Clean up active - for(easy_active_list_t::iterator iter = mEasyActiveList.begin(); - iter != mEasyActiveList.end(); ++iter) - { - Easy* easy = *iter; - LL_CHECK_MEMORY - check_curl_multi_code(curl_multi_remove_handle(mCurlMultiHandle, easy->getCurlHandle())); - LL_CHECK_MEMORY - if(deleted) - { - easy->mResponder = NULL ; //avoid triggering mResponder. - LL_CHECK_MEMORY - } - delete easy; - LL_CHECK_MEMORY - } - mEasyActiveList.clear(); - mEasyActiveMap.clear(); - - LL_CHECK_MEMORY - - // Clean up freed - for_each(mEasyFreeList.begin(), mEasyFreeList.end(), DeletePointer()); - mEasyFreeList.clear(); - - LL_CHECK_MEMORY - - check_curl_multi_code(LLCurl::deleteMultiHandle(mCurlMultiHandle)); - mCurlMultiHandle = NULL ; - - LL_CHECK_MEMORY - - delete mMutexp ; - mMutexp = NULL ; - - LL_CHECK_MEMORY - - delete mEasyMutexp ; - mEasyMutexp = NULL ; - - LL_CHECK_MEMORY - - mQueued = 0 ; - mState = STATE_COMPLETED; - - --gCurlMultiCount; - - return ; -} - -void LLCurl::Multi::lock() -{ - if(mMutexp) - { - mMutexp->lock() ; - } -} - -void LLCurl::Multi::unlock() -{ - if(mMutexp) - { - mMutexp->unlock() ; - } -} - -void LLCurl::Multi::markDead() -{ - { - LLMutexLock lock(mDeletionMutexp) ; - - if(mCurlMultiHandle != NULL) - { - mDead = TRUE ; - LLCurl::getCurlThread()->setPriority(mHandle, LLQueuedThread::PRIORITY_URGENT) ; - - return; - } - } - - //not valid, delete it. - delete this; -} - -void LLCurl::Multi::setState(LLCurl::Multi::ePerformState state) -{ - lock() ; - mState = state ; - unlock() ; - - if(mState == STATE_READY) - { - LLCurl::getCurlThread()->setPriority(mHandle, LLQueuedThread::PRIORITY_NORMAL) ; - } -} - -LLCurl::Multi::ePerformState LLCurl::Multi::getState() -{ - return mState; -} - -bool LLCurl::Multi::isCompleted() -{ - return STATE_COMPLETED == getState() ; -} - -bool LLCurl::Multi::waitToComplete() -{ - if(!isValid()) - { - return true ; - } - - if(!mMutexp) //not threaded - { - doPerform() ; - return true ; - } - - bool completed = (STATE_COMPLETED == mState) ; - if(!completed) - { - LLCurl::getCurlThread()->setPriority(mHandle, LLQueuedThread::PRIORITY_HIGH) ; - } - - return completed; -} - -CURLMsg* LLCurl::Multi::info_read(S32* msgs_in_queue) -{ - LLMutexLock lock(mMutexp) ; - - CURLMsg* curlmsg = curl_multi_info_read(mCurlMultiHandle, msgs_in_queue); - return curlmsg; -} - -//return true if dead -bool LLCurl::Multi::doPerform() -{ - LLMutexLock lock(mDeletionMutexp) ; - - bool dead = mDead ; - - if(mDead) - { - setState(STATE_COMPLETED); - mQueued = 0 ; - } - else if(getState() != STATE_COMPLETED) - { - setState(STATE_PERFORMING); - - S32 q = 0; - for (S32 call_count = 0; - call_count < MULTI_PERFORM_CALL_REPEAT; - call_count++) - { - LLMutexLock lock(mMutexp) ; - - //WARNING: curl_multi_perform will block for many hundreds of milliseconds - // NEVER call this from the main thread, and NEVER allow the main thread to - // wait on a mutex held by this thread while curl_multi_perform is executing - CURLMcode code = curl_multi_perform(mCurlMultiHandle, &q); - if (CURLM_CALL_MULTI_PERFORM != code || q == 0) - { - check_curl_multi_code(code); - - break; - } - } - - mQueued = q; - setState(STATE_COMPLETED) ; - mIdleTimer.reset() ; - } - else if(!mValid && mIdleTimer.getElapsedTimeF32() > mIdleTimeOut) //idle for too long, remove it. - { - dead = true ; - } - else if(mValid && mIdleTimer.getElapsedTimeF32() > mIdleTimeOut - 1.f) //idle for too long, mark it invalid. - { - mValid = FALSE ; - } - - return dead ; -} - -S32 LLCurl::Multi::process() -{ - if(!isValid()) - { - return 0 ; - } - - waitToComplete() ; - - if (getState() != STATE_COMPLETED) - { - return 0; - } - - CURLMsg* msg; - int msgs_in_queue; - - S32 processed = 0; - while ((msg = info_read(&msgs_in_queue))) - { - ++processed; - if (msg->msg == CURLMSG_DONE) - { - S32 response = 0; - Easy* easy = NULL ; - - { - LLMutexLock lock(mEasyMutexp) ; - easy_active_map_t::iterator iter = mEasyActiveMap.find(msg->easy_handle); - if (iter != mEasyActiveMap.end()) - { - easy = iter->second; - } - } - - if(easy) - { - response = easy->report(msg->data.result); - removeEasy(easy); - } - else - { - response = HTTP_INTERNAL_ERROR; - //*TODO: change to LL_WARNS() - LL_ERRS() << "cleaned up curl request completed!" << LL_ENDL; - } - if (response >= 400) - { - // failure of some sort, inc mErrorCount for debugging and flagging multi for destruction - ++mErrorCount; - } - } - } - - setState(STATE_READY); - - return processed; -} - -LLCurl::Easy* LLCurl::Multi::allocEasy() -{ - Easy* easy = 0; - - if (mEasyFreeList.empty()) - { - easy = Easy::getEasy(); - } - else - { - LLMutexLock lock(mEasyMutexp) ; - easy = *(mEasyFreeList.begin()); - mEasyFreeList.erase(easy); - } - if (easy) - { - LLMutexLock lock(mEasyMutexp) ; - mEasyActiveList.insert(easy); - mEasyActiveMap[easy->getCurlHandle()] = easy; - } - return easy; -} - -bool LLCurl::Multi::addEasy(Easy* easy) -{ - LLMutexLock lock(mMutexp) ; - CURLMcode mcode = curl_multi_add_handle(mCurlMultiHandle, easy->getCurlHandle()); - check_curl_multi_code(mcode); - //if (mcode != CURLM_OK) - //{ - // LL_WARNS() << "Curl Error: " << curl_multi_strerror(mcode) << LL_ENDL; - // return false; - //} - return true; -} - -void LLCurl::Multi::easyFree(Easy* easy) -{ - if(mEasyMutexp) - { - mEasyMutexp->lock() ; - } - - mEasyActiveList.erase(easy); - mEasyActiveMap.erase(easy->getCurlHandle()); - - if (mEasyFreeList.size() < EASY_HANDLE_POOL_SIZE) - { - mEasyFreeList.insert(easy); - - if(mEasyMutexp) - { - mEasyMutexp->unlock() ; - } - - easy->resetState(); - } - else - { - if(mEasyMutexp) - { - mEasyMutexp->unlock() ; - } - delete easy; - } -} - -void LLCurl::Multi::removeEasy(Easy* easy) -{ - { - LLMutexLock lock(mMutexp) ; - check_curl_multi_code(curl_multi_remove_handle(mCurlMultiHandle, easy->getCurlHandle())); - } - easyFree(easy); -} - -//------------------------------------------------------------ -//LLCurlThread -LLCurlThread::CurlRequest::CurlRequest(handle_t handle, LLCurl::Multi* multi, LLCurlThread* curl_thread) : - LLQueuedThread::QueuedRequest(handle, LLQueuedThread::PRIORITY_NORMAL, FLAG_AUTO_COMPLETE), - mMulti(multi), - mCurlThread(curl_thread) -{ -} - -LLCurlThread::CurlRequest::~CurlRequest() -{ - if(mMulti) - { - mCurlThread->deleteMulti(mMulti) ; - mMulti = NULL ; - } -} - -bool LLCurlThread::CurlRequest::processRequest() -{ - bool completed = true ; - if(mMulti) - { - completed = mCurlThread->doMultiPerform(mMulti) ; - - if(!completed) - { - setPriority(LLQueuedThread::PRIORITY_LOW) ; - } - } - - return completed ; -} - -void LLCurlThread::CurlRequest::finishRequest(bool completed) -{ - if(mMulti->isDead()) - { - mCurlThread->deleteMulti(mMulti) ; - } - else - { - mCurlThread->cleanupMulti(mMulti) ; //being idle too long, remove the request. - } - - mMulti = NULL ; -} - -LLCurlThread::LLCurlThread(bool threaded) : - LLQueuedThread("curlthread", threaded) -{ -} - -//virtual -LLCurlThread::~LLCurlThread() -{ -} - -S32 LLCurlThread::update(F32 max_time_ms) -{ - return LLQueuedThread::update(max_time_ms); -} - -void LLCurlThread::addMulti(LLCurl::Multi* multi) -{ - multi->mHandle = generateHandle() ; - - CurlRequest* req = new CurlRequest(multi->mHandle, multi, this) ; - - if (!addRequest(req)) - { - LL_WARNS() << "curl request added when the thread is quitted" << LL_ENDL; - } -} - -void LLCurlThread::killMulti(LLCurl::Multi* multi) -{ - if(!multi) - { - return ; - } - - - multi->markDead() ; -} - -//private -bool LLCurlThread::doMultiPerform(LLCurl::Multi* multi) -{ - return multi->doPerform() ; -} - -//private -void LLCurlThread::deleteMulti(LLCurl::Multi* multi) -{ - delete multi ; -} - -//private -void LLCurlThread::cleanupMulti(LLCurl::Multi* multi) -{ - multi->cleanup() ; - if(multi->isDead()) //check if marked dead during cleaning up. - { - deleteMulti(multi) ; - } -} - -//------------------------------------------------------------ - -//static -std::string LLCurl::strerror(CURLcode errorcode) -{ - return std::string(curl_easy_strerror(errorcode)); -} - -//////////////////////////////////////////////////////////////////////////// -// For generating a simple request for data -// using one multi and one easy per request - -LLCurlRequest::LLCurlRequest() : - mActiveMulti(NULL), - mActiveRequestCount(0) -{ - mProcessing = FALSE; -} - -LLCurlRequest::~LLCurlRequest() -{ - //stop all Multi handle background threads - for (curlmulti_set_t::iterator iter = mMultiSet.begin(); iter != mMultiSet.end(); ++iter) - { - LLCurl::getCurlThread()->killMulti(*iter) ; - } - mMultiSet.clear() ; -} - -void LLCurlRequest::addMulti() -{ - LLCurl::Multi* multi = new LLCurl::Multi(); - if(!multi->isValid()) - { - LLCurl::getCurlThread()->killMulti(multi) ; - mActiveMulti = NULL ; - mActiveRequestCount = 0 ; - return; - } - - mMultiSet.insert(multi); - mActiveMulti = multi; - mActiveRequestCount = 0; -} - -LLCurl::Easy* LLCurlRequest::allocEasy() -{ - if (!mActiveMulti || - mActiveRequestCount >= MAX_ACTIVE_REQUEST_COUNT || - mActiveMulti->mErrorCount > 0) - { - addMulti(); - } - if(!mActiveMulti) - { - return NULL ; - } - - //llassert_always(mActiveMulti); - ++mActiveRequestCount; - LLCurl::Easy* easy = mActiveMulti->allocEasy(); - return easy; -} - -bool LLCurlRequest::addEasy(LLCurl::Easy* easy) -{ - llassert_always(mActiveMulti); - - if (mProcessing) - { - LL_ERRS() << "Posting to a LLCurlRequest instance from within a responder is not allowed (causes DNS timeouts)." << LL_ENDL; - } - bool res = mActiveMulti->addEasy(easy); - return res; -} - -void LLCurlRequest::get(const std::string& url, LLCurl::ResponderPtr responder) -{ - getByteRange(url, headers_t(), 0, -1, responder); -} - -// Note: (length==0) is interpreted as "the rest of the file", i.e. the whole file if (offset==0) or -// the remainder of the file if not. -bool LLCurlRequest::getByteRange(const std::string& url, - const headers_t& headers, - S32 offset, S32 length, - LLCurl::ResponderPtr responder) -{ - llassert(LLCurl::sNotQuitting); - LLCurl::Easy* easy = allocEasy(); - if (!easy) - { - return false; - } - easy->prepRequest(url, headers, responder); - easy->setopt(CURLOPT_HTTPGET, 1); - if (length > 0) - { - std::string range = llformat("bytes=%d-%d", offset,offset+length-1); - easy->slist_append(HTTP_OUT_HEADER_RANGE, range); - } - else if (offset > 0) - { - std::string range = llformat("bytes=%d-", offset); - easy->slist_append(HTTP_OUT_HEADER_RANGE, range); - } - easy->setHeaders(); - bool res = addEasy(easy); - return res; -} - -bool LLCurlRequest::post(const std::string& url, - const headers_t& headers, - const LLSD& data, - LLCurl::ResponderPtr responder, S32 time_out) -{ - llassert(LLCurl::sNotQuitting); - LLCurl::Easy* easy = allocEasy(); - if (!easy) - { - return false; - } - easy->prepRequest(url, headers, responder, time_out); - - LLSDSerialize::toXML(data, easy->getInput()); - S32 bytes = easy->getInput().str().length(); - - easy->setopt(CURLOPT_POST, 1); - easy->setopt(CURLOPT_POSTFIELDS, (void*)NULL); - easy->setopt(CURLOPT_POSTFIELDSIZE, bytes); - - easy->slist_append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_LLSD_XML); - easy->setHeaders(); - - LL_DEBUGS() << "POSTING: " << bytes << " bytes." << LL_ENDL; - bool res = addEasy(easy); - return res; -} - -bool LLCurlRequest::post(const std::string& url, - const headers_t& headers, - const std::string& data, - LLCurl::ResponderPtr responder, S32 time_out) -{ - llassert(LLCurl::sNotQuitting); - LLCurl::Easy* easy = allocEasy(); - if (!easy) - { - return false; - } - easy->prepRequest(url, headers, responder, time_out); - - easy->getInput().write(data.data(), data.size()); - S32 bytes = easy->getInput().str().length(); - - easy->setopt(CURLOPT_POST, 1); - easy->setopt(CURLOPT_POSTFIELDS, (void*)NULL); - easy->setopt(CURLOPT_POSTFIELDSIZE, bytes); - - easy->slist_append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_OCTET_STREAM); - easy->setHeaders(); - - LL_DEBUGS() << "POSTING: " << bytes << " bytes." << LL_ENDL; - bool res = addEasy(easy); - return res; -} - -// Note: call once per frame -S32 LLCurlRequest::process() -{ - S32 res = 0; - - mProcessing = TRUE; - for (curlmulti_set_t::iterator iter = mMultiSet.begin(); - iter != mMultiSet.end(); ) - { - curlmulti_set_t::iterator curiter = iter++; - LLCurl::Multi* multi = *curiter; - - if(!multi->isValid()) - { - if(multi == mActiveMulti) - { - mActiveMulti = NULL ; - mActiveRequestCount = 0 ; - } - mMultiSet.erase(curiter) ; - LLCurl::getCurlThread()->killMulti(multi) ; - continue ; - } - - S32 tres = multi->process(); - res += tres; - if (multi != mActiveMulti && tres == 0 && multi->mQueued == 0) - { - mMultiSet.erase(curiter); - LLCurl::getCurlThread()->killMulti(multi); - } - } - mProcessing = FALSE; - return res; -} - -S32 LLCurlRequest::getQueued() -{ - S32 queued = 0; - for (curlmulti_set_t::iterator iter = mMultiSet.begin(); - iter != mMultiSet.end(); ) - { - curlmulti_set_t::iterator curiter = iter++; - LLCurl::Multi* multi = *curiter; - - if(!multi->isValid()) - { - if(multi == mActiveMulti) - { - mActiveMulti = NULL ; - mActiveRequestCount = 0 ; - } - LLCurl::getCurlThread()->killMulti(multi); - mMultiSet.erase(curiter) ; - continue ; - } - - queued += multi->mQueued; - if (multi->getState() != LLCurl::Multi::STATE_READY) - { - ++queued; - } - } - return queued; -} - -LLCurlTextureRequest::LLCurlTextureRequest(S32 concurrency) : - LLCurlRequest(), - mConcurrency(concurrency), - mInQueue(0), - mMutex(NULL), - mHandleCounter(1), - mTotalIssuedRequests(0), - mTotalReceivedBits(0) -{ - mGlobalTimer.reset(); -} - -LLCurlTextureRequest::~LLCurlTextureRequest() -{ - mRequestMap.clear(); - - for(req_queue_t::iterator iter = mCachedRequests.begin(); iter != mCachedRequests.end(); ++iter) - { - delete *iter; - } - mCachedRequests.clear(); -} - -//return 0: success -// > 0: cached handle -U32 LLCurlTextureRequest::getByteRange(const std::string& url, - const headers_t& headers, - S32 offset, S32 length, U32 pri, - LLCurl::ResponderPtr responder, F32 delay_time) -{ - U32 ret_val = 0; - bool success = false; - - if(mInQueue < mConcurrency && delay_time < 0.f) - { - success = LLCurlRequest::getByteRange(url, headers, offset, length, responder); - } - - LLMutexLock lock(&mMutex); - - if(success) - { - mInQueue++; - mTotalIssuedRequests++; - } - else - { - request_t* request = new request_t(mHandleCounter, url, headers, offset, length, pri, responder); - if(delay_time > 0.f) - { - request->mStartTime = mGlobalTimer.getElapsedTimeF32() + delay_time; - } - - mCachedRequests.insert(request); - mRequestMap[mHandleCounter] = request; - ret_val = mHandleCounter; - mHandleCounter++; - - if(!mHandleCounter) - { - mHandleCounter = 1; - } - } - - return ret_val; -} - -void LLCurlTextureRequest::completeRequest(S32 received_bytes) -{ - LLMutexLock lock(&mMutex); - - llassert_always(mInQueue > 0); - - mInQueue--; - mTotalReceivedBits += received_bytes * 8; -} - -void LLCurlTextureRequest::nextRequests() -{ - if(mCachedRequests.empty() || mInQueue >= mConcurrency) - { - return; - } - - F32 cur_time = mGlobalTimer.getElapsedTimeF32(); - - req_queue_t::iterator iter; - { - LLMutexLock lock(&mMutex); - iter = mCachedRequests.begin(); - } - while(1) - { - request_t* request = *iter; - if(request->mStartTime < cur_time) - { - if(!LLCurlRequest::getByteRange(request->mUrl, request->mHeaders, request->mOffset, request->mLength, request->mResponder)) - { - break; - } - - LLMutexLock lock(&mMutex); - ++iter; - mInQueue++; - mTotalIssuedRequests++; - mCachedRequests.erase(request); - mRequestMap.erase(request->mHandle); - delete request; - - if(iter == mCachedRequests.end() || mInQueue >= mConcurrency) - { - break; - } - } - else - { - LLMutexLock lock(&mMutex); - ++iter; - if(iter == mCachedRequests.end() || mInQueue >= mConcurrency) - { - break; - } - } - } - - return; -} - -void LLCurlTextureRequest::updatePriority(U32 handle, U32 pri) -{ - if(!handle) - { - return; - } - - LLMutexLock lock(&mMutex); - - std::map<S32, request_t*>::iterator iter = mRequestMap.find(handle); - if(iter != mRequestMap.end()) - { - request_t* req = iter->second; - - if(req->mPriority != pri) - { - mCachedRequests.erase(req); - req->mPriority = pri; - mCachedRequests.insert(req); - } - } -} - -void LLCurlTextureRequest::removeRequest(U32 handle) -{ - if(!handle) - { - return; - } - - LLMutexLock lock(&mMutex); - - std::map<S32, request_t*>::iterator iter = mRequestMap.find(handle); - if(iter != mRequestMap.end()) - { - request_t* req = iter->second; - mRequestMap.erase(iter); - mCachedRequests.erase(req); - delete req; - } -} - -bool LLCurlTextureRequest::isWaiting(U32 handle) -{ - if(!handle) - { - return false; - } - - LLMutexLock lock(&mMutex); - return mRequestMap.find(handle) != mRequestMap.end(); -} - -U32 LLCurlTextureRequest::getTotalReceivedBits() -{ - LLMutexLock lock(&mMutex); - - U32 bits = mTotalReceivedBits; - mTotalReceivedBits = 0; - return bits; -} - -U32 LLCurlTextureRequest::getTotalIssuedRequests() -{ - LLMutexLock lock(&mMutex); - return mTotalIssuedRequests; -} - -S32 LLCurlTextureRequest::getNumRequests() -{ - LLMutexLock lock(&mMutex); - return mInQueue; -} - -//////////////////////////////////////////////////////////////////////////// -// For generating one easy request -// associated with a single multi request - -LLCurlEasyRequest::LLCurlEasyRequest() - : mRequestSent(false), - mResultReturned(false) -{ - mMulti = new LLCurl::Multi(); - - if(mMulti->isValid()) - { - mEasy = mMulti->allocEasy(); - if (mEasy) - { - mEasy->setErrorBuffer(); - mEasy->setCA(); - // Set proxy settings if configured to do so. - LLProxy::getInstance()->applyProxySettings(mEasy); - } -} - else - { - LLCurl::getCurlThread()->killMulti(mMulti) ; - mEasy = NULL ; - mMulti = NULL ; - } -} - -LLCurlEasyRequest::~LLCurlEasyRequest() -{ - LLCurl::getCurlThread()->killMulti(mMulti) ; -} - -void LLCurlEasyRequest::setopt(CURLoption option, S32 value) -{ - if (isValid() && mEasy) - { - mEasy->setopt(option, value); - } -} - -void LLCurlEasyRequest::setoptString(CURLoption option, const std::string& value) -{ - if (isValid() && mEasy) - { - mEasy->setoptString(option, value); - } -} - -void LLCurlEasyRequest::setPost(char* postdata, S32 size) -{ - if (isValid() && mEasy) - { - mEasy->setopt(CURLOPT_POST, 1); - mEasy->setopt(CURLOPT_POSTFIELDS, postdata); - mEasy->setopt(CURLOPT_POSTFIELDSIZE, size); - } -} - -void LLCurlEasyRequest::setHeaderCallback(curl_header_callback callback, void* userdata) -{ - if (isValid() && mEasy) - { - mEasy->setopt(CURLOPT_HEADERFUNCTION, (void*)callback); - mEasy->setopt(CURLOPT_HEADERDATA, userdata); // aka CURLOPT_WRITEHEADER - } -} - -void LLCurlEasyRequest::setWriteCallback(curl_write_callback callback, void* userdata) -{ - if (isValid() && mEasy) - { - mEasy->setopt(CURLOPT_WRITEFUNCTION, (void*)callback); - mEasy->setopt(CURLOPT_WRITEDATA, userdata); - } -} - -void LLCurlEasyRequest::setReadCallback(curl_read_callback callback, void* userdata) -{ - if (isValid() && mEasy) - { - mEasy->setopt(CURLOPT_READFUNCTION, (void*)callback); - mEasy->setopt(CURLOPT_READDATA, userdata); - } -} - -void LLCurlEasyRequest::setSSLCtxCallback(curl_ssl_ctx_callback callback, void* userdata) -{ - if (isValid() && mEasy) - { - mEasy->setopt(CURLOPT_SSL_CTX_FUNCTION, (void*)callback); - mEasy->setopt(CURLOPT_SSL_CTX_DATA, userdata); - } -} - -void LLCurlEasyRequest::slist_append(const std::string& header, const std::string& value) -{ - if (isValid() && mEasy) - { - mEasy->slist_append(header, value); - } -} - -void LLCurlEasyRequest::slist_append(const char* str) -{ - if (isValid() && mEasy) - { - mEasy->slist_append(str); - } -} - -void LLCurlEasyRequest::sendRequest(const std::string& url) -{ - llassert_always(!mRequestSent); - mRequestSent = true; - LL_DEBUGS() << url << LL_ENDL; - if (isValid() && mEasy) - { - mEasy->setHeaders(); - mEasy->setoptString(CURLOPT_URL, url); - mMulti->addEasy(mEasy); - } -} - -void LLCurlEasyRequest::requestComplete() -{ - llassert_always(mRequestSent); - mRequestSent = false; - if (isValid() && mEasy) - { - mMulti->removeEasy(mEasy); - } -} - -// Usage: Call getRestult until it returns false (no more messages) -bool LLCurlEasyRequest::getResult(CURLcode* result, LLCurl::TransferInfo* info) -{ - if(!isValid()) - { - return false ; - } - if (!mMulti->isCompleted()) - { //we're busy, try again later - return false; - } - mMulti->setState(LLCurl::Multi::STATE_READY) ; - - if (!mEasy) - { - // Special case - we failed to initialize a curl_easy (can happen if too many open files) - // Act as though the request failed to connect - if (mResultReturned) - { - return false; - } - else - { - *result = CURLE_FAILED_INIT; - mResultReturned = true; - return true; - } - } - // In theory, info_read might return a message with a status other than CURLMSG_DONE - // In practice for all messages returned, msg == CURLMSG_DONE - // Ignore other messages just in case - while(1) - { - S32 q; - CURLMsg* curlmsg = info_read(&q, info); - if (curlmsg) - { - if (curlmsg->msg == CURLMSG_DONE) - { - *result = curlmsg->data.result; - return true; - } - // else continue - } - else - { - return false; - } - } -} - -// private -CURLMsg* LLCurlEasyRequest::info_read(S32* q, LLCurl::TransferInfo* info) -{ - if (mEasy) - { - CURLMsg* curlmsg = mMulti->info_read(q); - if (curlmsg && curlmsg->msg == CURLMSG_DONE) - { - if (info) - { - mEasy->getTransferInfo(info); - } - } - return curlmsg; - } - return NULL; -} - -std::string LLCurlEasyRequest::getErrorString() -{ - return isValid() && mEasy ? std::string(mEasy->getErrorBuffer()) : std::string(); -} - -//////////////////////////////////////////////////////////////////////////// - -#if SAFE_SSL -//static -void LLCurl::ssl_locking_callback(int mode, int type, const char *file, int line) -{ - if (mode & CRYPTO_LOCK) - { - LLCurl::sSSLMutex[type]->lock(); - } - else - { - LLCurl::sSSLMutex[type]->unlock(); - } -} - -//static -unsigned long LLCurl::ssl_thread_id(void) -{ - return LLThread::currentID(); -} -#endif - -void LLCurl::initClass(F32 curl_reuest_timeout, S32 max_number_handles, bool multi_threaded) -{ - sCurlRequestTimeOut = curl_reuest_timeout ; //seconds - sMaxHandles = max_number_handles ; //max number of handles, (multi handles and easy handles combined). - - // Do not change this "unless you are familiar with and mean to control - // internal operations of libcurl" - // - http://curl.haxx.se/libcurl/c/curl_global_init.html - CURLcode code = curl_global_init(CURL_GLOBAL_ALL); - - check_curl_code(code); - -#if SAFE_SSL - S32 mutex_count = CRYPTO_num_locks(); - for (S32 i=0; i<mutex_count; i++) - { - sSSLMutex.push_back(new LLMutex(NULL)); - } - CRYPTO_set_id_callback(&LLCurl::ssl_thread_id); - CRYPTO_set_locking_callback(&LLCurl::ssl_locking_callback); -#endif - - sCurlThread = new LLCurlThread(multi_threaded) ; - if(multi_threaded) - { - sHandleMutexp = new LLMutex(NULL) ; - Easy::sHandleMutexp = new LLMutex(NULL) ; - } -} - -void LLCurl::cleanupClass() -{ - sNotQuitting = false; //set quitting - - //shut down curl thread - while(1) - { - if(!sCurlThread->update(1)) //finish all tasks - { - break ; - } - } - LL_CHECK_MEMORY - sCurlThread->shutdown() ; - LL_CHECK_MEMORY - delete sCurlThread ; - sCurlThread = NULL ; - LL_CHECK_MEMORY - -#if SAFE_SSL - CRYPTO_set_locking_callback(NULL); - for_each(sSSLMutex.begin(), sSSLMutex.end(), DeletePointer()); - sSSLMutex.clear(); -#endif - - LL_CHECK_MEMORY - Easy::deleteAllFreeHandles(); - LL_CHECK_MEMORY - Easy::deleteAllActiveHandles(); - LL_CHECK_MEMORY - - // Free the template easy handle - curl_easy_cleanup(sCurlTemplateStandardHandle); - sCurlTemplateStandardHandle = NULL; - LL_CHECK_MEMORY - - delete Easy::sHandleMutexp ; - Easy::sHandleMutexp = NULL ; - - LL_CHECK_MEMORY - - delete sHandleMutexp ; - sHandleMutexp = NULL ; - - LL_CHECK_MEMORY - - // removed as per https://jira.secondlife.com/browse/SH-3115 - //llassert(Easy::sActiveHandles.empty()); -} - -//static -CURLM* LLCurl::newMultiHandle() -{ - llassert(sNotQuitting); - - LLMutexLock lock(sHandleMutexp) ; - - if(sTotalHandles + 1 > sMaxHandles) - { - LL_WARNS() << "no more handles available." << LL_ENDL ; - return NULL ; //failed - } - sTotalHandles++; - - CURLM* ret = curl_multi_init() ; - if(!ret) - { - LL_WARNS() << "curl_multi_init failed." << LL_ENDL ; - } - - return ret ; -} - -//static -CURLMcode LLCurl::deleteMultiHandle(CURLM* handle) -{ - if(handle) - { - LLMutexLock lock(sHandleMutexp) ; - sTotalHandles-- ; - return curl_multi_cleanup(handle) ; - } - return CURLM_OK ; -} - -//static -CURL* LLCurl::newEasyHandle() -{ - llassert(sNotQuitting); - LLMutexLock lock(sHandleMutexp) ; - - if(sTotalHandles + 1 > sMaxHandles) - { - LL_WARNS() << "no more handles available." << LL_ENDL ; - return NULL ; //failed - } - sTotalHandles++; - - CURL* ret = createStandardCurlHandle(); - if(!ret) - { - LL_WARNS() << "failed to create curl handle." << LL_ENDL ; - } - - return ret ; -} - -//static -void LLCurl::deleteEasyHandle(CURL* handle) -{ - if(handle) - { - LLMutexLock lock(sHandleMutexp) ; - LL_CHECK_MEMORY - curl_easy_cleanup(handle) ; - LL_CHECK_MEMORY - sTotalHandles-- ; - } -} - -const unsigned int LLCurl::MAX_REDIRECTS = 5; - -// Provide access to LLCurl free functions outside of llcurl.cpp without polluting the global namespace. -void LLCurlFF::check_easy_code(CURLcode code) -{ - check_curl_code(code); -} -void LLCurlFF::check_multi_code(CURLMcode code) -{ - check_curl_multi_code(code); -} - - -// Static -CURL* LLCurl::createStandardCurlHandle() -{ - if (sCurlTemplateStandardHandle == NULL) - { // Late creation of the template curl handle - sCurlTemplateStandardHandle = curl_easy_init(); - if (sCurlTemplateStandardHandle == NULL) - { - LL_WARNS() << "curl error calling curl_easy_init()" << LL_ENDL; - } - else - { - CURLcode result = curl_easy_setopt(sCurlTemplateStandardHandle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); - check_curl_code(result); - result = curl_easy_setopt(sCurlTemplateStandardHandle, CURLOPT_NOSIGNAL, 1); - check_curl_code(result); - result = curl_easy_setopt(sCurlTemplateStandardHandle, CURLOPT_NOPROGRESS, 1); - check_curl_code(result); - result = curl_easy_setopt(sCurlTemplateStandardHandle, CURLOPT_ENCODING, ""); - check_curl_code(result); - result = curl_easy_setopt(sCurlTemplateStandardHandle, CURLOPT_AUTOREFERER, 1); - check_curl_code(result); - result = curl_easy_setopt(sCurlTemplateStandardHandle, CURLOPT_FOLLOWLOCATION, 1); - check_curl_code(result); - result = curl_easy_setopt(sCurlTemplateStandardHandle, CURLOPT_SSL_VERIFYPEER, 1); - check_curl_code(result); - result = curl_easy_setopt(sCurlTemplateStandardHandle, CURLOPT_SSL_VERIFYHOST, 0); - check_curl_code(result); - - // 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. - result = curl_easy_setopt(sCurlTemplateStandardHandle, CURLOPT_DNS_CACHE_TIMEOUT, 15); - check_curl_code(result); - } - } - - return curl_easy_duphandle(sCurlTemplateStandardHandle); -} diff --git a/indra/llmessage/llcurl.h b/indra/llmessage/llcurl.h deleted file mode 100644 index 34758433c8..0000000000 --- a/indra/llmessage/llcurl.h +++ /dev/null @@ -1,557 +0,0 @@ -/** - * @file llcurl.h - * @author Zero / Donovan - * @date 2006-10-15 - * @brief A wrapper around libcurl. - * - * $LicenseInfo:firstyear=2006&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_LLCURL_H -#define LL_LLCURL_H - -#include "linden_common.h" - -#include <sstream> -#include <string> -#include <vector> - -#include <boost/intrusive_ptr.hpp> -#include <curl/curl.h> // TODO: remove dependency - -#include "llbuffer.h" -#include "llhttpconstants.h" -#include "lliopipe.h" -#include "llsd.h" -#include "llqueuedthread.h" -#include "llframetimer.h" -#include "llpointer.h" -#include "llsingleton.h" - -class LLMutex; -class LLCurlThread; - -// For whatever reason, this is not typedef'd in curl.h -typedef size_t (*curl_header_callback)(void *ptr, size_t size, size_t nmemb, void *stream); - -class LLCurl -{ - LOG_CLASS(LLCurl); - -public: - class Easy; - class Multi; - - struct TransferInfo - { - TransferInfo() : mSizeDownload(0.0), mTotalTime(0.0), mSpeedDownload(0.0) {} - F64 mSizeDownload; - F64 mTotalTime; - F64 mSpeedDownload; - }; - - class Responder : public LLThreadSafeRefCount - { - //LOG_CLASS(Responder); - public: - - Responder(); - virtual ~Responder(); - - virtual bool followRedir() - { - return false; - } - - /** - * @brief return true if the status code indicates success. - */ - bool isGoodStatus() const { return isHttpGoodStatus(mStatus); } - - S32 getStatus() const { return mStatus; } - const std::string& getReason() const { return mReason; } - const LLSD& getContent() const { return mContent; } - bool hasResponseHeader(const std::string& header) const; - const std::string& getResponseHeader(const std::string& header) const; - const LLSD& getResponseHeaders() const { return mResponseHeaders; } - const std::string& getURL() const { return mURL; } - EHTTPMethod getHTTPMethod() const { return mHTTPMethod; } - - // This formats response information for use in log spam. Includes content spam. - std::string dumpResponse() const; - - // Allows direct triggering of success/error with different results. - void completeResult(S32 status, const std::string& reason, const LLSD& content = LLSD()); - void successResult(const LLSD& content); - void failureResult(S32 status, const std::string& reason, const LLSD& content = LLSD()); - - // The default implementation will try to parse body content as an LLSD, however - // it should not spam about parsing failures unless the server sent a - // Content-Type: application/llsd+xml header. - virtual void completedRaw( - const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer); - /**< Override point for clients that may want to use this - class when the response is some other format besides LLSD - */ - - - // The http* methods are not public since these should be triggered internally - // after status, reason, content, etc have been set. - // If you need to trigger a completion method, use the *Result methods, above. - protected: - // These methods are the preferred way to process final results. - // By default, when one of these is called the following information will be resolved: - // * HTTP status code - getStatus() - // * Reason string - getReason() - // * Content - getContent() - // * Response Headers - getResponseHeaders() - - // By default, httpSuccess is triggered whenever httpCompleted is called with a 2xx status code. - virtual void httpSuccess(); - //< called by completed for good status codes. - - // By default, httpFailure is triggered whenever httpCompleted is called with a non-2xx status code. - virtual void httpFailure(); - //< called by httpCompleted() on bad status - - // httpCompleted does not generally need to be overridden, unless - // you don't care about the status code (which determine httpFailure or httpSuccess) - // or if you want to re-interpret what a 'good' vs' bad' status code is. - virtual void httpCompleted(); - /**< The default implementation calls - either: - * httpSuccess(), or - * httpFailure() - */ - - public: - void setHTTPMethod(EHTTPMethod method); - void setURL(const std::string& url); - const std::string& getURL(); - void setResult(S32 status, const std::string& reason, const LLSD& content = LLSD()); - void setResponseHeader(const std::string& header, const std::string& value); - - private: - // These can be accessed by the get* methods. Treated as 'read-only' during completion handlers. - EHTTPMethod mHTTPMethod; - std::string mURL; - LLSD mResponseHeaders; - - protected: - // These should also generally be treated as 'read-only' during completion handlers - // and should be accessed by the get* methods. The exception to this rule would - // be when overriding the completedRaw method in preparation for calling httpCompleted(). - S32 mStatus; - std::string mReason; - LLSD mContent; - }; - typedef LLPointer<Responder> ResponderPtr; - - - /** - * @ brief Set certificate authority file used to verify HTTPS certs. - */ - static void setCAFile(const std::string& file); - - /** - * @ brief Set certificate authority path used to verify HTTPS certs. - */ - static void setCAPath(const std::string& path); - - /** - * @ brief Return human-readable string describing libcurl version. - */ - static std::string getVersionString(); - - /** - * @ brief Get certificate authority file used to verify HTTPS certs. - */ - static const std::string& getCAFile() { return sCAFile; } - - /** - * @ brief Get certificate authority path used to verify HTTPS certs. - */ - static const std::string& getCAPath() { return sCAPath; } - - /** - * @ brief Initialize LLCurl class - */ - static void initClass(F32 curl_reuest_timeout = 120.f, S32 max_number_handles = 256, bool multi_threaded = false); - - /** - * @ brief Cleanup LLCurl class - */ - static void cleanupClass(); - - /** - * @ brief curl error code -> string - */ - static std::string strerror(CURLcode errorcode); - - // For OpenSSL callbacks - static std::vector<LLMutex*> sSSLMutex; - - // OpenSSL callbacks - static void ssl_locking_callback(int mode, int type, const char *file, int line); - static unsigned long ssl_thread_id(void); - - static LLCurlThread* getCurlThread() { return sCurlThread ;} - - static CURLM* newMultiHandle() ; - static CURLMcode deleteMultiHandle(CURLM* handle) ; - static CURL* newEasyHandle() ; - static void deleteEasyHandle(CURL* handle) ; - - static CURL* createStandardCurlHandle(); - -private: - static std::string sCAPath; - static std::string sCAFile; - static const unsigned int MAX_REDIRECTS; - static LLCurlThread* sCurlThread; - - static LLMutex* sHandleMutexp ; - static S32 sTotalHandles ; - static S32 sMaxHandles; - static CURL* sCurlTemplateStandardHandle; -public: - static bool sNotQuitting; - static F32 sCurlRequestTimeOut; -}; - -class LLCurl::Easy -{ - LOG_CLASS(Easy); - -private: - Easy(); - -public: - static Easy* getEasy(); - ~Easy(); - - CURL* getCurlHandle() const { return mCurlEasyHandle; } - - void setErrorBuffer(); - void setCA(); - - void setopt(CURLoption option, S32 value); - // These assume the setter does not free value! - void setopt(CURLoption option, void* value); - void setopt(CURLoption option, char* value); - // Copies the string so that it is guaranteed to stick around - void setoptString(CURLoption option, const std::string& value); - - void slist_append(const std::string& header, const std::string& value); - void slist_append(const char* str); - void setHeaders(); - - S32 report(CURLcode); - void getTransferInfo(LLCurl::TransferInfo* info); - - void prepRequest(const std::string& url, const std::vector<std::string>& headers, LLCurl::ResponderPtr, S32 time_out = 0, bool post = false); - - const char* getErrorBuffer(); - - std::stringstream& getInput() { return mInput; } - std::stringstream& getHeaderOutput() { return mHeaderOutput; } - LLIOPipe::buffer_ptr_t& getOutput() { return mOutput; } - const LLChannelDescriptors& getChannels() { return mChannels; } - - void resetState(); - - static CURL* allocEasyHandle(); - static void releaseEasyHandle(CURL* handle); - -private: - friend class LLCurl; - friend class LLCurl::Multi; - - CURL* mCurlEasyHandle; - struct curl_slist* mHeaders; - - std::stringstream mRequest; - LLChannelDescriptors mChannels; - LLIOPipe::buffer_ptr_t mOutput; - std::stringstream mInput; - std::stringstream mHeaderOutput; - char mErrorBuffer[CURL_ERROR_SIZE]; - - // Note: char*'s not strings since we pass pointers to curl - std::vector<char*> mStrings; - - LLCurl::ResponderPtr mResponder; - - static std::set<CURL*> sFreeHandles; - static std::set<CURL*> sActiveHandles; - static LLMutex* sHandleMutexp ; - - static void deleteAllActiveHandles(); - static void deleteAllFreeHandles(); -}; - -class LLCurl::Multi -{ - LOG_CLASS(Multi); - - friend class LLCurlThread ; - -private: - ~Multi(); - - void markDead() ; - bool doPerform(); - -public: - - typedef enum - { - STATE_READY=0, - STATE_PERFORMING=1, - STATE_COMPLETED=2 - } ePerformState; - - Multi(F32 idle_time_out = 0.f); - - LLCurl::Easy* allocEasy(); - bool addEasy(LLCurl::Easy* easy); - void removeEasy(LLCurl::Easy* easy); - - void lock() ; - void unlock() ; - - void setState(ePerformState state) ; - ePerformState getState() ; - - bool isCompleted() ; - bool isValid() {return mCurlMultiHandle != NULL && mValid;} - bool isDead() {return mDead;} - - bool waitToComplete() ; - - S32 process(); - - CURLMsg* info_read(S32* msgs_in_queue); - - S32 mQueued; - S32 mErrorCount; - -private: - void easyFree(LLCurl::Easy*); - void cleanup(bool deleted = false) ; - - CURLM* mCurlMultiHandle; - - typedef std::set<LLCurl::Easy*> easy_active_list_t; - easy_active_list_t mEasyActiveList; - typedef std::map<CURL*, LLCurl::Easy*> easy_active_map_t; - easy_active_map_t mEasyActiveMap; - typedef std::set<LLCurl::Easy*> easy_free_list_t; - easy_free_list_t mEasyFreeList; - - LLQueuedThread::handle_t mHandle ; - ePerformState mState; - - BOOL mDead ; - BOOL mValid ; - LLMutex* mMutexp ; - LLMutex* mDeletionMutexp ; - LLMutex* mEasyMutexp ; - LLFrameTimer mIdleTimer ; - F32 mIdleTimeOut; -}; - -class LLCurlThread : public LLQueuedThread -{ -public: - - class CurlRequest : public LLQueuedThread::QueuedRequest - { - protected: - virtual ~CurlRequest(); // use deleteRequest() - - public: - CurlRequest(handle_t handle, LLCurl::Multi* multi, LLCurlThread* curl_thread); - - /*virtual*/ bool processRequest(); - /*virtual*/ void finishRequest(bool completed); - - private: - // input - LLCurl::Multi* mMulti; - LLCurlThread* mCurlThread; - }; - friend class CurlRequest; - -public: - LLCurlThread(bool threaded = true) ; - virtual ~LLCurlThread() ; - - S32 update(F32 max_time_ms); - - void addMulti(LLCurl::Multi* multi) ; - void killMulti(LLCurl::Multi* multi) ; - -private: - bool doMultiPerform(LLCurl::Multi* multi) ; - void deleteMulti(LLCurl::Multi* multi) ; - void cleanupMulti(LLCurl::Multi* multi) ; -} ; - - -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: - LLCurlEasyRequest(); - ~LLCurlEasyRequest(); - void setopt(CURLoption option, S32 value); - void setoptString(CURLoption option, const std::string& value); - void setPost(char* postdata, S32 size); - void setHeaderCallback(curl_header_callback callback, void* userdata); - void setWriteCallback(curl_write_callback callback, void* userdata); - void setReadCallback(curl_read_callback callback, void* userdata); - void setSSLCtxCallback(curl_ssl_ctx_callback callback, void* userdata); - void slist_append(const std::string& header, const std::string& value); - void slist_append(const char* str); - void sendRequest(const std::string& url); - void requestComplete(); - bool getResult(CURLcode* result, LLCurl::TransferInfo* info = NULL); - std::string getErrorString(); - bool isCompleted() {return mMulti->isCompleted() ;} - bool wait() { return mMulti->waitToComplete(); } - bool isValid() {return mMulti && mMulti->isValid(); } - - LLCurl::Easy* getEasy() const { return mEasy; } - -private: - CURLMsg* info_read(S32* queue, LLCurl::TransferInfo* info); - -private: - LLCurl::Multi* mMulti; - LLCurl::Easy* mEasy; - bool mRequestSent; - bool mResultReturned; -}; - -// Provide access to LLCurl free functions outside of llcurl.cpp without polluting the global namespace. -namespace LLCurlFF -{ - void check_easy_code(CURLcode code); - void check_multi_code(CURLMcode code); -} - -#endif // LL_LLCURL_H diff --git a/indra/llmessage/llexperiencecache.cpp b/indra/llmessage/llexperiencecache.cpp index 52b60a176e..779d1d9d99 100644 --- a/indra/llmessage/llexperiencecache.cpp +++ b/indra/llmessage/llexperiencecache.cpp @@ -26,616 +26,986 @@ #include "llexperiencecache.h" #include "llavatarname.h" -#include "llframetimer.h" -#include "llhttpclient.h" #include "llsdserialize.h" +#include "llcoros.h" +#include "lleventcoro.h" +#include "lleventfilter.h" +#include "llcoproceduremanager.h" +#include "lldir.h" #include <set> #include <map> -#include "boost/tokenizer.hpp" +#include <boost/tokenizer.hpp> +#include <boost/concept_check.hpp> - -namespace LLExperienceCache +//========================================================================= +namespace LLExperienceCacheImpl { + void mapKeys(const LLSD& legacyKeys); + F64 getErrorRetryDeltaTime(S32 status, LLSD headers); + bool maxAgeFromCacheControl(const std::string& cache_control, S32 *max_age); + + static const std::string PRIVATE_KEY = "private_id"; + static const std::string EXPERIENCE_ID = "public_id"; + + static const std::string MAX_AGE("max-age"); + static const boost::char_separator<char> EQUALS_SEPARATOR("="); + static const boost::char_separator<char> COMMA_SEPARATOR(","); + // *TODO$: this seems to be tied to mapKeys which is used by bootstrap.... but I don't think that bootstrap is used. typedef std::map<LLUUID, LLUUID> KeyMap; KeyMap privateToPublicKeyMap; +} - void mapKeys(const LLSD& legacyKeys); +//========================================================================= +const std::string LLExperienceCache::PRIVATE_KEY = "private_id"; +const std::string LLExperienceCache::MISSING = "DoesNotExist"; + +const std::string LLExperienceCache::AGENT_ID = "agent_id"; +const std::string LLExperienceCache::GROUP_ID = "group_id"; +const std::string LLExperienceCache::EXPERIENCE_ID = "public_id"; +const std::string LLExperienceCache::NAME = "name"; +const std::string LLExperienceCache::PROPERTIES = "properties"; +const std::string LLExperienceCache::EXPIRES = "expiration"; +const std::string LLExperienceCache::DESCRIPTION = "description"; +const std::string LLExperienceCache::QUOTA = "quota"; +const std::string LLExperienceCache::MATURITY = "maturity"; +const std::string LLExperienceCache::METADATA = "extended_metadata"; +const std::string LLExperienceCache::SLURL = "slurl"; + +// should be in sync with experience-api/experiences/models.py +const int LLExperienceCache::PROPERTY_INVALID = 1 << 0; +const int LLExperienceCache::PROPERTY_PRIVILEGED = 1 << 3; +const int LLExperienceCache::PROPERTY_GRID = 1 << 4; +const int LLExperienceCache::PROPERTY_PRIVATE = 1 << 5; +const int LLExperienceCache::PROPERTY_DISABLED = 1 << 6; +const int LLExperienceCache::PROPERTY_SUSPENDED = 1 << 7; + +// default values +const F64 LLExperienceCache::DEFAULT_EXPIRATION = 600.0; +const S32 LLExperienceCache::DEFAULT_QUOTA = 128; // this is megabytes +const int LLExperienceCache::SEARCH_PAGE_SIZE = 30; + +//========================================================================= +LLExperienceCache::LLExperienceCache(): + mShutdown(false) +{ +} - std::string sLookupURL; +LLExperienceCache::~LLExperienceCache() +{ - typedef std::map<LLUUID, std::string> ask_queue_t; - ask_queue_t sAskQueue; +} - typedef std::map<LLUUID, F64> pending_queue_t; - pending_queue_t sPendingQueue; +void LLExperienceCache::initSingleton() +{ + mCacheFileName = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "experience_cache.xml"); - cache_t sCache; - int sMaximumLookups = 10; + LL_INFOS("ExperienceCache") << "Loading " << mCacheFileName << LL_ENDL; + llifstream cache_stream(mCacheFileName.c_str()); - LLFrameTimer sRequestTimer; + if (cache_stream.is_open()) + { + cache_stream >> (*this); + } - // Periodically clean out expired entries from the cache - LLFrameTimer sEraseExpiredTimer; + LLCoros::instance().launch("LLExperienceCache::idleCoro", + boost::bind(&LLExperienceCache::idleCoro, this)); - // May have multiple callbacks for a single ID, which are - // represented as multiple slots bound to the signal. - // Avoid copying signals via pointers. - typedef std::map<LLUUID, callback_signal_t*> signal_map_t; - signal_map_t sSignalMap; +} +void LLExperienceCache::cleanup() +{ + LL_INFOS("ExperienceCache") << "Saving " << mCacheFileName << LL_ENDL; + + llofstream cache_stream(mCacheFileName.c_str()); + if (cache_stream.is_open()) + { + cache_stream << (*this); + } + mShutdown = true; +} +//------------------------------------------------------------------------- +void LLExperienceCache::importFile(std::istream& istr) +{ + LLSD data; + S32 parse_count = LLSDSerialize::fromXMLDocument(data, istr); + if (parse_count < 1) return; - bool max_age_from_cache_control(const std::string& cache_control, S32 *max_age); - void eraseExpired(); + LLSD experiences = data["experiences"]; - void processExperience( const LLUUID& public_key, const LLSD& experience ) - { - sCache[public_key]=experience; - LLSD & row = sCache[public_key]; + LLUUID public_key; + LLSD::map_const_iterator it = experiences.beginMap(); + for (; it != experiences.endMap(); ++it) + { + public_key.set(it->first); + mCache[public_key] = it->second; + } - if(row.has(EXPIRES)) - { - row[EXPIRES] = row[EXPIRES].asReal() + LLFrameTimer::getTotalSeconds(); - } + LL_DEBUGS("ExperienceCache") << "importFile() loaded " << mCache.size() << LL_ENDL; +} - if(row.has(EXPERIENCE_ID)) - { - sPendingQueue.erase(row[EXPERIENCE_ID].asUUID()); - } +void LLExperienceCache::exportFile(std::ostream& ostr) const +{ + LLSD experiences; - //signal - signal_map_t::iterator sig_it = sSignalMap.find(public_key); - if (sig_it != sSignalMap.end()) - { - callback_signal_t* signal = sig_it->second; - (*signal)(experience); + cache_t::const_iterator it = mCache.begin(); + for (; it != mCache.end(); ++it) + { + if (!it->second.has(EXPERIENCE_ID) || it->second[EXPERIENCE_ID].asUUID().isNull() || + it->second.has("DoesNotExist") || (it->second.has(PROPERTIES) && it->second[PROPERTIES].asInteger() & PROPERTY_INVALID)) + continue; - sSignalMap.erase(public_key); + experiences[it->first.asString()] = it->second; + } - delete signal; - } - } + LLSD data; + data["experiences"] = experiences; - void initClass( ) - { - } + LLSDSerialize::toPrettyXML(data, ostr); +} + +// *TODO$: Rider: This method does not seem to be used... it may be useful in testing. +void LLExperienceCache::bootstrap(const LLSD& legacyKeys, int initialExpiration) +{ + LLExperienceCacheImpl::mapKeys(legacyKeys); + LLSD::array_const_iterator it = legacyKeys.beginArray(); + for (/**/; it != legacyKeys.endArray(); ++it) + { + LLSD experience = *it; + if (experience.has(EXPERIENCE_ID)) + { + if (!experience.has(EXPIRES)) + { + experience[EXPIRES] = initialExpiration; + } + processExperience(experience[EXPERIENCE_ID].asUUID(), experience); + } + else + { + LL_WARNS("ExperienceCache") + << "Skipping bootstrap entry which is missing " << EXPERIENCE_ID + << LL_ENDL; + } + } +} + +LLUUID LLExperienceCache::getExperienceId(const LLUUID& private_key, bool null_if_not_found) +{ + if (private_key.isNull()) + return LLUUID::null; + + LLExperienceCacheImpl::KeyMap::const_iterator it = LLExperienceCacheImpl::privateToPublicKeyMap.find(private_key); + if (it == LLExperienceCacheImpl::privateToPublicKeyMap.end()) + { + if (null_if_not_found) + { + return LLUUID::null; + } + return private_key; + } + LL_WARNS("LLExperience") << "converted private key " << private_key << " to experience_id " << it->second << LL_ENDL; + return it->second; +} - const cache_t& getCached() +//========================================================================= +void LLExperienceCache::processExperience(const LLUUID& public_key, const LLSD& experience) +{ + LL_INFOS("ExperienceCache") << "Processing experience \"" << experience[NAME] << "\" with key " << public_key.asString() << LL_ENDL; + + mCache[public_key]=experience; + LLSD & row = mCache[public_key]; + + if(row.has(EXPIRES)) { - return sCache; + row[EXPIRES] = row[EXPIRES].asReal() + LLFrameTimer::getTotalSeconds(); } - void setMaximumLookups( int maximumLookups) + if(row.has(EXPERIENCE_ID)) { - sMaximumLookups = maximumLookups; + mPendingQueue.erase(row[EXPERIENCE_ID].asUUID()); } - void bootstrap(const LLSD& legacyKeys, int initialExpiration) + //signal + signal_map_t::iterator sig_it = mSignalMap.find(public_key); + if (sig_it != mSignalMap.end()) { - mapKeys(legacyKeys); - LLSD::array_const_iterator it = legacyKeys.beginArray(); - for(/**/; it != legacyKeys.endArray(); ++it) - { - LLSD experience = *it; - if(experience.has(EXPERIENCE_ID)) - { - if(!experience.has(EXPIRES)) - { - experience[EXPIRES] = initialExpiration; - } - processExperience(experience[EXPERIENCE_ID].asUUID(), experience); - } - else - { - LL_WARNS("ExperienceCache") - << "Skipping bootstrap entry which is missing " << EXPERIENCE_ID - << LL_ENDL; - } - } + signal_ptr signal = sig_it->second; + (*signal)(experience); + + mSignalMap.erase(public_key); } +} + +const LLExperienceCache::cache_t& LLExperienceCache::getCached() +{ + return mCache; +} +void LLExperienceCache::requestExperiencesCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &httpAdapter, std::string url, RequestQueue_t requests) +{ + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest()); + + //LL_INFOS("requestExperiencesCoro") << "url: " << url << LL_ENDL; + + LLSD result = httpAdapter->getAndSuspend(httpRequest, url); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) + { + F64 now = LLFrameTimer::getTotalSeconds(); + + LLSD headers = httpResults[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_HEADERS]; + // build dummy entries for the failed requests + for (RequestQueue_t::const_iterator it = requests.begin(); it != requests.end(); ++it) + { + LLSD exp = get(*it); + //leave the properties alone if we already have a cache entry for this xp + if (exp.isUndefined()) + { + exp[PROPERTIES] = PROPERTY_INVALID; + } + exp[EXPIRES] = now + LLExperienceCacheImpl::getErrorRetryDeltaTime(status, headers); + exp[EXPERIENCE_ID] = *it; + exp["key_type"] = EXPERIENCE_ID; + exp["uuid"] = *it; + exp["error"] = (LLSD::Integer)status.getType(); + exp[QUOTA] = DEFAULT_QUOTA; + + processExperience(*it, exp); + } + return; + } + + LLSD experiences = result["experience_keys"]; + + for (LLSD::array_const_iterator it = experiences.beginArray(); + it != experiences.endArray(); ++it) + { + const LLSD& row = *it; + LLUUID public_key = row[EXPERIENCE_ID].asUUID(); + + LL_DEBUGS("ExperienceCache") << "Received result for " << public_key + << " display '" << row[LLExperienceCache::NAME].asString() << "'" << LL_ENDL; + + processExperience(public_key, row); + } + + LLSD error_ids = result["error_ids"]; + + for (LLSD::array_const_iterator errIt = error_ids.beginArray(); + errIt != error_ids.endArray(); ++errIt) + { + LLUUID id = errIt->asUUID(); + LLSD exp; + exp[EXPIRES] = DEFAULT_EXPIRATION; + exp[EXPERIENCE_ID] = id; + exp[PROPERTIES] = PROPERTY_INVALID; + exp[MISSING] = true; + exp[QUOTA] = DEFAULT_QUOTA; + + processExperience(id, exp); + LL_WARNS("ExperienceCache") << "LLExperienceResponder::result() error result for " << id << LL_ENDL; + } +} - bool expirationFromCacheControl(LLSD headers, F64 *expires) + +void LLExperienceCache::requestExperiences() +{ + if (mCapability.empty()) + { + LL_WARNS("ExperienceCache") << "Capability query method not set." << LL_ENDL; + return; + } + + std::string urlBase = mCapability("GetExperienceInfo"); + if (urlBase.empty()) + { + LL_WARNS("ExperienceCache") << "No Experience capability." << LL_ENDL; + return; + } + + if (*urlBase.rbegin() != '/') + { + urlBase += "/"; + } + urlBase += "id/"; + + + F64 now = LLFrameTimer::getTotalSeconds(); + + const U32 EXP_URL_SEND_THRESHOLD = 3000; + const U32 PAGE_SIZE = EXP_URL_SEND_THRESHOLD / UUID_STR_LENGTH; + + std::ostringstream ostr; + ostr << urlBase << "?page_size=" << PAGE_SIZE; + RequestQueue_t requests; + + while (!mRequestQueue.empty()) + { + RequestQueue_t::iterator it = mRequestQueue.begin(); + LLUUID key = (*it); + mRequestQueue.erase(it); + requests.insert(key); + + ostr << "&" << EXPERIENCE_ID << "=" << key.asString(); + mPendingQueue[key] = now; + + if (mRequestQueue.empty() || (ostr.tellp() > EXP_URL_SEND_THRESHOLD)) + { // request is placed in the coprocedure pool for the ExpCache cache. Throttling is done by the pool itself. + LLCoprocedureManager::instance().enqueueCoprocedure("ExpCache", "RequestExperiences", + boost::bind(&LLExperienceCache::requestExperiencesCoro, this, _1, ostr.str(), requests) ); + + ostr.str(std::string()); + ostr << urlBase << "?page_size=" << PAGE_SIZE; + requests.clear(); + } + } + +} + + +bool LLExperienceCache::isRequestPending(const LLUUID& public_key) +{ + bool isPending = false; + const F64 PENDING_TIMEOUT_SECS = 5.0 * 60.0; + + PendingQueue_t::const_iterator it = mPendingQueue.find(public_key); + + if(it != mPendingQueue.end()) { - // Allow the header to override the default - LLSD cache_control_header = headers["cache-control"]; - if (cache_control_header.isDefined()) - { - S32 max_age = 0; - std::string cache_control = cache_control_header.asString(); - if (max_age_from_cache_control(cache_control, &max_age)) - { - LL_WARNS("ExperienceCache") - << "got EXPIRES from headers, max_age " << max_age - << LL_ENDL; - F64 now = LLFrameTimer::getTotalSeconds(); - *expires = now + (F64)max_age; - return true; - } - } - return false; + F64 expire_time = LLFrameTimer::getTotalSeconds() - PENDING_TIMEOUT_SECS; + isPending = (it->second > expire_time); } + return isPending; +} + +void LLExperienceCache::setCapabilityQuery(LLExperienceCache::CapabilityQuery_t queryfn) +{ + mCapability = queryfn; +} + + +void LLExperienceCache::idleCoro() +{ + const F32 SECS_BETWEEN_REQUESTS = 0.5f; + const F32 ERASE_EXPIRED_TIMEOUT = 60.f; // seconds - static const std::string MAX_AGE("max-age"); - static const boost::char_separator<char> EQUALS_SEPARATOR("="); - static const boost::char_separator<char> COMMA_SEPARATOR(","); + LL_INFOS("ExperienceCache") << "Launching Experience cache idle coro." << LL_ENDL; + do + { + llcoro::suspendUntilTimeout(SECS_BETWEEN_REQUESTS); - bool max_age_from_cache_control(const std::string& cache_control, S32 *max_age) + if (mEraseExpiredTimer.checkExpirationAndReset(ERASE_EXPIRED_TIMEOUT)) + { + eraseExpired(); + } + + if (!mRequestQueue.empty()) + { + requestExperiences(); + } + + } while (!mShutdown); + + // The coroutine system will likely be shut down by the time we get to this point + // (or at least no further cycling will occur on it since the user has decided to quit.) +} + +void LLExperienceCache::erase(const LLUUID& key) +{ + cache_t::iterator it = mCache.find(key); + + if(it != mCache.end()) { - // Split the string on "," to get a list of directives - typedef boost::tokenizer<boost::char_separator<char> > tokenizer; - tokenizer directives(cache_control, COMMA_SEPARATOR); + mCache.erase(it); + } +} - tokenizer::iterator token_it = directives.begin(); - for ( ; token_it != directives.end(); ++token_it) - { - // Tokens may have leading or trailing whitespace - std::string token = *token_it; - LLStringUtil::trim(token); +void LLExperienceCache::eraseExpired() +{ + F64 now = LLFrameTimer::getTotalSeconds(); + cache_t::iterator it = mCache.begin(); + while (it != mCache.end()) + { + cache_t::iterator cur = it; + LLSD& exp = cur->second; + ++it; - if (token.compare(0, MAX_AGE.size(), MAX_AGE) == 0) + //LL_INFOS("ExperienceCache") << "Testing experience \"" << exp[NAME] << "\" with exp time " << exp[EXPIRES].asReal() << "(now = " << now << ")" << LL_ENDL; + + if(exp.has(EXPIRES) && exp[EXPIRES].asReal() < now) + { + if(!exp.has(EXPERIENCE_ID)) { - // ...this token starts with max-age, so let's chop it up by "=" - tokenizer subtokens(token, EQUALS_SEPARATOR); - tokenizer::iterator subtoken_it = subtokens.begin(); - - // Must have a token - if (subtoken_it == subtokens.end()) return false; - std::string subtoken = *subtoken_it; - - // Must exactly equal "max-age" - LLStringUtil::trim(subtoken); - if (subtoken != MAX_AGE) return false; - - // Must have another token - ++subtoken_it; - if (subtoken_it == subtokens.end()) return false; - subtoken = *subtoken_it; - - // Must be a valid integer - // *NOTE: atoi() returns 0 for invalid values, so we have to - // check the string first. - // *TODO: Do servers ever send "0000" for zero? We don't handle it - LLStringUtil::trim(subtoken); - if (subtoken == "0") + LL_WARNS("ExperienceCache") << "Removing experience with no id " << LL_ENDL ; + mCache.erase(cur); + } + else + { + LLUUID id = exp[EXPERIENCE_ID].asUUID(); + LLUUID private_key = exp.has(LLExperienceCache::PRIVATE_KEY) ? exp[LLExperienceCache::PRIVATE_KEY].asUUID():LLUUID::null; + if(private_key.notNull() || !exp.has("DoesNotExist")) { - *max_age = 0; - return true; + fetch(id, true); } - S32 val = atoi( subtoken.c_str() ); - if (val > 0 && val < S32_MAX) + else { - *max_age = val; - return true; + LL_WARNS("ExperienceCache") << "Removing invalid experience " << id << LL_ENDL ; + mCache.erase(cur); } - return false; } } - return false; } - - - void importFile(std::istream& istr) +} + +bool LLExperienceCache::fetch(const LLUUID& key, bool refresh/* = true*/) +{ + if(!key.isNull() && !isRequestPending(key) && (refresh || mCache.find(key)==mCache.end())) { - LLSD data; - S32 parse_count = LLSDSerialize::fromXMLDocument(data, istr); - if(parse_count < 1) return; + LL_DEBUGS("ExperienceCache") << " queue request for " << EXPERIENCE_ID << " " << key << LL_ENDL; - LLSD experiences = data["experiences"]; - - LLUUID public_key; - LLSD::map_const_iterator it = experiences.beginMap(); - for(; it != experiences.endMap() ; ++it) - { - public_key.set(it->first); - sCache[public_key]=it->second; - } - - LL_DEBUGS("ExperienceCache") << "importFile() loaded " << sCache.size() << LL_ENDL; + mRequestQueue.insert(key); + return true; } + return false; +} - void exportFile(std::ostream& ostr) +void LLExperienceCache::insert(const LLSD& experience_data) +{ + if(experience_data.has(EXPERIENCE_ID)) { - LLSD experiences; - - cache_t::const_iterator it =sCache.begin(); - for( ; it != sCache.end() ; ++it) - { - if(!it->second.has(EXPERIENCE_ID) || it->second[EXPERIENCE_ID].asUUID().isNull() || - it->second.has("DoesNotExist") || (it->second.has(PROPERTIES) && it->second[PROPERTIES].asInteger() & PROPERTY_INVALID)) - continue; - - experiences[it->first.asString()] = it->second; - } + processExperience(experience_data[EXPERIENCE_ID].asUUID(), experience_data); + } + else + { + LL_WARNS("ExperienceCache") << ": Ignoring cache insert of experience which is missing " << EXPERIENCE_ID << LL_ENDL; + } +} - LLSD data; - data["experiences"] = experiences; +const LLSD& LLExperienceCache::get(const LLUUID& key) +{ + static const LLSD empty; + + if(key.isNull()) + return empty; + cache_t::const_iterator it = mCache.find(key); - LLSDSerialize::toPrettyXML(data, ostr); + if (it != mCache.end()) + { + return it->second; } + fetch(key); - class LLExperienceResponder : public LLHTTPClient::Responder - { - public: - LLExperienceResponder(const ask_queue_t& keys) - :mKeys(keys) - { + return empty; +} - } +void LLExperienceCache::get(const LLUUID& key, LLExperienceCache::ExperienceGetFn_t slot) +{ + if(key.isNull()) + return; - /*virtual*/ void httpCompleted() - { - LLSD experiences = getContent()["experience_keys"]; - LLSD::array_const_iterator it = experiences.beginArray(); - for( /**/ ; it != experiences.endArray(); ++it) - { - const LLSD& row = *it; - LLUUID public_key = row[EXPERIENCE_ID].asUUID(); + cache_t::const_iterator it = mCache.find(key); + if (it != mCache.end()) + { + // ...name already exists in cache, fire callback now + callback_signal_t signal; + signal.connect(slot); + + signal(it->second); + return; + } + fetch(key); - LL_DEBUGS("ExperienceCache") << "Received result for " << public_key - << " display '" << row[LLExperienceCache::NAME].asString() << "'" << LL_ENDL ; + signal_ptr signal = signal_ptr(new callback_signal_t()); + + std::pair<signal_map_t::iterator, bool> result = mSignalMap.insert(signal_map_t::value_type(key, signal)); + if (!result.second) + signal = (*result.first).second; + signal->connect(slot); +} - processExperience(public_key, row); - } +//========================================================================= +void LLExperienceCache::fetchAssociatedExperience(const LLUUID& objectId, const LLUUID& itemId, ExperienceGetFn_t fn) +{ + if (mCapability.empty()) + { + LL_WARNS("ExperienceCache") << "Capability query method not set." << LL_ENDL; + return; + } + + LLCoprocedureManager::instance().enqueueCoprocedure("ExpCache", "Fetch Associated", + boost::bind(&LLExperienceCache::fetchAssociatedExperienceCoro, this, _1, objectId, itemId, fn)); +} - LLSD error_ids = getContent()["error_ids"]; - LLSD::array_const_iterator errIt = error_ids.beginArray(); - for( /**/ ; errIt != error_ids.endArray() ; ++errIt ) - { - LLUUID id = errIt->asUUID(); - LLSD exp; - exp[EXPIRES]=DEFAULT_EXPIRATION; - exp[EXPERIENCE_ID] = id; - exp[PROPERTIES]=PROPERTY_INVALID; - exp[MISSING]=true; - exp[QUOTA] = DEFAULT_QUOTA; - - processExperience(id, exp); - LL_WARNS("ExperienceCache") << "LLExperienceResponder::result() error result for " << id << LL_ENDL ; - } +void LLExperienceCache::fetchAssociatedExperienceCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &httpAdapter, LLUUID objectId, LLUUID itemId, ExperienceGetFn_t fn) +{ + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest()); + std::string url = mCapability("GetMetadata"); + + if (url.empty()) + { + LL_WARNS("ExperienceCache") << "No Metadata capability." << LL_ENDL; + return; + } + + LLSD fields; + fields.append("experience"); + LLSD data; + data["object-id"] = objectId; + data["item-id"] = itemId; + data["fields"] = fields; + + LLSD result = httpAdapter->postAndSuspend(httpRequest, url, data); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if ((!status) || (!result.has("experience"))) + { + LLSD failure; + if (!status) + { + failure["error"] = (LLSD::Integer)status.getType(); + failure["message"] = status.getMessage(); + } + else + { + failure["error"] = -1; + failure["message"] = "no experience"; + } + if (fn && !fn.empty()) + fn(failure); + return; + } + + LLUUID expId = result["experience"].asUUID(); + get(expId, fn); +} - LL_DEBUGS("ExperienceCache") << sCache.size() << " cached experiences" << LL_ENDL; - } +//------------------------------------------------------------------------- +void LLExperienceCache::findExperienceByName(const std::string text, int page, ExperienceGetFn_t fn) +{ + if (mCapability.empty()) + { + LL_WARNS("ExperienceCache") << "Capability query method not set." << LL_ENDL; + return; + } + + LLCoprocedureManager::instance().enqueueCoprocedure("ExpCache", "Search Name", + boost::bind(&LLExperienceCache::findExperienceByNameCoro, this, _1, text, page, fn)); +} - /*virtual*/ void httpFailure() - { - LL_WARNS("ExperienceCache") << "Request failed "<<getStatus()<<" "<<getReason()<< LL_ENDL; - // We're going to construct a dummy record and cache it for a while, - // either briefly for a 503 Service Unavailable, or longer for other - // errors. - F64 retry_timestamp = errorRetryTimestamp(getStatus()); - - - // Add dummy records for all agent IDs in this request - ask_queue_t::const_iterator it = mKeys.begin(); - for ( ; it != mKeys.end(); ++it) - { +void LLExperienceCache::findExperienceByNameCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &httpAdapter, std::string text, int page, ExperienceGetFn_t fn) +{ + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest()); + std::ostringstream url; - LLSD exp = get(it->first); - //leave the properties alone if we already have a cache entry for this xp - if(exp.isUndefined()) - { - exp[PROPERTIES]=PROPERTY_INVALID; - } - exp[EXPIRES]=retry_timestamp; - exp[EXPERIENCE_ID] = it->first; - exp["key_type"] = it->second; - exp["uuid"] = it->first; - exp["error"] = (LLSD::Integer)getStatus(); - exp[QUOTA] = DEFAULT_QUOTA; - - LLExperienceCache::processExperience(it->first, exp); - } - } + url << mCapability("FindExperienceByName") << "?page=" << page << "&page_size=" << SEARCH_PAGE_SIZE << "&query=" << LLURI::escape(text); - // Return time to retry a request that generated an error, based on - // error type and headers. Return value is seconds-since-epoch. - F64 errorRetryTimestamp(S32 status) - { + LLSD result = httpAdapter->getAndSuspend(httpRequest, url.str()); - // Retry-After takes priority - LLSD retry_after = getResponseHeaders()["retry-after"]; - if (retry_after.isDefined()) - { - // We only support the delta-seconds type - S32 delta_seconds = retry_after.asInteger(); - if (delta_seconds > 0) - { - // ...valid delta-seconds - return F64(delta_seconds); - } - } + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); - // If no Retry-After, look for Cache-Control max-age - F64 expires = 0.0; - if (LLExperienceCache::expirationFromCacheControl(getResponseHeaders(), &expires)) - { - return expires; - } + if (!status) + { + fn(LLSD()); + return; + } - // No information in header, make a guess - if (status == 503) - { - // ...service unavailable, retry soon - const F64 SERVICE_UNAVAILABLE_DELAY = 600.0; // 10 min - return SERVICE_UNAVAILABLE_DELAY; - } - else if (status == 499) - { - // ...we were probably too busy, retry quickly - const F64 BUSY_DELAY = 10.0; // 10 seconds - return BUSY_DELAY; + result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS); - } - else - { - // ...other unexpected error - const F64 DEFAULT_DELAY = 3600.0; // 1 hour - return DEFAULT_DELAY; - } - } + const LLSD& experiences = result["experience_keys"]; + for (LLSD::array_const_iterator it = experiences.beginArray(); it != experiences.endArray(); ++it) + { + insert(*it); + } - private: - ask_queue_t mKeys; - }; + fn(result); +} - void requestExperiences() - { - if(sAskQueue.empty() || sLookupURL.empty()) - return; +//------------------------------------------------------------------------- +void LLExperienceCache::getGroupExperiences(const LLUUID &groupId, ExperienceGetFn_t fn) +{ + if (mCapability.empty()) + { + LL_WARNS("ExperienceCache") << "Capability query method not set." << LL_ENDL; + return; + } + + LLCoprocedureManager::instance().enqueueCoprocedure("ExpCache", "Group Experiences", + boost::bind(&LLExperienceCache::getGroupExperiencesCoro, this, _1, groupId, fn)); +} - F64 now = LLFrameTimer::getTotalSeconds(); +void LLExperienceCache::getGroupExperiencesCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &httpAdapter, LLUUID groupId, ExperienceGetFn_t fn) +{ + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest()); - const U32 EXP_URL_SEND_THRESHOLD = 3000; - const U32 PAGE_SIZE = EXP_URL_SEND_THRESHOLD/UUID_STR_LENGTH; + // search for experiences owned by the current group + std::string url = mCapability("GroupExperiences"); + if (url.empty()) + { + LL_WARNS("ExperienceCache") << "No Group Experiences capability" << LL_ENDL; + return; + } - std::ostringstream ostr; + url += "?" + groupId.asString(); - ask_queue_t keys; + LLSD result = httpAdapter->getAndSuspend(httpRequest, url); - ostr << sLookupURL << "?page_size=" << PAGE_SIZE; + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + if (!status) + { + fn(LLSD()); + return; + } - int request_count = 0; - while(!sAskQueue.empty() && request_count < sMaximumLookups) - { - ask_queue_t::iterator it = sAskQueue.begin(); - const LLUUID& key = it->first; - const std::string& key_type = it->second; + const LLSD& experienceIds = result["experience_ids"]; + fn(experienceIds); +} - ostr << '&' << key_type << '=' << key.asString() ; - - keys[key]=key_type; - request_count++; +//------------------------------------------------------------------------- +void LLExperienceCache::getRegionExperiences(CapabilityQuery_t regioncaps, ExperienceGetFn_t fn) +{ + LLCoprocedureManager::instance().enqueueCoprocedure("ExpCache", "Region Experiences", + boost::bind(&LLExperienceCache::regionExperiencesCoro, this, _1, regioncaps, false, LLSD(), fn)); +} - sPendingQueue[key] = now; - - if(ostr.tellp() > EXP_URL_SEND_THRESHOLD) - { - LL_DEBUGS("ExperienceCache") << "requestExperiences() query: " << ostr.str() << LL_ENDL; - LLHTTPClient::get(ostr.str(), new LLExperienceResponder(keys)); - ostr.clear(); - ostr.str(sLookupURL); - ostr << "?page_size=" << PAGE_SIZE; - keys.clear(); - } - sAskQueue.erase(it); - } +void LLExperienceCache::setRegionExperiences(CapabilityQuery_t regioncaps, const LLSD &experiences, ExperienceGetFn_t fn) +{ + LLCoprocedureManager::instance().enqueueCoprocedure("ExpCache", "Region Experiences", + boost::bind(&LLExperienceCache::regionExperiencesCoro, this, _1, regioncaps, true, experiences, fn)); +} - if(ostr.tellp() > sLookupURL.size()) - { - LL_DEBUGS("ExperienceCache") << "requestExperiences() query 2: " << ostr.str() << LL_ENDL; - LLHTTPClient::get(ostr.str(), new LLExperienceResponder(keys)); - } - } +void LLExperienceCache::regionExperiencesCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &httpAdapter, + CapabilityQuery_t regioncaps, bool update, LLSD experiences, ExperienceGetFn_t fn) +{ + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest()); + + // search for experiences owned by the current group + std::string url = regioncaps("RegionExperiences"); + if (url.empty()) + { + LL_WARNS("ExperienceCache") << "No Region Experiences capability" << LL_ENDL; + return; + } + + LLSD result; + if (update) + result = httpAdapter->postAndSuspend(httpRequest, url, experiences); + else + result = httpAdapter->getAndSuspend(httpRequest, url); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) + { +// fn(LLSD()); + return; + } + + result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS); + fn(result); - bool isRequestPending(const LLUUID& public_key) - { - bool isPending = false; - const F64 PENDING_TIMEOUT_SECS = 5.0 * 60.0; +} - pending_queue_t::const_iterator it = sPendingQueue.find(public_key); +//------------------------------------------------------------------------- +void LLExperienceCache::getExperiencePermission(const LLUUID &experienceId, ExperienceGetFn_t fn) +{ + if (mCapability.empty()) + { + LL_WARNS("ExperienceCache") << "Capability query method not set." << LL_ENDL; + return; + } + + std::string url = mCapability("ExperiencePreferences") + "?" + experienceId.asString(); + + permissionInvoker_fn invoker(boost::bind( + // Humans ignore next line. It is just a cast to specify which LLCoreHttpUtil::HttpCoroutineAdapter routine overload. + static_cast<LLSD(LLCoreHttpUtil::HttpCoroutineAdapter::*)(LLCore::HttpRequest::ptr_t, const std::string &, LLCore::HttpOptions::ptr_t, LLCore::HttpHeaders::ptr_t)> + //---- + // _1 -> httpAdapter + // _2 -> httpRequest + // _3 -> url + (&LLCoreHttpUtil::HttpCoroutineAdapter::getAndSuspend), _1, _2, _3, LLCore::HttpOptions::ptr_t(), LLCore::HttpHeaders::ptr_t())); + + + LLCoprocedureManager::instance().enqueueCoprocedure("ExpCache", "Preferences Set", + boost::bind(&LLExperienceCache::experiencePermissionCoro, this, _1, invoker, url, fn)); +} - if(it != sPendingQueue.end()) - { - F64 expire_time = LLFrameTimer::getTotalSeconds() - PENDING_TIMEOUT_SECS; - isPending = (it->second > expire_time); - } +void LLExperienceCache::setExperiencePermission(const LLUUID &experienceId, const std::string &permission, ExperienceGetFn_t fn) +{ + if (mCapability.empty()) + { + LL_WARNS("ExperienceCache") << "Capability query method not set." << LL_ENDL; + return; + } + + std::string url = mCapability("ExperiencePreferences"); + if (url.empty()) + return; + LLSD permData; + LLSD data; + permData["permission"] = permission; + data[experienceId.asString()] = permData; + + permissionInvoker_fn invoker(boost::bind( + // Humans ignore next line. It is just a cast to specify which LLCoreHttpUtil::HttpCoroutineAdapter routine overload. + static_cast<LLSD(LLCoreHttpUtil::HttpCoroutineAdapter::*)(LLCore::HttpRequest::ptr_t, const std::string &, const LLSD &, LLCore::HttpOptions::ptr_t, LLCore::HttpHeaders::ptr_t)> + //---- + // _1 -> httpAdapter + // _2 -> httpRequest + // _3 -> url + (&LLCoreHttpUtil::HttpCoroutineAdapter::putAndSuspend), _1, _2, _3, data, LLCore::HttpOptions::ptr_t(), LLCore::HttpHeaders::ptr_t())); + + + LLCoprocedureManager::instance().enqueueCoprocedure("ExpCache", "Preferences Set", + boost::bind(&LLExperienceCache::experiencePermissionCoro, this, _1, invoker, url, fn)); +} - return isPending; - } +void LLExperienceCache::forgetExperiencePermission(const LLUUID &experienceId, ExperienceGetFn_t fn) +{ + if (mCapability.empty()) + { + LL_WARNS("ExperienceCache") << "Capability query method not set." << LL_ENDL; + return; + } + std::string url = mCapability("ExperiencePreferences") + "?" + experienceId.asString(); - void setLookupURL( const std::string& lookup_url ) - { - sLookupURL = lookup_url; - if(!sLookupURL.empty()) - { - sLookupURL += "id/"; - } - } - bool hasLookupURL() - { - return !sLookupURL.empty(); - } + permissionInvoker_fn invoker(boost::bind( + // Humans ignore next line. It is just a cast to specify which LLCoreHttpUtil::HttpCoroutineAdapter routine overload. + static_cast<LLSD(LLCoreHttpUtil::HttpCoroutineAdapter::*)(LLCore::HttpRequest::ptr_t, const std::string &, LLCore::HttpOptions::ptr_t, LLCore::HttpHeaders::ptr_t)> + //---- + // _1 -> httpAdapter + // _2 -> httpRequest + // _3 -> url + (&LLCoreHttpUtil::HttpCoroutineAdapter::deleteAndSuspend), _1, _2, _3, LLCore::HttpOptions::ptr_t(), LLCore::HttpHeaders::ptr_t())); - void idle() - { - const F32 SECS_BETWEEN_REQUESTS = 0.1f; - if (!sRequestTimer.checkExpirationAndReset(SECS_BETWEEN_REQUESTS)) - { - return; - } + LLCoprocedureManager::instance().enqueueCoprocedure("ExpCache", "Preferences Set", + boost::bind(&LLExperienceCache::experiencePermissionCoro, this, _1, invoker, url, fn)); +} - // Must be large relative to above - const F32 ERASE_EXPIRED_TIMEOUT = 60.f; // seconds - if (sEraseExpiredTimer.checkExpirationAndReset(ERASE_EXPIRED_TIMEOUT)) - { - eraseExpired(); - } +void LLExperienceCache::experiencePermissionCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &httpAdapter, permissionInvoker_fn invokerfn, std::string url, ExperienceGetFn_t fn) +{ + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest()); + // search for experiences owned by the current group - if(!sAskQueue.empty()) - { - requestExperiences(); - } - } + LLSD result = invokerfn(httpAdapter, httpRequest, url); - void erase( const LLUUID& key ) - { - cache_t::iterator it = sCache.find(key); - - if(it != sCache.end()) - { - sCache.erase(it); - } - } + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS); - void eraseExpired() - { - F64 now = LLFrameTimer::getTotalSeconds(); - cache_t::iterator it = sCache.begin(); - while (it != sCache.end()) - { - cache_t::iterator cur = it; - LLSD& exp = cur->second; - ++it; + if (status) + { + fn(result); + } +} - if(exp.has(EXPIRES) && exp[EXPIRES].asReal() < now) - { - if(!exp.has(EXPERIENCE_ID)) - { - LL_WARNS("ExperienceCache") << "Removing experience with no id " << LL_ENDL ; - sCache.erase(cur); - } - else - { - LLUUID id = exp[EXPERIENCE_ID].asUUID(); - LLUUID private_key = exp.has(LLExperienceCache::PRIVATE_KEY) ? exp[LLExperienceCache::PRIVATE_KEY].asUUID():LLUUID::null; - if(private_key.notNull() || !exp.has("DoesNotExist")) - { - fetch(id, true); - } - else - { - LL_WARNS("ExperienceCache") << "Removing invalid experience " << id << LL_ENDL ; - sCache.erase(cur); - } - } - } - } - } +//------------------------------------------------------------------------- +void LLExperienceCache::getExperienceAdmin(const LLUUID &experienceId, ExperienceGetFn_t fn) +{ + if (mCapability.empty()) + { + LL_WARNS("ExperienceCache") << "Capability query method not set." << LL_ENDL; + return; + } + + LLCoprocedureManager::instance().enqueueCoprocedure("ExpCache", "IsAdmin", + boost::bind(&LLExperienceCache::getExperienceAdminCoro, this, _1, experienceId, fn)); +} - - bool fetch( const LLUUID& key, bool refresh/* = true*/ ) - { - if(!key.isNull() && !isRequestPending(key) && (refresh || sCache.find(key)==sCache.end())) - { - LL_DEBUGS("ExperienceCache") << " queue request for " << EXPERIENCE_ID << " " << key << LL_ENDL ; - sAskQueue[key]=EXPERIENCE_ID; +void LLExperienceCache::getExperienceAdminCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &httpAdapter, LLUUID experienceId, ExperienceGetFn_t fn) +{ + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest()); - return true; - } - return false; - } + std::string url = mCapability("IsExperienceAdmin"); + if (url.empty()) + { + LL_WARNS("ExperienceCache") << "No Region Experiences capability" << LL_ENDL; + return; + } + url += "?experience_id=" + experienceId.asString(); - void insert(const LLSD& experience_data ) - { - if(experience_data.has(EXPERIENCE_ID)) - { - processExperience(experience_data[EXPERIENCE_ID].asUUID(), experience_data); - } - else - { - LL_WARNS("ExperienceCache") << ": Ignoring cache insert of experience which is missing " << EXPERIENCE_ID << LL_ENDL; - } - } - static LLSD empty; - const LLSD& get(const LLUUID& key) - { - if(key.isNull()) return empty; - cache_t::const_iterator it = sCache.find(key); + LLSD result = httpAdapter->getAndSuspend(httpRequest, url); +// LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; +// LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); - if (it != sCache.end()) - { - return it->second; - } + fn(result); +} - fetch(key); +//------------------------------------------------------------------------- +void LLExperienceCache::updateExperience(LLSD updateData, ExperienceGetFn_t fn) +{ + if (mCapability.empty()) + { + LL_WARNS("ExperienceCache") << "Capability query method not set." << LL_ENDL; + return; + } + + LLCoprocedureManager::instance().enqueueCoprocedure("ExpCache", "IsAdmin", + boost::bind(&LLExperienceCache::updateExperienceCoro, this, _1, updateData, fn)); +} - return empty; - } - void get( const LLUUID& key, callback_slot_t slot ) - { - if(key.isNull()) return; +void LLExperienceCache::updateExperienceCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &httpAdapter, LLSD updateData, ExperienceGetFn_t fn) +{ + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest()); - cache_t::const_iterator it = sCache.find(key); - if (it != sCache.end()) - { - // ...name already exists in cache, fire callback now - callback_signal_t signal; - signal.connect(slot); - - signal(it->second); - return; - } + std::string url = mCapability("UpdateExperience"); + if (url.empty()) + { + LL_WARNS("ExperienceCache") << "No Region Experiences capability" << LL_ENDL; + return; + } - fetch(key); + updateData.erase(LLExperienceCache::QUOTA); + updateData.erase(LLExperienceCache::EXPIRES); + updateData.erase(LLExperienceCache::AGENT_ID); - // always store additional callback, even if request is pending - signal_map_t::iterator sig_it = sSignalMap.find(key); - if (sig_it == sSignalMap.end()) - { - // ...new callback for this id - callback_signal_t* signal = new callback_signal_t(); - signal->connect(slot); - sSignalMap[key] = signal; - } - else - { - // ...existing callback, bind additional slot - callback_signal_t* signal = sig_it->second; - signal->connect(slot); - } - } + LLSD result = httpAdapter->postAndSuspend(httpRequest, url, updateData); + fn(result); } - -void LLExperienceCache::mapKeys( const LLSD& legacyKeys ) +//========================================================================= +void LLExperienceCacheImpl::mapKeys(const LLSD& legacyKeys) { LLSD::array_const_iterator exp = legacyKeys.beginArray(); - for(/**/ ; exp != legacyKeys.endArray() ; ++exp) + for (/**/; exp != legacyKeys.endArray(); ++exp) { - if(exp->has(LLExperienceCache::EXPERIENCE_ID) && exp->has(LLExperienceCache::PRIVATE_KEY)) + if (exp->has(LLExperienceCacheImpl::EXPERIENCE_ID) && exp->has(LLExperienceCacheImpl::PRIVATE_KEY)) { - privateToPublicKeyMap[(*exp)[LLExperienceCache::PRIVATE_KEY].asUUID()]=(*exp)[LLExperienceCache::EXPERIENCE_ID].asUUID(); + LLExperienceCacheImpl::privateToPublicKeyMap[(*exp)[LLExperienceCacheImpl::PRIVATE_KEY].asUUID()] = + (*exp)[LLExperienceCacheImpl::EXPERIENCE_ID].asUUID(); } } } - -LLUUID LLExperienceCache::getExperienceId(const LLUUID& private_key, bool null_if_not_found) +// Return time to retry a request that generated an error, based on +// error type and headers. Return value is seconds-since-epoch. +F64 LLExperienceCacheImpl::getErrorRetryDeltaTime(S32 status, LLSD headers) { - if (private_key.isNull()) - return LLUUID::null; - KeyMap::const_iterator it=privateToPublicKeyMap.find(private_key); - if(it == privateToPublicKeyMap.end()) + // Retry-After takes priority + LLSD retry_after = headers["retry-after"]; + if (retry_after.isDefined()) + { + // We only support the delta-seconds type + S32 delta_seconds = retry_after.asInteger(); + if (delta_seconds > 0) + { + // ...valid delta-seconds + return F64(delta_seconds); + } + } + + // If no Retry-After, look for Cache-Control max-age + // Allow the header to override the default + LLSD cache_control_header = headers["cache-control"]; + if (cache_control_header.isDefined()) + { + S32 max_age = 0; + std::string cache_control = cache_control_header.asString(); + if (LLExperienceCacheImpl::maxAgeFromCacheControl(cache_control, &max_age)) + { + LL_WARNS("ExperienceCache") + << "got EXPIRES from headers, max_age " << max_age + << LL_ENDL; + return (F64)max_age; + } + } + + // No information in header, make a guess + if (status == 503) + { + // ...service unavailable, retry soon + const F64 SERVICE_UNAVAILABLE_DELAY = 600.0; // 10 min + return SERVICE_UNAVAILABLE_DELAY; + } + else if (status == 499) + { + // ...we were probably too busy, retry quickly + const F64 BUSY_DELAY = 10.0; // 10 seconds + return BUSY_DELAY; + + } + else + { + // ...other unexpected error + const F64 DEFAULT_DELAY = 3600.0; // 1 hour + return DEFAULT_DELAY; + } +} + +bool LLExperienceCacheImpl::maxAgeFromCacheControl(const std::string& cache_control, S32 *max_age) +{ + // Split the string on "," to get a list of directives + typedef boost::tokenizer<boost::char_separator<char> > tokenizer; + tokenizer directives(cache_control, COMMA_SEPARATOR); + + tokenizer::iterator token_it = directives.begin(); + for ( ; token_it != directives.end(); ++token_it) { - if(null_if_not_found) + // Tokens may have leading or trailing whitespace + std::string token = *token_it; + LLStringUtil::trim(token); + + if (token.compare(0, MAX_AGE.size(), MAX_AGE) == 0) { - return LLUUID::null; + // ...this token starts with max-age, so let's chop it up by "=" + tokenizer subtokens(token, EQUALS_SEPARATOR); + tokenizer::iterator subtoken_it = subtokens.begin(); + + // Must have a token + if (subtoken_it == subtokens.end()) return false; + std::string subtoken = *subtoken_it; + + // Must exactly equal "max-age" + LLStringUtil::trim(subtoken); + if (subtoken != MAX_AGE) return false; + + // Must have another token + ++subtoken_it; + if (subtoken_it == subtokens.end()) return false; + subtoken = *subtoken_it; + + // Must be a valid integer + // *NOTE: atoi() returns 0 for invalid values, so we have to + // check the string first. + // *TODO: Do servers ever send "0000" for zero? We don't handle it + LLStringUtil::trim(subtoken); + if (subtoken == "0") + { + *max_age = 0; + return true; + } + S32 val = atoi( subtoken.c_str() ); + if (val > 0 && val < S32_MAX) + { + *max_age = val; + return true; + } + return false; } - return private_key; } - LL_WARNS("LLExperience") << "converted private key " << private_key << " to experience_id " << it->second << LL_ENDL; - return it->second; + return false; } + + + + diff --git a/indra/llmessage/llexperiencecache.h b/indra/llmessage/llexperiencecache.h index e669ee888e..1002b33f80 100644 --- a/indra/llmessage/llexperiencecache.h +++ b/indra/llmessage/llexperiencecache.h @@ -30,75 +30,155 @@ #define LL_LLEXPERIENCECACHE_H #include "linden_common.h" +#include "llsingleton.h" +#include "llframetimer.h" +#include "llsd.h" +#include "llcorehttputil.h" #include <boost/signals2.hpp> +#include <boost/function.hpp> class LLSD; class LLUUID; - -namespace LLExperienceCache +class LLExperienceCache: public LLSingleton < LLExperienceCache > { - const std::string PRIVATE_KEY = "private_id"; - const std::string MISSING = "DoesNotExist"; - - const std::string AGENT_ID = "agent_id"; - const std::string GROUP_ID = "group_id"; - const std::string EXPERIENCE_ID = "public_id"; - const std::string NAME = "name"; - const std::string PROPERTIES = "properties"; - const std::string EXPIRES = "expiration"; - const std::string DESCRIPTION = "description"; - const std::string QUOTA = "quota"; - const std::string MATURITY = "maturity"; - const std::string METADATA = "extended_metadata"; - const std::string SLURL = "slurl"; - - - // should be in sync with experience-api/experiences/models.py - const int PROPERTY_INVALID = 1 << 0; - const int PROPERTY_PRIVILEGED = 1 << 3; - const int PROPERTY_GRID = 1 << 4; - const int PROPERTY_PRIVATE = 1 << 5; - const int PROPERTY_DISABLED = 1 << 6; - const int PROPERTY_SUSPENDED = 1 << 7; - - - // default values - const static F64 DEFAULT_EXPIRATION = 600.0; - const static S32 DEFAULT_QUOTA = 128; // this is megabytes - - // Callback types for get() below - typedef boost::signals2::signal<void (const LLSD& experience)> - callback_signal_t; - typedef callback_signal_t::slot_type callback_slot_t; + friend class LLSingleton < LLExperienceCache > ; + +public: + typedef boost::function<std::string(const std::string &)> CapabilityQuery_t; + typedef boost::function<void(const LLSD &)> ExperienceGetFn_t; + + void setCapabilityQuery(CapabilityQuery_t queryfn); + void cleanup(); + + //------------------------------------------- + // Cache methods + void erase(const LLUUID& key); + bool fetch(const LLUUID& key, bool refresh = false); + void insert(const LLSD& experience_data); + const LLSD& get(const LLUUID& key); + void get(const LLUUID& key, ExperienceGetFn_t slot); // If name information is in cache, callback will be called immediately. + + bool isRequestPending(const LLUUID& public_key); + + //------------------------------------------- + void fetchAssociatedExperience(const LLUUID& objectId, const LLUUID& itemId, ExperienceGetFn_t fn); + void findExperienceByName(const std::string text, int page, ExperienceGetFn_t fn); + void getGroupExperiences(const LLUUID &groupId, ExperienceGetFn_t fn); + + // the Get/Set Region Experiences take a CapabilityQuery to get the capability since + // the region being queried may not be the region that the agent is standing on. + void getRegionExperiences(CapabilityQuery_t regioncaps, ExperienceGetFn_t fn); + void setRegionExperiences(CapabilityQuery_t regioncaps, const LLSD &experiences, ExperienceGetFn_t fn); + + void getExperiencePermission(const LLUUID &experienceId, ExperienceGetFn_t fn); + void setExperiencePermission(const LLUUID &experienceId, const std::string &permission, ExperienceGetFn_t fn); + void forgetExperiencePermission(const LLUUID &experienceId, ExperienceGetFn_t fn); + + void getExperienceAdmin(const LLUUID &experienceId, ExperienceGetFn_t fn); + + void updateExperience(LLSD updateData, ExperienceGetFn_t fn); + //------------------------------------------- + static const std::string NAME; // "name" + static const std::string EXPERIENCE_ID; // "public_id" + static const std::string AGENT_ID; // "agent_id" + static const std::string GROUP_ID; // "group_id" + static const std::string PROPERTIES; // "properties" + static const std::string EXPIRES; // "expiration" + static const std::string DESCRIPTION; // "description" + static const std::string QUOTA; // "quota" + static const std::string MATURITY; // "maturity" + static const std::string METADATA; // "extended_metadata" + static const std::string SLURL; // "slurl" + + static const std::string MISSING; // "DoesNotExist" + + // should be in sync with experience-api/experiences/models.py + static const int PROPERTY_INVALID; // 1 << 0 + static const int PROPERTY_PRIVILEGED; // 1 << 3 + static const int PROPERTY_GRID; // 1 << 4 + static const int PROPERTY_PRIVATE; // 1 << 5 + static const int PROPERTY_DISABLED; // 1 << 6 + static const int PROPERTY_SUSPENDED; // 1 << 7 + +private: + LLExperienceCache(); + virtual ~LLExperienceCache(); + + virtual void initSingleton(); + + typedef boost::function<LLSD(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &, LLCore::HttpRequest::ptr_t, std::string)> permissionInvoker_fn; + + // Callback types for get() + typedef boost::signals2::signal < void(const LLSD &) > callback_signal_t; + typedef boost::shared_ptr<callback_signal_t> signal_ptr; + // May have multiple callbacks for a single ID, which are + // represented as multiple slots bound to the signal. + // Avoid copying signals via pointers. + typedef std::map<LLUUID, signal_ptr> signal_map_t; typedef std::map<LLUUID, LLSD> cache_t; - - - void setLookupURL(const std::string& lookup_url); - bool hasLookupURL(); - - void setMaximumLookups(int maximumLookups); - - void idle(); - void exportFile(std::ostream& ostr); - void importFile(std::istream& istr); - void initClass(); - void bootstrap(const LLSD& legacyKeys, int initialExpiration); - void erase(const LLUUID& key); - bool fetch(const LLUUID& key, bool refresh=false); - void insert(const LLSD& experience_data); - const LLSD& get(const LLUUID& key); - - // If name information is in cache, callback will be called immediately. - void get(const LLUUID& key, callback_slot_t slot); + typedef std::set<LLUUID> RequestQueue_t; + typedef std::map<LLUUID, F64> PendingQueue_t; + //-------------------------------------------- + static const std::string PRIVATE_KEY; // "private_id" + + // default values + static const F64 DEFAULT_EXPIRATION; // 600.0 + static const S32 DEFAULT_QUOTA; // 128 this is megabytes + static const int SEARCH_PAGE_SIZE; + +//-------------------------------------------- + void processExperience(const LLUUID& public_key, const LLSD& experience); + +//-------------------------------------------- + cache_t mCache; + signal_map_t mSignalMap; + RequestQueue_t mRequestQueue; + PendingQueue_t mPendingQueue; + + LLFrameTimer mEraseExpiredTimer; // Periodically clean out expired entries from the cache + CapabilityQuery_t mCapability; + std::string mCacheFileName; + bool mShutdown; + + void idleCoro(); + void eraseExpired(); + void requestExperiencesCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &, std::string, RequestQueue_t); + void requestExperiences(); + + void fetchAssociatedExperienceCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &, LLUUID, LLUUID, ExperienceGetFn_t); + void findExperienceByNameCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &, std::string, int, ExperienceGetFn_t); + void getGroupExperiencesCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &, LLUUID , ExperienceGetFn_t); + void regionExperiencesCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &httpAdapter, CapabilityQuery_t regioncaps, bool update, LLSD experiences, ExperienceGetFn_t fn); + void experiencePermissionCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &httpAdapter, permissionInvoker_fn invokerfn, std::string url, ExperienceGetFn_t fn); + void getExperienceAdminCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &httpAdapter, LLUUID experienceId, ExperienceGetFn_t fn); + void updateExperienceCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &httpAdapter, LLSD updateData, ExperienceGetFn_t fn); + + void bootstrap(const LLSD& legacyKeys, int initialExpiration); + void exportFile(std::ostream& ostr) const; + void importFile(std::istream& istr); + + // const cache_t& getCached(); // maps an experience private key to the experience id LLUUID getExperienceId(const LLUUID& private_key, bool null_if_not_found=false); + //===================================================================== + inline friend std::ostream &operator << (std::ostream &os, const LLExperienceCache &cache) + { + cache.exportFile(os); + return os; + } + + inline friend std::istream &operator >> (std::istream &is, LLExperienceCache &cache) + { + cache.importFile(is); + return is; + } }; #endif // LL_LLEXPERIENCECACHE_H diff --git a/indra/llmessage/llhost.cpp b/indra/llmessage/llhost.cpp index 63c15f0d5e..ae5c2ecf69 100644 --- a/indra/llmessage/llhost.cpp +++ b/indra/llmessage/llhost.cpp @@ -41,8 +41,6 @@ #include <arpa/inet.h> #endif -LLHost LLHost::invalid(INVALID_PORT,INVALID_HOST_IP_ADDRESS); - LLHost::LLHost(const std::string& ip_and_port) { std::string::size_type colon_index = ip_and_port.find(":"); diff --git a/indra/llmessage/llhost.h b/indra/llmessage/llhost.h index 0cf52a4151..79cad4b123 100644 --- a/indra/llmessage/llhost.h +++ b/indra/llmessage/llhost.h @@ -40,9 +40,8 @@ class LLHost { protected: U32 mPort; U32 mIP; + std::string mUntrustedSimCap; public: - - static LLHost invalid; // CREATORS LLHost() @@ -89,13 +88,17 @@ public: // READERS U32 getAddress() const { return mIP; } U32 getPort() const { return mPort; } - BOOL isOk() const { return (mIP != INVALID_HOST_IP_ADDRESS) && (mPort != INVALID_PORT); } + bool isOk() const { return (mIP != INVALID_HOST_IP_ADDRESS) && (mPort != INVALID_PORT); } + bool isInvalid() { return (mIP == INVALID_HOST_IP_ADDRESS) || (mPort == INVALID_PORT); } size_t hash() const { return (mIP << 16) | (mPort & 0xffff); } std::string getString() const; std::string getIPString() const; std::string getHostName() const; std::string getIPandPort() const; + std::string getUntrustedSimulatorCap() const { return mUntrustedSimCap; } + void setUntrustedSimulatorCap(const std::string &capurl) { mUntrustedSimCap = capurl; } + friend std::ostream& operator<< (std::ostream& os, const LLHost &hh); // This operator is not well defined. does it expect a diff --git a/indra/llmessage/llhttpassetstorage.cpp b/indra/llmessage/llhttpassetstorage.cpp deleted file mode 100644 index ace65760c3..0000000000 --- a/indra/llmessage/llhttpassetstorage.cpp +++ /dev/null @@ -1,1454 +0,0 @@ -/** - * @file llhttpassetstorage.cpp - * @brief Subclass capable of loading asset data to/from an external - * source. Currently, a web server accessed via curl - * - * $LicenseInfo:firstyear=2003&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" - -#include "llhttpassetstorage.h" - -#include <sys/stat.h> - -#include "indra_constants.h" -#include "message.h" -#include "llproxy.h" -#include "llvfile.h" -#include "llvfs.h" -#include "llxfer.h" - -#ifdef LL_USESYSTEMLIBS -# include <zlib.h> -#else -# include "zlib/zlib.h" -#endif - -const char* const LOCAL_ASSET_URL_FORMAT = "http://%s:12041/asset"; - -const U32 MAX_RUNNING_REQUESTS = 1; - -// Try for 30 minutes for now. -const F32 GET_URL_TO_FILE_TIMEOUT = 1800.0f; - -const S32 COMPRESSED_INPUT_BUFFER_SIZE = 4096; - -///////////////////////////////////////////////////////////////////////////////// -// LLTempAssetData -// An asset not stored on central asset store, but on a simulator node somewhere. -///////////////////////////////////////////////////////////////////////////////// -struct LLTempAssetData -{ - LLUUID mAssetID; - LLUUID mAgentID; - std::string mHostName; -}; - -///////////////////////////////////////////////////////////////////////////////// -// LLHTTPAssetRequest -///////////////////////////////////////////////////////////////////////////////// - -class LLHTTPAssetRequest : public LLAssetRequest -{ -public: - LLHTTPAssetRequest(LLHTTPAssetStorage *asp, const LLUUID &uuid, - LLAssetType::EType type, LLAssetStorage::ERequestType rt, - const std::string& url, CURLM *curl_multi); - virtual ~LLHTTPAssetRequest(); - - void setupCurlHandle(); - void cleanupCurlHandle(); - - void prepareCompressedUpload(); - void finishCompressedUpload(); - size_t readCompressedData(void* data, size_t size); - - static size_t curlCompressedUploadCallback( - void *data, size_t size, size_t nmemb, void *user_data); - - virtual LLSD getTerseDetails() const; - virtual LLSD getFullDetails() const; - -public: - LLHTTPAssetStorage *mAssetStoragep; - - CURL *mCurlHandle; - CURLM *mCurlMultiHandle; - std::string mURLBuffer; - struct curl_slist *mHTTPHeaders; - LLVFile *mVFile; - LLUUID mTmpUUID; - LLAssetStorage::ERequestType mRequestType; - - bool mZInitialized; - z_stream mZStream; - char* mZInputBuffer; - bool mZInputExhausted; - - FILE *mFP; -}; - - -LLHTTPAssetRequest::LLHTTPAssetRequest(LLHTTPAssetStorage *asp, - const LLUUID &uuid, - LLAssetType::EType type, - LLAssetStorage::ERequestType rt, - const std::string& url, - CURLM *curl_multi) - : LLAssetRequest(uuid, type), - mZInitialized(false) -{ - memset(&mZStream, 0, sizeof(mZStream)); // we'll initialize this later, but for now zero the whole C-style struct to avoid debug/coverity noise - mAssetStoragep = asp; - mCurlHandle = NULL; - mCurlMultiHandle = curl_multi; - mVFile = NULL; - mRequestType = rt; - mHTTPHeaders = NULL; - mFP = NULL; - mZInputBuffer = NULL; - mZInputExhausted = false; - - mURLBuffer = url; -} - -LLHTTPAssetRequest::~LLHTTPAssetRequest() -{ - // Cleanup/cancel the request - if (mCurlHandle) - { - curl_multi_remove_handle(mCurlMultiHandle, mCurlHandle); - cleanupCurlHandle(); - } - if (mHTTPHeaders) - { - curl_slist_free_all(mHTTPHeaders); - } - delete mVFile; - finishCompressedUpload(); -} - -// virtual -LLSD LLHTTPAssetRequest::getTerseDetails() const -{ - LLSD sd = LLAssetRequest::getTerseDetails(); - - sd["url"] = mURLBuffer; - - return sd; -} - -// virtual -LLSD LLHTTPAssetRequest::getFullDetails() const -{ - LLSD sd = LLAssetRequest::getFullDetails(); - - if (mCurlHandle) - { - long curl_response = -1; - long curl_connect = -1; - double curl_total_time = -1.0f; - double curl_size_upload = -1.0f; - double curl_size_download = -1.0f; - double curl_content_length_upload = -1.0f; - double curl_content_length_download = -1.0f; - long curl_request_size = -1; - const char* curl_content_type = NULL; - - curl_easy_getinfo(mCurlHandle, CURLINFO_HTTP_CODE, &curl_response); - curl_easy_getinfo(mCurlHandle, CURLINFO_HTTP_CONNECTCODE, &curl_connect); - curl_easy_getinfo(mCurlHandle, CURLINFO_TOTAL_TIME, &curl_total_time); - curl_easy_getinfo(mCurlHandle, CURLINFO_SIZE_UPLOAD, &curl_size_upload); - curl_easy_getinfo(mCurlHandle, CURLINFO_SIZE_DOWNLOAD, &curl_size_download); - curl_easy_getinfo(mCurlHandle, CURLINFO_CONTENT_LENGTH_UPLOAD, &curl_content_length_upload); - curl_easy_getinfo(mCurlHandle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &curl_content_length_download); - curl_easy_getinfo(mCurlHandle, CURLINFO_REQUEST_SIZE, &curl_request_size); - curl_easy_getinfo(mCurlHandle, CURLINFO_CONTENT_TYPE, &curl_content_type); - - sd["curl_response_code"] = (int) curl_response; - sd["curl_http_connect_code"] = (int) curl_connect; - sd["curl_total_time"] = curl_total_time; - sd["curl_size_upload"] = curl_size_upload; - sd["curl_size_download"] = curl_size_download; - sd["curl_content_length_upload"] = curl_content_length_upload; - sd["curl_content_length_download"] = curl_content_length_download; - sd["curl_request_size"] = (int) curl_request_size; - if (curl_content_type) - { - sd["curl_content_type"] = curl_content_type; - } - else - { - sd["curl_content_type"] = ""; - } - } - - sd["temp_id"] = mTmpUUID; - sd["request_type"] = LLAssetStorage::getRequestName(mRequestType); - sd["z_initialized"] = mZInitialized; - sd["z_input_exhausted"] = mZInputExhausted; - - S32 file_size = -1; - if (mFP) - { - struct stat file_stat; - int file_desc = fileno(mFP); - if ( fstat(file_desc, &file_stat) == 0) - { - file_size = file_stat.st_size; - } - } - sd["file_size"] = file_size; - - return sd; -} - - -void LLHTTPAssetRequest::setupCurlHandle() -{ - // *NOTE: Similar code exists in mapserver/llcurlutil.cpp JC - mCurlHandle = LLCurl::newEasyHandle(); - llassert_always(mCurlHandle != NULL) ; - - // Apply proxy settings if configured to do so - LLProxy::getInstance()->applyProxySettings(mCurlHandle); - - curl_easy_setopt(mCurlHandle, CURLOPT_NOSIGNAL, 1); - curl_easy_setopt(mCurlHandle, CURLOPT_NOPROGRESS, 1); - curl_easy_setopt(mCurlHandle, CURLOPT_URL, mURLBuffer.c_str()); - curl_easy_setopt(mCurlHandle, CURLOPT_PRIVATE, this); - if (LLAssetStorage::RT_DOWNLOAD == mRequestType) - { - curl_easy_setopt(mCurlHandle, CURLOPT_ENCODING, ""); - // only do this on downloads, as uploads - // to some apache configs (like our test grids) - // mistakenly claim the response is gzip'd if the resource - // name ends in .gz, even though in a PUT, the response is - // just plain HTML saying "created" - } - /* Remove the Pragma: no-cache header that libcurl inserts by default; - we want the cached version, if possible. */ - if (mZInitialized) - { - curl_easy_setopt(mCurlHandle, CURLOPT_PROXY, ""); - // disable use of proxy, which can't handle chunked transfers - } - mHTTPHeaders = curl_slist_append(mHTTPHeaders, "Pragma:"); - - // bug in curl causes DNS to be cached for too long a time, 0 sets it to never cache DNS results internally (to curl) - curl_easy_setopt(mCurlHandle, CURLOPT_DNS_CACHE_TIMEOUT, 0); - - // resist the temptation to explicitly add the Transfer-Encoding: chunked - // header here - invokes a libCURL bug - curl_easy_setopt(mCurlHandle, CURLOPT_HTTPHEADER, mHTTPHeaders); - if (mAssetStoragep) - { - // Set the appropriate pending upload or download flag - mAssetStoragep->addRunningRequest(mRequestType, this); - } - else - { - LL_ERRS() << "LLHTTPAssetRequest::setupCurlHandle - No asset storage associated with this request!" << LL_ENDL; - } -} - -void LLHTTPAssetRequest::cleanupCurlHandle() -{ - LLCurl::deleteEasyHandle(mCurlHandle); - if (mAssetStoragep) - { - // Terminating a request. Thus upload or download is no longer pending. - mAssetStoragep->removeRunningRequest(mRequestType, this); - } - else - { - LL_ERRS() << "LLHTTPAssetRequest::~LLHTTPAssetRequest - No asset storage associated with this request!" << LL_ENDL; - } - mCurlHandle = NULL; -} - -void LLHTTPAssetRequest::prepareCompressedUpload() -{ - mZStream.next_in = Z_NULL; - mZStream.avail_in = 0; - mZStream.zalloc = Z_NULL; - mZStream.zfree = Z_NULL; - mZStream.opaque = Z_NULL; - - int r = deflateInit2(&mZStream, - 1, // compression level - Z_DEFLATED, // the only method defined - 15 + 16, // the default windowBits + gzip header flag - 8, // the default memLevel - Z_DEFAULT_STRATEGY); - - if (r != Z_OK) - { - LL_ERRS() << "LLHTTPAssetRequest::prepareCompressedUpload defalateInit2() failed" << LL_ENDL; - } - - mZInitialized = true; - mZInputBuffer = new char[COMPRESSED_INPUT_BUFFER_SIZE]; - mZInputExhausted = false; - - mVFile = new LLVFile(gAssetStorage->mVFS, - getUUID(), getType(), LLVFile::READ); -} - -void LLHTTPAssetRequest::finishCompressedUpload() -{ - if (mZInitialized) - { - LL_INFOS() << "LLHTTPAssetRequest::finishCompressedUpload: " - << "read " << mZStream.total_in << " byte asset file, " - << "uploaded " << mZStream.total_out << " byte compressed asset" - << LL_ENDL; - - deflateEnd(&mZStream); - delete[] mZInputBuffer; - } -} - -size_t LLHTTPAssetRequest::readCompressedData(void* data, size_t size) -{ - llassert(mZInitialized); - - mZStream.next_out = (Bytef*)data; - mZStream.avail_out = size; - - while (mZStream.avail_out > 0) - { - if (mZStream.avail_in == 0 && !mZInputExhausted) - { - S32 to_read = llmin(COMPRESSED_INPUT_BUFFER_SIZE, - (S32)(mVFile->getSize() - mVFile->tell())); - - if ( to_read > 0 ) - { - mVFile->read((U8*)mZInputBuffer, to_read); /*Flawfinder: ignore*/ - mZStream.next_in = (Bytef*)mZInputBuffer; - mZStream.avail_in = mVFile->getLastBytesRead(); - } - - mZInputExhausted = mZStream.avail_in == 0; - } - - int r = deflate(&mZStream, - mZInputExhausted ? Z_FINISH : Z_NO_FLUSH); - - if (r == Z_STREAM_END || r < 0 || mZInputExhausted) - { - if (r < 0) - { - LL_WARNS() << "LLHTTPAssetRequest::readCompressedData: deflate returned error code " - << (S32) r << LL_ENDL; - } - break; - } - } - - return size - mZStream.avail_out; -} - -//static -size_t LLHTTPAssetRequest::curlCompressedUploadCallback( - void *data, size_t size, size_t nmemb, void *user_data) -{ - size_t num_read = 0; - - if (gAssetStorage) - { - CURL *curl_handle = (CURL *)user_data; - LLHTTPAssetRequest *req = NULL; - curl_easy_getinfo(curl_handle, CURLINFO_PRIVATE, &req); - if (req) - { - num_read = req->readCompressedData(data, size * nmemb); - } - } - - return num_read; -} - -///////////////////////////////////////////////////////////////////////////////// -// LLHTTPAssetStorage -///////////////////////////////////////////////////////////////////////////////// - - -LLHTTPAssetStorage::LLHTTPAssetStorage(LLMessageSystem *msg, LLXferManager *xfer, - LLVFS *vfs, LLVFS *static_vfs, - const LLHost &upstream_host, - const std::string& web_host, - const std::string& local_web_host, - const std::string& host_name) - : LLAssetStorage(msg, xfer, vfs, static_vfs, upstream_host) -{ - _init(web_host, local_web_host, host_name); -} - -LLHTTPAssetStorage::LLHTTPAssetStorage(LLMessageSystem *msg, LLXferManager *xfer, - LLVFS *vfs, - LLVFS *static_vfs, - const std::string& web_host, - const std::string& local_web_host, - const std::string& host_name) - : LLAssetStorage(msg, xfer, vfs, static_vfs) -{ - _init(web_host, local_web_host, host_name); -} - -void LLHTTPAssetStorage::_init(const std::string& web_host, const std::string& local_web_host, const std::string& host_name) -{ - mBaseURL = web_host; - mLocalBaseURL = local_web_host; - mHostName = host_name; - - // curl_global_init moved to LLCurl::initClass() - - mCurlMultiHandle = LLCurl::newMultiHandle() ; - llassert_always(mCurlMultiHandle != NULL) ; -} - -LLHTTPAssetStorage::~LLHTTPAssetStorage() -{ - LLCurl::deleteMultiHandle(mCurlMultiHandle); - mCurlMultiHandle = NULL; - - // curl_global_cleanup moved to LLCurl::initClass() -} - -// storing data is simpler than getting it, so we just overload the whole method -void LLHTTPAssetStorage::storeAssetData( - const LLUUID& uuid, - LLAssetType::EType type, - LLAssetStorage::LLStoreAssetCallback callback, - void* user_data, - bool temp_file, - bool is_priority, - bool store_local, - const LLUUID& requesting_agent_id, - bool user_waiting, - F64Seconds timeout) -{ - if (mVFS->getExists(uuid, type)) // VFS treats nonexistant and zero-length identically - { - LLAssetRequest *req = new LLAssetRequest(uuid, type); - req->mUpCallback = callback; - req->mUserData = user_data; - req->mRequestingAgentID = requesting_agent_id; - req->mIsUserWaiting = user_waiting; - req->mTimeout = timeout; - - // LLAssetStorage metric: Successful Request - S32 size = mVFS->getSize(uuid, type); - const char *message; - if( store_local ) - { - message = "Added to local upload queue"; - } - else - { - message = "Added to upload queue"; - } - reportMetric( uuid, type, LLStringUtil::null, requesting_agent_id, size, MR_OKAY, __FILE__, __LINE__, message ); - - // this will get picked up and transmitted in checkForTimeouts - if(store_local) - { - mPendingLocalUploads.push_back(req); - } - else if(is_priority) - { - mPendingUploads.push_front(req); - } - else - { - mPendingUploads.push_back(req); - } - } - else - { - LL_WARNS() << "AssetStorage: attempt to upload non-existent vfile " << uuid << ":" << LLAssetType::lookup(type) << LL_ENDL; - if (callback) - { - // LLAssetStorage metric: Zero size VFS - reportMetric( uuid, type, LLStringUtil::null, requesting_agent_id, 0, MR_ZERO_SIZE, __FILE__, __LINE__, "The file didn't exist or was zero length (VFS - can't tell which)" ); - callback(uuid, user_data, LL_ERR_ASSET_REQUEST_NONEXISTENT_FILE, LL_EXSTAT_NONEXISTENT_FILE); - } - } -} - -// virtual -void LLHTTPAssetStorage::storeAssetData( - const std::string& filename, - const LLUUID& asset_id, - LLAssetType::EType asset_type, - LLStoreAssetCallback callback, - void* user_data, - bool temp_file, - bool is_priority, - bool user_waiting, - F64Seconds timeout) -{ - LL_INFOS() << "LLAssetStorage::storeAssetData (legacy)" << asset_id << ":" << LLAssetType::lookup(asset_type) << LL_ENDL; - - LLLegacyAssetRequest *legacy = new LLLegacyAssetRequest; - - legacy->mUpCallback = callback; - legacy->mUserData = user_data; - - FILE *fp = LLFile::fopen(filename, "rb"); /*Flawfinder: ignore*/ - S32 size = 0; - if (fp) - { - fseek(fp, 0, SEEK_END); - size = ftell(fp); - fseek(fp, 0, SEEK_SET); - } - - if( size ) - { - LLVFile file(mVFS, asset_id, asset_type, LLVFile::WRITE); - - file.setMaxSize(size); - - const S32 buf_size = 65536; - U8 copy_buf[buf_size]; - while ((size = (S32)fread(copy_buf, 1, buf_size, fp))) - { - file.write(copy_buf, size); - } - fclose(fp); - - // if this upload fails, the caller needs to setup a new tempfile for us - if (temp_file) - { - LLFile::remove(filename); - } - - // LLAssetStorage metric: Success not needed; handled in the overloaded method here: - storeAssetData( - asset_id, - asset_type, - legacyStoreDataCallback, - (void**)legacy, - temp_file, - is_priority, - false, - LLUUID::null, - user_waiting, - timeout); - } - else // !size - { - if( fp ) - { - // LLAssetStorage metric: Zero size - reportMetric( asset_id, asset_type, filename, LLUUID::null, 0, MR_ZERO_SIZE, __FILE__, __LINE__, "The file was zero length" ); - fclose( fp ); - } - else - { - // LLAssetStorage metric: Missing File - reportMetric( asset_id, asset_type, filename, LLUUID::null, 0, MR_FILE_NONEXIST, __FILE__, __LINE__, "The file didn't exist" ); - } - if (callback) - { - callback(LLUUID::null, user_data, LL_ERR_CANNOT_OPEN_FILE, LL_EXSTAT_BLOCKED_FILE); - } - delete legacy; - } -} - -// virtual -LLSD LLHTTPAssetStorage::getPendingDetails(LLAssetStorage::ERequestType rt, - LLAssetType::EType asset_type, - const std::string& detail_prefix) const -{ - LLSD sd = LLAssetStorage::getPendingDetails(rt, asset_type, detail_prefix); - const request_list_t* running = getRunningList(rt); - if (running) - { - // Loop through the pending requests sd, and add extra info about its running status. - S32 num_pending = sd["requests"].size(); - S32 i; - for (i = 0; i < num_pending; ++i) - { - LLSD& pending = sd["requests"][i]; - // See if this pending request is running. - const LLAssetRequest* req = findRequest(running, - LLAssetType::lookup(pending["type"].asString()), - pending["asset_id"]); - if (req) - { - // Keep the detail_url so we don't have to rebuild it. - LLURI detail_url = pending["detail"]; - pending = req->getTerseDetails(); - pending["detail"] = detail_url; - pending["is_running"] = true; - } - else - { - pending["is_running"] = false; - } - } - } - return sd; -} - -// virtual -LLSD LLHTTPAssetStorage::getPendingRequest(LLAssetStorage::ERequestType rt, - LLAssetType::EType asset_type, - const LLUUID& asset_id) const -{ - // Look for this asset in the running list first. - const request_list_t* running = getRunningList(rt); - if (running) - { - LLSD sd = LLAssetStorage::getPendingRequestImpl(running, asset_type, asset_id); - if (sd) - { - sd["is_running"] = true; - return sd; - } - } - LLSD sd = LLAssetStorage::getPendingRequest(rt, asset_type, asset_id); - if (sd) - { - sd["is_running"] = false; - } - return sd; -} - -// virtual -bool LLHTTPAssetStorage::deletePendingRequest(LLAssetStorage::ERequestType rt, - LLAssetType::EType asset_type, - const LLUUID& asset_id) -{ - // Try removing this from the running list first. - request_list_t* running = getRunningList(rt); - if (running) - { - LLAssetRequest* req = findRequest(running, asset_type, asset_id); - if (req) - { - // Remove this request from the running list to get it out of curl. - running->remove(req); - - // Find this request in the pending list, so we can move it to the end of the line. - request_list_t* pending = getRequestList(rt); - if (pending) - { - request_list_t::iterator result = std::find_if(pending->begin(), pending->end(), - std::bind2nd(ll_asset_request_equal<LLAssetRequest*>(), req)); - if (pending->end() != result) - { - // This request was found in the pending list. Move it to the end! - LLAssetRequest* pending_req = *result; - pending->remove(pending_req); - - if (!pending_req->mIsUserWaiting) //A user is waiting on this request. Toss it. - { - pending->push_back(pending_req); - } - else - { - if (pending_req->mUpCallback) //Clean up here rather than _callUploadCallbacks because this request is already cleared the req. - { - pending_req->mUpCallback(pending_req->getUUID(), pending_req->mUserData, -1, LL_EXSTAT_REQUEST_DROPPED); - } - - } - - LL_INFOS() << "Asset " << getRequestName(rt) << " request for " - << asset_id << "." << LLAssetType::lookup(asset_type) - << " removed from curl and placed at the end of the pending queue." - << LL_ENDL; - } - else - { - LL_WARNS() << "Unable to find pending " << getRequestName(rt) << " request for " - << asset_id << "." << LLAssetType::lookup(asset_type) << LL_ENDL; - } - } - delete req; - - return true; - } - } - return LLAssetStorage::deletePendingRequest(rt, asset_type, asset_id); -} - -// internal requester, used by getAssetData in superclass -void LLHTTPAssetStorage::_queueDataRequest(const LLUUID& uuid, LLAssetType::EType type, - void (*callback)(LLVFS *vfs, const LLUUID&, LLAssetType::EType, void *, S32, LLExtStat), - void *user_data, BOOL duplicate, - BOOL is_priority) -{ - // stash the callback info so we can find it after we get the response message - LLAssetRequest *req = new LLAssetRequest(uuid, type); - req->mDownCallback = callback; - req->mUserData = user_data; - req->mIsPriority = is_priority; - - // this will get picked up and downloaded in checkForTimeouts - - // - // HAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACK! Asset requests were taking too long and timing out. - // Since texture requests are the LEAST sensitive (on the simulator) to being delayed, add - // non-texture requests to the front, and add texture requests to the back. The theory is - // that we always want them first, even if they're out of order. - // - - if (req->getType() == LLAssetType::AT_TEXTURE) - { - mPendingDownloads.push_back(req); - } - else - { - mPendingDownloads.push_front(req); - } -} - -LLAssetRequest* LLHTTPAssetStorage::findNextRequest(LLAssetStorage::request_list_t& pending, - LLAssetStorage::request_list_t& running) -{ - // Early exit if the running list is full, or we don't have more pending than running. - if (running.size() >= MAX_RUNNING_REQUESTS - || pending.size() <= running.size()) return NULL; - - // Look for the first pending request that is not already running. - request_list_t::iterator running_begin = running.begin(); - request_list_t::iterator running_end = running.end(); - - request_list_t::iterator pending_iter = pending.begin(); - - // Loop over all pending requests until we miss finding it in the running list. - for (; pending_iter != pending.end(); ++pending_iter) - { - LLAssetRequest* req = *pending_iter; - // Look for this pending request in the running list. - if (running_end == std::find_if(running_begin, running_end, - std::bind2nd(ll_asset_request_equal<LLAssetRequest*>(), req))) - { - // It isn't running! Return it. - return req; - } - } - return NULL; -} - -// overloaded to additionally move data to/from the webserver -void LLHTTPAssetStorage::checkForTimeouts() -{ - CURLMcode mcode; - LLAssetRequest *req; - while ( (req = findNextRequest(mPendingDownloads, mRunningDownloads)) ) - { - // Setup this curl download request - // We need to generate a new request here - // since the one in the list could go away - std::string tmp_url; - std::string uuid_str; - req->getUUID().toString(uuid_str); - std::string base_url = getBaseURL(req->getUUID(), req->getType()); - tmp_url = llformat("%s/%36s.%s", base_url.c_str() , uuid_str.c_str(), LLAssetType::lookup(req->getType())); - - LLHTTPAssetRequest *new_req = new LLHTTPAssetRequest(this, req->getUUID(), - req->getType(), RT_DOWNLOAD, tmp_url, mCurlMultiHandle); - new_req->mTmpUUID.generate(); - - // Sets pending download flag internally - new_req->setupCurlHandle(); - curl_easy_setopt(new_req->mCurlHandle, CURLOPT_FOLLOWLOCATION, TRUE); - curl_easy_setopt(new_req->mCurlHandle, CURLOPT_WRITEFUNCTION, &curlDownCallback); - curl_easy_setopt(new_req->mCurlHandle, CURLOPT_WRITEDATA, new_req->mCurlHandle); - - mcode = curl_multi_add_handle(mCurlMultiHandle, new_req->mCurlHandle); - if (mcode > CURLM_OK) - { - // Failure. Deleting the pending request will remove it from the running - // queue, and push it to the end of the pending queue. - new_req->cleanupCurlHandle(); - deletePendingRequest(RT_DOWNLOAD, new_req->getType(), new_req->getUUID()); - break; - } - else - { - LL_INFOS() << "Requesting " << new_req->mURLBuffer << LL_ENDL; - } - } - - while ( (req = findNextRequest(mPendingUploads, mRunningUploads)) ) - { - // setup this curl upload request - - bool do_compress = req->getType() == LLAssetType::AT_OBJECT; - - std::string tmp_url; - std::string uuid_str; - req->getUUID().toString(uuid_str); - tmp_url = mBaseURL + "/" + uuid_str + "." + LLAssetType::lookup(req->getType()); - if (do_compress) tmp_url += ".gz"; - - LLHTTPAssetRequest *new_req = new LLHTTPAssetRequest(this, req->getUUID(), - req->getType(), RT_UPLOAD, tmp_url, mCurlMultiHandle); - - if (req->mIsUserWaiting) //If a user is waiting on a realtime response, we want to perserve information across upload attempts. - { - new_req->mTime = req->mTime; - new_req->mTimeout = req->mTimeout; - new_req->mIsUserWaiting = req->mIsUserWaiting; - } - - if (do_compress) - { - new_req->prepareCompressedUpload(); - } - - // Sets pending upload flag internally - new_req->setupCurlHandle(); - curl_easy_setopt(new_req->mCurlHandle, CURLOPT_UPLOAD, 1); - curl_easy_setopt(new_req->mCurlHandle, CURLOPT_WRITEFUNCTION, &nullOutputCallback); - - if (do_compress) - { - curl_easy_setopt(new_req->mCurlHandle, CURLOPT_READFUNCTION, - &LLHTTPAssetRequest::curlCompressedUploadCallback); - } - else - { - LLVFile file(mVFS, req->getUUID(), req->getType()); - curl_easy_setopt(new_req->mCurlHandle, CURLOPT_INFILESIZE, file.getSize()); - curl_easy_setopt(new_req->mCurlHandle, CURLOPT_READFUNCTION, - &curlUpCallback); - } - curl_easy_setopt(new_req->mCurlHandle, CURLOPT_READDATA, new_req->mCurlHandle); - - mcode = curl_multi_add_handle(mCurlMultiHandle, new_req->mCurlHandle); - if (mcode > CURLM_OK) - { - // Failure. Deleting the pending request will remove it from the running - // queue, and push it to the end of the pending queue. - new_req->cleanupCurlHandle(); - deletePendingRequest(RT_UPLOAD, new_req->getType(), new_req->getUUID()); - break; - } - else - { - // Get the uncompressed file size. - LLVFile file(mVFS,new_req->getUUID(),new_req->getType()); - S32 size = file.getSize(); - LL_INFOS() << "Requesting PUT " << new_req->mURLBuffer << ", asset size: " << size << " bytes" << LL_ENDL; - if (size == 0) - { - LL_WARNS() << "Rejecting zero size PUT request!" << LL_ENDL; - new_req->cleanupCurlHandle(); - deletePendingRequest(RT_UPLOAD, new_req->getType(), new_req->getUUID()); - } - } - // Pending upload will have been flagged by the request - } - - while ( (req = findNextRequest(mPendingLocalUploads, mRunningLocalUploads)) ) - { - // setup this curl upload request - LLVFile file(mVFS, req->getUUID(), req->getType()); - - std::string tmp_url; - std::string uuid_str; - req->getUUID().toString(uuid_str); - - // KLW - All temporary uploads are saved locally "http://localhost:12041/asset" - tmp_url = llformat("%s/%36s.%s", mLocalBaseURL.c_str(), uuid_str.c_str(), LLAssetType::lookup(req->getType())); - - LLHTTPAssetRequest *new_req = new LLHTTPAssetRequest(this, req->getUUID(), - req->getType(), RT_LOCALUPLOAD, tmp_url, mCurlMultiHandle); - new_req->mRequestingAgentID = req->mRequestingAgentID; - - // Sets pending upload flag internally - new_req->setupCurlHandle(); - curl_easy_setopt(new_req->mCurlHandle, CURLOPT_PUT, 1); - curl_easy_setopt(new_req->mCurlHandle, CURLOPT_INFILESIZE, file.getSize()); - curl_easy_setopt(new_req->mCurlHandle, CURLOPT_WRITEFUNCTION, &nullOutputCallback); - curl_easy_setopt(new_req->mCurlHandle, CURLOPT_READFUNCTION, &curlUpCallback); - curl_easy_setopt(new_req->mCurlHandle, CURLOPT_READDATA, new_req->mCurlHandle); - - mcode = curl_multi_add_handle(mCurlMultiHandle, new_req->mCurlHandle); - if (mcode > CURLM_OK) - { - // Failure. Deleting the pending request will remove it from the running - // queue, and push it to the end of the pending queue. - new_req->cleanupCurlHandle(); - deletePendingRequest(RT_LOCALUPLOAD, new_req->getType(), new_req->getUUID()); - break; - } - else - { - // Get the uncompressed file size. - S32 size = file.getSize(); - - LL_INFOS() << "TAT: LLHTTPAssetStorage::checkForTimeouts() : pending local!" - << " Requesting PUT " << new_req->mURLBuffer << ", asset size: " << size << " bytes" << LL_ENDL; - if (size == 0) - { - - LL_WARNS() << "Rejecting zero size PUT request!" << LL_ENDL; - new_req->cleanupCurlHandle(); - deletePendingRequest(RT_UPLOAD, new_req->getType(), new_req->getUUID()); - } - - } - // Pending upload will have been flagged by the request - } - S32 count = 0; - int queue_length; - do - { - mcode = curl_multi_perform(mCurlMultiHandle, &queue_length); - count++; - } while (mcode == CURLM_CALL_MULTI_PERFORM && (count < 5)); - - CURLMsg *curl_msg; - do - { - curl_msg = curl_multi_info_read(mCurlMultiHandle, &queue_length); - if (curl_msg && curl_msg->msg == CURLMSG_DONE) - { - long curl_result = 0; - S32 xfer_result = LL_ERR_NOERR; - - LLHTTPAssetRequest *http_req = NULL; - curl_easy_getinfo(curl_msg->easy_handle, CURLINFO_PRIVATE, &http_req); - - // TODO: Throw curl_result at all callbacks. - curl_easy_getinfo(curl_msg->easy_handle, CURLINFO_HTTP_CODE, &curl_result); - if (RT_UPLOAD == http_req->mRequestType || RT_LOCALUPLOAD == http_req->mRequestType) - { - if (curl_msg->data.result == CURLE_OK && - ( curl_result == HTTP_OK - || curl_result == HTTP_CREATED - || curl_result == HTTP_NO_CONTENT)) - { - LL_INFOS() << "Success uploading " << http_req->getUUID() << " to " << http_req->mURLBuffer << LL_ENDL; - if (RT_LOCALUPLOAD == http_req->mRequestType) - { - addTempAssetData(http_req->getUUID(), http_req->mRequestingAgentID, mHostName); - } - } - else if (curl_msg->data.result == CURLE_COULDNT_CONNECT || - curl_msg->data.result == CURLE_OPERATION_TIMEOUTED || - curl_result == HTTP_BAD_GATEWAY || - curl_result == HTTP_SERVICE_UNAVAILABLE) - { - LL_WARNS() << "Re-requesting upload for " << http_req->getUUID() << ". Received upload error to " << http_req->mURLBuffer << - " with result " << curl_easy_strerror(curl_msg->data.result) << ", http result " << curl_result << LL_ENDL; - - ////HACK (probably) I am sick of this getting requeued and driving me mad. - //if (http_req->mIsUserWaiting) - //{ - // deletePendingRequest(RT_UPLOAD, http_req->getType(), http_req->getUUID()); - //} - } - else - { - LL_WARNS() << "Failure uploading " << http_req->getUUID() << " to " << http_req->mURLBuffer << - " with result " << curl_easy_strerror(curl_msg->data.result) << ", http result " << curl_result << LL_ENDL; - - xfer_result = LL_ERR_ASSET_REQUEST_FAILED; - } - - if (!(curl_msg->data.result == CURLE_COULDNT_CONNECT || - curl_msg->data.result == CURLE_OPERATION_TIMEOUTED || - curl_result == HTTP_BAD_GATEWAY || - curl_result == HTTP_SERVICE_UNAVAILABLE)) - { - // shared upload finished callback - // in the base class, this is called from processUploadComplete - _callUploadCallbacks(http_req->getUUID(), http_req->getType(), (xfer_result == 0), LL_EXSTAT_CURL_RESULT | curl_result); - // Pending upload flag will get cleared when the request is deleted - } - } - else if (RT_DOWNLOAD == http_req->mRequestType) - { - if (curl_result == HTTP_OK && curl_msg->data.result == CURLE_OK) - { - if (http_req->mVFile && http_req->mVFile->getSize() > 0) - { - LL_INFOS() << "Success downloading " << http_req->mURLBuffer << ", size " << http_req->mVFile->getSize() << LL_ENDL; - - http_req->mVFile->rename(http_req->getUUID(), http_req->getType()); - } - else - { - // *TODO: if this actually indicates a bad asset on the server - // (not certain at this point), then delete it - LL_WARNS() << "Found " << http_req->mURLBuffer << " to be zero size" << LL_ENDL; - xfer_result = LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE; - } - } - else - { - // KLW - TAT See if an avatar owns this texture, and if so request re-upload. - LL_WARNS() << "Failure downloading " << http_req->mURLBuffer << - " with result " << curl_easy_strerror(curl_msg->data.result) << ", http result " << curl_result << LL_ENDL; - - xfer_result = (curl_result == HTTP_NOT_FOUND) ? LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE : LL_ERR_ASSET_REQUEST_FAILED; - - if (http_req->mVFile) - { - http_req->mVFile->remove(); - } - } - - // call the static callback for transfer completion - // this will cleanup all requests for this asset, including ours - downloadCompleteCallback( - xfer_result, - http_req->getUUID(), - http_req->getType(), - http_req, - LL_EXSTAT_CURL_RESULT | curl_result); - // Pending download flag will get cleared when the request is deleted - } - else - { - // nothing, just axe this request - // currently this can only mean an asset delete - } - - // Deleting clears the pending upload/download flag if it's set and the request is transferring - delete http_req; - http_req = NULL; - } - - } while (curl_msg && queue_length > 0); - - - // Cleanup - // We want to bump to the back of the line any running uploads that have timed out. - bumpTimedOutUploads(); - - LLAssetStorage::checkForTimeouts(); -} - -void LLHTTPAssetStorage::bumpTimedOutUploads() -{ - bool user_waiting=FALSE; - - F64Seconds mt_secs = LLMessageSystem::getMessageTimeSeconds(); - - if (mPendingUploads.size()) - { - request_list_t::iterator it = mPendingUploads.begin(); - LLAssetRequest* req = *it; - user_waiting=req->mIsUserWaiting; - } - - // No point bumping currently running uploads if there are no others in line. - if (!(mPendingUploads.size() > mRunningUploads.size()) && !user_waiting) - { - return; - } - - // deletePendingRequest will modify the mRunningUploads list so we don't want to iterate over it. - request_list_t temp_running = mRunningUploads; - - request_list_t::iterator it = temp_running.begin(); - request_list_t::iterator end = temp_running.end(); - for ( ; it != end; ++it) - { - //request_list_t::iterator curiter = iter++; - LLAssetRequest* req = *it; - - if ( req->mTimeout < (mt_secs - req->mTime) ) - { - LL_WARNS() << "Asset upload request timed out for " - << req->getUUID() << "." - << LLAssetType::lookup(req->getType()) - << ", bumping to the back of the line!" << LL_ENDL; - - deletePendingRequest(RT_UPLOAD, req->getType(), req->getUUID()); - } - } -} - -// static -size_t LLHTTPAssetStorage::curlDownCallback(void *data, size_t size, size_t nmemb, void *user_data) -{ - if (!gAssetStorage) - { - LL_WARNS() << "Missing gAssetStorage, aborting curl download callback!" << LL_ENDL; - return 0; - } - S32 bytes = (S32)(size * nmemb); - CURL *curl_handle = (CURL *)user_data; - LLHTTPAssetRequest *req = NULL; - curl_easy_getinfo(curl_handle, CURLINFO_PRIVATE, &req); - - if (! req->mVFile) - { - req->mVFile = new LLVFile(gAssetStorage->mVFS, req->mTmpUUID, LLAssetType::AT_NONE, LLVFile::APPEND); - } - - double content_length = 0.0; - curl_easy_getinfo(curl_handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &content_length); - - // sanitize content_length, reconcile w/ actual data - S32 file_length = llmax(0, (S32)llmin(content_length, 20000000.0), bytes + req->mVFile->getSize()); - - req->mVFile->setMaxSize(file_length); - req->mVFile->write((U8*)data, bytes); - - return nmemb; -} - -// static -size_t LLHTTPAssetStorage::curlUpCallback(void *data, size_t size, size_t nmemb, void *user_data) -{ - if (!gAssetStorage) - { - LL_WARNS() << "Missing gAssetStorage, aborting curl download callback!" << LL_ENDL; - return 0; - } - CURL *curl_handle = (CURL *)user_data; - LLHTTPAssetRequest *req = NULL; - curl_easy_getinfo(curl_handle, CURLINFO_PRIVATE, &req); - - if (! req->mVFile) - { - req->mVFile = new LLVFile(gAssetStorage->mVFS, req->getUUID(), req->getType(), LLVFile::READ); - } - - S32 bytes = llmin((S32)(size * nmemb), (S32)(req->mVFile->getSize() - req->mVFile->tell())); - - req->mVFile->read((U8*)data, bytes);/*Flawfinder: ignore*/ - - return req->mVFile->getLastBytesRead(); -} - -// static -size_t LLHTTPAssetStorage::nullOutputCallback(void *data, size_t size, size_t nmemb, void *user_data) -{ - // do nothing, this is here to soak up script output so it doesn't end up on stdout - - return nmemb; -} - - - -// blocking asset fetch which bypasses the VFS -// this is a very limited function for use by the simstate loader and other one-offs -S32 LLHTTPAssetStorage::getURLToFile(const LLUUID& uuid, LLAssetType::EType asset_type, const std::string &url, const std::string& filename, progress_callback callback, void *userdata) -{ - // *NOTE: There is no guarantee that the uuid and the asset_type match - // - not that it matters. - Doug - LL_DEBUGS() << "LLHTTPAssetStorage::getURLToFile() - " << url << LL_ENDL; - - FILE *fp = LLFile::fopen(filename, "wb"); /*Flawfinder: ignore*/ - if (! fp) - { - LL_WARNS() << "Failed to open " << filename << " for writing" << LL_ENDL; - return LL_ERR_ASSET_REQUEST_FAILED; - } - - // make sure we use the normal curl setup, even though we don't really need a request object - LLHTTPAssetRequest req(this, uuid, asset_type, RT_DOWNLOAD, url, mCurlMultiHandle); - req.mFP = fp; - - req.setupCurlHandle(); - curl_easy_setopt(req.mCurlHandle, CURLOPT_FOLLOWLOCATION, TRUE); - curl_easy_setopt(req.mCurlHandle, CURLOPT_WRITEFUNCTION, &curlFileDownCallback); - curl_easy_setopt(req.mCurlHandle, CURLOPT_WRITEDATA, req.mCurlHandle); - - curl_multi_add_handle(mCurlMultiHandle, req.mCurlHandle); - LL_INFOS() << "Requesting as file " << req.mURLBuffer << LL_ENDL; - - // braindead curl loop - int queue_length; - CURLMsg *curl_msg; - LLTimer timeout; - timeout.setTimerExpirySec(GET_URL_TO_FILE_TIMEOUT); - bool success = false; - S32 xfer_result = 0; - do - { - curl_multi_perform(mCurlMultiHandle, &queue_length); - curl_msg = curl_multi_info_read(mCurlMultiHandle, &queue_length); - - if (callback) - { - callback(userdata); - } - - if ( curl_msg && (CURLMSG_DONE == curl_msg->msg) ) - { - success = true; - } - else if (timeout.hasExpired()) - { - LL_WARNS() << "Request for " << url << " has timed out." << LL_ENDL; - success = false; - xfer_result = LL_ERR_ASSET_REQUEST_FAILED; - break; - } - } while (!success); - - if (success) - { - long curl_result = 0; - curl_easy_getinfo(curl_msg->easy_handle, CURLINFO_HTTP_CODE, &curl_result); - - if (curl_result == HTTP_OK && curl_msg->data.result == CURLE_OK) - { - S32 size = ftell(req.mFP); - if (size > 0) - { - // everything seems to be in order - LL_INFOS() << "Success downloading " << req.mURLBuffer << " to file, size " << size << LL_ENDL; - } - else - { - LL_WARNS() << "Found " << req.mURLBuffer << " to be zero size" << LL_ENDL; - xfer_result = LL_ERR_ASSET_REQUEST_FAILED; - } - } - else - { - xfer_result = curl_result == HTTP_NOT_FOUND ? LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE : LL_ERR_ASSET_REQUEST_FAILED; - LL_INFOS() << "Failure downloading " << req.mURLBuffer << - " with result " << curl_easy_strerror(curl_msg->data.result) << ", http result " << curl_result << LL_ENDL; - } - } - - fclose(fp); - if (xfer_result) - { - LLFile::remove(filename); - } - return xfer_result; -} - - -// static -size_t LLHTTPAssetStorage::curlFileDownCallback(void *data, size_t size, size_t nmemb, void *user_data) -{ - CURL *curl_handle = (CURL *)user_data; - LLHTTPAssetRequest *req = NULL; - curl_easy_getinfo(curl_handle, CURLINFO_PRIVATE, &req); - - if (! req->mFP) - { - LL_WARNS() << "Missing mFP, aborting curl file download callback!" << LL_ENDL; - return 0; - } - - return fwrite(data, size, nmemb, req->mFP); -} - -LLAssetStorage::request_list_t* LLHTTPAssetStorage::getRunningList(LLAssetStorage::ERequestType rt) -{ - switch (rt) - { - case RT_DOWNLOAD: - return &mRunningDownloads; - case RT_UPLOAD: - return &mRunningUploads; - case RT_LOCALUPLOAD: - return &mRunningLocalUploads; - default: - return NULL; - } -} - -const LLAssetStorage::request_list_t* LLHTTPAssetStorage::getRunningList(LLAssetStorage::ERequestType rt) const -{ - switch (rt) - { - case RT_DOWNLOAD: - return &mRunningDownloads; - case RT_UPLOAD: - return &mRunningUploads; - case RT_LOCALUPLOAD: - return &mRunningLocalUploads; - default: - return NULL; - } -} - - -void LLHTTPAssetStorage::addRunningRequest(ERequestType rt, LLHTTPAssetRequest* request) -{ - request_list_t* requests = getRunningList(rt); - if (requests) - { - requests->push_back(request); - } - else - { - LL_ERRS() << "LLHTTPAssetStorage::addRunningRequest - Request is not an upload OR download, this is bad!" << LL_ENDL; - } -} - -void LLHTTPAssetStorage::removeRunningRequest(ERequestType rt, LLHTTPAssetRequest* request) -{ - request_list_t* requests = getRunningList(rt); - if (requests) - { - requests->remove(request); - } - else - { - LL_ERRS() << "LLHTTPAssetStorage::removeRunningRequest - Destroyed request is not an upload OR download, this is bad!" << LL_ENDL; - } -} - -// virtual -void LLHTTPAssetStorage::addTempAssetData(const LLUUID& asset_id, const LLUUID& agent_id, const std::string& host_name) -{ - if (agent_id.isNull() || asset_id.isNull()) - { - LL_WARNS() << "TAT: addTempAssetData bad id's asset_id: " << asset_id << " agent_id: " << agent_id << LL_ENDL; - return; - } - - LLTempAssetData temp_asset_data; - temp_asset_data.mAssetID = asset_id; - temp_asset_data.mAgentID = agent_id; - temp_asset_data.mHostName = host_name; - - mTempAssets[asset_id] = temp_asset_data; -} - -// virtual -BOOL LLHTTPAssetStorage::hasTempAssetData(const LLUUID& texture_id) const -{ - uuid_tempdata_map::const_iterator citer = mTempAssets.find(texture_id); - BOOL found = (citer != mTempAssets.end()); - return found; -} - -// virtual -std::string LLHTTPAssetStorage::getTempAssetHostName(const LLUUID& texture_id) const -{ - uuid_tempdata_map::const_iterator citer = mTempAssets.find(texture_id); - if (citer != mTempAssets.end()) - { - return citer->second.mHostName; - } - else - { - return std::string(); - } -} - -// virtual -LLUUID LLHTTPAssetStorage::getTempAssetAgentID(const LLUUID& texture_id) const -{ - uuid_tempdata_map::const_iterator citer = mTempAssets.find(texture_id); - if (citer != mTempAssets.end()) - { - return citer->second.mAgentID; - } - else - { - return LLUUID::null; - } -} - -// virtual -void LLHTTPAssetStorage::removeTempAssetData(const LLUUID& asset_id) -{ - mTempAssets.erase(asset_id); -} - -// virtual -void LLHTTPAssetStorage::removeTempAssetDataByAgentID(const LLUUID& agent_id) -{ - uuid_tempdata_map::iterator it = mTempAssets.begin(); - uuid_tempdata_map::iterator end = mTempAssets.end(); - - while (it != end) - { - const LLTempAssetData& asset_data = it->second; - if (asset_data.mAgentID == agent_id) - { - mTempAssets.erase(it++); - } - else - { - ++it; - } - } -} - -std::string LLHTTPAssetStorage::getBaseURL(const LLUUID& asset_id, LLAssetType::EType asset_type) -{ - if (LLAssetType::AT_TEXTURE == asset_type) - { - uuid_tempdata_map::const_iterator citer = mTempAssets.find(asset_id); - if (citer != mTempAssets.end()) - { - const std::string& host_name = citer->second.mHostName; - std::string url = llformat(LOCAL_ASSET_URL_FORMAT, host_name.c_str()); - return url; - } - } - - return mBaseURL; -} - -void LLHTTPAssetStorage::dumpTempAssetData(const LLUUID& avatar_id) const -{ - uuid_tempdata_map::const_iterator it = mTempAssets.begin(); - uuid_tempdata_map::const_iterator end = mTempAssets.end(); - S32 count = 0; - for ( ; it != end; ++it) - { - const LLTempAssetData& temp_asset_data = it->second; - if (avatar_id.isNull() - || avatar_id == temp_asset_data.mAgentID) - { - LL_INFOS() << "TAT: dump agent " << temp_asset_data.mAgentID - << " texture " << temp_asset_data.mAssetID - << " host " << temp_asset_data.mHostName - << LL_ENDL; - count++; - } - } - - if (avatar_id.isNull()) - { - LL_INFOS() << "TAT: dumped " << count << " entries for all avatars" << LL_ENDL; - } - else - { - LL_INFOS() << "TAT: dumped " << count << " entries for avatar " << avatar_id << LL_ENDL; - } -} - -void LLHTTPAssetStorage::clearTempAssetData() -{ - LL_INFOS() << "TAT: Clearing temp asset data map" << LL_ENDL; - mTempAssets.clear(); -} diff --git a/indra/llmessage/llhttpassetstorage.h b/indra/llmessage/llhttpassetstorage.h deleted file mode 100644 index 783e95cac6..0000000000 --- a/indra/llmessage/llhttpassetstorage.h +++ /dev/null @@ -1,159 +0,0 @@ -/** - * @file llhttpassetstorage.h - * @brief Class for loading asset data to/from an external source over http. - * - * $LicenseInfo:firstyear=2003&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 LLHTTPASSETSTORAGE_H -#define LLHTTPASSETSTORAGE_H - -#include "llassetstorage.h" -#include "curl/curl.h" - -class LLVFile; -class LLHTTPAssetRequest; -typedef void (*progress_callback)(void* userdata); - -struct LLTempAssetData; - -typedef std::map<LLUUID,LLTempAssetData> uuid_tempdata_map; - -class LLHTTPAssetStorage : public LLAssetStorage -{ -public: - LLHTTPAssetStorage(LLMessageSystem *msg, LLXferManager *xfer, - LLVFS *vfs, LLVFS *static_vfs, - const LLHost &upstream_host, - const std::string& web_host, - const std::string& local_web_host, - const std::string& host_name); - - LLHTTPAssetStorage(LLMessageSystem *msg, LLXferManager *xfer, - LLVFS *vfs, LLVFS *static_vfs, - const std::string& web_host, - const std::string& local_web_host, - const std::string& host_name); - - - virtual ~LLHTTPAssetStorage(); - - using LLAssetStorage::storeAssetData; // Unhiding virtuals... - - virtual void storeAssetData( - const LLUUID& uuid, - LLAssetType::EType atype, - LLStoreAssetCallback callback, - void* user_data, - bool temp_file = false, - bool is_priority = false, - bool store_local = false, - const LLUUID& requesting_agent_id = LLUUID::null, - bool user_waiting=FALSE, - F64Seconds timeout=LL_ASSET_STORAGE_TIMEOUT); - - virtual void storeAssetData( - const std::string& filename, - const LLUUID& asset_id, - LLAssetType::EType atype, - LLStoreAssetCallback callback, - void* user_data, - bool temp_file, - bool is_priority, - bool user_waiting=FALSE, - F64Seconds timeout=LL_ASSET_STORAGE_TIMEOUT); - - virtual LLSD getPendingDetails(ERequestType rt, - LLAssetType::EType asset_type, - const std::string& detail_prefix) const; - - virtual LLSD getPendingRequest(ERequestType rt, - LLAssetType::EType asset_type, - const LLUUID& asset_id) const; - - virtual bool deletePendingRequest(ERequestType rt, - LLAssetType::EType asset_type, - const LLUUID& asset_id); - - // Hack. One off curl download an URL to a file. Probably should be elsewhere. - // Only used by lldynamicstate. The API is broken, and should be replaced with - // a generic HTTP file fetch - Doug 9/25/06 - S32 getURLToFile(const LLUUID& uuid, LLAssetType::EType asset_type, const std::string &url, const std::string& filename, progress_callback callback, void *userdata); - - LLAssetRequest* findNextRequest(request_list_t& pending, request_list_t& running); - - void checkForTimeouts(); - - static size_t curlDownCallback(void *data, size_t size, size_t nmemb, void *user_data); - static size_t curlFileDownCallback(void *data, size_t size, size_t nmemb, void *user_data); - static size_t curlUpCallback(void *data, size_t size, size_t nmemb, void *user_data); - static size_t nullOutputCallback(void *data, size_t size, size_t nmemb, void *user_data); - - // Should only be used by the LLHTTPAssetRequest - void addRunningRequest(ERequestType rt, LLHTTPAssetRequest* request); - void removeRunningRequest(ERequestType rt, LLHTTPAssetRequest* request); - - request_list_t* getRunningList(ERequestType rt); - const request_list_t* getRunningList(ERequestType rt) const; - - // Temp assets are stored on sim nodes, they have agent ID and location data associated with them. - virtual void addTempAssetData(const LLUUID& asset_id, const LLUUID& agent_id, const std::string& host_name); - virtual BOOL hasTempAssetData(const LLUUID& texture_id) const; - virtual std::string getTempAssetHostName(const LLUUID& texture_id) const; - virtual LLUUID getTempAssetAgentID(const LLUUID& texture_id) const; - virtual void removeTempAssetData(const LLUUID& asset_id); - virtual void removeTempAssetDataByAgentID(const LLUUID& agent_id); - - // Pass LLUUID::null for all - virtual void dumpTempAssetData(const LLUUID& avatar_id) const; - virtual void clearTempAssetData(); - -protected: - void _queueDataRequest(const LLUUID& uuid, LLAssetType::EType type, - void (*callback)(LLVFS *vfs, const LLUUID&, LLAssetType::EType, void *, S32, LLExtStat), - void *user_data, BOOL duplicate, BOOL is_priority); - -private: - void _init(const std::string& web_host, const std::string& local_web_host, const std::string& host_name); - - // This will return the correct base URI for any http asset request - std::string getBaseURL(const LLUUID& asset_id, LLAssetType::EType asset_type); - - // Check for running uploads that have timed out - // Bump these to the back of the line to let other uploads complete. - void bumpTimedOutUploads(); - -protected: - std::string mBaseURL; - std::string mLocalBaseURL; - std::string mHostName; - - CURLM *mCurlMultiHandle; - - request_list_t mRunningDownloads; - request_list_t mRunningUploads; - request_list_t mRunningLocalUploads; - - uuid_tempdata_map mTempAssets; -}; - -#endif diff --git a/indra/llmessage/llhttpclient.cpp b/indra/llmessage/llhttpclient.cpp deleted file mode 100644 index 60f17e6870..0000000000 --- a/indra/llmessage/llhttpclient.cpp +++ /dev/null @@ -1,673 +0,0 @@ -/** - * @file llhttpclient.cpp - * @brief Implementation of classes for making HTTP requests. - * - * $LicenseInfo:firstyear=2006&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" -#include <openssl/x509_vfy.h> -#include "llhttpclient.h" - -#include "llassetstorage.h" -#include "lliopipe.h" -#include "llurlrequest.h" -#include "llbufferstream.h" -#include "llsdserialize.h" -#include "llvfile.h" -#include "llvfs.h" -#include "lluri.h" - -#include "message.h" -#include <curl/curl.h> - - -const F32 HTTP_REQUEST_EXPIRY_SECS = 60.0f; -LLURLRequest::SSLCertVerifyCallback LLHTTPClient::mCertVerifyCallback = NULL; - -//////////////////////////////////////////////////////////////////////////// - -// Responder class moved to LLCurl - -namespace -{ - class LLHTTPClientURLAdaptor : public LLURLRequestComplete - { - public: - LLHTTPClientURLAdaptor(LLCurl::ResponderPtr responder) - : LLURLRequestComplete(), mResponder(responder), mStatus(HTTP_INTERNAL_ERROR), - mReason("LLURLRequest complete w/no status") - { - } - - ~LLHTTPClientURLAdaptor() - { - } - - virtual void httpStatus(S32 status, const std::string& reason) - { - LLURLRequestComplete::httpStatus(status,reason); - - mStatus = status; - mReason = reason; - } - - virtual void complete(const LLChannelDescriptors& channels, - const buffer_ptr_t& buffer) - { - // *TODO: Re-interpret mRequestStatus codes? - // Would like to detect curl errors, such as - // connection errors, write erros, etc. - if (mResponder.get()) - { - mResponder->setResult(mStatus, mReason); - mResponder->completedRaw(channels, buffer); - } - } - virtual void header(const std::string& header, const std::string& value) - { - if (mResponder.get()) - { - mResponder->setResponseHeader(header, value); - } - } - - private: - LLCurl::ResponderPtr mResponder; - S32 mStatus; - std::string mReason; - }; - - class Injector : public LLIOPipe - { - public: - virtual const std::string& contentType() = 0; - }; - - class LLSDInjector : public Injector - { - public: - LLSDInjector(const LLSD& sd) : mSD(sd) {} - virtual ~LLSDInjector() {} - - const std::string& contentType() { return HTTP_CONTENT_LLSD_XML; } - - virtual EStatus process_impl(const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, bool& eos, LLSD& context, LLPumpIO* pump) - { - LLBufferStream ostream(channels, buffer.get()); - LLSDSerialize::toXML(mSD, ostream); - eos = true; - return STATUS_DONE; - } - - const LLSD mSD; - }; - - class RawInjector : public Injector - { - public: - RawInjector(const U8* data, S32 size) : mData(data), mSize(size) {} - virtual ~RawInjector() {delete [] mData;} - - const std::string& contentType() { return HTTP_CONTENT_OCTET_STREAM; } - - virtual EStatus process_impl(const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, bool& eos, LLSD& context, LLPumpIO* pump) - { - LLBufferStream ostream(channels, buffer.get()); - ostream.write((const char *)mData, mSize); // hopefully chars are always U8s - eos = true; - return STATUS_DONE; - } - - const U8* mData; - S32 mSize; - }; - - class FileInjector : public Injector - { - public: - FileInjector(const std::string& filename) : mFilename(filename) {} - virtual ~FileInjector() {} - - const std::string& contentType() { return HTTP_CONTENT_OCTET_STREAM; } - - virtual EStatus process_impl(const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, bool& eos, LLSD& context, LLPumpIO* pump) - { - LLBufferStream ostream(channels, buffer.get()); - - llifstream fstream(mFilename.c_str(), std::iostream::binary | std::iostream::out); - if(fstream.is_open()) - { - fstream.seekg(0, std::ios::end); - U32 fileSize = (U32)fstream.tellg(); - fstream.seekg(0, std::ios::beg); - std::vector<char> fileBuffer(fileSize); - fstream.read(&fileBuffer[0], fileSize); - ostream.write(&fileBuffer[0], fileSize); - fstream.close(); - eos = true; - return STATUS_DONE; - } - - return STATUS_ERROR; - } - - const std::string mFilename; - }; - - class VFileInjector : public Injector - { - public: - VFileInjector(const LLUUID& uuid, LLAssetType::EType asset_type) : mUUID(uuid), mAssetType(asset_type) {} - virtual ~VFileInjector() {} - - const std::string& contentType() { return HTTP_CONTENT_OCTET_STREAM; } - - virtual EStatus process_impl(const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, bool& eos, LLSD& context, LLPumpIO* pump) - { - LLBufferStream ostream(channels, buffer.get()); - - LLVFile vfile(gVFS, mUUID, mAssetType, LLVFile::READ); - S32 fileSize = vfile.getSize(); - U8* fileBuffer; - fileBuffer = new U8 [fileSize]; - vfile.read(fileBuffer, fileSize); - ostream.write((char*)fileBuffer, fileSize); - delete [] fileBuffer; - eos = true; - return STATUS_DONE; - } - - const LLUUID mUUID; - LLAssetType::EType mAssetType; - }; - - - LLPumpIO* theClientPump = NULL; -} - -void LLHTTPClient::setCertVerifyCallback(LLURLRequest::SSLCertVerifyCallback callback) -{ - LLHTTPClient::mCertVerifyCallback = callback; -} - -static void request( - const std::string& url, - EHTTPMethod method, - Injector* body_injector, - LLCurl::ResponderPtr responder, - const F32 timeout = HTTP_REQUEST_EXPIRY_SECS, - const LLSD& headers = LLSD(), - bool follow_redirects = true - ) -{ - if (!LLHTTPClient::hasPump()) - { - if (responder) - { - responder->completeResult(HTTP_INTERNAL_ERROR, "No pump"); - } - delete body_injector; - return; - } - LLPumpIO::chain_t chain; - - LLURLRequest* req = new LLURLRequest(method, url, follow_redirects); - if(!req->isValid())//failed - { - if (responder) - { - responder->completeResult(HTTP_INTERNAL_CURL_ERROR, "Internal Error - curl failure"); - } - delete req; - delete body_injector; - return; - } - - req->setSSLVerifyCallback(LLHTTPClient::getCertVerifyCallback(), (void *)req); - - LL_DEBUGS("LLHTTPClient") << httpMethodAsVerb(method) << " " << url << " " << headers << LL_ENDL; - - // Insert custom headers if the caller sent any - if (headers.isMap()) - { - if (headers.has(HTTP_OUT_HEADER_COOKIE)) - { - req->allowCookies(); - } - - LLSD::map_const_iterator iter = headers.beginMap(); - LLSD::map_const_iterator end = headers.endMap(); - - for (; iter != end; ++iter) - { - //if the header is "Pragma" with no value - //the caller intends to force libcurl to drop - //the Pragma header it so gratuitously inserts - //Before inserting the header, force libcurl - //to not use the proxy (read: llurlrequest.cpp) - if ((iter->first == HTTP_OUT_HEADER_PRAGMA) && (iter->second.asString().empty())) - { - req->useProxy(false); - } - LL_DEBUGS("LLHTTPClient") << "header = " << iter->first - << ": " << iter->second.asString() << LL_ENDL; - req->addHeader(iter->first, iter->second.asString()); - } - } - - // Check to see if we have already set Accept or not. If no one - // set it, set it to application/llsd+xml since that's what we - // almost always want. - if( method != HTTP_PUT && method != HTTP_POST ) - { - if(!headers.has(HTTP_OUT_HEADER_ACCEPT)) - { - req->addHeader(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_LLSD_XML); - } - } - - if (responder) - { - responder->setURL(url); - responder->setHTTPMethod(method); - } - - req->setCallback(new LLHTTPClientURLAdaptor(responder)); - - if (method == HTTP_POST && gMessageSystem) - { - req->addHeader("X-SecondLife-UDP-Listen-Port", llformat("%d", - gMessageSystem->mPort)); - } - - if (method == HTTP_PUT || method == HTTP_POST || method == HTTP_PATCH) - { - if(!headers.has(HTTP_OUT_HEADER_CONTENT_TYPE)) - { - // If the Content-Type header was passed in, it has - // already been added as a header through req->addHeader - // in the loop above. We defer to the caller's wisdom, but - // if they did not specify a Content-Type, then ask the - // injector. - req->addHeader(HTTP_OUT_HEADER_CONTENT_TYPE, body_injector->contentType()); - } - chain.push_back(LLIOPipe::ptr_t(body_injector)); - } - - chain.push_back(LLIOPipe::ptr_t(req)); - - theClientPump->addChain(chain, timeout); -} - - -void LLHTTPClient::getByteRange( - const std::string& url, - S32 offset, - S32 bytes, - ResponderPtr responder, - const LLSD& hdrs, - const F32 timeout, - bool follow_redirects /* = true */) -{ - LLSD headers = hdrs; - if(offset > 0 || bytes > 0) - { - std::string range = llformat("bytes=%d-%d", offset, offset+bytes-1); - headers[HTTP_OUT_HEADER_RANGE] = range; - } - request(url,HTTP_GET, NULL, responder, timeout, headers, follow_redirects); -} - -void LLHTTPClient::head( - const std::string& url, - ResponderPtr responder, - const LLSD& headers, - const F32 timeout, - bool follow_redirects /* = true */) -{ - request(url, HTTP_HEAD, NULL, responder, timeout, headers, follow_redirects); -} - -void LLHTTPClient::get(const std::string& url, ResponderPtr responder, const LLSD& headers, const F32 timeout, - bool follow_redirects /* = true */) -{ - request(url, HTTP_GET, NULL, responder, timeout, headers, follow_redirects); -} -void LLHTTPClient::getHeaderOnly(const std::string& url, ResponderPtr responder, const LLSD& headers, - const F32 timeout, bool follow_redirects /* = true */) -{ - request(url, HTTP_HEAD, NULL, responder, timeout, headers, follow_redirects); -} -void LLHTTPClient::getHeaderOnly(const std::string& url, ResponderPtr responder, const F32 timeout, - bool follow_redirects /* = true */) -{ - getHeaderOnly(url, responder, LLSD(), timeout, follow_redirects); -} - -void LLHTTPClient::get(const std::string& url, const LLSD& query, ResponderPtr responder, const LLSD& headers, - const F32 timeout, bool follow_redirects /* = true */) -{ - LLURI uri; - - uri = LLURI::buildHTTP(url, LLSD::emptyArray(), query); - get(uri.asString(), responder, headers, timeout, follow_redirects); -} - -// A simple class for managing data returned from a curl http request. -class LLHTTPBuffer -{ -public: - LLHTTPBuffer() { } - - static size_t curl_write( void *ptr, size_t size, size_t nmemb, void *user_data) - { - LLHTTPBuffer* self = (LLHTTPBuffer*)user_data; - - size_t bytes = (size * nmemb); - self->mBuffer.append((char*)ptr,bytes); - return nmemb; - } - - LLSD asLLSD() - { - LLSD content; - - if (mBuffer.empty()) return content; - - std::istringstream istr(mBuffer); - LLSDSerialize::fromXML(content, istr); - return content; - } - - const std::string& asString() - { - return mBuffer; - } - -private: - std::string mBuffer; -}; - -// These calls are blocking! This is usually bad, unless you're a dataserver. Then it's awesome. - -/** - @brief does a blocking request on the url, returning the data or bad status. - - @param url URI to verb on. - @param method the verb to hit the URI with. - @param body the body of the call (if needed - for instance not used for GET and DELETE, but is for POST and PUT) - @param headers HTTP headers to use for the request. - @param timeout Curl timeout to use. Defaults to 5. Rationale: - Without this timeout, blockingGet() calls have been observed to take - up to 90 seconds to complete. Users of blockingGet() already must - check the HTTP return code for validity, so this will not introduce - new errors. A 5 second timeout will succeed > 95% of the time (and - probably > 99% of the time) based on my statistics. JC - - @returns an LLSD map: {status: integer, body: map} - */ -static LLSD blocking_request( - const std::string& url, - EHTTPMethod method, - const LLSD& body, - const LLSD& headers = LLSD(), - const F32 timeout = 5 -) -{ - LL_DEBUGS() << "blockingRequest of " << url << LL_ENDL; - char curl_error_buffer[CURL_ERROR_SIZE] = "\0"; - CURL* curlp = LLCurl::newEasyHandle(); - llassert_always(curlp != NULL) ; - - LLHTTPBuffer http_buffer; - std::string body_str; - - // other request method checks root cert first, we skip? - - // Apply configured proxy settings - LLProxy::getInstance()->applyProxySettings(curlp); - - // * Set curl handle options - curl_easy_setopt(curlp, CURLOPT_NOSIGNAL, 1); // don't use SIGALRM for timeouts - curl_easy_setopt(curlp, CURLOPT_TIMEOUT, timeout); // seconds, see warning at top of function. - curl_easy_setopt(curlp, CURLOPT_WRITEFUNCTION, LLHTTPBuffer::curl_write); - curl_easy_setopt(curlp, CURLOPT_WRITEDATA, &http_buffer); - curl_easy_setopt(curlp, CURLOPT_URL, url.c_str()); - curl_easy_setopt(curlp, CURLOPT_ERRORBUFFER, curl_error_buffer); - - // * Setup headers (don't forget to free them after the call!) - curl_slist* headers_list = NULL; - if (headers.isMap()) - { - LLSD::map_const_iterator iter = headers.beginMap(); - LLSD::map_const_iterator end = headers.endMap(); - for (; iter != end; ++iter) - { - std::ostringstream header; - header << iter->first << ": " << iter->second.asString() ; - LL_DEBUGS() << "header = " << header.str() << LL_ENDL; - headers_list = curl_slist_append(headers_list, header.str().c_str()); - } - } - - // * Setup specific method / "verb" for the URI (currently only GET and POST supported + poppy) - if (method == HTTP_GET) - { - curl_easy_setopt(curlp, CURLOPT_HTTPGET, 1); - } - else if (method == HTTP_POST) - { - curl_easy_setopt(curlp, CURLOPT_POST, 1); - //serialize to ostr then copy to str - need to because ostr ptr is unstable :( - std::ostringstream ostr; - LLSDSerialize::toXML(body, ostr); - body_str = ostr.str(); - curl_easy_setopt(curlp, CURLOPT_POSTFIELDS, body_str.c_str()); - //copied from PHP libs, correct? - headers_list = curl_slist_append(headers_list, - llformat("%s: %s", HTTP_OUT_HEADER_CONTENT_TYPE.c_str(), HTTP_CONTENT_LLSD_XML.c_str()).c_str()); - - // copied from llurlrequest.cpp - // it appears that apache2.2.3 or django in etch is busted. If - // we do not clear the expect header, we get a 500. May be - // limited to django/mod_wsgi. - headers_list = curl_slist_append(headers_list, llformat("%s:", HTTP_OUT_HEADER_EXPECT.c_str()).c_str()); - } - - // * Do the action using curl, handle results - LL_DEBUGS() << "HTTP body: " << body_str << LL_ENDL; - headers_list = curl_slist_append(headers_list, - llformat("%s: %s", HTTP_OUT_HEADER_ACCEPT.c_str(), HTTP_CONTENT_LLSD_XML.c_str()).c_str()); - CURLcode curl_result = curl_easy_setopt(curlp, CURLOPT_HTTPHEADER, headers_list); - if ( curl_result != CURLE_OK ) - { - LL_INFOS() << "Curl is hosed - can't add headers" << LL_ENDL; - } - - LLSD response = LLSD::emptyMap(); - S32 curl_success = curl_easy_perform(curlp); - S32 http_status = HTTP_INTERNAL_ERROR; - curl_easy_getinfo(curlp, CURLINFO_RESPONSE_CODE, &http_status); - response["status"] = http_status; - // if we get a non-404 and it's not a 200 OR maybe it is but you have error bits, - if ( http_status != HTTP_NOT_FOUND && (http_status != HTTP_OK || curl_success != 0) ) - { - // We expect 404s, don't spam for them. - LL_WARNS() << "CURL REQ URL: " << url << LL_ENDL; - LL_WARNS() << "CURL REQ METHOD TYPE: " << method << LL_ENDL; - LL_WARNS() << "CURL REQ HEADERS: " << headers.asString() << LL_ENDL; - LL_WARNS() << "CURL REQ BODY: " << body_str << LL_ENDL; - LL_WARNS() << "CURL HTTP_STATUS: " << http_status << LL_ENDL; - LL_WARNS() << "CURL ERROR: " << curl_error_buffer << LL_ENDL; - LL_WARNS() << "CURL ERROR BODY: " << http_buffer.asString() << LL_ENDL; - response["body"] = http_buffer.asString(); - } - else - { - response["body"] = http_buffer.asLLSD(); - LL_DEBUGS() << "CURL response: " << http_buffer.asString() << LL_ENDL; - } - - if(headers_list) - { // free the header list - curl_slist_free_all(headers_list); - } - - // * Cleanup - LLCurl::deleteEasyHandle(curlp); - return response; -} - -LLSD LLHTTPClient::blockingGet(const std::string& url) -{ - return blocking_request(url, HTTP_GET, LLSD()); -} - -LLSD LLHTTPClient::blockingPost(const std::string& url, const LLSD& body) -{ - return blocking_request(url, HTTP_POST, body); -} - -void LLHTTPClient::put( - const std::string& url, - const LLSD& body, - ResponderPtr responder, - const LLSD& headers, - const F32 timeout) -{ - request(url, HTTP_PUT, new LLSDInjector(body), responder, timeout, headers); -} - -void LLHTTPClient::patch( - const std::string& url, - const LLSD& body, - ResponderPtr responder, - const LLSD& headers, - const F32 timeout) -{ - request(url, HTTP_PATCH, new LLSDInjector(body), responder, timeout, headers); -} - -void LLHTTPClient::putRaw( - const std::string& url, - const U8* data, - S32 size, - ResponderPtr responder, - const LLSD& headers, - const F32 timeout) -{ - request(url, HTTP_PUT, new RawInjector(data, size), responder, timeout, headers); -} - -void LLHTTPClient::post( - const std::string& url, - const LLSD& body, - ResponderPtr responder, - const LLSD& headers, - const F32 timeout) -{ - request(url, HTTP_POST, new LLSDInjector(body), responder, timeout, headers); -} - -void LLHTTPClient::postRaw( - const std::string& url, - const U8* data, - S32 size, - ResponderPtr responder, - const LLSD& headers, - const F32 timeout) -{ - request(url, HTTP_POST, new RawInjector(data, size), responder, timeout, headers); -} - -void LLHTTPClient::postFile( - const std::string& url, - const std::string& filename, - ResponderPtr responder, - const LLSD& headers, - const F32 timeout) -{ - request(url, HTTP_POST, new FileInjector(filename), responder, timeout, headers); -} - -void LLHTTPClient::postFile( - const std::string& url, - const LLUUID& uuid, - LLAssetType::EType asset_type, - ResponderPtr responder, - const LLSD& headers, - const F32 timeout) -{ - request(url, HTTP_POST, new VFileInjector(uuid, asset_type), responder, timeout, headers); -} - -// static -void LLHTTPClient::del( - const std::string& url, - ResponderPtr responder, - const LLSD& headers, - const F32 timeout) -{ - request(url, HTTP_DELETE, NULL, responder, timeout, headers); -} - -// static -void LLHTTPClient::move( - const std::string& url, - const std::string& destination, - ResponderPtr responder, - const LLSD& hdrs, - const F32 timeout) -{ - LLSD headers = hdrs; - headers[HTTP_OUT_HEADER_DESTINATION] = destination; - request(url, HTTP_MOVE, NULL, responder, timeout, headers); -} - -// static -void LLHTTPClient::copy( - const std::string& url, - const std::string& destination, - ResponderPtr responder, - const LLSD& hdrs, - const F32 timeout) -{ - LLSD headers = hdrs; - headers[HTTP_OUT_HEADER_DESTINATION] = destination; - request(url, HTTP_COPY, NULL, responder, timeout, headers); -} - - -void LLHTTPClient::setPump(LLPumpIO& pump) -{ - theClientPump = &pump; -} - -bool LLHTTPClient::hasPump() -{ - return theClientPump != NULL; -} diff --git a/indra/llmessage/llhttpclient.h b/indra/llmessage/llhttpclient.h deleted file mode 100644 index fd48b4a743..0000000000 --- a/indra/llmessage/llhttpclient.h +++ /dev/null @@ -1,201 +0,0 @@ -/** - * @file llhttpclient.h - * @brief Declaration of classes for making HTTP client requests. - * - * $LicenseInfo:firstyear=2006&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_LLHTTPCLIENT_H -#define LL_LLHTTPCLIENT_H - -/** - * These classes represent the HTTP client framework. - */ - -#include <string> - -#include <boost/intrusive_ptr.hpp> -#include <openssl/x509_vfy.h> -#include "llurlrequest.h" -#include "llassettype.h" -#include "llcurl.h" -#include "lliopipe.h" - -extern const F32 HTTP_REQUEST_EXPIRY_SECS; - -class LLUUID; -class LLPumpIO; -class LLSD; - - -class LLHTTPClient -{ -public: - // class Responder moved to LLCurl - - // For convenience - typedef LLCurl::Responder Responder; - typedef LLCurl::ResponderPtr ResponderPtr; - - - /** @name non-blocking API */ - //@{ - static void head( - const std::string& url, - ResponderPtr, - const LLSD& headers = LLSD(), - const F32 timeout=HTTP_REQUEST_EXPIRY_SECS, - bool follow_redirects = true); - static void getByteRange(const std::string& url, S32 offset, S32 bytes, ResponderPtr, - const LLSD& headers=LLSD(), const F32 timeout=HTTP_REQUEST_EXPIRY_SECS, - bool follow_redirects = true); - static void get(const std::string& url, ResponderPtr, const LLSD& headers = LLSD(), - const F32 timeout=HTTP_REQUEST_EXPIRY_SECS, bool follow_redirects = true); - static void get(const std::string& url, const LLSD& query, ResponderPtr, const LLSD& headers = LLSD(), - const F32 timeout=HTTP_REQUEST_EXPIRY_SECS, bool follow_redirects = true); - - static void put( - const std::string& url, - const LLSD& body, - ResponderPtr, - const LLSD& headers = LLSD(), - const F32 timeout=HTTP_REQUEST_EXPIRY_SECS); - static void putRaw( - const std::string& url, - const U8* data, - S32 size, - ResponderPtr responder, - const LLSD& headers = LLSD(), - const F32 timeout=HTTP_REQUEST_EXPIRY_SECS); - - - static void patch( - const std::string& url, - const LLSD& body, - ResponderPtr, - const LLSD& headers = LLSD(), - const F32 timeout=HTTP_REQUEST_EXPIRY_SECS); - - static void getHeaderOnly(const std::string& url, ResponderPtr, const F32 timeout=HTTP_REQUEST_EXPIRY_SECS, - bool follow_redirects = true); - static void getHeaderOnly(const std::string& url, ResponderPtr, const LLSD& headers, - const F32 timeout=HTTP_REQUEST_EXPIRY_SECS, bool follow_redirects = true); - - static void post( - const std::string& url, - const LLSD& body, - ResponderPtr, - const LLSD& headers = LLSD(), - const F32 timeout=HTTP_REQUEST_EXPIRY_SECS); - /** Takes ownership of data and deletes it when sent */ - static void postRaw( - const std::string& url, - const U8* data, - S32 size, - ResponderPtr responder, - const LLSD& headers = LLSD(), - const F32 timeout=HTTP_REQUEST_EXPIRY_SECS); - static void postFile( - const std::string& url, - const std::string& filename, - ResponderPtr, - const LLSD& headers = LLSD(), - const F32 timeout=HTTP_REQUEST_EXPIRY_SECS); - static void postFile( - const std::string& url, - const LLUUID& uuid, - LLAssetType::EType asset_type, - ResponderPtr responder, - const LLSD& headers = LLSD(), - const F32 timeout=HTTP_REQUEST_EXPIRY_SECS); - - static void del( - const std::string& url, - ResponderPtr responder, - const LLSD& headers = LLSD(), - const F32 timeout=HTTP_REQUEST_EXPIRY_SECS); - ///< sends a DELETE method, but we can't call it delete in c++ - - /** - * @brief Send a MOVE webdav method - * - * @param url The complete serialized (and escaped) url to get. - * @param destination The complete serialized destination url. - * @param responder The responder that will handle the result. - * @param headers A map of key:value headers to pass to the request - * @param timeout The number of seconds to give the server to respond. - */ - static void move( - const std::string& url, - const std::string& destination, - ResponderPtr responder, - const LLSD& headers = LLSD(), - const F32 timeout=HTTP_REQUEST_EXPIRY_SECS); - - /** - * @brief Send a COPY webdav method - * - * @param url The complete serialized (and escaped) url to get. - * @param destination The complete serialized destination url. - * @param responder The responder that will handle the result. - * @param headers A map of key:value headers to pass to the request - * @param timeout The number of seconds to give the server to respond. - */ - static void copy( - const std::string& url, - const std::string& destination, - ResponderPtr responder, - const LLSD& headers = LLSD(), - const F32 timeout=HTTP_REQUEST_EXPIRY_SECS); - - //@} - - /** - * @brief Blocking HTTP get that returns an LLSD map of status and body. - * - * @param url the complete serialized (and escaped) url to get - * @return An LLSD of { 'status':status, 'body':payload } - */ - static LLSD blockingGet(const std::string& url); - - /** - * @brief Blocking HTTP POST that returns an LLSD map of status and body. - * - * @param url the complete serialized (and escaped) url to get - * @param body the LLSD post body - * @return An LLSD of { 'status':status (an int), 'body':payload (an LLSD) } - */ - static LLSD blockingPost(const std::string& url, const LLSD& body); - - - static void setPump(LLPumpIO& pump); - ///< must be called before any of the above calls are made - static bool hasPump(); - - static void setCertVerifyCallback(LLURLRequest::SSLCertVerifyCallback callback); - static LLURLRequest::SSLCertVerifyCallback getCertVerifyCallback() { return mCertVerifyCallback; } - -protected: - static LLURLRequest::SSLCertVerifyCallback mCertVerifyCallback; -}; - -#endif // LL_LLHTTPCLIENT_H diff --git a/indra/llmessage/llhttpclientadapter.cpp b/indra/llmessage/llhttpclientadapter.cpp deleted file mode 100644 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 100644 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 deleted file mode 100644 index 12a3857a61..0000000000 --- a/indra/llmessage/llhttpclientinterface.h +++ /dev/null @@ -1,45 +0,0 @@ -/** - * @file llhttpclientinterface.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_LLHTTPCLIENTINTERFACE_H -#define LL_LLHTTPCLIENTINTERFACE_H - -#include "linden_common.h" -#include "llcurl.h" - -#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; -}; - -#endif // LL_LLHTTPCLIENTINTERFACE_H - diff --git a/indra/llmessage/llhttpsdhandler.cpp b/indra/llmessage/llhttpsdhandler.cpp new file mode 100644 index 0000000000..648bc5cfd8 --- /dev/null +++ b/indra/llmessage/llhttpsdhandler.cpp @@ -0,0 +1,77 @@ +/** +* @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() +{ +} + +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); + } + +} diff --git a/indra/llmessage/llhttpsdhandler.h b/indra/llmessage/llhttpsdhandler.h new file mode 100644 index 0000000000..ce40bdfc08 --- /dev/null +++ b/indra/llmessage/llhttpsdhandler.h @@ -0,0 +1,55 @@ +/** +* @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(); + + virtual void onSuccess(LLCore::HttpResponse * response, const LLSD &content) = 0; + virtual void onFailure(LLCore::HttpResponse * response, LLCore::HttpStatus status) = 0; + + +}; + +#endif diff --git a/indra/llmessage/llhttpsender.cpp b/indra/llmessage/llhttpsender.cpp deleted file mode 100644 index 5363088d79..0000000000 --- a/indra/llmessage/llhttpsender.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/** - * @file llhttpsender.cpp - * @brief Abstracts details of sending messages via HTTP. - * - * $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 "linden_common.h" - -#include "llhttpsender.h" - -#include <map> -#include <sstream> - -#include "llhost.h" -#include "llsd.h" - -namespace -{ - typedef std::map<LLHost, LLHTTPSender*> SenderMap; - static SenderMap senderMap; - static LLPointer<LLHTTPSender> defaultSender(new LLHTTPSender()); -} - -//virtual -LLHTTPSender::~LLHTTPSender() -{ -} - -//virtual -void LLHTTPSender::send(const LLHost& host, const std::string& name, - const LLSD& body, - LLHTTPClient::ResponderPtr response) const -{ - // Default implementation inserts sender, message and sends HTTP POST - std::ostringstream stream; - stream << "http://" << host << "/trusted-message/" << name; - LL_INFOS() << "LLHTTPSender::send: POST to " << stream.str() << LL_ENDL; - LLHTTPClient::post(stream.str(), body, response); -} - -//static -void LLHTTPSender::setSender(const LLHost& host, LLHTTPSender* sender) -{ - LL_INFOS() << "LLHTTPSender::setSender " << host << LL_ENDL; - senderMap[host] = sender; -} - -//static -const LLHTTPSender& LLHTTPSender::getSender(const LLHost& host) -{ - SenderMap::const_iterator iter = senderMap.find(host); - if(iter == senderMap.end()) - { - return *defaultSender; - } - return *(iter->second); -} - -//static -void LLHTTPSender::clearSender(const LLHost& host) -{ - SenderMap::iterator iter = senderMap.find(host); - if(iter != senderMap.end()) - { - delete iter->second; - senderMap.erase(iter); - } -} - -//static -void LLHTTPSender::setDefaultSender(LLHTTPSender* sender) -{ - defaultSender = sender; -} diff --git a/indra/llmessage/llhttpsender.h b/indra/llmessage/llhttpsender.h deleted file mode 100644 index ff8fa2f95b..0000000000 --- a/indra/llmessage/llhttpsender.h +++ /dev/null @@ -1,59 +0,0 @@ -/** - * @file llhttpsender.h - * @brief Abstracts details of sending messages via HTTP. - * - * $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_HTTP_SENDER_H -#define LL_HTTP_SENDER_H - -#include "llhttpclient.h" - -class LLHost; -class LLSD; - -class LLHTTPSender : public LLThreadSafeRefCount -{ - public: - - virtual ~LLHTTPSender(); - - /** @brief Send message to host with body, call response when done */ - virtual void send(const LLHost& host, - const std::string& message, const LLSD& body, - LLHTTPClient::ResponderPtr response) const; - - /** @brief Set sender for host, takes ownership of sender. */ - static void setSender(const LLHost& host, LLHTTPSender* sender); - - /** @brief Get sender for host, retains ownership of returned sender. */ - static const LLHTTPSender& getSender(const LLHost& host); - - /** @brief Clear sender for host. */ - static void clearSender(const LLHost& host); - - /** @brief Set default sender, takes ownership of sender. */ - static void setDefaultSender(LLHTTPSender* sender); -}; - -#endif // LL_HTTP_SENDER_H diff --git a/indra/llmessage/llproxy.cpp b/indra/llmessage/llproxy.cpp index 9b8d19cc3e..537efa69d8 100644 --- a/indra/llmessage/llproxy.cpp +++ b/indra/llmessage/llproxy.cpp @@ -30,9 +30,8 @@ #include <string> #include <curl/curl.h> - +#include "httpcommon.h" #include "llapr.h" -#include "llcurl.h" #include "llhost.h" // Static class variable instances @@ -408,16 +407,6 @@ void LLProxy::cleanupClass() deleteSingleton(); } -void LLProxy::applyProxySettings(LLCurlEasyRequest* handle) -{ - applyProxySettings(handle->getEasy()); -} - -void LLProxy::applyProxySettings(LLCurl::Easy* handle) -{ - applyProxySettings(handle->getCurlHandle()); -} - /** * @brief Apply proxy settings to a CuRL request if an HTTP proxy is enabled. * @@ -439,21 +428,21 @@ void LLProxy::applyProxySettings(CURL* handle) // Now test again to verify that the proxy wasn't disabled between the first check and the lock. if (mHTTPProxyEnabled) { - LLCurlFF::check_easy_code(curl_easy_setopt(handle, CURLOPT_PROXY, mHTTPProxy.getIPString().c_str())); - LLCurlFF::check_easy_code(curl_easy_setopt(handle, CURLOPT_PROXYPORT, mHTTPProxy.getPort())); + LLCore::LLHttp::check_curl_code(curl_easy_setopt(handle, CURLOPT_PROXY, mHTTPProxy.getIPString().c_str()), CURLOPT_PROXY); + LLCore::LLHttp::check_curl_code(curl_easy_setopt(handle, CURLOPT_PROXYPORT, mHTTPProxy.getPort()), CURLOPT_PROXYPORT); if (mProxyType == LLPROXY_SOCKS) { - LLCurlFF::check_easy_code(curl_easy_setopt(handle, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5)); + LLCore::LLHttp::check_curl_code(curl_easy_setopt(handle, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5), CURLOPT_PROXYTYPE); if (mAuthMethodSelected == METHOD_PASSWORD) { std::string auth_string = mSocksUsername + ":" + mSocksPassword; - LLCurlFF::check_easy_code(curl_easy_setopt(handle, CURLOPT_PROXYUSERPWD, auth_string.c_str())); + LLCore::LLHttp::check_curl_code(curl_easy_setopt(handle, CURLOPT_PROXYUSERPWD, auth_string.c_str()), CURLOPT_PROXYUSERPWD); } } else { - LLCurlFF::check_easy_code(curl_easy_setopt(handle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP)); + LLCore::LLHttp::check_curl_code(curl_easy_setopt(handle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP), CURLOPT_PROXYTYPE); } } } diff --git a/indra/llmessage/llproxy.h b/indra/llmessage/llproxy.h index a919370540..bd23dd39de 100644 --- a/indra/llmessage/llproxy.h +++ b/indra/llmessage/llproxy.h @@ -27,12 +27,12 @@ #ifndef LL_PROXY_H #define LL_PROXY_H -#include "llcurl.h" #include "llhost.h" #include "lliosocket.h" #include "llmemory.h" #include "llsingleton.h" #include "llthread.h" +#include <curl/curl.h> #include <string> // SOCKS error codes returned from the StartProxy method @@ -208,16 +208,13 @@ enum LLSocks5AuthType * thread-safe method to apply those options to a curl request * (LLProxy::applyProxySettings()). This method is overloaded * to accommodate the various abstraction libcurl layers that exist - * throughout the viewer (LLCurlEasyRequest, LLCurl::Easy, and CURL). - * - * If you are working with LLCurl or LLCurlEasyRequest objects, - * the configured proxy settings will be applied in the constructors - * of those request handles. If you are working with CURL objects - * directly, you will need to pass the handle of the request to - * applyProxySettings() before issuing the request. + * throughout the viewer (CURL). * * To ensure thread safety, all LLProxy members that relate to the HTTP * proxy require the LLProxyMutex to be locked before accessing. + * + * *TODO$: This should be moved into the LLCore::Http space. + * */ class LLProxy: public LLSingleton<LLProxy> { @@ -252,9 +249,6 @@ public: // Apply the current proxy settings to a curl request. Doesn't do anything if mHTTPProxyEnabled is false. // Safe to call from any thread. void applyProxySettings(CURL* handle); - void applyProxySettings(LLCurl::Easy* handle); - void applyProxySettings(LLCurlEasyRequest* handle); - // Start a connection to the SOCKS 5 proxy. Call from main thread only. S32 startSOCKSProxy(LLHost host); diff --git a/indra/llmessage/llsdmessage.cpp b/indra/llmessage/llsdmessage.cpp deleted file mode 100644 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 100644 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.cpp b/indra/llmessage/llsdrpcclient.cpp deleted file mode 100644 index eb773ceb3a..0000000000 --- a/indra/llmessage/llsdrpcclient.cpp +++ /dev/null @@ -1,245 +0,0 @@ -/** - * @file llsdrpcclient.cpp - * @author Phoenix - * @date 2005-11-05 - * @brief Implementation of the llsd client classes. - * - * $LicenseInfo:firstyear=2005&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" -#include "llsdrpcclient.h" - -#include "llbufferstream.h" -#include "llfasttimer.h" -#include "llfiltersd2xmlrpc.h" -#include "llpumpio.h" -#include "llsd.h" -#include "llsdserialize.h" -#include "llurlrequest.h" - -/** - * String constants - */ -static std::string LLSDRPC_RESPONSE_NAME("response"); -static std::string LLSDRPC_FAULT_NAME("fault"); - -/** - * LLSDRPCResponse - */ -LLSDRPCResponse::LLSDRPCResponse() : - mIsError(false), - mIsFault(false) -{ -} - -// virtual -LLSDRPCResponse::~LLSDRPCResponse() -{ -} - -bool LLSDRPCResponse::extractResponse(const LLSD& sd) -{ - bool rv = true; - if(sd.has(LLSDRPC_RESPONSE_NAME)) - { - mReturnValue = sd[LLSDRPC_RESPONSE_NAME]; - mIsFault = false; - } - else if(sd.has(LLSDRPC_FAULT_NAME)) - { - mReturnValue = sd[LLSDRPC_FAULT_NAME]; - mIsFault = true; - } - else - { - mReturnValue.clear(); - mIsError = true; - rv = false; - } - return rv; -} - -static LLTrace::BlockTimerStatHandle FTM_SDRPC_RESPONSE("SDRPC Response"); - -// virtual -LLIOPipe::EStatus LLSDRPCResponse::process_impl( - const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, - bool& eos, - LLSD& context, - LLPumpIO* pump) -{ - LL_RECORD_BLOCK_TIME(FTM_SDRPC_RESPONSE); - PUMP_DEBUG; - if(mIsError) - { - error(pump); - } - else if(mIsFault) - { - fault(pump); - } - else - { - response(pump); - } - PUMP_DEBUG; - return STATUS_DONE; -} - -/** - * LLSDRPCClient - */ - -LLSDRPCClient::LLSDRPCClient() : - mState(STATE_NONE), - mQueue(EPBQ_PROCESS) -{ -} - -// virtual -LLSDRPCClient::~LLSDRPCClient() -{ -} - -bool LLSDRPCClient::call( - const std::string& uri, - const std::string& method, - const LLSD& parameter, - LLSDRPCResponse* response, - EPassBackQueue queue) -{ - //LL_INFOS() << "RPC: " << uri << "." << method << "(" << *parameter << ")" - // << LL_ENDL; - if(method.empty() || !response) - { - return false; - } - mState = STATE_READY; - mURI.assign(uri); - std::stringstream req; - req << LLSDRPC_REQUEST_HEADER_1 << method - << LLSDRPC_REQUEST_HEADER_2; - LLSDSerialize::toNotation(parameter, req); - req << LLSDRPC_REQUEST_FOOTER; - mRequest = req.str(); - mQueue = queue; - mResponse = response; - return true; -} - -bool LLSDRPCClient::call( - const std::string& uri, - const std::string& method, - const std::string& parameter, - LLSDRPCResponse* response, - EPassBackQueue queue) -{ - //LL_INFOS() << "RPC: " << uri << "." << method << "(" << parameter << ")" - // << LL_ENDL; - if(method.empty() || parameter.empty() || !response) - { - return false; - } - mState = STATE_READY; - mURI.assign(uri); - std::stringstream req; - req << LLSDRPC_REQUEST_HEADER_1 << method - << LLSDRPC_REQUEST_HEADER_2 << parameter - << LLSDRPC_REQUEST_FOOTER; - mRequest = req.str(); - mQueue = queue; - mResponse = response; - return true; -} - -static LLTrace::BlockTimerStatHandle FTM_PROCESS_SDRPC_CLIENT("SDRPC Client"); - -// virtual -LLIOPipe::EStatus LLSDRPCClient::process_impl( - const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, - bool& eos, - LLSD& context, - LLPumpIO* pump) -{ - LL_RECORD_BLOCK_TIME(FTM_PROCESS_SDRPC_CLIENT); - PUMP_DEBUG; - if((STATE_NONE == mState) || (!pump)) - { - // You should have called the call() method already. - return STATUS_PRECONDITION_NOT_MET; - } - EStatus rv = STATUS_DONE; - switch(mState) - { - case STATE_READY: - { - PUMP_DEBUG; -// LL_DEBUGS() << "LLSDRPCClient::process_impl STATE_READY" << LL_ENDL; - buffer->append( - channels.out(), - (U8*)mRequest.c_str(), - mRequest.length()); - context[CONTEXT_DEST_URI_SD_LABEL] = mURI; - mState = STATE_WAITING_FOR_RESPONSE; - break; - } - case STATE_WAITING_FOR_RESPONSE: - { - PUMP_DEBUG; - // The input channel has the sd response in it. - //LL_DEBUGS() << "LLSDRPCClient::process_impl STATE_WAITING_FOR_RESPONSE" - // << LL_ENDL; - LLBufferStream resp(channels, buffer.get()); - LLSD sd; - LLSDSerialize::fromNotation(sd, resp, buffer->count(channels.in())); - LLSDRPCResponse* response = (LLSDRPCResponse*)mResponse.get(); - if (!response) - { - mState = STATE_DONE; - break; - } - response->extractResponse(sd); - if(EPBQ_PROCESS == mQueue) - { - LLPumpIO::chain_t chain; - chain.push_back(mResponse); - pump->addChain(chain, DEFAULT_CHAIN_EXPIRY_SECS); - } - else - { - pump->respond(mResponse.get()); - } - mState = STATE_DONE; - break; - } - case STATE_DONE: - default: - PUMP_DEBUG; - LL_INFOS() << "invalid state to process" << LL_ENDL; - rv = STATUS_ERROR; - break; - } - return rv; -} diff --git a/indra/llmessage/llsdrpcclient.h b/indra/llmessage/llsdrpcclient.h deleted file mode 100644 index c4e0333ca3..0000000000 --- a/indra/llmessage/llsdrpcclient.h +++ /dev/null @@ -1,323 +0,0 @@ -/** - * @file llsdrpcclient.h - * @author Phoenix - * @date 2005-11-05 - * @brief Implementation and helpers for structure data RPC clients. - * - * $LicenseInfo:firstyear=2005&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_LLSDRPCCLIENT_H -#define LL_LLSDRPCCLIENT_H - -/** - * This file declares classes to encapsulate a basic structured data - * remote procedure client. - */ - -#include "llchainio.h" -#include "llfiltersd2xmlrpc.h" -#include "lliopipe.h" -#include "llurlrequest.h" - -/** - * @class LLSDRPCClientResponse - * @brief Abstract base class to represent a response from an SD server. - * - * This is used as a base class for callbacks generated from an - * structured data remote procedure call. The - * <code>extractResponse</code> method will deal with the llsdrpc method - * call overhead, and keep track of what to call during the next call - * into <code>process</code>. If you use this as a base class, you - * need to implement <code>response</code>, <code>fault</code>, and - * <code>error</code> to do something useful. When in those methods, - * you can parse and utilize the mReturnValue member data. - */ -class LLSDRPCResponse : public LLIOPipe -{ -public: - LLSDRPCResponse(); - virtual ~LLSDRPCResponse(); - - /** - * @brief This method extracts the response out of the sd passed in - * - * Any appropriate data found in the sd passed in will be - * extracted and managed by this object - not copied or cloned. It - * will still be up to the caller to delete the pointer passed in. - * @param sd The raw structured data response from the remote server. - * @return Returns true if this was able to parse the structured data. - */ - bool extractResponse(const LLSD& sd); - -protected: - /** - * @brief Method called when the response is ready. - */ - virtual bool response(LLPumpIO* pump) = 0; - - /** - * @brief Method called when a fault is generated by the remote server. - */ - virtual bool fault(LLPumpIO* pump) = 0; - - /** - * @brief Method called when there was an error - */ - virtual bool error(LLPumpIO* pump) = 0; - -protected: - /* @name LLIOPipe virtual implementations - */ - //@{ - /** - * @brief Process the data in buffer - */ - virtual EStatus process_impl( - const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, - bool& eos, - LLSD& context, - LLPumpIO* pump); - //@} - -protected: - LLSD mReturnValue; - bool mIsError; - bool mIsFault; -}; - -/** - * @class LLSDRPCClient - * @brief Client class for a structured data remote procedure call. - * - * This class helps deal with making structured data calls to a remote - * server. You can visualize the calls as: - * <code> - * response = uri.method(parameter) - * </code> - * where you pass in everything to <code>call</code> and this class - * takes care of the rest of the details. - * In typical usage, you will derive a class from this class and - * provide an API more useful for the specific application at - * hand. For example, if you were writing a service to send an instant - * message, you could create an API for it to send the messsage, and - * that class would do the work of translating it into the method and - * parameter, find the destination, and invoke <code>call</call> with - * a useful implementation of LLSDRPCResponse passed in to handle the - * response from the network. - */ -class LLSDRPCClient : public LLIOPipe -{ -public: - LLSDRPCClient(); - virtual ~LLSDRPCClient(); - - /** - * @brief Enumeration for tracking which queue to process the - * response. - */ - enum EPassBackQueue - { - EPBQ_PROCESS, - EPBQ_CALLBACK, - }; - - /** - * @brief Call a method on a remote LLSDRPCServer - * - * @param uri The remote object to call, eg, - * http://localhost/usher. If you are using a factory with a fixed - * url, the uri passed in will probably be ignored. - * @param method The method to call on the remote object - * @param parameter The parameter to pass into the remote - * object. It is up to the caller to delete the value passed in. - * @param response The object which gets the response. - * @param queue Specifies to call the response on the process or - * callback queue. - * @return Returns true if this object will be able to make the RPC call. - */ - bool call( - const std::string& uri, - const std::string& method, - const LLSD& parameter, - LLSDRPCResponse* response, - EPassBackQueue queue); - - /** - * @brief Call a method on a remote LLSDRPCServer - * - * @param uri The remote object to call, eg, - * http://localhost/usher. If you are using a factory with a fixed - * url, the uri passed in will probably be ignored. - * @param method The method to call on the remote object - * @param parameter The seriailized parameter to pass into the - * remote object. - * @param response The object which gets the response. - * @param queue Specifies to call the response on the process or - * callback queue. - * @return Returns true if this object will be able to make the RPC call. - */ - bool call( - const std::string& uri, - const std::string& method, - const std::string& parameter, - LLSDRPCResponse* response, - EPassBackQueue queue); - -protected: - /** - * @brief Enumeration for tracking client state. - */ - enum EState - { - STATE_NONE, - STATE_READY, - STATE_WAITING_FOR_RESPONSE, - STATE_DONE - }; - - /* @name LLIOPipe virtual implementations - */ - //@{ - /** - * @brief Process the data in buffer - */ - virtual EStatus process_impl( - const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, - bool& eos, - LLSD& context, - LLPumpIO* pump); - //@} - -protected: - EState mState; - std::string mURI; - std::string mRequest; - EPassBackQueue mQueue; - LLIOPipe::ptr_t mResponse; -}; - -/** - * @class LLSDRPCClientFactory - * @brief Basic implementation for making an SD RPC client factory - * - * This class eases construction of a basic sd rpc client. Here is an - * example of it's use: - * <code> - * class LLUsefulService : public LLService { ... } - * LLService::registerCreator( - * "useful", - * LLService::creator_t(new LLSDRPCClientFactory<LLUsefulService>)) - * </code> - */ -template<class Client> -class LLSDRPCClientFactory : public LLChainIOFactory -{ -public: - LLSDRPCClientFactory() {} - LLSDRPCClientFactory(const std::string& fixed_url) : mURL(fixed_url) {} - virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const - { - LL_DEBUGS() << "LLSDRPCClientFactory::build" << LL_ENDL; - LLURLRequest* http(new LLURLRequest(HTTP_POST)); - if(!http->isValid()) - { - LL_WARNS() << "Creating LLURLRequest failed." << LL_ENDL ; - delete http; - return false; - } - - LLIOPipe::ptr_t service(new Client); - chain.push_back(service); - LLIOPipe::ptr_t http_pipe(http); - http->addHeader(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_TEXT_LLSD); - if(mURL.empty()) - { - chain.push_back(LLIOPipe::ptr_t(new LLContextURLExtractor(http))); - } - else - { - http->setURL(mURL); - } - chain.push_back(http_pipe); - chain.push_back(service); - return true; - } -protected: - std::string mURL; -}; - -/** - * @class LLXMLSDRPCClientFactory - * @brief Basic implementation for making an XMLRPC to SD RPC client factory - * - * This class eases construction of a basic sd rpc client which uses - * xmlrpc as a serialization grammar. Here is an example of it's use: - * <code> - * class LLUsefulService : public LLService { ... } - * LLService::registerCreator( - * "useful", - * LLService::creator_t(new LLXMLSDRPCClientFactory<LLUsefulService>)) - * </code> - */ -template<class Client> -class LLXMLSDRPCClientFactory : public LLChainIOFactory -{ -public: - LLXMLSDRPCClientFactory() {} - LLXMLSDRPCClientFactory(const std::string& fixed_url) : mURL(fixed_url) {} - virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const - { - LL_DEBUGS() << "LLXMLSDRPCClientFactory::build" << LL_ENDL; - - LLURLRequest* http(new LLURLRequest(HTTP_POST)); - if(!http->isValid()) - { - LL_WARNS() << "Creating LLURLRequest failed." << LL_ENDL ; - delete http; - return false ; - } - LLIOPipe::ptr_t service(new Client); - chain.push_back(service); - LLIOPipe::ptr_t http_pipe(http); - http->addHeader(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_TEXT_XML); - if(mURL.empty()) - { - chain.push_back(LLIOPipe::ptr_t(new LLContextURLExtractor(http))); - } - else - { - http->setURL(mURL); - } - chain.push_back(LLIOPipe::ptr_t(new LLFilterSD2XMLRPCRequest(NULL))); - chain.push_back(http_pipe); - chain.push_back(LLIOPipe::ptr_t(new LLFilterXMLRPCResponse2LLSD)); - chain.push_back(service); - return true; - } -protected: - std::string mURL; -}; - -#endif // LL_LLSDRPCCLIENT_H diff --git a/indra/llmessage/llsdrpcserver.cpp b/indra/llmessage/llsdrpcserver.cpp deleted file mode 100644 index c3ed19889e..0000000000 --- a/indra/llmessage/llsdrpcserver.cpp +++ /dev/null @@ -1,339 +0,0 @@ -/** - * @file llsdrpcserver.cpp - * @author Phoenix - * @date 2005-10-11 - * @brief Implementation of the LLSDRPCServer and related classes. - * - * $LicenseInfo:firstyear=2005&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" -#include "llsdrpcserver.h" - -#include "llbuffer.h" -#include "llbufferstream.h" -#include "llfasttimer.h" -#include "llpumpio.h" -#include "llsdserialize.h" -#include "llstl.h" - -static const char FAULT_PART_1[] = "{'fault':{'code':i"; -static const char FAULT_PART_2[] = ", 'description':'"; -static const char FAULT_PART_3[] = "'}}"; - -static const char RESPONSE_PART_1[] = "{'response':"; -static const char RESPONSE_PART_2[] = "}"; - -static const S32 FAULT_GENERIC = 1000; -static const S32 FAULT_METHOD_NOT_FOUND = 1001; - -static const std::string LLSDRPC_METHOD_SD_NAME("method"); -static const std::string LLSDRPC_PARAMETER_SD_NAME("parameter"); - - -/** - * LLSDRPCServer - */ -LLSDRPCServer::LLSDRPCServer() : - mState(LLSDRPCServer::STATE_NONE), - mPump(NULL), - mLock(0) -{ -} - -LLSDRPCServer::~LLSDRPCServer() -{ - std::for_each( - mMethods.begin(), - mMethods.end(), - llcompose1( - DeletePointerFunctor<LLSDRPCMethodCallBase>(), - llselect2nd<method_map_t::value_type>())); - std::for_each( - mCallbackMethods.begin(), - mCallbackMethods.end(), - llcompose1( - DeletePointerFunctor<LLSDRPCMethodCallBase>(), - llselect2nd<method_map_t::value_type>())); -} - - -// virtual -ESDRPCSStatus LLSDRPCServer::deferredResponse( - const LLChannelDescriptors& channels, - LLBufferArray* data) { - // subclass should provide a sane implementation - return ESDRPCS_DONE; -} - -void LLSDRPCServer::clearLock() -{ - if(mLock && mPump) - { - mPump->clearLock(mLock); - mPump = NULL; - mLock = 0; - } -} - -static LLTrace::BlockTimerStatHandle FTM_PROCESS_SDRPC_SERVER("SDRPC Server"); - -// virtual -LLIOPipe::EStatus LLSDRPCServer::process_impl( - const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, - bool& eos, - LLSD& context, - LLPumpIO* pump) -{ - LL_RECORD_BLOCK_TIME(FTM_PROCESS_SDRPC_SERVER); - PUMP_DEBUG; -// LL_DEBUGS() << "LLSDRPCServer::process_impl" << LL_ENDL; - // Once we have all the data, We need to read the sd on - // the the in channel, and respond on the out channel - if(!eos) return STATUS_BREAK; - if(!pump || !buffer) return STATUS_PRECONDITION_NOT_MET; - - std::string method_name; - LLIOPipe::EStatus status = STATUS_DONE; - - switch(mState) - { - case STATE_DEFERRED: - PUMP_DEBUG; - if(ESDRPCS_DONE != deferredResponse(channels, buffer.get())) - { - buildFault( - channels, - buffer.get(), - FAULT_GENERIC, - "deferred response failed."); - } - mState = STATE_DONE; - return STATUS_DONE; - - case STATE_DONE: -// LL_DEBUGS() << "STATE_DONE" << LL_ENDL; - break; - case STATE_CALLBACK: -// LL_DEBUGS() << "STATE_CALLBACK" << LL_ENDL; - PUMP_DEBUG; - method_name = mRequest[LLSDRPC_METHOD_SD_NAME].asString(); - if(!method_name.empty() && mRequest.has(LLSDRPC_PARAMETER_SD_NAME)) - { - if(ESDRPCS_DONE != callbackMethod( - method_name, - mRequest[LLSDRPC_PARAMETER_SD_NAME], - channels, - buffer.get())) - { - buildFault( - channels, - buffer.get(), - FAULT_GENERIC, - "Callback method call failed."); - } - } - else - { - // this should never happen, since we should not be in - // this state unless we originally found a method and - // params during the first call to process. - buildFault( - channels, - buffer.get(), - FAULT_GENERIC, - "Invalid LLSDRPC sever state - callback without method."); - } - pump->clearLock(mLock); - mLock = 0; - mState = STATE_DONE; - break; - case STATE_NONE: -// LL_DEBUGS() << "STATE_NONE" << LL_ENDL; - default: - { - // First time we got here - process the SD request, and call - // the method. - PUMP_DEBUG; - LLBufferStream istr(channels, buffer.get()); - mRequest.clear(); - LLSDSerialize::fromNotation( - mRequest, - istr, - buffer->count(channels.in())); - - // { 'method':'...', 'parameter': ... } - method_name = mRequest[LLSDRPC_METHOD_SD_NAME].asString(); - if(!method_name.empty() && mRequest.has(LLSDRPC_PARAMETER_SD_NAME)) - { - ESDRPCSStatus rv = callMethod( - method_name, - mRequest[LLSDRPC_PARAMETER_SD_NAME], - channels, - buffer.get()); - switch(rv) - { - case ESDRPCS_DEFERRED: - mPump = pump; - mLock = pump->setLock(); - mState = STATE_DEFERRED; - status = STATUS_BREAK; - break; - - case ESDRPCS_CALLBACK: - { - mState = STATE_CALLBACK; - LLPumpIO::LLLinkInfo link; - link.mPipe = LLIOPipe::ptr_t(this); - link.mChannels = channels; - LLPumpIO::links_t links; - links.push_back(link); - pump->respond(links, buffer, context); - mLock = pump->setLock(); - status = STATUS_BREAK; - break; - } - case ESDRPCS_DONE: - mState = STATE_DONE; - break; - case ESDRPCS_ERROR: - default: - buildFault( - channels, - buffer.get(), - FAULT_GENERIC, - "Method call failed."); - break; - } - } - else - { - // send a fault - buildFault( - channels, - buffer.get(), - FAULT_GENERIC, - "Unable to find method and parameter in request."); - } - break; - } - } - - PUMP_DEBUG; - return status; -} - -// virtual -ESDRPCSStatus LLSDRPCServer::callMethod( - const std::string& method, - const LLSD& params, - const LLChannelDescriptors& channels, - LLBufferArray* response) -{ - // Try to find the method in the method table. - ESDRPCSStatus rv = ESDRPCS_DONE; - method_map_t::iterator it = mMethods.find(method); - if(it != mMethods.end()) - { - rv = (*it).second->call(params, channels, response); - } - else - { - it = mCallbackMethods.find(method); - if(it == mCallbackMethods.end()) - { - // method not found. - std::ostringstream message; - message << "rpc server unable to find method: " << method; - buildFault( - channels, - response, - FAULT_METHOD_NOT_FOUND, - message.str()); - } - else - { - // we found it in the callback methods - tell the process - // to coordinate calling on the pump callback. - return ESDRPCS_CALLBACK; - } - } - return rv; -} - -// virtual -ESDRPCSStatus LLSDRPCServer::callbackMethod( - const std::string& method, - const LLSD& params, - const LLChannelDescriptors& channels, - LLBufferArray* response) -{ - // Try to find the method in the callback method table. - ESDRPCSStatus rv = ESDRPCS_DONE; - method_map_t::iterator it = mCallbackMethods.find(method); - if(it != mCallbackMethods.end()) - { - rv = (*it).second->call(params, channels, response); - } - else - { - std::ostringstream message; - message << "pcserver unable to find callback method: " << method; - buildFault( - channels, - response, - FAULT_METHOD_NOT_FOUND, - message.str()); - } - return rv; -} - -// static -void LLSDRPCServer::buildFault( - const LLChannelDescriptors& channels, - LLBufferArray* data, - S32 code, - const std::string& msg) -{ - LLBufferStream ostr(channels, data); - ostr << FAULT_PART_1 << code << FAULT_PART_2 << msg << FAULT_PART_3; - LL_INFOS() << "LLSDRPCServer::buildFault: " << code << ", " << msg << LL_ENDL; -} - -// static -void LLSDRPCServer::buildResponse( - const LLChannelDescriptors& channels, - LLBufferArray* data, - const LLSD& response) -{ - LLBufferStream ostr(channels, data); - ostr << RESPONSE_PART_1; - LLSDSerialize::toNotation(response, ostr); - ostr << RESPONSE_PART_2; -#if LL_DEBUG - std::ostringstream debug_ostr; - debug_ostr << "LLSDRPCServer::buildResponse: "; - LLSDSerialize::toNotation(response, debug_ostr); - LL_INFOS() << debug_ostr.str() << LL_ENDL; -#endif -} diff --git a/indra/llmessage/llsdrpcserver.h b/indra/llmessage/llsdrpcserver.h deleted file mode 100644 index 415bd31c26..0000000000 --- a/indra/llmessage/llsdrpcserver.h +++ /dev/null @@ -1,360 +0,0 @@ -/** - * @file llsdrpcserver.h - * @author Phoenix - * @date 2005-10-11 - * @brief Declaration of the structured data remote procedure call server. - * - * $LicenseInfo:firstyear=2005&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_LLSDRPCSERVER_H -#define LL_LLSDRPCSERVER_H - -/** - * I've set this up to be pretty easy to use when you want to make a - * structured data rpc server which responds to methods by - * name. Derive a class from the LLSDRPCServer, and during - * construction (or initialization if you have the luxury) map method - * names to pointers to member functions. This will look a lot like: - * - * <code> - * class LLMessageAgents : public LLSDRPCServer {<br> - * public:<br> - * typedef LLSDRPCServer<LLUsher> mem_fn_t;<br> - * LLMessageAgents() {<br> - * mMethods["message"] = new mem_fn_t(this, &LLMessageAgents::rpc_IM);<br> - * mMethods["alert"] = new mem_fn_t(this, &LLMessageAgents::rpc_Alrt);<br> - * }<br> - * protected:<br> - * rpc_IM(const LLSD& params, - * const LLChannelDescriptors& channels, - * LLBufferArray* data) - * {...}<br> - * rpc_Alert(const LLSD& params, - * const LLChannelDescriptors& channels, - * LLBufferArray* data) - * {...}<br> - * };<br> - * </code> - * - * The params are an array where each element in the array is a single - * parameter in the call. - * - * It is up to you to pack a valid serialized llsd response into the - * data object passed into the method, but you can use the helper - * methods below to help. - */ - -#include <map> -#include "lliopipe.h" -#include "lliohttpserver.h" -#include "llfiltersd2xmlrpc.h" - -class LLSD; - -/** - * @brief Enumeration for specifying server method call status. This - * enumeration controls how the server class will manage the pump - * process/callback mechanism. - */ -enum ESDRPCSStatus -{ - // The call went ok, but the response is not yet ready. The - // method will arrange for the clearLock() call to be made at - // a later date, after which, once the chain is being pumped - // again, deferredResponse() will be called to gather the result - ESDRPCS_DEFERRED, - - // The LLSDRPCServer would like to handle the method on the - // callback queue of the pump. - ESDRPCS_CALLBACK, - - // The method call finished and generated output. - ESDRPCS_DONE, - - // Method failed for some unspecified reason - you should avoid - // this. A generic fault will be sent to the output. - ESDRPCS_ERROR, - - ESDRPCS_COUNT, -}; - -/** - * @class LLSDRPCMethodCallBase - * @brief Base class for calling a member function in an sd rpcserver - * implementation. - */ -class LLSDRPCMethodCallBase -{ -public: - LLSDRPCMethodCallBase() {} - virtual ~LLSDRPCMethodCallBase() {} - - virtual ESDRPCSStatus call( - const LLSD& params, - const LLChannelDescriptors& channels, - LLBufferArray* response) = 0; -protected: -}; - -/** - * @class LLSDRPCMethodCall - * @brief Class which implements member function calls. - */ -template<class Server> -class LLSDRPCMethodCall : public LLSDRPCMethodCallBase -{ -public: - typedef ESDRPCSStatus (Server::*mem_fn)( - const LLSD& params, - const LLChannelDescriptors& channels, - LLBufferArray* data); - LLSDRPCMethodCall(Server* s, mem_fn fn) : - mServer(s), - mMemFn(fn) - { - } - virtual ~LLSDRPCMethodCall() {} - virtual ESDRPCSStatus call( - const LLSD& params, - const LLChannelDescriptors& channels, - LLBufferArray* data) - { - return (*mServer.*mMemFn)(params, channels, data); - } - -protected: - Server* mServer; - mem_fn mMemFn; - //bool (Server::*mMemFn)(const LLSD& params, LLBufferArray& data); -}; - - -/** - * @class LLSDRPCServer - * @brief Basic implementation of a structure data rpc server - * - * The rpc server is also designed to appropriately straddle the pump - * <code>process()</code> and <code>callback()</code> to specify which - * thread you want to work on when handling a method call. The - * <code>mMethods</code> methods are called from - * <code>process()</code>, while the <code>mCallbackMethods</code> are - * called when a pump is in a <code>callback()</code> cycle. - */ -class LLSDRPCServer : public LLIOPipe -{ -public: - LLSDRPCServer(); - virtual ~LLSDRPCServer(); - - /** - * enumeration for generic fault codes - */ - enum - { - FAULT_BAD_REQUEST = 2000, - FAULT_NO_RESPONSE = 2001, - }; - - /** - * @brief Call this method to return an rpc fault. - * - * @param channel The channel for output on the data buffer - * @param data buffer which will recieve the final output - * @param code The fault code - * @param msg The fault message - */ - static void buildFault( - const LLChannelDescriptors& channels, - LLBufferArray* data, - S32 code, - const std::string& msg); - - /** - * @brief Call this method to build an rpc response. - * - * @param channel The channel for output on the data buffer - * @param data buffer which will recieve the final output - * @param response The return value from the method call - */ - static void buildResponse( - const LLChannelDescriptors& channels, - LLBufferArray* data, - const LLSD& response); - -protected: - /* @name LLIOPipe virtual implementations - */ - //@{ - /** - * @brief Process the data in buffer - */ - virtual EStatus process_impl( - const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, - bool& eos, - LLSD& context, - LLPumpIO* pump); - //@} - -protected: - - /** - * @brief Enumeration to track the state of the rpc server instance - */ - enum EState - { - STATE_NONE, - STATE_CALLBACK, - STATE_DEFERRED, - STATE_DONE - }; - - /** - * @brief This method is called when an http post comes in. - * - * The default behavior is to look at the method name, look up the - * method in the method table, and call it. If the method is not - * found, this function will build a fault response. You can - * implement your own version of this function if you want to hard - * wire some behavior or optimize things a bit. - * @param method The method name being called - * @param params The parameters - * @param channel The channel for output on the data buffer - * @param data The http data - * @return Returns the status of the method call, done/deferred/etc - */ - virtual ESDRPCSStatus callMethod( - const std::string& method, - const LLSD& params, - const LLChannelDescriptors& channels, - LLBufferArray* data); - - /** - * @brief This method is called when a pump callback is processed. - * - * The default behavior is to look at the method name, look up the - * method in the callback method table, and call it. If the method - * is not found, this function will build a fault response. You - * can implement your own version of this function if you want to - * hard wire some behavior or optimize things a bit. - * @param method The method name being called - * @param params The parameters - * @param channel The channel for output on the data buffer - * @param data The http data - * @return Returns the status of the method call, done/deferred/etc - */ - virtual ESDRPCSStatus callbackMethod( - const std::string& method, - const LLSD& params, - const LLChannelDescriptors& channels, - LLBufferArray* data); - - /** - * @brief Called after a deferred service is unlocked - * - * If a method returns ESDRPCS_DEFERRED, then the service chain - * will be locked and not processed until some other system calls - * clearLock() on the service instance again. At that point, - * once the pump starts processing the chain again, this method - * will be called so the service can output the final result - * into the buffers. - */ - virtual ESDRPCSStatus deferredResponse( - const LLChannelDescriptors& channels, - LLBufferArray* data); - - // donovan put this public here 7/27/06 -public: - /** - * @brief unlock a service that as ESDRPCS_DEFERRED - */ - void clearLock(); - -protected: - EState mState; - LLSD mRequest; - LLPumpIO* mPump; - S32 mLock; - typedef std::map<std::string, LLSDRPCMethodCallBase*> method_map_t; - method_map_t mMethods; - method_map_t mCallbackMethods; -}; - -/** - * @name Helper Templates for making LLHTTPNodes - * - * These templates help in creating nodes for handing a service from - * either SDRPC or XMLRPC, given a single implementation of LLSDRPCServer. - * - * To use it: - * \code - * class LLUsefulServer : public LLSDRPCServer { ... } - * - * LLHTTPNode& root = LLCreateHTTPWireServer(...); - * root.addNode("llsdrpc/useful", new LLSDRPCNode<LLUsefulServer>); - * root.addNode("xmlrpc/useful", new LLXMLRPCNode<LLUsefulServer>); - * \endcode - */ -//@{ - -template<class Server> -class LLSDRPCServerFactory : public LLChainIOFactory -{ -public: - virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const - { - LL_DEBUGS() << "LLXMLSDRPCServerFactory::build" << LL_ENDL; - chain.push_back(LLIOPipe::ptr_t(new Server)); - return true; - } -}; - -template<class Server> -class LLSDRPCNode : public LLHTTPNodeForFactory< - LLSDRPCServerFactory<Server> > -{ -}; - -template<class Server> -class LLXMLRPCServerFactory : public LLChainIOFactory -{ -public: - virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const - { - LL_DEBUGS() << "LLXMLSDRPCServerFactory::build" << LL_ENDL; - chain.push_back(LLIOPipe::ptr_t(new LLFilterXMLRPCRequest2LLSD)); - chain.push_back(LLIOPipe::ptr_t(new Server)); - chain.push_back(LLIOPipe::ptr_t(new LLFilterSD2XMLRPCResponse)); - return true; - } -}; - -template<class Server> -class LLXMLRPCNode : public LLHTTPNodeForFactory< - LLXMLRPCServerFactory<Server> > -{ -}; - -//@} - -#endif // LL_LLSDRPCSERVER_H diff --git a/indra/llmessage/lltrustedmessageservice.cpp b/indra/llmessage/lltrustedmessageservice.cpp index 5bd1112cfe..33944f7883 100644 --- a/indra/llmessage/lltrustedmessageservice.cpp +++ b/indra/llmessage/lltrustedmessageservice.cpp @@ -30,6 +30,7 @@ #include "llhost.h" #include "llmessageconfig.h" #include "message.h" +#include "llhttpconstants.h" bool LLTrustedMessageService::validate(const std::string& name, LLSD& context) diff --git a/indra/llmessage/llurlrequest.cpp b/indra/llmessage/llurlrequest.cpp deleted file mode 100644 index 1294379eca..0000000000 --- a/indra/llmessage/llurlrequest.cpp +++ /dev/null @@ -1,783 +0,0 @@ -/** - * @file llurlrequest.cpp - * @author Phoenix - * @date 2005-04-28 - * @brief Implementation of the URLRequest class and related classes. - * - * $LicenseInfo:firstyear=2005&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" -#include "llurlrequest.h" - -#include <algorithm> -#include <openssl/x509_vfy.h> -#include <openssl/ssl.h> -#include "llcurl.h" -#include "llfasttimer.h" -#include "llioutil.h" -#include "llproxy.h" -#include "llpumpio.h" -#include "llsd.h" -#include "llstring.h" -#include "apr_env.h" -#include "llapr.h" - -/** - * String constants - */ -const std::string CONTEXT_DEST_URI_SD_LABEL("dest_uri"); -const std::string CONTEXT_TRANSFERED_BYTES("transfered_bytes"); - -// These are defined in llhttpnode.h/llhttpnode.cpp -extern const std::string CONTEXT_REQUEST; -extern const std::string CONTEXT_RESPONSE; - -static size_t headerCallback(void* data, size_t size, size_t nmemb, void* user); - -/** - * class LLURLRequestDetail - */ -class LLURLRequestDetail -{ -public: - LLURLRequestDetail(); - ~LLURLRequestDetail(); - std::string mURL; - LLCurlEasyRequest* mCurlRequest; - LLIOPipe::buffer_ptr_t mResponseBuffer; - LLChannelDescriptors mChannels; - U8* mLastRead; - U32 mBodyLimit; - S32 mByteAccumulator; - bool mIsBodyLimitSet; - LLURLRequest::SSLCertVerifyCallback mSSLVerifyCallback; -}; - -LLURLRequestDetail::LLURLRequestDetail() : - mCurlRequest(NULL), - mLastRead(NULL), - mBodyLimit(0), - mByteAccumulator(0), - mIsBodyLimitSet(false), - mSSLVerifyCallback(NULL) -{ - mCurlRequest = new LLCurlEasyRequest(); - - if(!mCurlRequest->isValid()) //failed. - { - delete mCurlRequest ; - mCurlRequest = NULL ; - } -} - -LLURLRequestDetail::~LLURLRequestDetail() -{ - delete mCurlRequest; - mLastRead = NULL; -} - -void LLURLRequest::setSSLVerifyCallback(SSLCertVerifyCallback callback, void *param) -{ - mDetail->mSSLVerifyCallback = callback; - mDetail->mCurlRequest->setSSLCtxCallback(LLURLRequest::_sslCtxCallback, (void *)this); - mDetail->mCurlRequest->setopt(CURLOPT_SSL_VERIFYPEER, true); - mDetail->mCurlRequest->setopt(CURLOPT_SSL_VERIFYHOST, 2); -} - - -// _sslCtxFunction -// Callback function called when an SSL Context is created via CURL -// used to configure the context for custom cert validation - -CURLcode LLURLRequest::_sslCtxCallback(CURL * curl, void *sslctx, void *param) -{ - LLURLRequest *req = (LLURLRequest *)param; - if(req == NULL || req->mDetail->mSSLVerifyCallback == NULL) - { - SSL_CTX_set_cert_verify_callback((SSL_CTX *)sslctx, NULL, NULL); - return CURLE_OK; - } - SSL_CTX * ctx = (SSL_CTX *) sslctx; - // disable any default verification for server certs - SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL); - // set the verification callback. - SSL_CTX_set_cert_verify_callback(ctx, req->mDetail->mSSLVerifyCallback, (void *)req); - // the calls are void - return CURLE_OK; - -} - -/** - * class LLURLRequest - */ - - -LLURLRequest::LLURLRequest(EHTTPMethod action, bool follow_redirects /* = true */) : - mAction(action), - mFollowRedirects(follow_redirects) -{ - initialize(); -} - -LLURLRequest::LLURLRequest( - EHTTPMethod action, - const std::string& url, - bool follow_redirects /* = true */) : - mAction(action), - mFollowRedirects(follow_redirects) -{ - initialize(); - setURL(url); -} - -LLURLRequest::~LLURLRequest() -{ - delete mDetail; - mDetail = NULL ; -} - -void LLURLRequest::setURL(const std::string& url) -{ - mDetail->mURL = url; - if (url.empty()) - { - LL_WARNS() << "empty URL specified" << LL_ENDL; - } -} - -const std::string& LLURLRequest::getURL() const -{ - return mDetail->mURL; -} - -void LLURLRequest::addHeader(const std::string& header, const std::string& value /* = "" */) -{ - mDetail->mCurlRequest->slist_append(header, value); -} - -void LLURLRequest::addHeaderRaw(const char* header) -{ - mDetail->mCurlRequest->slist_append(header); -} - -void LLURLRequest::setBodyLimit(U32 size) -{ - mDetail->mBodyLimit = size; - mDetail->mIsBodyLimitSet = true; -} - -void LLURLRequest::setCallback(LLURLRequestComplete* callback) -{ - mCompletionCallback = callback; - mDetail->mCurlRequest->setHeaderCallback(&headerCallback, (void*)callback); -} - -// Added to mitigate the effect of libcurl looking -// for the ALL_PROXY and http_proxy env variables -// and deciding to insert a Pragma: no-cache -// header! The only usage of this method at the -// time of this writing is in llhttpclient.cpp -// in the request() method, where this method -// is called with use_proxy = FALSE -void LLURLRequest::useProxy(bool use_proxy) -{ - static char *env_proxy; - - if (use_proxy && (env_proxy == NULL)) - { - apr_status_t status; - LLAPRPool pool; - status = apr_env_get(&env_proxy, "ALL_PROXY", pool.getAPRPool()); - if (status != APR_SUCCESS) - { - status = apr_env_get(&env_proxy, "http_proxy", pool.getAPRPool()); - } - if (status != APR_SUCCESS) - { - use_proxy = FALSE; - } - } - - - LL_DEBUGS() << "use_proxy = " << (use_proxy?'Y':'N') << ", env_proxy = " << (env_proxy ? env_proxy : "(null)") << LL_ENDL; - - if (env_proxy && use_proxy) - { - mDetail->mCurlRequest->setoptString(CURLOPT_PROXY, env_proxy); - } - else - { - mDetail->mCurlRequest->setoptString(CURLOPT_PROXY, ""); - } -} - -void LLURLRequest::useProxy(const std::string &proxy) -{ - mDetail->mCurlRequest->setoptString(CURLOPT_PROXY, proxy); -} - -void LLURLRequest::allowCookies() -{ - mDetail->mCurlRequest->setoptString(CURLOPT_COOKIEFILE, ""); -} - -//virtual -bool LLURLRequest::isValid() -{ - return mDetail->mCurlRequest && mDetail->mCurlRequest->isValid(); -} - -// virtual -LLIOPipe::EStatus LLURLRequest::handleError( - LLIOPipe::EStatus status, - LLPumpIO* pump) -{ - if(!isValid()) - { - return STATUS_EXPIRED ; - } - - if(mCompletionCallback && pump) - { - LLURLRequestComplete* complete = NULL; - complete = (LLURLRequestComplete*)mCompletionCallback.get(); - complete->httpStatus( - HTTP_INTERNAL_ERROR, - LLIOPipe::lookupStatusString(status)); - complete->responseStatus(status); - pump->respond(complete); - mCompletionCallback = NULL; - } - return status; -} - -static LLTrace::BlockTimerStatHandle FTM_PROCESS_URL_REQUEST("URL Request"); -static LLTrace::BlockTimerStatHandle FTM_PROCESS_URL_REQUEST_GET_RESULT("Get Result"); -static LLTrace::BlockTimerStatHandle FTM_URL_PERFORM("Perform"); -static LLTrace::BlockTimerStatHandle FTM_PROCESS_URL_PUMP_RESPOND("Pump Respond"); -static LLTrace::BlockTimerStatHandle FTM_URL_ADJUST_TIMEOUT("Adjust Timeout"); - -// virtual -LLIOPipe::EStatus LLURLRequest::process_impl( - const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, - bool& eos, - LLSD& context, - LLPumpIO* pump) -{ - LL_RECORD_BLOCK_TIME(FTM_PROCESS_URL_REQUEST); - PUMP_DEBUG; - //LL_INFOS() << "LLURLRequest::process_impl()" << LL_ENDL; - if (!buffer) return STATUS_ERROR; - - // we're still waiting or prcessing, check how many - // bytes we have accumulated. - const S32 MIN_ACCUMULATION = 100000; - if(pump && (mDetail->mByteAccumulator > MIN_ACCUMULATION)) - { - LL_RECORD_BLOCK_TIME(FTM_URL_ADJUST_TIMEOUT); - // This is a pretty sloppy calculation, but this - // tries to make the gross assumption that if data - // is coming in at 56kb/s, then this transfer will - // probably succeed. So, if we're accumlated - // 100,000 bytes (MIN_ACCUMULATION) then let's - // give this client another 2s to complete. - const F32 TIMEOUT_ADJUSTMENT = 2.0f; - mDetail->mByteAccumulator = 0; - pump->adjustTimeoutSeconds(TIMEOUT_ADJUSTMENT); - LL_DEBUGS() << "LLURLRequest adjustTimeoutSeconds for request: " << mDetail->mURL << LL_ENDL; - if (mState == STATE_INITIALIZED) - { - LL_INFOS() << "LLURLRequest adjustTimeoutSeconds called during upload" << LL_ENDL; - } - } - - switch(mState) - { - case STATE_INITIALIZED: - { - PUMP_DEBUG; - // We only need to wait for input if we are uploading - // something. - if(((HTTP_PUT == mAction) || (HTTP_POST == mAction)) && !eos) - { - // we're waiting to get all of the information - return STATUS_BREAK; - } - - // *FIX: bit of a hack, but it should work. The configure and - // callback method expect this information to be ready. - mDetail->mResponseBuffer = buffer; - mDetail->mChannels = channels; - if(!configure()) - { - return STATUS_ERROR; - } - mState = STATE_WAITING_FOR_RESPONSE; - - // *FIX: Maybe we should just go to the next state now... - return STATUS_BREAK; - } - case STATE_WAITING_FOR_RESPONSE: - case STATE_PROCESSING_RESPONSE: - { - PUMP_DEBUG; - LLIOPipe::EStatus status = STATUS_BREAK; - { - LL_RECORD_BLOCK_TIME(FTM_URL_PERFORM); - if(!mDetail->mCurlRequest->wait()) - { - return status ; - } - } - - bool keep_looping = true; - while(keep_looping) - { - CURLcode result; - - bool newmsg = false; - { - LL_RECORD_BLOCK_TIME(FTM_PROCESS_URL_REQUEST_GET_RESULT); - newmsg = mDetail->mCurlRequest->getResult(&result); - } - - if(!newmsg) - { - // keep processing - break; - } - - - mState = STATE_HAVE_RESPONSE; - context[CONTEXT_REQUEST][CONTEXT_TRANSFERED_BYTES] = mRequestTransferedBytes; - context[CONTEXT_RESPONSE][CONTEXT_TRANSFERED_BYTES] = mResponseTransferedBytes; - LL_DEBUGS() << this << "Setting context to " << context << LL_ENDL; - switch(result) - { - case CURLE_OK: - case CURLE_WRITE_ERROR: - // NB: The error indication means that we stopped the - // writing due the body limit being reached - if(mCompletionCallback && pump) - { - LLURLRequestComplete* complete = NULL; - complete = (LLURLRequestComplete*) - mCompletionCallback.get(); - complete->responseStatus( - result == CURLE_OK - ? STATUS_OK : STATUS_STOP); - LLPumpIO::links_t chain; - LLPumpIO::LLLinkInfo link; - link.mPipe = mCompletionCallback; - link.mChannels = LLBufferArray::makeChannelConsumer( - channels); - chain.push_back(link); - { - LL_RECORD_BLOCK_TIME(FTM_PROCESS_URL_PUMP_RESPOND); - pump->respond(chain, buffer, context); - } - mCompletionCallback = NULL; - } - break; - case CURLE_FAILED_INIT: - case CURLE_COULDNT_CONNECT: - status = STATUS_NO_CONNECTION; - keep_looping = false; - break; - default: // CURLE_URL_MALFORMAT - LL_WARNS() << "URLRequest Error: " << result - << ", " - << LLCurl::strerror(result) - << ", " - << (mDetail->mURL.empty() ? "<EMPTY URL>" : mDetail->mURL) - << LL_ENDL; - status = STATUS_ERROR; - keep_looping = false; - break; - } - } - return status; - } - case STATE_HAVE_RESPONSE: - PUMP_DEBUG; - // we already stuffed everything into channel in in the curl - // callback, so we are done. - eos = true; - context[CONTEXT_REQUEST][CONTEXT_TRANSFERED_BYTES] = mRequestTransferedBytes; - context[CONTEXT_RESPONSE][CONTEXT_TRANSFERED_BYTES] = mResponseTransferedBytes; - LL_DEBUGS() << this << "Setting context to " << context << LL_ENDL; - return STATUS_DONE; - - default: - PUMP_DEBUG; - context[CONTEXT_REQUEST][CONTEXT_TRANSFERED_BYTES] = mRequestTransferedBytes; - context[CONTEXT_RESPONSE][CONTEXT_TRANSFERED_BYTES] = mResponseTransferedBytes; - LL_DEBUGS() << this << "Setting context to " << context << LL_ENDL; - return STATUS_ERROR; - } -} - -void LLURLRequest::initialize() -{ - mState = STATE_INITIALIZED; - mDetail = new LLURLRequestDetail; - - if(!isValid()) - { - return ; - } - - mDetail->mCurlRequest->setopt(CURLOPT_NOSIGNAL, 1); - mDetail->mCurlRequest->setWriteCallback(&downCallback, (void*)this); - mDetail->mCurlRequest->setReadCallback(&upCallback, (void*)this); - mRequestTransferedBytes = 0; - mResponseTransferedBytes = 0; -} - -static LLTrace::BlockTimerStatHandle FTM_URL_REQUEST_CONFIGURE("URL Configure"); -bool LLURLRequest::configure() -{ - LL_RECORD_BLOCK_TIME(FTM_URL_REQUEST_CONFIGURE); - - bool rv = false; - S32 bytes = mDetail->mResponseBuffer->countAfter( - mDetail->mChannels.in(), - NULL); - switch(mAction) - { - case HTTP_HEAD: - mDetail->mCurlRequest->setopt(CURLOPT_HEADER, 1); - mDetail->mCurlRequest->setopt(CURLOPT_NOBODY, 1); - if (mFollowRedirects) - { - mDetail->mCurlRequest->setopt(CURLOPT_FOLLOWLOCATION, 1); - } - rv = true; - break; - case HTTP_GET: - mDetail->mCurlRequest->setopt(CURLOPT_HTTPGET, 1); - if (mFollowRedirects) - { - mDetail->mCurlRequest->setopt(CURLOPT_FOLLOWLOCATION, 1); - } - - // Set Accept-Encoding to allow response compression - mDetail->mCurlRequest->setoptString(CURLOPT_ENCODING, ""); - rv = true; - break; - - case HTTP_PUT: - // Disable the expect http 1.1 extension. POST and PUT default - // to turning this on, and I am not too sure what it means. - addHeader(HTTP_OUT_HEADER_EXPECT); - - mDetail->mCurlRequest->setopt(CURLOPT_UPLOAD, 1); - mDetail->mCurlRequest->setopt(CURLOPT_INFILESIZE, bytes); - rv = true; - break; - - case HTTP_PATCH: - // Disable the expect http 1.1 extension. POST and PUT default - // to turning this on, and I am not too sure what it means. - addHeader(HTTP_OUT_HEADER_EXPECT); - - mDetail->mCurlRequest->setopt(CURLOPT_UPLOAD, 1); - mDetail->mCurlRequest->setopt(CURLOPT_INFILESIZE, bytes); - mDetail->mCurlRequest->setoptString(CURLOPT_CUSTOMREQUEST, "PATCH"); - rv = true; - break; - - case HTTP_POST: - // Disable the expect http 1.1 extension. POST and PUT default - // to turning this on, and I am not too sure what it means. - addHeader(HTTP_OUT_HEADER_EXPECT); - - // Disable the content type http header. - // *FIX: what should it be? - addHeader(HTTP_OUT_HEADER_CONTENT_TYPE); - - // Set the handle for an http post - mDetail->mCurlRequest->setPost(NULL, bytes); - - // Set Accept-Encoding to allow response compression - mDetail->mCurlRequest->setoptString(CURLOPT_ENCODING, ""); - rv = true; - break; - - case HTTP_DELETE: - // Set the handle for an http delete - mDetail->mCurlRequest->setoptString(CURLOPT_CUSTOMREQUEST, "DELETE"); - rv = true; - break; - - case HTTP_COPY: - // Set the handle for an http copy - mDetail->mCurlRequest->setoptString(CURLOPT_CUSTOMREQUEST, "COPY"); - rv = true; - break; - - case HTTP_MOVE: - // Set the handle for an http move - mDetail->mCurlRequest->setoptString(CURLOPT_CUSTOMREQUEST, "MOVE"); - // *NOTE: should we check for the Destination header? - rv = true; - break; - - default: - LL_WARNS() << "Unhandled URLRequest action: " << mAction << LL_ENDL; - break; - } - if(rv) - { - mDetail->mCurlRequest->sendRequest(mDetail->mURL); - } - return rv; -} - -// static -size_t LLURLRequest::downCallback( - char* data, - size_t size, - size_t nmemb, - void* user) -{ - LLURLRequest* req = (LLURLRequest*)user; - if(STATE_WAITING_FOR_RESPONSE == req->mState) - { - req->mState = STATE_PROCESSING_RESPONSE; - } - U32 bytes = size * nmemb; - if (req->mDetail->mIsBodyLimitSet) - { - if (bytes > req->mDetail->mBodyLimit) - { - bytes = req->mDetail->mBodyLimit; - req->mDetail->mBodyLimit = 0; - } - else - { - req->mDetail->mBodyLimit -= bytes; - } - } - - req->mDetail->mResponseBuffer->append( - req->mDetail->mChannels.out(), - (U8*)data, - bytes); - req->mResponseTransferedBytes += bytes; - req->mDetail->mByteAccumulator += bytes; - return bytes; -} - -// static -size_t LLURLRequest::upCallback( - char* data, - size_t size, - size_t nmemb, - void* user) -{ - LLURLRequest* req = (LLURLRequest*)user; - S32 bytes = llmin( - (S32)(size * nmemb), - req->mDetail->mResponseBuffer->countAfter( - req->mDetail->mChannels.in(), - req->mDetail->mLastRead)); - req->mDetail->mLastRead = req->mDetail->mResponseBuffer->readAfter( - req->mDetail->mChannels.in(), - req->mDetail->mLastRead, - (U8*)data, - bytes); - req->mRequestTransferedBytes += bytes; - return bytes; -} - -static size_t headerCallback(void* data, size_t size, size_t nmemb, void* user) -{ - const char* header_line = (const char*)data; - size_t header_len = size * nmemb; - LLURLRequestComplete* complete = (LLURLRequestComplete*)user; - - if (!complete || !header_line) - { - return header_len; - } - - // *TODO: This should be a utility in llstring.h: isascii() - for (size_t i = 0; i < header_len; ++i) - { - if (header_line[i] < 0) - { - return header_len; - } - } - - std::string header(header_line, header_len); - - // Per HTTP spec the first header line must be the status line. - if (header.substr(0,5) == "HTTP/") - { - std::string::iterator end = header.end(); - std::string::iterator pos1 = std::find(header.begin(), end, ' '); - if (pos1 != end) ++pos1; - std::string::iterator pos2 = std::find(pos1, end, ' '); - if (pos2 != end) ++pos2; - std::string::iterator pos3 = std::find(pos2, end, '\r'); - - std::string version(header.begin(), pos1); - std::string status(pos1, pos2); - std::string reason(pos2, pos3); - - S32 status_code = atoi(status.c_str()); - if (status_code > 0) - { - complete->httpStatus(status_code, reason); - return header_len; - } - } - - std::string::iterator sep = std::find(header.begin(),header.end(),':'); - - if (sep != header.end()) - { - std::string key(header.begin(), sep); - std::string value(sep + 1, header.end()); - - key = utf8str_tolower(utf8str_trim(key)); - value = utf8str_trim(value); - - complete->header(key, value); - } - else - { - LLStringUtil::trim(header); - if (!header.empty()) - { - LL_WARNS() << "Unable to parse header: " << header << LL_ENDL; - } - } - - return header_len; -} - -static LLTrace::BlockTimerStatHandle FTM_PROCESS_URL_EXTRACTOR("URL Extractor"); -/** - * LLContextURLExtractor - */ -// virtual -LLIOPipe::EStatus LLContextURLExtractor::process_impl( - const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, - bool& eos, - LLSD& context, - LLPumpIO* pump) -{ - LL_RECORD_BLOCK_TIME(FTM_PROCESS_URL_EXTRACTOR); - PUMP_DEBUG; - // The destination host is in the context. - if(context.isUndefined() || !mRequest) - { - return STATUS_PRECONDITION_NOT_MET; - } - - // copy in to out, since this just extract the URL and does not - // actually change the data. - LLChangeChannel change(channels.in(), channels.out()); - std::for_each(buffer->beginSegment(), buffer->endSegment(), change); - - // find the context url - if(context.has(CONTEXT_DEST_URI_SD_LABEL)) - { - mRequest->setURL(context[CONTEXT_DEST_URI_SD_LABEL].asString()); - return STATUS_DONE; - } - return STATUS_ERROR; -} - - -/** - * LLURLRequestComplete - */ -LLURLRequestComplete::LLURLRequestComplete() : - mRequestStatus(LLIOPipe::STATUS_ERROR) -{ -} - -// virtual -LLURLRequestComplete::~LLURLRequestComplete() -{ -} - -//virtual -void LLURLRequestComplete::header(const std::string& header, const std::string& value) -{ -} - -//virtual -void LLURLRequestComplete::complete(const LLChannelDescriptors& channels, - const buffer_ptr_t& buffer) -{ - if(STATUS_OK == mRequestStatus) - { - response(channels, buffer); - } - else - { - noResponse(); - } -} - -//virtual -void LLURLRequestComplete::response(const LLChannelDescriptors& channels, - const buffer_ptr_t& buffer) -{ - LL_WARNS() << "LLURLRequestComplete::response default implementation called" - << LL_ENDL; -} - -//virtual -void LLURLRequestComplete::noResponse() -{ - LL_WARNS() << "LLURLRequestComplete::noResponse default implementation called" - << LL_ENDL; -} - -void LLURLRequestComplete::responseStatus(LLIOPipe::EStatus status) -{ - mRequestStatus = status; -} - -static LLTrace::BlockTimerStatHandle FTM_PROCESS_URL_COMPLETE("URL Complete"); -// virtual -LLIOPipe::EStatus LLURLRequestComplete::process_impl( - const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, - bool& eos, - LLSD& context, - LLPumpIO* pump) -{ - LL_RECORD_BLOCK_TIME(FTM_PROCESS_URL_COMPLETE); - PUMP_DEBUG; - complete(channels, buffer); - return STATUS_OK; -} diff --git a/indra/llmessage/llurlrequest.h b/indra/llmessage/llurlrequest.h deleted file mode 100644 index 88fccd4bf6..0000000000 --- a/indra/llmessage/llurlrequest.h +++ /dev/null @@ -1,357 +0,0 @@ -/** - * @file llurlrequest.h - * @author Phoenix - * @date 2005-04-21 - * @brief Declaration of url based requests on pipes. - * - * $LicenseInfo:firstyear=2005&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_LLURLREQUEST_H -#define LL_LLURLREQUEST_H - -/** - * This file holds the declaration of useful classes for dealing with - * url based client requests. - */ - -#include <string> -#include "lliopipe.h" -#include "llchainio.h" -#include "llerror.h" -#include <openssl/x509_vfy.h> -#include "llcurl.h" - - -/** - * External constants - */ -extern const std::string CONTEXT_DEST_URI_SD_LABEL; -extern const std::string CONTEXT_TRANSFERED_BYTES; - -class LLURLRequestDetail; - -class LLURLRequestComplete; - -/** - * @class LLURLRequest - * @brief Class to handle url based requests. - * @see LLIOPipe - * - * Currently, this class is implemented on top of curl. From the - * vantage of a programmer using this class, you do not care so much, - * but it's useful to know since in order to accomplish 'non-blocking' - * behavior, we have to use a more expensive curl interface which can - * still block if the server enters a half-accepted state. It would be - * worth the time and effort to eventually port this to a raw client - * socket. - */ -class LLURLRequest : public LLIOPipe -{ - LOG_CLASS(LLURLRequest); -public: - typedef int (* SSLCertVerifyCallback)(X509_STORE_CTX *ctx, void *param); - - /** - * @brief Constructor. - * - * @param action One of the EHTTPMethod enumerations. - */ - LLURLRequest(EHTTPMethod action, bool follow_redirects = true); - - /** - * @brief Constructor. - * - * @param action One of the EHTTPMethod enumerations. - * @param url The url of the request. It should already be encoded. - */ - LLURLRequest(EHTTPMethod action, const std::string& url, bool follow_redirects = true); - - /** - * @brief Destructor. - */ - virtual ~LLURLRequest(); - - /* @name Instance methods - */ - //@{ - /** - * @brief Set the url for the request - * - * This method assumes the url is encoded appropriately for the - * request. - * The url must be set somehow before the first call to process(), - * or the url will not be set correctly. - * - */ - void setURL(const std::string& url); - const std::string& getURL() const; - /** - * @brief Add a header to the http post. - * - * This provides a raw interface if you know what kind of request you - * will be making during construction of this instance. All - * required headers will be automatically constructed, so this is - * usually useful for encoding parameters. - */ - void addHeader(const std::string& header, const std::string& value = ""); - void addHeaderRaw(const char* header); - - /** - * @brief Check remote server certificate signed by a known root CA. - * - * Set whether request will check that remote server - * certificates are signed by a known root CA when using HTTPS. - */ - void setSSLVerifyCallback(SSLCertVerifyCallback callback, void * param); - - - /** - * @brief Return at most size bytes of body. - * - * If the body had more bytes than this limit, they will not be - * returned and the connection closed. In this case, STATUS_STOP - * will be passed to responseStatus(); - */ - void setBodyLimit(U32 size); - - /** - * @brief Set a completion callback for this URLRequest. - * - * The callback is added to this URLRequet's pump when either the - * entire buffer is known or an error like timeout or connection - * refused has happened. In the case of a complete transfer, this - * object builds a response chain such that the callback and the - * next process consumer get to read the output. - * - * This setup is a little fragile since the url request consumer - * might not just read the data - it may do a channel change, - * which invalidates the input to the callback, but it works well - * in practice. - */ - void setCallback(LLURLRequestComplete* callback); - //@} - - /* @name LLIOPipe virtual implementations - */ - - /** - * @ brief Turn off (or on) the CURLOPT_PROXY header. - */ - void useProxy(bool use_proxy); - - /** - * @ brief Set the CURLOPT_PROXY header to the given value. - */ - void useProxy(const std::string& proxy); - - /** - * @brief Turn on cookie handling for this request with CURLOPT_COOKIEFILE. - */ - void allowCookies(); - - /*virtual*/ bool isValid() ; - -public: - /** - * @brief Give this pipe a chance to handle a generated error - */ - virtual EStatus handleError(EStatus status, LLPumpIO* pump); - - -protected: - /** - * @brief Process the data in buffer - */ - virtual EStatus process_impl( - const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, - bool& eos, - LLSD& context, - LLPumpIO* pump); - //@} - -protected: - enum EState - { - STATE_INITIALIZED, - STATE_WAITING_FOR_RESPONSE, - STATE_PROCESSING_RESPONSE, - STATE_HAVE_RESPONSE, - }; - EState mState; - EHTTPMethod mAction; - bool mFollowRedirects; - LLURLRequestDetail* mDetail; - LLIOPipe::ptr_t mCompletionCallback; - S32 mRequestTransferedBytes; - S32 mResponseTransferedBytes; - - static CURLcode _sslCtxCallback(CURL * curl, void *sslctx, void *param); - -private: - /** - * @brief Initialize the object. Called during construction. - */ - void initialize(); - - /** - * @brief Handle action specific url request configuration. - * - * @return Returns true if this is configured. - */ - bool configure(); - - /** - * @brief Download callback method. - */ - static size_t downCallback( - char* data, - size_t size, - size_t nmemb, - void* user); - - /** - * @brief Upload callback method. - */ - static size_t upCallback( - char* data, - size_t size, - size_t nmemb, - void* user); - - /** - * @brief Declaration of unimplemented method to prevent copy - * construction. - */ - LLURLRequest(const LLURLRequest&); -}; - - -/** - * @class LLContextURLExtractor - * @brief This class unpacks the url out of a agent usher service so - * it can be packed into a LLURLRequest object. - * @see LLIOPipe - * - * This class assumes that the context is a map that contains an entry - * named CONTEXT_DEST_URI_SD_LABEL. - */ -class LLContextURLExtractor : public LLIOPipe -{ -public: - LLContextURLExtractor(LLURLRequest* req) : mRequest(req) {} - ~LLContextURLExtractor() {} - -protected: - /* @name LLIOPipe virtual implementations - */ - //@{ - /** - * @brief Process the data in buffer - */ - virtual EStatus process_impl( - const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, - bool& eos, - LLSD& context, - LLPumpIO* pump); - //@} - -protected: - LLURLRequest* mRequest; -}; - - -/** - * @class LLURLRequestComplete - * @brief Class which can optionally be used with an LLURLRequest to - * get notification when the url request is complete. - */ -class LLURLRequestComplete : public LLIOPipe -{ -public: - - // Called once for each header received, except status lines - virtual void header(const std::string& header, const std::string& value); - - // May be called more than once, particularly for redirects and proxy madness. - // Ex. a 200 for a connection to https through a proxy, followed by the "real" status - // a 3xx for a redirect followed by a "real" status, or more redirects. - virtual void httpStatus(S32 status, const std::string& reason) { } - - virtual void complete( - const LLChannelDescriptors& channels, - const buffer_ptr_t& buffer); - - /** - * @brief This method is called when we got a valid response. - * - * It is up to class implementers to do something useful here. - */ - virtual void response( - const LLChannelDescriptors& channels, - const buffer_ptr_t& buffer); - - /** - * @brief This method is called if there was no response. - * - * It is up to class implementers to do something useful here. - */ - virtual void noResponse(); - - /** - * @brief This method will be called by the LLURLRequest object. - * - * If this is set to STATUS_OK or STATUS_STOP, then the transfer - * is asssumed to have worked. This will lead to calling response() - * on the next call to process(). Otherwise, this object will call - * noResponse() on the next call to process. - * @param status The status of the URLRequest. - */ - void responseStatus(EStatus status); - - // constructor & destructor. - LLURLRequestComplete(); - virtual ~LLURLRequestComplete(); - -protected: - /* @name LLIOPipe virtual implementations - */ - //@{ - /** - * @brief Process the data in buffer - */ - virtual EStatus process_impl( - const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, - bool& eos, - LLSD& context, - LLPumpIO* pump); - //@} - - // value to note if we actually got the response. This value - // depends on correct usage from the LLURLRequest instance. - EStatus mRequestStatus; -}; - -#endif // LL_LLURLREQUEST_H diff --git a/indra/llmessage/message.cpp b/indra/llmessage/message.cpp index 026f71ff43..cecb2021e7 100644 --- a/indra/llmessage/message.cpp +++ b/indra/llmessage/message.cpp @@ -50,9 +50,7 @@ #include "lldir.h" #include "llerror.h" #include "llfasttimer.h" -#include "llhttpclient.h" #include "llhttpnodeadapter.h" -#include "llhttpsender.h" #include "llmd5.h" #include "llmessagebuilder.h" #include "llmessageconfig.h" @@ -77,6 +75,7 @@ #include "v3math.h" #include "v4math.h" #include "lltransfertargetvfile.h" +#include "llcorehttputil.h" // Constants //const char* MESSAGE_LOG_FILENAME = "message.log"; @@ -97,45 +96,6 @@ public: apr_pollfd_t mPollFD; }; -namespace -{ - class LLFnPtrResponder : public LLHTTPClient::Responder - { - LOG_CLASS(LLFnPtrResponder); - public: - LLFnPtrResponder(void (*callback)(void **,S32), void **callbackData, const std::string& name) : - mCallback(callback), - mCallbackData(callbackData), - mMessageName(name) - { - } - - protected: - virtual void httpFailure() - { - // don't spam when agent communication disconnected already - if (HTTP_GONE != getStatus()) - { - LL_WARNS("Messaging") << "error for message " << mMessageName - << " " << dumpResponse() << LL_ENDL; - } - // TODO: Map status in to useful error code. - if(NULL != mCallback) mCallback(mCallbackData, LL_ERR_TCP_TIMEOUT); - } - - virtual void httpSuccess() - { - if(NULL != mCallback) mCallback(mCallbackData, LL_ERR_NOERR); - } - - private: - - void (*mCallback)(void **,S32); - void **mCallbackData; - std::string mMessageName; - }; -} - class LLMessageHandlerBridge : public LLHTTPNode { virtual bool validate(const std::string& name, LLSD& context) const @@ -1129,29 +1089,6 @@ S32 LLMessageSystem::flushReliable(const LLHost &host) return send_bytes; } -LLHTTPClient::ResponderPtr LLMessageSystem::createResponder(const std::string& name) -{ - if(mSendReliable) - { - return new LLFnPtrResponder( - mReliablePacketParams.mCallback, - mReliablePacketParams.mCallbackData, - name); - } - else - { - // These messages aren't really unreliable, they just weren't - // explicitly sent as reliable, so they don't have a callback -// LL_WARNS("Messaging") << "LLMessageSystem::sendMessage: Sending unreliable " -// << mMessageBuilder->getMessageName() << " message via HTTP" -// << LL_ENDL; - return new LLFnPtrResponder( - NULL, - NULL, - name); - } -} - // This can be called from signal handlers, // so should should not use LL_INFOS(). S32 LLMessageSystem::sendMessage(const LLHost &host) @@ -1216,13 +1153,17 @@ S32 LLMessageSystem::sendMessage(const LLHost &host) if(mMessageBuilder == mLLSDMessageBuilder) { LLSD message = mLLSDMessageBuilder->getMessage(); - - const LLHTTPSender& sender = LLHTTPSender::getSender(host); - sender.send( - host, - mLLSDMessageBuilder->getMessageName(), - message, - createResponder(mLLSDMessageBuilder->getMessageName())); + + UntrustedCallback_t cb = NULL; + if ((mSendReliable) && (mReliablePacketParams.mCallback)) + { + cb = boost::bind(mReliablePacketParams.mCallback, mReliablePacketParams.mCallbackData, _1); + } + + LLCoros::instance().launch("LLMessageSystem::sendUntrustedSimulatorMessageCoro", + boost::bind(&LLMessageSystem::sendUntrustedSimulatorMessageCoro, this, + host.getUntrustedSimulatorCap(), + mLLSDMessageBuilder->getMessageName(), message, cb)); mSendReliable = FALSE; mReliablePacketParams.clear(); @@ -1410,9 +1351,16 @@ S32 LLMessageSystem::sendMessage( return 0; } - const LLHTTPSender& sender = LLHTTPSender::getSender(host); - sender.send(host, name, message, createResponder(name)); - return 1; + UntrustedCallback_t cb = NULL; + if ((mSendReliable) && (mReliablePacketParams.mCallback)) + { + cb = boost::bind(mReliablePacketParams.mCallback, mReliablePacketParams.mCallbackData, _1); + } + + LLCoros::instance().launch("LLMessageSystem::sendUntrustedSimulatorMessageCoro", + boost::bind(&LLMessageSystem::sendUntrustedSimulatorMessageCoro, this, + host.getUntrustedSimulatorCap(), name, message, cb)); + return 1; } void LLMessageSystem::logTrustedMsgFromUntrustedCircuit( const LLHost& host ) @@ -1725,7 +1673,7 @@ LLHost LLMessageSystem::findHost(const U32 circuit_code) } else { - return LLHost::invalid; + return LLHost(); } } @@ -4055,6 +4003,36 @@ const LLHost& LLMessageSystem::getSender() const return mLastSender; } +void LLMessageSystem::sendUntrustedSimulatorMessageCoro(std::string url, std::string message, LLSD body, UntrustedCallback_t callback) +{ + 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); + + + if (url.empty()) + { + LL_WARNS() << "sendUntrustedSimulatorMessageCoro called with empty capability!" << LL_ENDL; + return; + } + + LL_INFOS() << "sendUntrustedSimulatorMessageCoro: message " << message << " to cap " << url << LL_ENDL; + LLSD postData; + postData["message"] = message; + postData["body"] = body; + + LLSD result = httpAdapter->postAndSuspend(httpRequest, url, postData, httpOpts); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if ((callback) && (!callback.empty())) + callback((status) ? LL_ERR_NOERR : LL_ERR_TCP_TIMEOUT); +} + + LLHTTPRegistration<LLHTTPNodeAdapter<LLTrustedMessageService> > gHTTPRegistrationTrustedMessageWildcard("/trusted-message/<message-name>"); diff --git a/indra/llmessage/message.h b/indra/llmessage/message.h index 348b09b992..133db620e6 100644 --- a/indra/llmessage/message.h +++ b/indra/llmessage/message.h @@ -50,7 +50,6 @@ #include "lltimer.h" #include "llpacketring.h" #include "llhost.h" -#include "llcurl.h" #include "llhttpnode.h" //#include "llpacketack.h" #include "llsingleton.h" @@ -60,6 +59,7 @@ #include "llmessagesenderinterface.h" #include "llstoredmessage.h" +#include "boost/function.hpp" const U32 MESSAGE_MAX_STRINGS_LENGTH = 64; const U32 MESSAGE_NUMBER_OF_HASH_BUCKETS = 8192; @@ -489,7 +489,6 @@ public: void (*callback)(void **,S32), void ** callback_data); - LLCurl::ResponderPtr createResponder(const std::string& name); S32 sendMessage(const LLHost &host); S32 sendMessage(const U32 circuit); private: @@ -740,6 +739,9 @@ public: void receivedMessageFromTrustedSender(); private: + typedef boost::function<void(S32)> UntrustedCallback_t; + void sendUntrustedSimulatorMessageCoro(std::string url, std::string message, LLSD body, UntrustedCallback_t callback); + bool mLastMessageFromTrustedMessageService; diff --git a/indra/llmessage/tests/llhttpclientadapter_test.cpp b/indra/llmessage/tests/llhttpclientadapter_test.cpp deleted file mode 100644 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 100644 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 100644 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 100644 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/llplugin/CMakeLists.txt b/indra/llplugin/CMakeLists.txt index 8c4ddd524e..84667f1b82 100644 --- a/indra/llplugin/CMakeLists.txt +++ b/indra/llplugin/CMakeLists.txt @@ -11,6 +11,7 @@ include(LLMessage) include(LLRender) include(LLXML) include(LLWindow) +include(Boost) include_directories( ${LLCOMMON_INCLUDE_DIRS} diff --git a/indra/llplugin/slplugin/CMakeLists.txt b/indra/llplugin/slplugin/CMakeLists.txt index 6e7fefeb21..0e5e835777 100644 --- a/indra/llplugin/slplugin/CMakeLists.txt +++ b/indra/llplugin/slplugin/CMakeLists.txt @@ -69,12 +69,6 @@ target_link_libraries(SLPlugin ${PLUGIN_API_WINDOWS_LIBRARIES} ) -add_dependencies(SLPlugin - ${LLPLUGIN_LIBRARIES} - ${LLMESSAGE_LIBRARIES} - ${LLCOMMON_LIBRARIES} -) - if (DARWIN) # Mac version needs to link against Carbon target_link_libraries(SLPlugin ${COCOA_LIBRARY}) diff --git a/indra/llprimitive/CMakeLists.txt b/indra/llprimitive/CMakeLists.txt index 75ce624a58..dd2e806dda 100644 --- a/indra/llprimitive/CMakeLists.txt +++ b/indra/llprimitive/CMakeLists.txt @@ -6,6 +6,7 @@ include(00-Common) include(LLCommon) include(LLMath) include(LLMessage) +include(LLCoreHttp) include(LLXML) include(LLPhysicsExtensions) include(LLCharacter) @@ -75,9 +76,12 @@ target_link_libraries(llprimitive ${LLCOMMON_LIBRARIES} ${LLMATH_LIBRARIES} ${LLMESSAGE_LIBRARIES} + ${LLCOREHTTP_LIBRARIES} ${LLXML_LIBRARIES} ${LLPHYSICSEXTENSIONS_LIBRARIES} ${LLCHARACTER_LIBRARIES} + ${BOOST_COROUTINE_LIBRARY} + ${BOOST_CONTEXT_LIBRARY} ) diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt index 52738aeb6f..7fb1db15fb 100644 --- a/indra/llui/CMakeLists.txt +++ b/indra/llui/CMakeLists.txt @@ -8,13 +8,16 @@ include(LLImage) include(LLInventory) include(LLMath) include(LLMessage) +include(LLCoreHttp) include(LLRender) include(LLWindow) +include(LLCoreHttp) include(LLVFS) include(LLXML) include_directories( ${LLCOMMON_INCLUDE_DIRS} + ${LLCOREHTTP_INCLUDE_DIRS} ${LLIMAGE_INCLUDE_DIRS} ${LLINVENTORY_INCLUDE_DIRS} ${LLMATH_INCLUDE_DIRS} @@ -268,16 +271,18 @@ add_library (llui ${llui_SOURCE_FILES}) # Libraries on which this library depends, needed for Linux builds # Sort by high-level to low-level target_link_libraries(llui - ${LLMESSAGE_LIBRARIES} ${LLRENDER_LIBRARIES} ${LLWINDOW_LIBRARIES} ${LLIMAGE_LIBRARIES} ${LLINVENTORY_LIBRARIES} + ${LLMESSAGE_LIBRARIES} + ${LLCOREHTTP_LIBRARIES} ${LLVFS_LIBRARIES} # ugh, just for LLDir ${LLXUIXML_LIBRARIES} ${LLXML_LIBRARIES} ${LLMATH_LIBRARIES} ${HUNSPELL_LIBRARY} + ${LLMESSAGE_LIBRARIES} ${LLCOMMON_LIBRARIES} # must be after llimage, llwindow, llrender ) @@ -289,6 +294,8 @@ if(LL_TESTS) ) LL_ADD_PROJECT_UNIT_TESTS(llui "${llui_TEST_SOURCE_FILES}") # INTEGRATION TESTS - set(test_libs llui llmessage llcommon ${LLCOMMON_LIBRARIES} ${WINDOWS_LIBRARIES}) - LL_ADD_INTEGRATION_TEST(llurlentry llurlentry.cpp "${test_libs}") + set(test_libs llui llmessage llcorehttp llcommon ${LLCOMMON_LIBRARIES} ${BOOST_COROUTINE_LIBRARY} ${BOOST_CONTEXT_LIBRARY} ${BOOST_SYSTEM_LIBRARY} ${WINDOWS_LIBRARIES}) + if(NOT LINUX) + LL_ADD_INTEGRATION_TEST(llurlentry llurlentry.cpp "${test_libs}") + endif(NOT LINUX) endif(LL_TESTS) diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp index 57de35dfde..e4848362a7 100644 --- a/indra/llui/llurlentry.cpp +++ b/indra/llui/llurlentry.cpp @@ -920,7 +920,7 @@ std::string LLUrlEntryObjectIM::getLocation(const std::string &url) const // LLUrlEntryParcel statics. LLUUID LLUrlEntryParcel::sAgentID(LLUUID::null); LLUUID LLUrlEntryParcel::sSessionID(LLUUID::null); -LLHost LLUrlEntryParcel::sRegionHost(LLHost::invalid); +LLHost LLUrlEntryParcel::sRegionHost; bool LLUrlEntryParcel::sDisconnected(false); std::set<LLUrlEntryParcel*> LLUrlEntryParcel::sParcelInfoObservers; @@ -969,7 +969,7 @@ std::string LLUrlEntryParcel::getLabel(const std::string &url, const LLUrlLabelC void LLUrlEntryParcel::sendParcelInfoRequest(const LLUUID& parcel_id) { - if (sRegionHost == LLHost::invalid || sDisconnected) return; + if (sRegionHost.isInvalid() || sDisconnected) return; LLMessageSystem *msg = gMessageSystem; msg->newMessage("ParcelInfoRequest"); @@ -1427,7 +1427,7 @@ std::string LLUrlEntryExperienceProfile::getLabel( const std::string &url, const return LLTrans::getString("ExperienceNameNull"); } - const LLSD& experience_details = LLExperienceCache::get(experience_id); + const LLSD& experience_details = LLExperienceCache::instance().get(experience_id); if(!experience_details.isUndefined()) { std::string experience_name_string = experience_details[LLExperienceCache::NAME].asString(); @@ -1435,7 +1435,7 @@ std::string LLUrlEntryExperienceProfile::getLabel( const std::string &url, const } addObserver(experience_id_string, url, cb); - LLExperienceCache::get(experience_id, boost::bind(&LLUrlEntryExperienceProfile::onExperienceDetails, this, _1)); + LLExperienceCache::instance().get(experience_id, boost::bind(&LLUrlEntryExperienceProfile::onExperienceDetails, this, _1)); return LLTrans::getString("LoadingData"); } diff --git a/indra/llui/tests/llurlentry_stub.cpp b/indra/llui/tests/llurlentry_stub.cpp index 5d3f9ac327..f01178c374 100644 --- a/indra/llui/tests/llurlentry_stub.cpp +++ b/indra/llui/tests/llurlentry_stub.cpp @@ -165,8 +165,6 @@ LLFontGL* LLFontGL::getFontDefault() char const* const _PREHASH_AgentData = (char *)"AgentData"; char const* const _PREHASH_AgentID = (char *)"AgentID"; -LLHost LLHost::invalid(INVALID_PORT,INVALID_HOST_IP_ADDRESS); - LLMessageSystem* gMessageSystem = NULL; // diff --git a/indra/llui/tests/llurlentry_test.cpp b/indra/llui/tests/llurlentry_test.cpp index d41930a492..233fb6da23 100644 --- a/indra/llui/tests/llurlentry_test.cpp +++ b/indra/llui/tests/llurlentry_test.cpp @@ -37,17 +37,17 @@ #include <boost/regex.hpp> -namespace LLExperienceCache -{ - const LLSD& get( const LLUUID& key) - { - static LLSD boo; - return boo; - } - - void get( const LLUUID& key, callback_slot_t slot ){} - -} +// namespace LLExperienceCache +// { +// const LLSD& get( const LLUUID& key) +// { +// static LLSD boo; +// return boo; +// } +// +// void get( const LLUUID& key, callback_slot_t slot ){} +// +// } typedef std::map<std::string, LLControlGroup*> settings_map_t; settings_map_t LLUI::sSettingGroups; diff --git a/indra/mac_crash_logger/CMakeLists.txt b/indra/mac_crash_logger/CMakeLists.txt index c59645bd70..ab20388261 100644 --- 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/media_plugins/cef/CMakeLists.txt b/indra/media_plugins/cef/CMakeLists.txt index 388030c979..db471c7906 100644 --- a/indra/media_plugins/cef/CMakeLists.txt +++ b/indra/media_plugins/cef/CMakeLists.txt @@ -2,6 +2,7 @@ project(media_plugin_cef) +include(Boost) include(00-Common) include(LLCommon) include(LLImage) @@ -52,8 +53,8 @@ set(media_plugin_cef_HEADER_FILES set (media_plugin_cef_LINK_LIBRARIES ${LLPLUGIN_LIBRARIES} ${MEDIA_PLUGIN_BASE_LIBRARIES} - ${LLCOMMON_LIBRARIES} ${CEF_PLUGIN_LIBRARIES} + ${LLCOMMON_LIBRARIES} ${PLUGIN_API_WINDOWS_LIBRARIES}) @@ -84,11 +85,9 @@ add_library(media_plugin_cef ${media_plugin_cef_SOURCE_FILES} ) -add_dependencies(media_plugin_cef - ${LLPLUGIN_LIBRARIES} - ${MEDIA_PLUGIN_BASE_LIBRARIES} - ${LLCOMMON_LIBRARIES} -) +#add_dependencies(media_plugin_cef +# ${MEDIA_PLUGIN_BASE_LIBRARIES} +#) target_link_libraries(media_plugin_cef ${media_plugin_cef_LINK_LIBRARIES} diff --git a/indra/media_plugins/quicktime/CMakeLists.txt b/indra/media_plugins/quicktime/CMakeLists.txt index d7a1874bf3..c5615145be 100644 --- a/indra/media_plugins/quicktime/CMakeLists.txt +++ b/indra/media_plugins/quicktime/CMakeLists.txt @@ -14,6 +14,7 @@ include(PluginAPI) include(MediaPluginBase) include(OpenGL) include(QuickTimePlugin) +include(Boost) include_directories( ${LLPLUGIN_INCLUDE_DIRS} @@ -53,12 +54,6 @@ target_link_libraries(media_plugin_quicktime ${PLUGIN_API_WINDOWS_LIBRARIES} ) -add_dependencies(media_plugin_quicktime - ${LLPLUGIN_LIBRARIES} - ${MEDIA_PLUGIN_BASE_LIBRARIES} - ${LLCOMMON_LIBRARIES} -) - if (WINDOWS) set_target_properties( media_plugin_quicktime diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 357d95277c..1d2a129d81 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -116,8 +116,6 @@ set(viewer_SOURCE_FILES llappearancemgr.cpp llappviewer.cpp llappviewerlistener.cpp - llassetuploadqueue.cpp - llassetuploadresponders.cpp llattachmentsmgr.cpp llaudiosourcevo.cpp llautoreplace.cpp @@ -135,8 +133,6 @@ set(viewer_SOURCE_FILES llbrowsernotification.cpp llbuycurrencyhtml.cpp llcallingcard.cpp - llcapabilitylistener.cpp - llcaphttpsender.cpp llchannelmanager.cpp llchatbar.cpp llchathistory.cpp @@ -145,7 +141,6 @@ set(viewer_SOURCE_FILES llchiclet.cpp llchicletbar.cpp llclassifiedinfo.cpp - llclassifiedstatsresponder.cpp llcofwearables.cpp llcolorswatch.cpp llcommanddispatcherlistener.cpp @@ -190,7 +185,7 @@ set(viewer_SOURCE_FILES lleventnotifier.cpp lleventpoll.cpp llexpandabletextbox.cpp - llexperienceassociationresponder.cpp + llexperiencelog.cpp llexperiencelog.cpp llexternaleditor.cpp llface.cpp @@ -228,7 +223,6 @@ set(viewer_SOURCE_FILES llfloaterdeleteenvpreset.cpp llfloaterdeleteprefpreset.cpp llfloaterdestinations.cpp - llfloaterdisplayname.cpp llfloatereditdaycycle.cpp llfloatereditsky.cpp llfloatereditwater.cpp @@ -272,7 +266,6 @@ set(viewer_SOURCE_FILES llfloaternotificationstabbed.cpp llfloaterobjectweights.cpp llfloateropenobject.cpp - llfloateroutbox.cpp llfloaterpathfindingcharacters.cpp llfloaterpathfindingconsole.cpp llfloaterpathfindinglinksets.cpp @@ -330,7 +323,6 @@ set(viewer_SOURCE_FILES llgroupmgr.cpp llhasheduniqueid.cpp llhints.cpp - llhomelocationresponder.cpp llhttpretrypolicy.cpp llhudeffect.cpp llhudeffectbeam.cpp @@ -569,7 +561,6 @@ set(viewer_SOURCE_FILES lltextureinfo.cpp lltextureinfodetails.cpp lltexturestats.cpp - lltexturestatsuploader.cpp lltextureview.cpp lltoast.cpp lltoastalertpanel.cpp @@ -605,7 +596,6 @@ set(viewer_SOURCE_FILES lltwitterconnect.cpp lluilistener.cpp lluploaddialog.cpp - lluploadfloaterobservers.cpp llurl.cpp llurldispatcher.cpp llurldispatcherlistener.cpp @@ -618,6 +608,7 @@ set(viewer_SOURCE_FILES llviewerassetstats.cpp llviewerassetstorage.cpp llviewerassettype.cpp + llviewerassetupload.cpp llviewerattachmenu.cpp llvieweraudio.cpp llviewercamera.cpp @@ -625,7 +616,6 @@ set(viewer_SOURCE_FILES llviewercontrol.cpp llviewercontrollistener.cpp llviewerdisplay.cpp - llviewerdisplayname.cpp llviewerfloaterreg.cpp llviewerfoldertype.cpp llviewergenericmessage.cpp @@ -740,8 +730,6 @@ set(viewer_HEADER_FILES llappearancemgr.h llappviewer.h llappviewerlistener.h - llassetuploadqueue.h - llassetuploadresponders.h llattachmentsmgr.h llaudiosourcevo.h llautoreplace.h @@ -758,9 +746,7 @@ set(viewer_HEADER_FILES llbreadcrumbview.h llbuycurrencyhtml.h llcallingcard.h - llcapabilitylistener.h llcapabilityprovider.h - llcaphttpsender.h llchannelmanager.h llchatbar.h llchathistory.h @@ -769,7 +755,6 @@ set(viewer_HEADER_FILES llchiclet.h llchicletbar.h llclassifiedinfo.h - llclassifiedstatsresponder.h llcofwearables.h llcolorswatch.h llcommanddispatcherlistener.h @@ -814,7 +799,6 @@ set(viewer_HEADER_FILES lleventnotifier.h lleventpoll.h llexpandabletextbox.h - llexperienceassociationresponder.h llexperiencelog.h llexternaleditor.h llface.h @@ -852,7 +836,6 @@ set(viewer_HEADER_FILES llfloaterdeleteprefpreset.h llfloaterdeleteenvpreset.h llfloaterdestinations.h - llfloaterdisplayname.h llfloatereditdaycycle.h llfloatereditsky.h llfloatereditwater.h @@ -899,7 +882,6 @@ set(viewer_HEADER_FILES llfloaternotificationstabbed.h llfloaterobjectweights.h llfloateropenobject.h - llfloateroutbox.h llfloaterpathfindingcharacters.h llfloaterpathfindingconsole.h llfloaterpathfindinglinksets.h @@ -957,7 +939,6 @@ set(viewer_HEADER_FILES llhasheduniqueid.h llhints.h llhttpretrypolicy.h - llhomelocationresponder.h llhudeffect.h llhudeffectbeam.h llhudeffectlookat.h @@ -1185,7 +1166,6 @@ set(viewer_HEADER_FILES lltextureinfo.h lltextureinfodetails.h lltexturestats.h - lltexturestatsuploader.h lltextureview.h lltoast.h lltoastalertpanel.h @@ -1235,6 +1215,7 @@ set(viewer_HEADER_FILES llviewerassetstats.h llviewerassetstorage.h llviewerassettype.h + llviewerassetupload.h llviewerattachmenu.h llvieweraudio.h llviewercamera.h @@ -1242,7 +1223,6 @@ set(viewer_HEADER_FILES llviewercontrol.h llviewercontrollistener.h llviewerdisplay.h - llviewerdisplayname.h llviewerfloaterreg.h llviewerfoldertype.h llviewergenericmessage.h @@ -1760,8 +1740,6 @@ if (WINDOWS) ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/vivoxsdk.dll ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/ortp.dll ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/libsndfile-1.dll - ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/zlib1.dll - ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/vivoxplatform.dll ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/vivoxoal.dll ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/ca-bundle.crt ${GOOGLE_PERF_TOOLS_SOURCE} @@ -1955,8 +1933,8 @@ target_link_libraries(${VIEWER_BINARY_NAME} ${viewer_LIBRARIES} ${BOOST_PROGRAM_OPTIONS_LIBRARY} ${BOOST_REGEX_LIBRARY} - ${BOOST_CONTEXT_LIBRARY} ${BOOST_COROUTINE_LIBRARY} + ${BOOST_CONTEXT_LIBRARY} ${DBUSGLIB_LIBRARIES} ${OPENGL_LIBRARIES} ${FMODWRAPPER_LIBRARY} # must come after LLAudio @@ -2202,10 +2180,9 @@ if (LL_TESTS) SET(viewer_TEST_SOURCE_FILES llagentaccess.cpp lldateutil.cpp - llmediadataclient.cpp +# llmediadataclient.cpp lllogininstance.cpp - llremoteparcelrequest.cpp - lltranslate.cpp +# llremoteparcelrequest.cpp llviewerhelputil.cpp llversioninfo.cpp llworldmap.cpp @@ -2222,20 +2199,15 @@ if (LL_TESTS) ) set(test_libs + ${LLCOMMON_LIBRARIES} ${JSONCPP_LIBRARIES} ${CURL_LIBRARIES} ) set_source_files_properties( - lltranslate.cpp - PROPERTIES - LL_TEST_ADDITIONAL_LIBRARIES "${test_libs}" - ) - - set_source_files_properties( llmediadataclient.cpp PROPERTIES - LL_TEST_ADDITIONAL_LIBRARIES "${CURL_LIBRARIES}" + LL_TEST_ADDITIONAL_LIBRARIES "${test_libs}" ) set_source_files_properties( @@ -2291,7 +2263,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 ################################################## @@ -2307,26 +2278,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_COROUTINE_LIBRARY} + ${BOOST_CONTEXT_LIBRARY} ) - LL_ADD_INTEGRATION_TEST(llsechandler_basic + LL_ADD_INTEGRATION_TEST(llsechandler_basic llsechandler_basic.cpp "${test_libs}" ) @@ -2357,13 +2331,12 @@ if (LL_TESTS) "${test_libs}" ) - LL_ADD_INTEGRATION_TEST(llhttpretrypolicy "llhttpretrypolicy.cpp" "${test_libs}") +# LL_ADD_INTEGRATION_TEST(llhttpretrypolicy "llhttpretrypolicy.cpp" "${test_libs}") #ADD_VIEWER_BUILD_TEST(llmemoryview viewer) #ADD_VIEWER_BUILD_TEST(llagentaccess viewer) #ADD_VIEWER_BUILD_TEST(lltextureinfo viewer) #ADD_VIEWER_BUILD_TEST(lltextureinfodetails viewer) - #ADD_VIEWER_BUILD_TEST(lltexturestatsuploader viewer) include(LLAddBuildTest) SET(viewer_TEST_SOURCE_FILES diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt index 4d54daddb6..c5106e6d13 100644 --- a/indra/newview/VIEWER_VERSION.txt +++ b/indra/newview/VIEWER_VERSION.txt @@ -1 +1 @@ -4.0.2 +4.0.4 diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 6c30c93ae7..355e1b766b 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -2414,6 +2414,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> @@ -14431,6 +14442,24 @@ <key>Value</key> <integer>1</integer> </map> + <key>PoolSizeAIS</key> + <map> + <key>Comment</key> + <string>Coroutine Pool size for AIS</string> + <key>Type</key> + <string>U32</string> + <key>Value</key> + <integer>25</integer> + </map> + <key>PoolSizeUpload</key> + <map> + <key>Comment</key> + <string>Coroutine Pool size for Upload</string> + <key>Type</key> + <string>U32</string> + <key>Value</key> + <real>1</real> + </map> <!-- Settings below are for back compatibility only. They are not used in current viewer anymore. But they can't be removed to avoid diff --git a/indra/newview/llaccountingcostmanager.cpp b/indra/newview/llaccountingcostmanager.cpp index a42286a9e4..92a5413adb 100644 --- a/indra/newview/llaccountingcostmanager.cpp +++ b/indra/newview/llaccountingcostmanager.cpp @@ -27,90 +27,145 @@ #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" +#include <algorithm> +#include <iterator> + //=============================================================================== LLAccountingCostManager::LLAccountingCostManager() { + } -//=============================================================================== -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; + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("AccountingCost", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + + try + { + uuid_set_t diffSet; + + std::set_difference(mObjectList.begin(), mObjectList.end(), + mPendingObjectQuota.begin(), mPendingObjectQuota.end(), + std::inserter(diffSet, diffSet.begin())); + + if (diffSet.empty()) + return; + + mObjectList.clear(); + + std::string keystr; + if (selectionType == Roots) + { + keystr = "selected_roots"; + } + else if (selectionType == Prims) + { + keystr = "selected_prims"; + } + else + { + LL_INFOS() << "Invalid selection type " << LL_ENDL; + return; + } + + LLSD objectList(LLSD::emptyMap()); + + for (uuid_set_t::iterator it = diffSet.begin(); it != diffSet.end(); ++it) + { + objectList.append(*it); + } + + mPendingObjectQuota.insert(diffSet.begin(), diffSet.end()); + + LLSD dataToPost = LLSD::emptyMap(); + dataToPost[keystr.c_str()] = objectList; - // Current request ID - LLUUID mTransactionID; + LLAccountingCostObserver* observer = observerHandle.get(); + LLUUID transactionId = observer->getTransactionID(); + observer = NULL; + + + + LLSD results = httpAdapter->postAndSuspend(httpRequest, url, dataToPost); + + LLSD httpResults = results["http_result"]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + // do/while(false) allows error conditions to break out of following + // block while normal flow goes forward once. + do + { + observer = observerHandle.get(); + + if (!status || results.has("error")) + { + LL_WARNS() << "Error on fetched data" << LL_ENDL; + if (!status) + observer->setErrorStatus(status.getType(), status.toString()); + else + observer->setErrorStatus(499, "Error on fetched data"); + + 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.has("selected")) + { + LLSD selected = results["selected"]; + + F32 physicsCost = 0.0f; + F32 networkCost = 0.0f; + F32 simulationCost = 0.0f; + + physicsCost = selected["physics"].asReal(); + networkCost = selected["streaming"].asReal(); + simulationCost = 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 +174,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..f251ceffd4 100644 --- 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 @@ -64,11 +71,13 @@ public: private: //Set of objects that will be used to generate a cost - std::set<LLUUID> mObjectList; + uuid_set_t mObjectList; //During fetchCosts we move object into a the pending set to signify that //a fetch has been instigated. - std::set<LLUUID> mPendingObjectQuota; - typedef std::set<LLUUID>::iterator IDIt; + uuid_set_t mPendingObjectQuota; + + void accountingCostCoro(std::string url, eSelectionType selectionType, const LLHandle<LLAccountingCostObserver> observerHandle); + }; //=============================================================================== diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index ff7944b3db..d933537d2e 100644 --- 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" @@ -64,7 +62,6 @@ #include "llparcel.h" #include "llrendersphere.h" #include "llscriptruntimeperms.h" -#include "llsdmessage.h" #include "llsdutil.h" #include "llsky.h" #include "llslurl.h" @@ -95,6 +92,7 @@ #include "llworldmap.h" #include "stringize.h" #include "boost/foreach.hpp" +#include "llcorehttputil.h" using namespace LLAvatarAppearanceDefines; @@ -376,7 +374,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(), @@ -476,6 +475,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; } @@ -942,7 +945,7 @@ LLHost LLAgent::getRegionHost() const } else { - return LLHost::invalid; + return LLHost(); } } @@ -965,6 +968,15 @@ BOOL LLAgent::inPrelude() } +std::string LLAgent::getRegionCapability(const std::string &name) +{ + if (!mRegionp) + return std::string(); + + return mRegionp->getCapability(name); +} + + //----------------------------------------------------------------------------- // canManageEstate() //----------------------------------------------------------------------------- @@ -2341,27 +2353,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 ) @@ -2370,32 +2364,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 ) { @@ -2532,87 +2545,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 @@ -2741,42 +2673,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(); @@ -3733,6 +3719,7 @@ void LLAgent::clearVisualParams(void *data) // protected bool LLAgent::teleportCore(bool is_local) { + LL_INFOS("Teleport") << "In teleport core!" << LL_ENDL; if ((TELEPORT_NONE != mTeleportState) && (mTeleportState != TELEPORT_PENDING)) { LL_WARNS() << "Attempt to teleport when already teleporting." << LL_ENDL; @@ -3826,6 +3813,7 @@ bool LLAgent::hasRestartableFailedTeleportRequest() void LLAgent::restartFailedTeleportRequest() { + LL_INFOS("Teleport") << "Agent wishes to restart failed teleport." << LL_ENDL; if (hasRestartableFailedTeleportRequest()) { mTeleportRequest->setStatus(LLTeleportRequest::kRestartPending); @@ -3857,6 +3845,7 @@ bool LLAgent::hasPendingTeleportRequest() void LLAgent::startTeleportRequest() { + LL_INFOS("Telport") << "Agent handling start teleport request." << LL_ENDL; if(LLVoiceClient::instanceExists()) { LLVoiceClient::getInstance()->setHidden(TRUE); @@ -3892,6 +3881,7 @@ void LLAgent::startTeleportRequest() void LLAgent::handleTeleportFinished() { + LL_INFOS("Teleport") << "Agent handling teleport finished." << LL_ENDL; clearTeleportRequest(); mTeleportCanceled.reset(); if (mIsMaturityRatingChangingDuringTeleport) @@ -3914,12 +3904,18 @@ void LLAgent::handleTeleportFinished() void LLAgent::handleTeleportFailed() { + LL_WARNS("Teleport") << "Agent handling teleport failure!" << LL_ENDL; if(LLVoiceClient::instanceExists()) { LLVoiceClient::getInstance()->setHidden(FALSE); } - if (mTeleportRequest != NULL) + setTeleportState(LLAgent::TELEPORT_NONE); + // Unlock the UI if the progress bar has been shown. +// gViewerWindow->setShowProgress(FALSE); +// gTeleportDisplay = FALSE; + + if (mTeleportRequest) { mTeleportRequest->setStatus(LLTeleportRequest::kFailed); } @@ -4131,8 +4127,22 @@ void LLAgent::doTeleportViaLocationLookAt(const LLVector3d& pos_global) teleportRequest(region_handle, pos_local, getTeleportKeepsLookAt()); } +LLAgent::ETeleportState LLAgent::getTeleportState() const +{ + return (mTeleportRequest && (mTeleportRequest->getStatus() == LLTeleportRequest::kFailed)) ? + TELEPORT_NONE : mTeleportState; +} + + void LLAgent::setTeleportState(ETeleportState state) { + if (mTeleportRequest && (state != TELEPORT_NONE) && (mTeleportRequest->getStatus() == LLTeleportRequest::kFailed)) + { // A late message has come in regarding a failed teleport. + // We have already decided that it failed so should not reinitiate the teleport sequence in the viewer. + LL_WARNS("Teleport") << "Attempt to set teleport state to " << state << + " for previously failed teleport. Ignore!" << LL_ENDL; + return; + } mTeleportState = state; if (mTeleportState > TELEPORT_NONE && gSavedSettings.getBOOL("FreezeTime")) { @@ -4480,24 +4490,29 @@ LLTeleportRequestViaLandmark::LLTeleportRequestViaLandmark(const LLUUID &pLandma : LLTeleportRequest(), mLandmarkId(pLandmarkId) { + LL_INFOS("Teleport") << "LLTeleportRequestViaLandmark created." << LL_ENDL; } LLTeleportRequestViaLandmark::~LLTeleportRequestViaLandmark() { + LL_INFOS("Teleport") << "~LLTeleportRequestViaLandmark" << LL_ENDL; } bool LLTeleportRequestViaLandmark::canRestartTeleport() { + LL_INFOS("Teleport") << "LLTeleportRequestViaLandmark::canRestartTeleport? -> true" << LL_ENDL; return true; } void LLTeleportRequestViaLandmark::startTeleport() { + LL_INFOS("Teleport") << "LLTeleportRequestViaLandmark::startTeleport" << LL_ENDL; gAgent.doTeleportViaLandmark(getLandmarkId()); } void LLTeleportRequestViaLandmark::restartTeleport() { + LL_INFOS("Teleport") << "LLTeleportRequestViaLandmark::restartTeleport" << LL_ENDL; gAgent.doTeleportViaLandmark(getLandmarkId()); } @@ -4509,10 +4524,12 @@ LLTeleportRequestViaLure::LLTeleportRequestViaLure(const LLUUID &pLureId, BOOL p : LLTeleportRequestViaLandmark(pLureId), mIsLureGodLike(pIsLureGodLike) { + LL_INFOS("Teleport") << "LLTeleportRequestViaLure created" << LL_ENDL; } LLTeleportRequestViaLure::~LLTeleportRequestViaLure() { + LL_INFOS("Teleport") << "~LLTeleportRequestViaLure" << LL_ENDL; } bool LLTeleportRequestViaLure::canRestartTeleport() @@ -4529,11 +4546,13 @@ bool LLTeleportRequestViaLure::canRestartTeleport() // 8. User B's viewer then attempts to teleport via lure again // 9. This request will time-out on the viewer-side because User A's initial request has been removed from the "queue" in step 4 - return false; + LL_INFOS("Teleport") << "LLTeleportRequestViaLure::canRestartTeleport? -> false" << LL_ENDL; + return false; } void LLTeleportRequestViaLure::startTeleport() { + LL_INFOS("Teleport") << "LLTeleportRequestViaLure::startTeleport" << LL_ENDL; gAgent.doTeleportViaLure(getLandmarkId(), isLureGodLike()); } @@ -4545,25 +4564,30 @@ LLTeleportRequestViaLocation::LLTeleportRequestViaLocation(const LLVector3d &pPo : LLTeleportRequest(), mPosGlobal(pPosGlobal) { + LL_INFOS("Teleport") << "LLTeleportRequestViaLocation created" << LL_ENDL; } LLTeleportRequestViaLocation::~LLTeleportRequestViaLocation() { + LL_INFOS("Teleport") << "~LLTeleportRequestViaLocation" << LL_ENDL; } bool LLTeleportRequestViaLocation::canRestartTeleport() { + LL_INFOS("Teleport") << "LLTeleportRequestViaLocation::canRestartTeleport -> true" << LL_ENDL; return true; } void LLTeleportRequestViaLocation::startTeleport() { + LL_INFOS("Teleport") << "LLTeleportRequestViaLocation::startTeleport" << LL_ENDL; gAgent.doTeleportViaLocation(getPosGlobal()); } void LLTeleportRequestViaLocation::restartTeleport() { - gAgent.doTeleportViaLocation(getPosGlobal()); + LL_INFOS("Teleport") << "LLTeleportRequestViaLocation::restartTeleport" << LL_ENDL; + gAgent.doTeleportViaLocation(getPosGlobal()); } //----------------------------------------------------------------------------- @@ -4573,25 +4597,30 @@ void LLTeleportRequestViaLocation::restartTeleport() LLTeleportRequestViaLocationLookAt::LLTeleportRequestViaLocationLookAt(const LLVector3d &pPosGlobal) : LLTeleportRequestViaLocation(pPosGlobal) { + LL_INFOS("Teleport") << "LLTeleportRequestViaLocationLookAt created" << LL_ENDL; } LLTeleportRequestViaLocationLookAt::~LLTeleportRequestViaLocationLookAt() { + LL_INFOS("Teleport") << "~LLTeleportRequestViaLocationLookAt" << LL_ENDL; } bool LLTeleportRequestViaLocationLookAt::canRestartTeleport() { - return true; + LL_INFOS("Teleport") << "LLTeleportRequestViaLocationLookAt::canRestartTeleport -> true" << LL_ENDL; + return true; } void LLTeleportRequestViaLocationLookAt::startTeleport() { - gAgent.doTeleportViaLocationLookAt(getPosGlobal()); + LL_INFOS("Teleport") << "LLTeleportRequestViaLocationLookAt::startTeleport" << LL_ENDL; + gAgent.doTeleportViaLocationLookAt(getPosGlobal()); } void LLTeleportRequestViaLocationLookAt::restartTeleport() { - gAgent.doTeleportViaLocationLookAt(getPosGlobal()); + LL_INFOS("Teleport") << "LLTeleportRequestViaLocationLookAt::restartTeleport" << LL_ENDL; + gAgent.doTeleportViaLocationLookAt(getPosGlobal()); } // EOF diff --git a/indra/newview/llagent.h b/indra/newview/llagent.h index 9e8550e280..3a533c2cba 100644 --- a/indra/newview/llagent.h +++ b/indra/newview/llagent.h @@ -34,7 +34,10 @@ #include "llcoordframe.h" // for mFrameAgent #include "llavatarappearancedefines.h" #include "llpermissionsflags.h" +#include "llevents.h" #include "v3dmath.h" +#include "httprequest.h" +#include "llcorehttputil.h" #include <boost/function.hpp> #include <boost/shared_ptr.hpp> @@ -61,6 +64,8 @@ class LLPauseRequestHandle; class LLUIColor; class LLTeleportRequest; + + typedef boost::shared_ptr<LLTeleportRequest> LLTeleportRequestPtr; //-------------------------------------------------------------------- @@ -112,6 +117,8 @@ public: void init(); void cleanup(); +private: + //-------------------------------------------------------------------- // Login //-------------------------------------------------------------------- @@ -227,6 +234,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; @@ -254,6 +263,9 @@ public: LLHost getRegionHost() const; BOOL inPrelude(); + // Capability + std::string getRegionCapability(const std::string &name); // short hand for if (getRegion()) { getRegion()->getCapability(name) } + /** * Register a boost callback to be called when the agent changes regions * Note that if you need to access a capability for the region, you may need to wait @@ -634,6 +646,8 @@ public: void setMaturityRatingChangeDuringTeleport(U8 pMaturityRatingChange); private: + + friend class LLTeleportRequest; friend class LLTeleportRequestViaLandmark; friend class LLTeleportRequestViaLure; @@ -666,7 +680,7 @@ private: // Teleport State //-------------------------------------------------------------------- public: - ETeleportState getTeleportState() const { return mTeleportState; } + ETeleportState getTeleportState() const; void setTeleportState(ETeleportState state); private: ETeleportState mTeleportState; @@ -762,11 +776,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(); @@ -915,6 +930,24 @@ 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); + + LLCore::HttpRequest::policy_t getAgentPolicy() const { return mHttpPolicy; } + +/** Utility + ** ** + *******************************************************************************/ + +/******************************************************************************** + ** ** ** DEBUGGING **/ diff --git a/indra/newview/llagentlanguage.cpp b/indra/newview/llagentlanguage.cpp index fe6236a32a..cdb0e3302d 100644 --- 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/llaisapi.cpp b/indra/newview/llaisapi.cpp index d8b6cc729d..3e3d5c7456 100644 --- a/indra/newview/llaisapi.cpp +++ b/indra/newview/llaisapi.cpp @@ -40,317 +40,381 @@ /// Classes for AISv3 support. ///---------------------------------------------------------------------------- -// AISCommand - base class for retry-able HTTP requests using the AISv3 cap. -AISCommand::AISCommand(LLPointer<LLInventoryCallback> callback): - mCommandFunc(NULL), - mCallback(callback) +//========================================================================= +const std::string AISAPI::INVENTORY_CAP_NAME("InventoryAPIv3"); +const std::string AISAPI::LIBRARY_CAP_NAME("LibraryAPIv3"); + +//------------------------------------------------------------------------- +/*static*/ +bool AISAPI::isAvailable() { - mRetryPolicy = new LLAdaptiveRetryPolicy(1.0, 32.0, 2.0, 10); + if (gAgent.getRegion()) + { + return gAgent.getRegion()->isCapabilityAvailable(INVENTORY_CAP_NAME); + } + return false; } -bool AISCommand::run_command() +/*static*/ +void AISAPI::getCapNames(LLSD& capNames) { - if (NULL == mCommandFunc) - { - // This may happen if a command failed to initiate itself. - LL_WARNS("Inventory") << "AIS command attempted with null command function" << LL_ENDL; - return false; - } - else - { - mCommandFunc(); - return true; - } + capNames.append(INVENTORY_CAP_NAME); + capNames.append(LIBRARY_CAP_NAME); } -void AISCommand::setCommandFunc(command_func_type command_func) +/*static*/ +std::string AISAPI::getInvCap() { - mCommandFunc = command_func; + if (gAgent.getRegion()) + { + return gAgent.getRegion()->getCapability(INVENTORY_CAP_NAME); + } + return std::string(); } - -// virtual -bool AISCommand::getResponseUUID(const LLSD& content, LLUUID& id) + +/*static*/ +std::string AISAPI::getLibCap() { - return false; + if (gAgent.getRegion()) + { + return gAgent.getRegion()->getCapability(LIBRARY_CAP_NAME); + } + return std::string(); } - -/* virtual */ -void AISCommand::httpSuccess() + +/*static*/ +void AISAPI::CreateInventory(const LLUUID& parentId, const LLSD& newInventory, completion_t callback) { - // Command func holds a reference to self, need to release it - // after a success or final failure. - setCommandFunc(no_op); - - const LLSD& content = getContent(); - if (!content.isMap()) - { - failureResult(HTTP_INTERNAL_ERROR, "Malformed response contents", content); - return; - } - mRetryPolicy->onSuccess(); - - gInventory.onAISUpdateReceived("AISCommand", content); + std::string cap = getInvCap(); + if (cap.empty()) + { + LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL; + return; + } - if (mCallback) - { - LLUUID id; // will default to null if parse fails. - getResponseUUID(content,id); - mCallback->fire(id); - } -} + LLUUID tid; + tid.generate(); + + std::string url = cap + std::string("/category/") + parentId.asString() + "?tid=" + tid.asString(); + LL_DEBUGS("Inventory") << "url: " << url << LL_ENDL; + + // I may be suffering from golden hammer here, but the first part of this bind + // is actually a static cast for &HttpCoroutineAdapter::postAndSuspend so that + // the compiler can identify the correct signature to select. + // + // Reads as follows: + // LLSD - method returning LLSD + // (LLCoreHttpUtil::HttpCoroutineAdapter::*) - pointer to member function of HttpCoroutineAdapter + // (LLCore::HttpRequest::ptr_t, const std::string &, const LLSD &, LLCore::HttpOptions::ptr_t, LLCore::HttpHeaders::ptr_t) - signature of method + // + invokationFn_t postFn = boost::bind( + // Humans ignore next line. It is just a cast. + static_cast<LLSD (LLCoreHttpUtil::HttpCoroutineAdapter::*)(LLCore::HttpRequest::ptr_t, const std::string &, const LLSD &, LLCore::HttpOptions::ptr_t, LLCore::HttpHeaders::ptr_t)> + //---- + // _1 -> httpAdapter + // _2 -> httpRequest + // _3 -> url + // _4 -> body + // _5 -> httpOptions + // _6 -> httpHeaders + (&LLCoreHttpUtil::HttpCoroutineAdapter::postAndSuspend), _1, _2, _3, _4, _5, _6); + + LLCoprocedureManager::CoProcedure_t proc(boost::bind(&AISAPI::InvokeAISCommandCoro, + _1, postFn, url, parentId, newInventory, callback, COPYINVENTORY)); + EnqueueAISCommand("CreateInventory", proc); +} + +/*static*/ +void AISAPI::SlamFolder(const LLUUID& folderId, const LLSD& newInventory, completion_t callback) +{ + std::string cap = getInvCap(); + if (cap.empty()) + { + LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL; + return; + } -/*virtual*/ -void AISCommand::httpFailure() -{ - LL_WARNS("Inventory") << dumpResponse() << LL_ENDL; - S32 status = getStatus(); - const LLSD& headers = getResponseHeaders(); - mRetryPolicy->onFailure(status, headers); - F32 seconds_to_wait; - if (mRetryPolicy->shouldRetry(seconds_to_wait)) - { - doAfterInterval(boost::bind(&AISCommand::run_command,this),seconds_to_wait); - } - else - { - // Command func holds a reference to self, need to release it - // after a success or final failure. - // *TODO: Notify user? This seems bad. - setCommandFunc(no_op); - } -} + LLUUID tid; + tid.generate(); -//static -bool AISCommand::isAPIAvailable() -{ - if (gAgent.getRegion()) - { - return gAgent.getRegion()->isCapabilityAvailable("InventoryAPIv3"); - } - return false; -} + std::string url = cap + std::string("/category/") + folderId.asString() + "/links?tid=" + tid.asString(); -//static -bool AISCommand::getInvCap(std::string& cap) -{ - if (gAgent.getRegion()) - { - cap = gAgent.getRegion()->getCapability("InventoryAPIv3"); - } - if (!cap.empty()) - { - return true; - } - return false; -} + // see comment above in CreateInventoryCommand + invokationFn_t putFn = boost::bind( + // Humans ignore next line. It is just a cast to specify which LLCoreHttpUtil::HttpCoroutineAdapter routine overload. + static_cast<LLSD(LLCoreHttpUtil::HttpCoroutineAdapter::*)(LLCore::HttpRequest::ptr_t, const std::string &, const LLSD &, LLCore::HttpOptions::ptr_t, LLCore::HttpHeaders::ptr_t)> + //---- + // _1 -> httpAdapter + // _2 -> httpRequest + // _3 -> url + // _4 -> body + // _5 -> httpOptions + // _6 -> httpHeaders + (&LLCoreHttpUtil::HttpCoroutineAdapter::putAndSuspend), _1, _2, _3, _4, _5, _6); -//static -bool AISCommand::getLibCap(std::string& cap) -{ - if (gAgent.getRegion()) - { - cap = gAgent.getRegion()->getCapability("LibraryAPIv3"); - } - if (!cap.empty()) - { - return true; - } - return false; -} + LLCoprocedureManager::CoProcedure_t proc(boost::bind(&AISAPI::InvokeAISCommandCoro, + _1, putFn, url, folderId, newInventory, callback, SLAMFOLDER)); -//static -void AISCommand::getCapabilityNames(LLSD& capabilityNames) -{ - capabilityNames.append("InventoryAPIv3"); - capabilityNames.append("LibraryAPIv3"); + EnqueueAISCommand("SlamFolder", proc); } -RemoveItemCommand::RemoveItemCommand(const LLUUID& item_id, - LLPointer<LLInventoryCallback> callback): - AISCommand(callback) +void AISAPI::RemoveCategory(const LLUUID &categoryId, completion_t callback) { - std::string cap; - if (!getInvCap(cap)) - { - LL_WARNS() << "No cap found" << LL_ENDL; - return; - } - std::string url = cap + std::string("/item/") + item_id.asString(); - LL_DEBUGS("Inventory") << "url: " << url << LL_ENDL; - LLHTTPClient::ResponderPtr responder = this; - LLSD headers; - F32 timeout = HTTP_REQUEST_EXPIRY_SECS; - command_func_type cmd = boost::bind(&LLHTTPClient::del, url, responder, headers, timeout); - setCommandFunc(cmd); -} + std::string cap; -RemoveCategoryCommand::RemoveCategoryCommand(const LLUUID& item_id, - LLPointer<LLInventoryCallback> callback): - AISCommand(callback) -{ - std::string cap; - if (!getInvCap(cap)) - { - LL_WARNS() << "No cap found" << LL_ENDL; - return; - } - std::string url = cap + std::string("/category/") + item_id.asString(); - LL_DEBUGS("Inventory") << "url: " << url << LL_ENDL; - LLHTTPClient::ResponderPtr responder = this; - LLSD headers; - F32 timeout = HTTP_REQUEST_EXPIRY_SECS; - command_func_type cmd = boost::bind(&LLHTTPClient::del, url, responder, headers, timeout); - setCommandFunc(cmd); + cap = getInvCap(); + if (cap.empty()) + { + LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL; + return; + } + + std::string url = cap + std::string("/category/") + categoryId.asString(); + LL_DEBUGS("Inventory") << "url: " << url << LL_ENDL; + + invokationFn_t delFn = boost::bind( + // Humans ignore next line. It is just a cast to specify which LLCoreHttpUtil::HttpCoroutineAdapter routine overload. + static_cast<LLSD(LLCoreHttpUtil::HttpCoroutineAdapter::*)(LLCore::HttpRequest::ptr_t, const std::string &, LLCore::HttpOptions::ptr_t, LLCore::HttpHeaders::ptr_t)> + //---- + // _1 -> httpAdapter + // _2 -> httpRequest + // _3 -> url + // _4 -> body + // _5 -> httpOptions + // _6 -> httpHeaders + (&LLCoreHttpUtil::HttpCoroutineAdapter::deleteAndSuspend), _1, _2, _3, _5, _6); + + LLCoprocedureManager::CoProcedure_t proc(boost::bind(&AISAPI::InvokeAISCommandCoro, + _1, delFn, url, categoryId, LLSD(), callback, REMOVECATEGORY)); + + EnqueueAISCommand("RemoveCategory", proc); } -PurgeDescendentsCommand::PurgeDescendentsCommand(const LLUUID& item_id, - LLPointer<LLInventoryCallback> callback): - AISCommand(callback) +/*static*/ +void AISAPI::RemoveItem(const LLUUID &itemId, completion_t callback) { - std::string cap; - if (!getInvCap(cap)) - { - LL_WARNS() << "No cap found" << LL_ENDL; - return; - } - std::string url = cap + std::string("/category/") + item_id.asString() + "/children"; - LL_DEBUGS("Inventory") << "url: " << url << LL_ENDL; - LLCurl::ResponderPtr responder = this; - LLSD headers; - F32 timeout = HTTP_REQUEST_EXPIRY_SECS; - command_func_type cmd = boost::bind(&LLHTTPClient::del, url, responder, headers, timeout); - setCommandFunc(cmd); + std::string cap; + + cap = getInvCap(); + if (cap.empty()) + { + LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL; + return; + } + + std::string url = cap + std::string("/item/") + itemId.asString(); + LL_DEBUGS("Inventory") << "url: " << url << LL_ENDL; + + invokationFn_t delFn = boost::bind( + // Humans ignore next line. It is just a cast to specify which LLCoreHttpUtil::HttpCoroutineAdapter routine overload. + static_cast<LLSD(LLCoreHttpUtil::HttpCoroutineAdapter::*)(LLCore::HttpRequest::ptr_t, const std::string &, LLCore::HttpOptions::ptr_t, LLCore::HttpHeaders::ptr_t)> + //---- + // _1 -> httpAdapter + // _2 -> httpRequest + // _3 -> url + // _4 -> body + // _5 -> httpOptions + // _6 -> httpHeaders + (&LLCoreHttpUtil::HttpCoroutineAdapter::deleteAndSuspend), _1, _2, _3, _5, _6); + + LLCoprocedureManager::CoProcedure_t proc(boost::bind(&AISAPI::InvokeAISCommandCoro, + _1, delFn, url, itemId, LLSD(), callback, REMOVEITEM)); + + EnqueueAISCommand("RemoveItem", proc); } -UpdateItemCommand::UpdateItemCommand(const LLUUID& item_id, - const LLSD& updates, - LLPointer<LLInventoryCallback> callback): - mUpdates(updates), - AISCommand(callback) +void AISAPI::CopyLibraryCategory(const LLUUID& sourceId, const LLUUID& destId, bool copySubfolders, completion_t callback) { - std::string cap; - if (!getInvCap(cap)) - { - LL_WARNS() << "No cap found" << LL_ENDL; - return; - } - std::string url = cap + std::string("/item/") + item_id.asString(); - LL_DEBUGS("Inventory") << "url: " << url << LL_ENDL; - LL_DEBUGS("Inventory") << "request: " << ll_pretty_print_sd(mUpdates) << LL_ENDL; - LLCurl::ResponderPtr responder = this; - LLSD headers; - headers["Content-Type"] = "application/llsd+xml"; - F32 timeout = HTTP_REQUEST_EXPIRY_SECS; - command_func_type cmd = boost::bind(&LLHTTPClient::patch, url, mUpdates, responder, headers, timeout); - setCommandFunc(cmd); + std::string cap; + + cap = getLibCap(); + if (cap.empty()) + { + LL_WARNS("Inventory") << "Library cap not found!" << LL_ENDL; + return; + } + + LL_DEBUGS("Inventory") << "Copying library category: " << sourceId << " => " << destId << LL_ENDL; + + LLUUID tid; + tid.generate(); + + std::string url = cap + std::string("/category/") + sourceId.asString() + "?tid=" + tid.asString(); + if (!copySubfolders) + { + url += ",depth=0"; + } + LL_INFOS() << url << LL_ENDL; + + std::string destination = destId.asString(); + + invokationFn_t copyFn = boost::bind( + // Humans ignore next line. It is just a cast to specify which LLCoreHttpUtil::HttpCoroutineAdapter routine overload. + static_cast<LLSD(LLCoreHttpUtil::HttpCoroutineAdapter::*)(LLCore::HttpRequest::ptr_t, const std::string &, const std::string, LLCore::HttpOptions::ptr_t, LLCore::HttpHeaders::ptr_t)> + //---- + // _1 -> httpAdapter + // _2 -> httpRequest + // _3 -> url + // _4 -> body + // _5 -> httpOptions + // _6 -> httpHeaders + (&LLCoreHttpUtil::HttpCoroutineAdapter::copyAndSuspend), _1, _2, _3, destination, _5, _6); + + LLCoprocedureManager::CoProcedure_t proc(boost::bind(&AISAPI::InvokeAISCommandCoro, + _1, copyFn, url, destId, LLSD(), callback, COPYLIBRARYCATEGORY)); + + EnqueueAISCommand("CopyLibraryCategory", proc); } -UpdateCategoryCommand::UpdateCategoryCommand(const LLUUID& cat_id, - const LLSD& updates, - LLPointer<LLInventoryCallback> callback): - mUpdates(updates), - AISCommand(callback) +/*static*/ +void AISAPI::PurgeDescendents(const LLUUID &categoryId, completion_t callback) { - std::string cap; - if (!getInvCap(cap)) - { - LL_WARNS() << "No cap found" << LL_ENDL; - return; - } - std::string url = cap + std::string("/category/") + cat_id.asString(); - LL_DEBUGS("Inventory") << "url: " << url << LL_ENDL; - LLCurl::ResponderPtr responder = this; - LLSD headers; - headers["Content-Type"] = "application/llsd+xml"; - F32 timeout = HTTP_REQUEST_EXPIRY_SECS; - command_func_type cmd = boost::bind(&LLHTTPClient::patch, url, mUpdates, responder, headers, timeout); - setCommandFunc(cmd); + std::string cap; + + cap = getInvCap(); + if (cap.empty()) + { + LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL; + return; + } + + std::string url = cap + std::string("/category/") + categoryId.asString() + "/children"; + LL_DEBUGS("Inventory") << "url: " << url << LL_ENDL; + + invokationFn_t delFn = boost::bind( + // Humans ignore next line. It is just a cast to specify which LLCoreHttpUtil::HttpCoroutineAdapter routine overload. + static_cast<LLSD(LLCoreHttpUtil::HttpCoroutineAdapter::*)(LLCore::HttpRequest::ptr_t, const std::string &, LLCore::HttpOptions::ptr_t, LLCore::HttpHeaders::ptr_t)> + //---- + // _1 -> httpAdapter + // _2 -> httpRequest + // _3 -> url + // _4 -> body + // _5 -> httpOptions + // _6 -> httpHeaders + (&LLCoreHttpUtil::HttpCoroutineAdapter::deleteAndSuspend), _1, _2, _3, _5, _6); + + LLCoprocedureManager::CoProcedure_t proc(boost::bind(&AISAPI::InvokeAISCommandCoro, + _1, delFn, url, categoryId, LLSD(), callback, PURGEDESCENDENTS)); + + EnqueueAISCommand("PurgeDescendents", proc); } -CreateInventoryCommand::CreateInventoryCommand(const LLUUID& parent_id, - const LLSD& new_inventory, - LLPointer<LLInventoryCallback> callback): - mNewInventory(new_inventory), - AISCommand(callback) + +/*static*/ +void AISAPI::UpdateCategory(const LLUUID &categoryId, const LLSD &updates, completion_t callback) { - std::string cap; - if (!getInvCap(cap)) - { - LL_WARNS() << "No cap found" << LL_ENDL; - return; - } - LLUUID tid; - tid.generate(); - std::string url = cap + std::string("/category/") + parent_id.asString() + "?tid=" + tid.asString(); - LL_DEBUGS("Inventory") << "url: " << url << LL_ENDL; - LLCurl::ResponderPtr responder = this; - LLSD headers; - headers["Content-Type"] = "application/llsd+xml"; - F32 timeout = HTTP_REQUEST_EXPIRY_SECS; - command_func_type cmd = boost::bind(&LLHTTPClient::post, url, mNewInventory, responder, headers, timeout); - setCommandFunc(cmd); + std::string cap; + + cap = getInvCap(); + if (cap.empty()) + { + LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL; + return; + } + std::string url = cap + std::string("/category/") + categoryId.asString(); + + invokationFn_t patchFn = boost::bind( + // Humans ignore next line. It is just a cast to specify which LLCoreHttpUtil::HttpCoroutineAdapter routine overload. + static_cast<LLSD(LLCoreHttpUtil::HttpCoroutineAdapter::*)(LLCore::HttpRequest::ptr_t, const std::string &, const LLSD &, LLCore::HttpOptions::ptr_t, LLCore::HttpHeaders::ptr_t)> + //---- + // _1 -> httpAdapter + // _2 -> httpRequest + // _3 -> url + // _4 -> body + // _5 -> httpOptions + // _6 -> httpHeaders + (&LLCoreHttpUtil::HttpCoroutineAdapter::patchAndSuspend), _1, _2, _3, _4, _5, _6); + + LLCoprocedureManager::CoProcedure_t proc(boost::bind(&AISAPI::InvokeAISCommandCoro, + _1, patchFn, url, categoryId, updates, callback, UPDATECATEGORY)); + + EnqueueAISCommand("UpdateCategory", proc); } -SlamFolderCommand::SlamFolderCommand(const LLUUID& folder_id, const LLSD& contents, LLPointer<LLInventoryCallback> callback): - mContents(contents), - AISCommand(callback) +/*static*/ +void AISAPI::UpdateItem(const LLUUID &itemId, const LLSD &updates, completion_t callback) { - std::string cap; - if (!getInvCap(cap)) - { - LL_WARNS() << "No cap found" << LL_ENDL; - return; - } - LLUUID tid; - tid.generate(); - std::string url = cap + std::string("/category/") + folder_id.asString() + "/links?tid=" + tid.asString(); - LL_INFOS() << url << LL_ENDL; - LLCurl::ResponderPtr responder = this; - LLSD headers; - headers["Content-Type"] = "application/llsd+xml"; - F32 timeout = HTTP_REQUEST_EXPIRY_SECS; - command_func_type cmd = boost::bind(&LLHTTPClient::put, url, mContents, responder, headers, timeout); - setCommandFunc(cmd); + + std::string cap; + + cap = getInvCap(); + if (cap.empty()) + { + LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL; + return; + } + std::string url = cap + std::string("/item/") + itemId.asString(); + + invokationFn_t patchFn = boost::bind( + // Humans ignore next line. It is just a cast to specify which LLCoreHttpUtil::HttpCoroutineAdapter routine overload. + static_cast<LLSD(LLCoreHttpUtil::HttpCoroutineAdapter::*)(LLCore::HttpRequest::ptr_t, const std::string &, const LLSD &, LLCore::HttpOptions::ptr_t, LLCore::HttpHeaders::ptr_t)> + //---- + // _1 -> httpAdapter + // _2 -> httpRequest + // _3 -> url + // _4 -> body + // _5 -> httpOptions + // _6 -> httpHeaders + (&LLCoreHttpUtil::HttpCoroutineAdapter::patchAndSuspend), _1, _2, _3, _4, _5, _6); + + LLCoprocedureManager::CoProcedure_t proc(boost::bind(&AISAPI::InvokeAISCommandCoro, + _1, patchFn, url, itemId, updates, callback, UPDATEITEM)); + + EnqueueAISCommand("UpdateItem", proc); } -CopyLibraryCategoryCommand::CopyLibraryCategoryCommand(const LLUUID& source_id, - const LLUUID& dest_id, - LLPointer<LLInventoryCallback> callback, - bool copy_subfolders): - AISCommand(callback) +/*static*/ +void AISAPI::EnqueueAISCommand(const std::string &procName, LLCoprocedureManager::CoProcedure_t proc) { - std::string cap; - if (!getLibCap(cap)) - { - LL_WARNS() << "No cap found" << LL_ENDL; - return; - } - LL_DEBUGS("Inventory") << "Copying library category: " << source_id << " => " << dest_id << LL_ENDL; - LLUUID tid; - tid.generate(); - std::string url = cap + std::string("/category/") + source_id.asString() + "?tid=" + tid.asString(); - if (!copy_subfolders) - { - url += ",depth=0"; - } - LL_INFOS() << url << LL_ENDL; - LLCurl::ResponderPtr responder = this; - LLSD headers; - F32 timeout = HTTP_REQUEST_EXPIRY_SECS; - command_func_type cmd = boost::bind(&LLHTTPClient::copy, url, dest_id.asString(), responder, headers, timeout); - setCommandFunc(cmd); + std::string procFullName = "AIS(" + procName + ")"; + LLCoprocedureManager::instance().enqueueCoprocedure("AIS", procFullName, proc); + } -bool CopyLibraryCategoryCommand::getResponseUUID(const LLSD& content, LLUUID& id) +/*static*/ +void AISAPI::InvokeAISCommandCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter, + invokationFn_t invoke, std::string url, + LLUUID targetId, LLSD body, completion_t callback, COMMAND_TYPE type) { - if (content.has("category_id")) - { - id = content["category_id"]; - return true; - } - return false; + LLCore::HttpOptions::ptr_t httpOptions(new LLCore::HttpOptions); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest()); + LLCore::HttpHeaders::ptr_t httpHeaders; + + httpOptions->setTimeout(LLCoreHttpUtil::HTTP_REQUEST_EXPIRY_SECS); + + LL_DEBUGS("Inventory") << "url: " << url << LL_ENDL; + + LLSD result = invoke(httpAdapter, httpRequest, url, body, httpOptions, httpHeaders); + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status || !result.isMap()) + { + if (!result.isMap()) + { + status = LLCore::HttpStatus(HTTP_INTERNAL_ERROR, "Malformed response contents"); + } + LL_WARNS("Inventory") << "Inventory error: " << status.toString() << LL_ENDL; + LL_WARNS("Inventory") << ll_pretty_print_sd(result) << LL_ENDL; + } + + gInventory.onAISUpdateReceived("AISCommand", result); + + if (callback && !callback.empty()) + { + LLUUID id(LLUUID::null); + + if (result.has("category_id") && (type == COPYLIBRARYCATEGORY)) + { + id = result["category_id"]; + } + + callback(id); + } + } +//------------------------------------------------------------------------- AISUpdate::AISUpdate(const LLSD& update) { parseUpdate(update); diff --git a/indra/newview/llaisapi.h b/indra/newview/llaisapi.h index bb483fb133..e97059014b 100644 --- a/indra/newview/llaisapi.h +++ b/indra/newview/llaisapi.h @@ -31,112 +31,55 @@ #include <map> #include <set> #include <string> -#include "llcurl.h" -#include "llhttpclient.h" #include "llhttpretrypolicy.h" #include "llviewerinventory.h" +#include "llcorehttputil.h" +#include "llcoproceduremanager.h" -class AISCommand: public LLHTTPClient::Responder +class AISAPI { public: - typedef boost::function<void()> command_func_type; + typedef boost::function<void(const LLUUID &invItem)> completion_t; - AISCommand(LLPointer<LLInventoryCallback> callback); + static bool isAvailable(); + static void getCapNames(LLSD& capNames); - virtual ~AISCommand() {} + static void CreateInventory(const LLUUID& parentId, const LLSD& newInventory, completion_t callback = completion_t()); + static void SlamFolder(const LLUUID& folderId, const LLSD& newInventory, completion_t callback = completion_t()); + static void RemoveCategory(const LLUUID &categoryId, completion_t callback = completion_t()); + static void RemoveItem(const LLUUID &itemId, completion_t callback = completion_t()); + static void PurgeDescendents(const LLUUID &categoryId, completion_t callback = completion_t()); + static void UpdateCategory(const LLUUID &categoryId, const LLSD &updates, completion_t callback = completion_t()); + static void UpdateItem(const LLUUID &itemId, const LLSD &updates, completion_t callback = completion_t()); + static void CopyLibraryCategory(const LLUUID& sourceId, const LLUUID& destId, bool copySubfolders, completion_t callback = completion_t()); - bool run_command(); - - void setCommandFunc(command_func_type command_func); - - // Need to do command-specific parsing to get an id here, for - // LLInventoryCallback::fire(). May or may not need to bother, - // since most LLInventoryCallbacks do their work in the - // destructor. - - /* virtual */ void httpSuccess(); - /* virtual */ void httpFailure(); - - static bool isAPIAvailable(); - static bool getInvCap(std::string& cap); - static bool getLibCap(std::string& cap); - static void getCapabilityNames(LLSD& capabilityNames); - -protected: - virtual bool getResponseUUID(const LLSD& content, LLUUID& id); - -private: - command_func_type mCommandFunc; - LLPointer<LLHTTPRetryPolicy> mRetryPolicy; - LLPointer<LLInventoryCallback> mCallback; -}; - -class RemoveItemCommand: public AISCommand -{ -public: - RemoveItemCommand(const LLUUID& item_id, - LLPointer<LLInventoryCallback> callback); -}; - -class RemoveCategoryCommand: public AISCommand -{ -public: - RemoveCategoryCommand(const LLUUID& item_id, - LLPointer<LLInventoryCallback> callback); -}; - -class PurgeDescendentsCommand: public AISCommand -{ -public: - PurgeDescendentsCommand(const LLUUID& item_id, - LLPointer<LLInventoryCallback> callback); -}; - -class UpdateItemCommand: public AISCommand -{ -public: - UpdateItemCommand(const LLUUID& item_id, - const LLSD& updates, - LLPointer<LLInventoryCallback> callback); private: - LLSD mUpdates; -}; + typedef enum { + COPYINVENTORY, + SLAMFOLDER, + REMOVECATEGORY, + REMOVEITEM, + PURGEDESCENDENTS, + UPDATECATEGORY, + UPDATEITEM, + COPYLIBRARYCATEGORY + } COMMAND_TYPE; -class UpdateCategoryCommand: public AISCommand -{ -public: - UpdateCategoryCommand(const LLUUID& cat_id, - const LLSD& updates, - LLPointer<LLInventoryCallback> callback); -private: - LLSD mUpdates; -}; + static const std::string INVENTORY_CAP_NAME; + static const std::string LIBRARY_CAP_NAME; -class SlamFolderCommand: public AISCommand -{ -public: - SlamFolderCommand(const LLUUID& folder_id, const LLSD& contents, LLPointer<LLInventoryCallback> callback); - -private: - LLSD mContents; -}; + typedef boost::function < LLSD (LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t, LLCore::HttpRequest::ptr_t, + const std::string, LLSD, LLCore::HttpOptions::ptr_t, LLCore::HttpHeaders::ptr_t) > invokationFn_t; -class CopyLibraryCategoryCommand: public AISCommand -{ -public: - CopyLibraryCategoryCommand(const LLUUID& source_id, const LLUUID& dest_id, LLPointer<LLInventoryCallback> callback, bool copy_subfolders = true); + static void EnqueueAISCommand(const std::string &procName, LLCoprocedureManager::CoProcedure_t proc); -protected: - /* virtual */ bool getResponseUUID(const LLSD& content, LLUUID& id); -}; + static std::string getInvCap(); + static std::string getLibCap(); -class CreateInventoryCommand: public AISCommand -{ -public: - CreateInventoryCommand(const LLUUID& parent_id, const LLSD& new_inventory, LLPointer<LLInventoryCallback> callback); + static void InvokeAISCommandCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter, + invokationFn_t invoke, std::string url, LLUUID targetId, LLSD body, + completion_t callback, COMMAND_TYPE type); -private: - LLSD mNewInventory; }; class AISUpdate diff --git a/indra/newview/llappcorehttp.cpp b/indra/newview/llappcorehttp.cpp index f5f224b83e..7dee309a62 100644 --- 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, false, + "Agent", + "Agent requests" } }; @@ -124,6 +138,9 @@ LLAppCoreHttp::~LLAppCoreHttp() void LLAppCoreHttp::init() { + + LLCore::LLHttp::initialize(); + LLCore::HttpStatus status = LLCore::HttpRequest::createService(); if (! status) { @@ -151,6 +168,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 +208,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) @@ -250,12 +278,25 @@ void setting_changed() LLAppViewer::instance()->getAppCoreHttp().refreshSettings(false); } +namespace +{ + // The NoOpDeletor is used when wrapping LLAppCoreHttp in a smart pointer below for + // passage into the LLCore::Http libararies. When the smart pointer is destroyed, + // no action will be taken since we do not in this case want the entire LLAppCoreHttp object + // to be destroyed at the end of the call. + // + // *NOTE$: Yes! It is "Deletor" + // http://english.stackexchange.com/questions/4733/what-s-the-rule-for-adding-er-vs-or-when-nouning-a-verb + // "delete" derives from Latin "deletus" + void NoOpDeletor(LLCore::HttpHandler *) + { /*NoOp*/ } +} void LLAppCoreHttp::requestStop() { llassert_always(mRequest); - mStopHandle = mRequest->requestStopThread(this); + mStopHandle = mRequest->requestStopThread(LLCore::HttpHandler::ptr_t(this, NoOpDeletor)); if (LLCORE_HTTP_HANDLE_INVALID != mStopHandle) { mStopRequested = LLTimer::getTotalSeconds(); @@ -325,6 +366,7 @@ void LLAppCoreHttp::refreshSettings(bool initial) mPipelined = pipelined; pipeline_changed = true; } + LL_INFOS("Init") << "HTTP Pipelining " << (mPipelined ? "enabled" : "disabled") << "!" << LL_ENDL; } for (int i(0); i < LL_ARRAY_SIZE(init_data); ++i) @@ -368,7 +410,7 @@ void LLAppCoreHttp::refreshSettings(bool initial) handle = mRequest->setPolicyOption(LLCore::HttpRequest::PO_PIPELINING_DEPTH, mHttpClasses[app_policy].mPolicy, new_depth, - NULL); + LLCore::HttpHandler::ptr_t()); if (LLCORE_HTTP_HANDLE_INVALID == handle) { status = mRequest->getStatus(); @@ -418,7 +460,7 @@ void LLAppCoreHttp::refreshSettings(bool initial) handle = mRequest->setPolicyOption(LLCore::HttpRequest::PO_CONNECTION_LIMIT, mHttpClasses[app_policy].mPolicy, (mHttpClasses[app_policy].mPipelined ? 2 * setting : setting), - NULL); + LLCore::HttpHandler::ptr_t()); if (LLCORE_HTTP_HANDLE_INVALID == handle) { status = mRequest->getStatus(); @@ -431,7 +473,7 @@ void LLAppCoreHttp::refreshSettings(bool initial) handle = mRequest->setPolicyOption(LLCore::HttpRequest::PO_PER_HOST_CONNECTION_LIMIT, mHttpClasses[app_policy].mPolicy, setting, - NULL); + LLCore::HttpHandler::ptr_t()); if (LLCORE_HTTP_HANDLE_INVALID == handle) { status = mRequest->getStatus(); @@ -457,6 +499,62 @@ void LLAppCoreHttp::refreshSettings(bool initial) } } +LLCore::HttpStatus LLAppCoreHttp::sslVerify(const std::string &url, + const LLCore::HttpHandler::ptr_t &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..95c138d598 100644 --- 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, const LLCore::HttpHandler::ptr_t &handler, void *appdata); }; diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index 3d9b1a72a8..99dcb80a4a 100644 --- 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,12 +54,28 @@ #include "llsdserialize.h" #include "llhttpretrypolicy.h" #include "llaisapi.h" +#include "llhttpsdhandler.h" +#include "llcorehttputil.h" +#include "llappviewer.h" +#include "llcoros.h" +#include "lleventcoro.h" #if LL_MSVC // disable boost::lexical_cast warning #pragma warning (disable:4702) #endif +#if 1 +// *TODO$: LLInventoryCallback should be deprecated to conform to the new boost::bind/coroutine model. +// temp code in transition +void doAppearanceCb(LLPointer<LLInventoryCallback> cb, LLUUID id) +{ + if (cb.notNull()) + cb->fire(id); +} +#endif + + std::string self_av_string() { // On logout gAgentAvatarp can already be invalid @@ -1257,6 +1273,8 @@ static void removeDuplicateItems(LLInventoryModel::item_array_t& items) items = new_items; } +//========================================================================= + const LLUUID LLAppearanceMgr::getCOF() const { return gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT); @@ -2462,8 +2480,7 @@ void LLAppearanceMgr::wearInventoryCategory(LLInventoryCategory* category, bool << " )" << LL_ENDL; // If we are copying from library, attempt to use AIS to copy the category. - bool ais_ran=false; - if (copy && AISCommand::isAPIAvailable()) + if (copy && AISAPI::isAvailable()) { LLUUID parent_id; parent_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_CLOTHING); @@ -2475,11 +2492,11 @@ void LLAppearanceMgr::wearInventoryCategory(LLInventoryCategory* category, bool LLPointer<LLInventoryCallback> copy_cb = new LLWearCategoryAfterCopy(append); LLPointer<LLInventoryCallback> track_cb = new LLTrackPhaseWrapper( std::string("wear_inventory_category_callback"), copy_cb); - LLPointer<AISCommand> cmd_ptr = new CopyLibraryCategoryCommand(category->getUUID(), parent_id, track_cb, false); - ais_ran=cmd_ptr->run_command(); - } - if (!ais_ran) + AISAPI::completion_t cr = boost::bind(&doAppearanceCb, track_cb, _1); + AISAPI::CopyLibraryCategory(category->getUUID(), parent_id, false, cr); + } + else { selfStartPhase("wear_inventory_category_fetch"); callAfterCategoryFetch(category->getUUID(),boost::bind(&LLAppearanceMgr::wearCategoryFinal, @@ -3271,276 +3288,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 { @@ -3607,99 +3354,224 @@ LLSD LLAppearanceMgr::dumpCOF() const void LLAppearanceMgr::requestServerAppearanceUpdate() { - mAppearanceResponder->onRequestRequested(); + LLCoprocedureManager::CoProcedure_t proc = boost::bind(&LLAppearanceMgr::serverAppearanceUpdateCoro, this, _1); + LLCoprocedureManager::instance().enqueueCoprocedure("AIS", "LLAppearanceMgr::serverAppearanceUpdateCoro", proc); } -class LLIncrementCofVersionResponder : public LLHTTPClient::Responder +void LLAppearanceMgr::serverAppearanceUpdateCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &httpAdapter) { - LOG_CLASS(LLIncrementCofVersionResponder); -public: - LLIncrementCofVersionResponder() : LLHTTPClient::Responder() - { - mRetryPolicy = new LLAdaptiveRetryPolicy(1.0, 16.0, 2.0, 5); - } + if (!gAgent.getRegion()) + { + LL_WARNS("Avatar") << "Region not set, cannot request server appearance update" << LL_ENDL; + return; + } + if (gAgent.getRegion()->getCentralBakeVersion() == 0) + { + LL_WARNS("Avatar") << "Region does not support baking" << LL_ENDL; + return; + } - virtual ~LLIncrementCofVersionResponder() - { - } + std::string url = gAgent.getRegion()->getCapability("UpdateAvatarAppearance"); + if (url.empty()) + { + LL_WARNS("Agent") << "Could not retrieve region capability \"UpdateAvatarAppearance\"" << LL_ENDL; + return; + } -protected: - virtual void httpSuccess() - { - 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(); + //---------------- + if (gAgentAvatarp->isEditingAppearance()) + { + LL_WARNS("Avatar") << "Avatar editing appearance, not sending request." << LL_ENDL; + // don't send out appearance updates if in appearance editing mode + return; + } - // cof_version should have increased - llassert(new_version > gAgentAvatarp->mLastUpdateRequestCOFVersion); +#if 0 + static int reqcount = 0; + int r_count = ++reqcount; + LL_WARNS("Avatar") << "START: Server Bake request #" << r_count << "!" << LL_ENDL; +#endif - gAgentAvatarp->mLastUpdateRequestCOFVersion = new_version; - } + // If we have already received an update for this or higher cof version, + // put a warning in the log but request anyway. + S32 cofVersion = getCOFVersion(); + S32 lastRcv = gAgentAvatarp->mLastUpdateReceivedCOFVersion; + S32 lastReq = gAgentAvatarp->mLastUpdateRequestCOFVersion; - virtual void httpFailure() - { - 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_INFOS("Avatar") << "Requesting COF version " << cofVersion << + " (Last Received:" << lastRcv << ")" << + " (Last Requested:" << lastReq << ")" << LL_ENDL; -private: - LLPointer<LLHTTPRetryPolicy> mRetryPolicy; -}; + if ((cofVersion != LLViewerInventoryCategory::VERSION_UNKNOWN)) + { + if (cofVersion < lastRcv) + { + LL_WARNS("Avatar") << "Have already received update for cof version " << lastRcv + << " but requesting for " << cofVersion << LL_ENDL; + } + if (lastReq > cofVersion) + { + LL_WARNS("Avatar") << "Request already in flight for cof version " << lastReq + << " but requesting for " << cofVersion << LL_ENDL; + } + } -void LLAppearanceMgr::incrementCofVersion(LLHTTPClient::ResponderPtr responder_ptr) -{ - // If we don't have a region, report it as an error - if (gAgent.getRegion() == NULL) - { - LL_WARNS() << "Region not set, cannot request cof_version increment" << LL_ENDL; - return; - } + // Actually send the request. + LL_DEBUGS("Avatar") << "Will send request for cof_version " << cofVersion << LL_ENDL; - std::string url = gAgent.getRegion()->getCapability("IncrementCofVersion"); - if (url.empty()) - { - LL_WARNS() << "No cap for IncrementCofVersion." << LL_ENDL; - return; - } +// LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter( +// "UpdateAvatarAppearance", gAgent.getAgentPolicy())); - LL_INFOS() << "Requesting cof_version be incremented via capability to: " - << url << LL_ENDL; - LLSD headers; - LLSD body = LLSD::emptyMap(); + bool bRetry; + do + { + bRetry = false; + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest()); - if (!responder_ptr.get()) - { - responder_ptr = LLHTTPClient::ResponderPtr(new LLIncrementCofVersionResponder()); - } + S32 reqCofVersion = getCOFVersion(); // Treat COF version (gets set by AISAPI as authoritative, + // not what the bake request tells us to use). + if (gSavedSettings.getBOOL("DebugForceAppearanceRequestFailure")) + { + reqCofVersion += 999; + LL_WARNS("Avatar") << "Forcing version failure on COF Baking" << LL_ENDL; + } + + LL_INFOS() << "Requesting bake for COF version " << reqCofVersion << LL_ENDL; + + LLSD postData; + if (gSavedSettings.getBOOL("DebugAvatarExperimentalServerAppearanceUpdate")) + { + postData = dumpCOF(); + } + else + { + postData["cof_version"] = reqCofVersion; + } + + gAgentAvatarp->mLastUpdateRequestCOFVersion = reqCofVersion; + + LLSD result = httpAdapter->postAndSuspend(httpRequest, url, postData); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status || !result["success"].asBoolean()) + { + std::string message = (result.has("error")) ? result["error"].asString() : status.toString(); + LL_WARNS("Avatar") << "Appearance Failure. server responded with \"" << message << "\"" << LL_ENDL; + + // We may have requested a bake for a stale COF (especially if the inventory + // is still updating. If that is the case re send the request with the + // corrected COF version. (This may also be the case if the viewer is running + // on multiple machines. + if (result.has("expected")) + { + S32 expectedCofVersion = result["expected"].asInteger(); + bRetry = true; + // Wait for a 1/2 second before trying again. Just to keep from asking too quickly. + llcoro::suspendUntilTimeout(0.5); + + LL_WARNS("Avatar") << "Server expected " << expectedCofVersion << " as COF version" << LL_ENDL; + continue; + } + } + + LL_DEBUGS("Avatar") << "succeeded" << LL_ENDL; + if (gSavedSettings.getBOOL("DebugAvatarAppearanceMessage")) + { + dump_sequential_xml(gAgentAvatarp->getFullname() + "_appearance_request_ok", result); + } + + } while (bRetry); + +#if 0 + LL_WARNS("Avatar") << "END: Server Bake request #" << r_count << "!" << LL_ENDL; +#endif - LLHTTPClient::get(url, body, responder_ptr, headers, 30.0f); } -U32 LLAppearanceMgr::getNumAttachmentsInCOF() +/*static*/ +void LLAppearanceMgr::debugAppearanceUpdateCOF(const LLSD& content) { - const LLUUID cof = getCOF(); - LLInventoryModel::item_array_t obj_items; - getDescendentsOfAssetType(cof, obj_items, LLAssetType::AT_OBJECT); - return obj_items.size(); + 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; + } } @@ -3778,7 +3650,7 @@ void LLAppearanceMgr::makeNewOutfitLinks(const std::string& new_folder_name, boo // First, make a folder in the My Outfits directory. const LLUUID parent_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); - if (AISCommand::isAPIAvailable()) + if (AISAPI::isAvailable()) { // cap-based category creation was buggy until recently. use // existence of AIS as an indicator the fix is present. Does @@ -3975,15 +3847,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)); @@ -3991,11 +3865,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..b97f9018c0 100644 --- a/indra/newview/llappearancemgr.h +++ b/indra/newview/llappearancemgr.h @@ -34,12 +34,11 @@ #include "llinventorymodel.h" #include "llinventoryobserver.h" #include "llviewerinventory.h" -#include "llhttpclient.h" +#include "llcorehttputil.h" class LLWearableHoldingPattern; class LLInventoryCallback; class LLOutfitUnLockTimer; -class RequestAgentUpdateAppearanceResponder; class LLAppearanceMgr: public LLSingleton<LLAppearanceMgr> { @@ -54,7 +53,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 +222,17 @@ public: void requestServerAppearanceUpdate(); - void incrementCofVersion(LLHTTPClient::ResponderPtr responder_ptr = NULL); - - U32 getNumAttachmentsInCOF(); - - // *HACK Remove this after server side texture baking is deployed on all sims. - void incrementCofVersionLegacy(); - void setAppearanceServiceURL(const std::string& url) { mAppearanceServiceURL = url; } std::string getAppearanceServiceURL() const; + + private: + void serverAppearanceUpdateCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &httpAdapter); + static void debugAppearanceUpdateCOF(const LLSD& content); + std::string mAppearanceServiceURL; - protected: LLAppearanceMgr(); ~LLAppearanceMgr(); @@ -261,13 +256,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/llappviewer.cpp b/indra/newview/llappviewer.cpp index c146e97c2e..aa152ffdd6 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -57,8 +57,6 @@ #include "llfocusmgr.h" #include "llviewerjoystick.h" #include "llallocator.h" -#include "llares.h" -#include "llcurl.h" #include "llcalc.h" #include "llconversationlog.h" #include "lldxhardware.h" @@ -230,7 +228,7 @@ #include "llmachineid.h" #include "llmainlooprepeater.h" - +#include "llcoproceduremanager.h" #include "llviewereventrecorder.h" @@ -759,8 +757,11 @@ void fast_exit(int rc) { _exit(rc); } + + } + bool LLAppViewer::init() { setupErrorHandling(mSecondInstance); @@ -829,12 +830,7 @@ bool LLAppViewer::init() // before consumers (LLTextureFetch). mAppCoreHttp.init(); - // *NOTE:Mani - LLCurl::initClass is not thread safe. - // Called before threads are created. - LLCurl::initClass(gSavedSettings.getF32("CurlRequestTimeOut"), - gSavedSettings.getS32("CurlMaximumNumberOfHandles"), - gSavedSettings.getBOOL("CurlUseMultipleThreads")); - LL_INFOS("InitInfo") << "LLCurl initialized." << LL_ENDL ; + LL_INFOS("InitInfo") << "LLCore::Http initialized." << LL_ENDL ; LLMachineID::init(); @@ -904,7 +900,7 @@ bool LLAppViewer::init() // the libs involved in getting to a full login screen. // LL_INFOS("InitInfo") << "J2C Engine is: " << LLImageJ2C::getEngineInfo() << LL_ENDL; - LL_INFOS("InitInfo") << "libcurl version is: " << LLCurl::getVersionString() << LL_ENDL; + LL_INFOS("InitInfo") << "libcurl version is: " << LLCore::LLHttp::getCURLVersion() << LL_ENDL; ///////////////////////////////////////////////// // OS-specific login dialogs @@ -1161,8 +1157,6 @@ bool LLAppViewer::init() { LLNotificationsUtil::add("CorruptedProtectedDataStore"); } - LLHTTPClient::setCertVerifyCallback(secapiSSLCertVerifyCallback); - gGLActive = FALSE; @@ -1220,6 +1214,12 @@ bool LLAppViewer::init() LLAgentLanguage::init(); + /// Tell the Coprocedure manager how to discover and store the pool sizes + // what I wanted + LLCoprocedureManager::getInstance()->setPropertyMethods( + boost::bind(&LLControlGroup::getU32, boost::ref(gSavedSettings), _1), + boost::bind(&LLControlGroup::declareU32, boost::ref(gSavedSettings), _1, _2, _3, LLControlVariable::PERSIST_ALWAYS)); + return true; } @@ -1286,7 +1286,6 @@ static LLTrace::BlockTimerStatHandle FTM_LFS("LFS Thread"); static LLTrace::BlockTimerStatHandle FTM_PAUSE_THREADS("Pause Threads"); static LLTrace::BlockTimerStatHandle FTM_IDLE("Idle"); static LLTrace::BlockTimerStatHandle FTM_PUMP("Pump"); -static LLTrace::BlockTimerStatHandle FTM_PUMP_ARES("Ares"); static LLTrace::BlockTimerStatHandle FTM_PUMP_SERVICE("Service"); static LLTrace::BlockTimerStatHandle FTM_SERVICE_CALLBACK("Callback"); static LLTrace::BlockTimerStatHandle FTM_AGENT_AUTOPILOT("Autopilot"); @@ -1310,8 +1309,6 @@ bool LLAppViewer::mainLoop() // Create IO Pump to use for HTTP Requests. gServicePump = new LLPumpIO(gAPRPoolp); - LLHTTPClient::setPump(*gServicePump); - LLCurl::setCAFile(gDirUtilp->getCAFile()); // Note: this is where gLocalSpeakerMgr and gActiveSpeakerMgr used to be instantiated. @@ -1430,26 +1427,6 @@ bool LLAppViewer::mainLoop() LL_RECORD_BLOCK_TIME(FTM_IDLE); idle(); - if (gAres != NULL && gAres->isInitialized()) - { - pingMainloopTimeout("Main:ServicePump"); - LL_RECORD_BLOCK_TIME(FTM_PUMP); - { - LL_RECORD_BLOCK_TIME(FTM_PUMP_ARES); - gAres->process(); - } - { - LL_RECORD_BLOCK_TIME(FTM_PUMP_SERVICE); - // this pump is necessary to make the login screen show up - gServicePump->pump(); - - { - LL_RECORD_BLOCK_TIME(FTM_SERVICE_CALLBACK); - gServicePump->callback(); - } - } - } - resumeMainloopTimeout(); } @@ -1554,11 +1531,6 @@ bool LLAppViewer::mainLoop() } gMeshRepo.update() ; - if(!LLCurl::getCurlThread()->update(1)) - { - LLCurl::getCurlThread()->pause() ; //nothing in the curl thread. - } - if(!total_work_pending) //pause texture fetching threads if nothing to process. { LLAppViewer::getTextureCache()->pause(); @@ -1799,13 +1771,14 @@ bool LLAppViewer::cleanup() if (gAudiop) { - // shut down the streaming audio sub-subsystem first, in case it relies on not outliving the general audio subsystem. - - LLStreamingAudioInterface *sai = gAudiop->getStreamingAudioImpl(); + // be sure to stop the internet stream cleanly BEFORE destroying the interface to stop it. + gAudiop->stopInternetStream(); + // shut down the streaming audio sub-subsystem first, in case it relies on not outliving the general audio subsystem. + LLStreamingAudioInterface *sai = gAudiop->getStreamingAudioImpl(); delete sai; gAudiop->setStreamingAudioImpl(NULL); - // shut down the audio subsystem + // shut down the audio subsystem gAudiop->shutdown(); delete gAudiop; @@ -1999,7 +1972,6 @@ bool LLAppViewer::cleanup() pending += LLAppViewer::getTextureFetch()->update(1); // unpauses the texture fetch thread pending += LLVFSThread::updateClass(0); pending += LLLFSThread::updateClass(0); - pending += LLCurl::getCurlThread()->update(1) ; F64 idle_time = idleTimer.getElapsedTimeF64(); if(!pending) { @@ -2011,7 +1983,6 @@ bool LLAppViewer::cleanup() break; } } - LLCurl::getCurlThread()->pause() ; // Delete workers first // shotdown all worker threads before deleting them in case of co-dependencies @@ -2026,17 +1997,9 @@ bool LLAppViewer::cleanup() LL_INFOS() << "Shutting down message system" << LL_ENDL; end_messaging_system(); - // *NOTE:Mani - The following call is not thread safe. - LL_CHECK_MEMORY - LLCurl::cleanupClass(); - LL_CHECK_MEMORY - // Non-LLCurl libcurl library mAppCoreHttp.cleanup(); - // NOTE The following call is not thread safe. - ll_cleanup_ares(); - LLFilePickerThread::cleanupClass(); //MUST happen AFTER LLCurl::cleanupClass @@ -2122,6 +2085,7 @@ bool LLAppViewer::cleanup() } LL_INFOS() << "Cleaning up LLProxy." << LL_ENDL; LLProxy::cleanupClass(); + LLCore::LLHttp::cleanup(); LLWearableType::cleanupClass(); @@ -4717,31 +4681,6 @@ void LLAppViewer::saveNameCache() } -void LLAppViewer::saveExperienceCache() -{ - std::string filename = - gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "experience_cache.xml"); - LL_INFOS("ExperienceCache") << "Saving " << filename << LL_ENDL; - llofstream cache_stream(filename.c_str()); - if(cache_stream.is_open()) - { - LLExperienceCache::exportFile(cache_stream); - } -} - -void LLAppViewer::loadExperienceCache() -{ - std::string filename = - gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "experience_cache.xml"); - LL_INFOS("ExperienceCache") << "Loading " << filename << LL_ENDL; - llifstream cache_stream(filename.c_str()); - if(cache_stream.is_open()) - { - LLExperienceCache::importFile(cache_stream); - } -} - - /*! @brief This class is an LLFrameTimer that can be created with an elapsed time that starts counting up from the given value rather than 0.0. @@ -4937,7 +4876,6 @@ void LLAppViewer::idle() // floating throughout the various object lists. // idleNameCache(); - idleExperienceCache(); idleNetwork(); @@ -5368,22 +5306,6 @@ void LLAppViewer::idleNameCache() LLAvatarNameCache::idle(); } -void LLAppViewer::idleExperienceCache() -{ - LLViewerRegion* region = gAgent.getRegion(); - if (!region) return; - - std::string lookup_url=region->getCapability("GetExperienceInfo"); - if(!lookup_url.empty() && *lookup_url.rbegin() != '/') - { - lookup_url += '/'; - } - - LLExperienceCache::setLookupURL(lookup_url); - - LLExperienceCache::idle(); -} - // // Handle messages, and all message related stuff // @@ -5546,7 +5468,9 @@ void LLAppViewer::disconnectViewer() } saveNameCache(); - saveExperienceCache(); + LLExperienceCache *expCache = LLExperienceCache::getIfExists(); + if (expCache) + expCache->cleanup(); // close inventory interface, close all windows LLFloaterInventory::cleanup(); diff --git a/indra/newview/llassetuploadqueue.cpp b/indra/newview/llassetuploadqueue.cpp deleted file mode 100644 index 359ee1e221..0000000000 --- a/indra/newview/llassetuploadqueue.cpp +++ /dev/null @@ -1,220 +0,0 @@ -/** - * @file llassetupload.cpp - * @brief Serializes asset upload request - * - * $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 "llassetuploadqueue.h" -#include "llviewerregion.h" -#include "llviewerobjectlist.h" - -#include "llassetuploadresponders.h" -#include "llsd.h" -#include <iostream> - -class LLAssetUploadChainResponder : public LLUpdateTaskInventoryResponder -{ - LOG_CLASS(LLAssetUploadChainResponder); -public: - - LLAssetUploadChainResponder(const LLSD& post_data, - const std::string& file_name, - const LLUUID& queue_id, - U8* data, - U32 data_size, - std::string script_name, - LLAssetUploadQueueSupplier *supplier) : - LLUpdateTaskInventoryResponder(post_data, file_name, queue_id, LLAssetType::AT_LSL_TEXT), - mSupplier(supplier), - mData(data), - mDataSize(data_size), - mScriptName(script_name) - { - } - - virtual ~LLAssetUploadChainResponder() - { - if(mSupplier) - { - LLAssetUploadQueue *queue = mSupplier->get(); - if (queue) - { - // Give ownership of supplier back to queue. - queue->mSupplier = mSupplier; - mSupplier = NULL; - } - } - delete mSupplier; - delete mData; - } - -protected: - virtual void httpFailure() - { - // Parent class will spam the failure. - //LL_WARNS() << dumpResponse() << LL_ENDL; - LLUpdateTaskInventoryResponder::httpFailure(); - LLAssetUploadQueue *queue = mSupplier->get(); - if (queue) - { - queue->request(&mSupplier); - } - } - - virtual void httpSuccess() - { - LLUpdateTaskInventoryResponder::httpSuccess(); - LLAssetUploadQueue *queue = mSupplier->get(); - if (queue) - { - // Responder is reused across 2 phase upload, - // so only start next upload after 2nd phase complete. - const std::string& state = getContent()["state"].asStringRef(); - if(state == "complete") - { - queue->request(&mSupplier); - } - } - } - -public: - virtual void uploadUpload(const LLSD& content) - { - std::string uploader = content["uploader"]; - - mSupplier->log(std::string("Compiling " + mScriptName).c_str()); - LL_INFOS() << "Compiling " << LL_ENDL; - - // postRaw takes ownership of mData and will delete it. - LLHTTPClient::postRaw(uploader, mData, mDataSize, this); - mData = NULL; - mDataSize = 0; - } - - virtual void uploadComplete(const LLSD& content) - { - // Bytecode save completed - if (content["compiled"]) - { - mSupplier->log("Compilation succeeded"); - LL_INFOS() << "Compiled!" << LL_ENDL; - } - else - { - LLSD compile_errors = content["errors"]; - for(LLSD::array_const_iterator line = compile_errors.beginArray(); - line < compile_errors.endArray(); line++) - { - std::string str = line->asString(); - str.erase(std::remove(str.begin(), str.end(), '\n'), str.end()); - mSupplier->log(str); - LL_INFOS() << content["errors"] << LL_ENDL; - } - } - LLUpdateTaskInventoryResponder::uploadComplete(content); - } - - LLAssetUploadQueueSupplier *mSupplier; - U8* mData; - U32 mDataSize; - std::string mScriptName; -}; - - -LLAssetUploadQueue::LLAssetUploadQueue(LLAssetUploadQueueSupplier *supplier) : - mSupplier(supplier) -{ -} - -//virtual -LLAssetUploadQueue::~LLAssetUploadQueue() -{ - delete mSupplier; -} - -// Takes ownership of supplier. -void LLAssetUploadQueue::request(LLAssetUploadQueueSupplier** supplier) -{ - if (mQueue.empty()) - return; - - UploadData data = mQueue.front(); - mQueue.pop_front(); - - LLSD body; - body["task_id"] = data.mTaskId; - body["item_id"] = data.mItemId; - body["is_script_running"] = data.mIsRunning; - body["target"] = data.mIsTargetMono? "mono" : "lsl2"; - body["experience"] = data.mExperienceId; - - std::string url = ""; - LLViewerObject* object = gObjectList.findObject(data.mTaskId); - if (object) - { - url = object->getRegion()->getCapability("UpdateScriptTask"); - LLHTTPClient::post(url, body, - new LLAssetUploadChainResponder( - body, data.mFilename, data.mQueueId, - data.mData, data.mDataSize, data.mScriptName, *supplier)); - } - - *supplier = NULL; -} - -void LLAssetUploadQueue::queue(const std::string& filename, - const LLUUID& task_id, - const LLUUID& item_id, - BOOL is_running, - BOOL is_target_mono, - const LLUUID& queue_id, - U8* script_data, - U32 data_size, - std::string script_name, - const LLUUID& experience_id) -{ - UploadData data; - data.mTaskId = task_id; - data.mItemId = item_id; - data.mIsRunning = is_running; - data.mIsTargetMono = is_target_mono; - data.mQueueId = queue_id; - data.mFilename = filename; - data.mData = script_data; - data.mDataSize = data_size; - data.mScriptName = script_name; - data.mExperienceId = experience_id; - - mQueue.push_back(data); - - if(mSupplier) - { - request(&mSupplier); - } -} - -LLAssetUploadQueueSupplier::~LLAssetUploadQueueSupplier() -{ -} diff --git a/indra/newview/llassetuploadqueue.h b/indra/newview/llassetuploadqueue.h deleted file mode 100644 index 2ceee8f700..0000000000 --- a/indra/newview/llassetuploadqueue.h +++ /dev/null @@ -1,93 +0,0 @@ -/** - * @file llassetuploadqueue.h - * @brief Serializes asset upload request - * - * $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_LLASSETUPLOADQUEUE_H -#define LL_LLASSETUPLOADQUEUE_H - -#include "lluuid.h" - -#include <string> -#include <deque> - -class LLAssetUploadQueueSupplier; - -class LLAssetUploadQueue -{ -public: - - // Takes ownership of supplier. - LLAssetUploadQueue(LLAssetUploadQueueSupplier* supplier); - virtual ~LLAssetUploadQueue(); - - void queue(const std::string& filename, - const LLUUID& task_id, - const LLUUID& item_id, - BOOL is_running, - BOOL is_target_mono, - const LLUUID& queue_id, - U8* data, - U32 data_size, - std::string script_name, - const LLUUID& experience_id); - - bool isEmpty() const {return mQueue.empty();} - -private: - - friend class LLAssetUploadChainResponder; - - struct UploadData - { - std::string mFilename; - LLUUID mTaskId; - LLUUID mItemId; - BOOL mIsRunning; - BOOL mIsTargetMono; - LLUUID mQueueId; - U8* mData; - U32 mDataSize; - std::string mScriptName; - LLUUID mExperienceId; - }; - - // Ownership of mSupplier passed to currently waiting responder - // and returned to queue when no requests in progress. - LLAssetUploadQueueSupplier* mSupplier; - std::deque<UploadData> mQueue; - - // Passes on ownership of mSupplier if request is made. - void request(LLAssetUploadQueueSupplier** supplier); -}; - -class LLAssetUploadQueueSupplier -{ -public: - virtual ~LLAssetUploadQueueSupplier(); - virtual LLAssetUploadQueue* get() const = 0; - virtual void log(std::string message) const = 0; -}; - -#endif // LL_LLASSETUPLOADQUEUE_H diff --git a/indra/newview/llassetuploadresponders.cpp b/indra/newview/llassetuploadresponders.cpp deleted file mode 100644 index 121ce647a6..0000000000 --- a/indra/newview/llassetuploadresponders.cpp +++ /dev/null @@ -1,1151 +0,0 @@ -/** - * @file llassetuploadresponders.cpp - * @brief Processes responses received for asset upload requests. - * - * $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 "llassetuploadresponders.h" - -// viewer includes -#include "llagent.h" -#include "llcompilequeue.h" -#include "llbuycurrencyhtml.h" -#include "llfilepicker.h" -#include "llinventorydefines.h" -#include "llinventoryobserver.h" -#include "llinventorypanel.h" -#include "llpermissionsflags.h" -#include "llpreviewnotecard.h" -#include "llpreviewscript.h" -#include "llpreviewgesture.h" -#include "llgesturemgr.h" -#include "llstatusbar.h" // sendMoneyBalanceRequest() -#include "llsdserialize.h" -#include "lluploaddialog.h" -#include "llviewerobject.h" -#include "llviewercontrol.h" -#include "llviewerobjectlist.h" -#include "llviewermenufile.h" -#include "llviewertexlayer.h" -#include "llviewerwindow.h" -#include "lltrans.h" - -// library includes -#include "lldir.h" -#include "lleconomy.h" -#include "llfloaterreg.h" -#include "llfocusmgr.h" -#include "llnotificationsutil.h" -#include "llscrolllistctrl.h" -#include "llsdserialize.h" -#include "llsdutil.h" -#include "llvfs.h" - -void dialog_refresh_all(); - -void on_new_single_inventory_upload_complete( - LLAssetType::EType asset_type, - LLInventoryType::EType inventory_type, - const std::string inventory_type_string, - const LLUUID& item_folder_id, - const std::string& item_name, - const std::string& item_description, - const LLSD& server_response, - S32 upload_price) -{ - bool success = false; - - if ( upload_price > 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", upload_price); - LLNotificationsUtil::add("UploadPayment", args); - } - - if( item_folder_id.notNull() ) - { - U32 everyone_perms = PERM_NONE; - U32 group_perms = PERM_NONE; - U32 next_owner_perms = PERM_ALL; - if( server_response.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. - everyone_perms = server_response["new_everyone_mask"].asInteger(); - group_perms = server_response["new_group_mask"].asInteger(); - next_owner_perms = server_response["new_next_owner_mask"].asInteger(); - } - else - { - // The server doesn't provide creation perms - // so use old assumption-based perms. - if( inventory_type_string != "snapshot") - { - next_owner_perms = 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, - everyone_perms, - group_perms, - next_owner_perms); - - U32 inventory_item_flags = 0; - if (server_response.has("inventory_flags")) - { - inventory_item_flags = (U32) server_response["inventory_flags"].asInteger(); - if (inventory_item_flags != 0) - { - LL_INFOS() << "inventory_item_flags " << inventory_item_flags << LL_ENDL; - } - } - S32 creation_date_now = time_corrected(); - LLPointer<LLViewerInventoryItem> item = new LLViewerInventoryItem( - server_response["new_inventory_item"].asUUID(), - item_folder_id, - new_perms, - server_response["new_asset"].asUUID(), - asset_type, - inventory_type, - item_name, - item_description, - LLSaleInfo::DEFAULT, - inventory_item_flags, - creation_date_now); - - gInventory.updateItem(item); - gInventory.notifyObservers(); - 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( - server_response["new_inventory_item"].asUUID(), - 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 - LLUploadDialog::modalUploadFinished(); - - // Let the Snapshot floater know we have finished uploading a snapshot to inventory. - LLFloater* floater_snapshot = LLFloaterReg::findInstance("snapshot"); - if (asset_type == LLAssetType::AT_TEXTURE && floater_snapshot) - { - floater_snapshot->notify(LLSD().with("set-finished", LLSD().with("ok", success).with("msg", "inventory"))); - } -} - -LLAssetUploadResponder::LLAssetUploadResponder(const LLSD &post_data, - const LLUUID& vfile_id, - LLAssetType::EType asset_type) - : LLHTTPClient::Responder(), - mPostData(post_data), - mVFileID(vfile_id), - mAssetType(asset_type) -{ - if (!gVFS->getExists(vfile_id, asset_type)) - { - LL_WARNS() << "LLAssetUploadResponder called with nonexistant vfile_id" << LL_ENDL; - mVFileID.setNull(); - mAssetType = LLAssetType::AT_NONE; - return; - } -} - -LLAssetUploadResponder::LLAssetUploadResponder( - const LLSD &post_data, - const std::string& file_name, - LLAssetType::EType asset_type) - : LLHTTPClient::Responder(), - mPostData(post_data), - mFileName(file_name), - mAssetType(asset_type) -{ -} - -LLAssetUploadResponder::~LLAssetUploadResponder() -{ - if (!mFileName.empty()) - { - // Delete temp file - LLFile::remove(mFileName); - } -} - -// virtual -void LLAssetUploadResponder::httpFailure() -{ - // *TODO: Add adaptive retry policy? - LL_WARNS() << dumpResponse() << LL_ENDL; - std::string reason; - if (isHttpClientErrorStatus(getStatus())) - { - reason = "Error in upload request. Please visit " - "http://secondlife.com/support for help fixing this problem."; - } - else - { - reason = "The server is experiencing unexpected " - "difficulties."; - } - LLSD args; - args["FILE"] = (mFileName.empty() ? mVFileID.asString() : mFileName); - args["REASON"] = reason; - LLNotificationsUtil::add("CannotUploadReason", args); - - // unfreeze script preview - if(mAssetType == LLAssetType::AT_LSL_TEXT) - { - LLPreviewLSL* preview = LLFloaterReg::findTypedInstance<LLPreviewLSL>("preview_script", mPostData["item_id"]); - if (preview) - { - LLSD errors; - errors.append(LLTrans::getString("UploadFailed") + reason); - preview->callbackLSLCompileFailed(errors); - } - } - - LLUploadDialog::modalUploadFinished(); - LLFilePicker::instance().reset(); // unlock file picker when bulk upload fails -} - -//virtual -void LLAssetUploadResponder::httpSuccess() -{ - const LLSD& content = getContent(); - if (!content.isMap()) - { - failureResult(HTTP_INTERNAL_ERROR, "Malformed response contents", content); - return; - } - LL_DEBUGS() << "LLAssetUploadResponder::result from capabilities" << LL_ENDL; - - const std::string& state = content["state"].asStringRef(); - - if (state == "upload") - { - uploadUpload(content); - } - else if (state == "complete") - { - // rename file in VFS with new asset id - if (mFileName.empty()) - { - // rename the file in the VFS to the actual asset id - // LL_INFOS() << "Changing uploaded asset UUID to " << content["new_asset"].asUUID() << LL_ENDL; - gVFS->renameFile(mVFileID, mAssetType, content["new_asset"].asUUID(), mAssetType); - } - uploadComplete(content); - } - else - { - uploadFailure(content); - } -} - -void LLAssetUploadResponder::uploadUpload(const LLSD& content) -{ - const std::string& uploader = content["uploader"].asStringRef(); - if (mFileName.empty()) - { - LLHTTPClient::postFile(uploader, mVFileID, mAssetType, this); - } - else - { - LLHTTPClient::postFile(uploader, mFileName, this); - } -} - -void LLAssetUploadResponder::uploadFailure(const LLSD& content) -{ - LL_WARNS() << dumpResponse() << LL_ENDL; - - // unfreeze script preview - if(mAssetType == LLAssetType::AT_LSL_TEXT) - { - LLPreviewLSL* preview = LLFloaterReg::findTypedInstance<LLPreviewLSL>("preview_script", mPostData["item_id"]); - if (preview) - { - LLSD errors; - errors.append(LLTrans::getString("UploadFailed") + content["message"].asString()); - preview->callbackLSLCompileFailed(errors); - } - } - - // remove the "Uploading..." message - LLUploadDialog::modalUploadFinished(); - - LLFloater* floater_snapshot = LLFloaterReg::findInstance("snapshot"); - if (floater_snapshot) - { - floater_snapshot->notify(LLSD().with("set-finished", LLSD().with("ok", false).with("msg", "inventory"))); - } - - const std::string& reason = content["state"].asStringRef(); - // deal with L$ errors - if (reason == "insufficient funds") - { - S32 price = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload(); - LLStringUtil::format_map_t args; - args["AMOUNT"] = llformat("%d", price); - LLBuyCurrencyHTML::openCurrencyFloater( LLTrans::getString("uploading_costs", args), price ); - } - else - { - LLSD args; - args["FILE"] = (mFileName.empty() ? mVFileID.asString() : mFileName); - args["REASON"] = content["message"].asString(); - LLNotificationsUtil::add("CannotUploadReason", args); - } -} - -void LLAssetUploadResponder::uploadComplete(const LLSD& content) -{ -} - -LLNewAgentInventoryResponder::LLNewAgentInventoryResponder( - const LLSD& post_data, - const LLUUID& vfile_id, - LLAssetType::EType asset_type) - : LLAssetUploadResponder(post_data, vfile_id, asset_type) -{ -} - -LLNewAgentInventoryResponder::LLNewAgentInventoryResponder( - const LLSD& post_data, - const std::string& file_name, - LLAssetType::EType asset_type) - : LLAssetUploadResponder(post_data, file_name, asset_type) -{ -} - -// virtual -void LLNewAgentInventoryResponder::httpFailure() -{ - LLAssetUploadResponder::httpFailure(); - //LLImportColladaAssetCache::getInstance()->assetUploaded(mVFileID, LLUUID(), FALSE); -} - - -//virtual -void LLNewAgentInventoryResponder::uploadFailure(const LLSD& content) -{ - LLAssetUploadResponder::uploadFailure(content); - - //LLImportColladaAssetCache::getInstance()->assetUploaded(mVFileID, content["new_asset"], FALSE); -} - -//virtual -void LLNewAgentInventoryResponder::uploadComplete(const LLSD& content) -{ - LL_DEBUGS() << "LLNewAgentInventoryResponder::result from capabilities" << LL_ENDL; - - //std::ostringstream llsdxml; - //LLSDSerialize::toXML(content, llsdxml); - //LL_INFOS() << "upload complete content:\n " << llsdxml.str() << LL_ENDL; - - LLAssetType::EType asset_type = LLAssetType::lookup(mPostData["asset_type"].asString()); - LLInventoryType::EType inventory_type = LLInventoryType::lookup(mPostData["inventory_type"].asString()); - S32 expected_upload_cost = 0; - - // Update L$ and ownership credit information - // since it probably changed on the server - if (asset_type == LLAssetType::AT_TEXTURE || - asset_type == LLAssetType::AT_SOUND || - asset_type == LLAssetType::AT_ANIMATION || - asset_type == LLAssetType::AT_MESH) - { - expected_upload_cost = - LLGlobalEconomy::Singleton::getInstance()->getPriceUpload(); - } - - on_new_single_inventory_upload_complete( - asset_type, - inventory_type, - mPostData["asset_type"].asString(), - mPostData["folder_id"].asUUID(), - mPostData["name"], - mPostData["description"], - content, - expected_upload_cost); - - // continue uploading for bulk uploads - - // *FIX: This is a pretty big hack. What this does is check the - // file picker if there are any more pending uploads. If so, - // upload that file. - std::string next_file = LLFilePicker::instance().getNextFile(); - if(!next_file.empty()) - { - std::string name = gDirUtilp->getBaseFileName(next_file, true); - - std::string asset_name = name; - LLStringUtil::replaceNonstandardASCII( asset_name, '?' ); - LLStringUtil::replaceChar(asset_name, '|', '?'); - LLStringUtil::stripNonprintable(asset_name); - LLStringUtil::trim(asset_name); - - // Continuing the horrible hack above, we need to extract the originally requested permissions data, if any, - // and use them for each next file to be uploaded. Note the requested perms are not the same as the - U32 everyone_perms = - content.has("new_everyone_mask") ? - content["new_everyone_mask"].asInteger() : - PERM_NONE; - - U32 group_perms = - content.has("new_group_mask") ? - content["new_group_mask"].asInteger() : - PERM_NONE; - - U32 next_owner_perms = - content.has("new_next_owner_mask") ? - content["new_next_owner_mask"].asInteger() : - PERM_NONE; - - std::string display_name = LLStringUtil::null; - LLAssetStorage::LLStoreAssetCallback callback = NULL; - void *userdata = NULL; - - upload_new_resource( - next_file, - asset_name, - asset_name, - 0, - LLFolderType::FT_NONE, - LLInventoryType::IT_NONE, - next_owner_perms, - group_perms, - everyone_perms, - display_name, - callback, - LLGlobalEconomy::Singleton::getInstance()->getPriceUpload(), - userdata); - } - - //LLImportColladaAssetCache::getInstance()->assetUploaded(mVFileID, content["new_asset"], TRUE); -} - -LLUpdateAgentInventoryResponder::LLUpdateAgentInventoryResponder( - const LLSD& post_data, - const LLUUID& vfile_id, - LLAssetType::EType asset_type) - : LLAssetUploadResponder(post_data, vfile_id, asset_type) -{ -} - -LLUpdateAgentInventoryResponder::LLUpdateAgentInventoryResponder( - const LLSD& post_data, - const std::string& file_name, - LLAssetType::EType asset_type) - : LLAssetUploadResponder(post_data, file_name, asset_type) -{ -} - -//virtual -void LLUpdateAgentInventoryResponder::uploadComplete(const LLSD& content) -{ - LL_INFOS() << "LLUpdateAgentInventoryResponder::result from capabilities" << LL_ENDL; - LLUUID item_id = mPostData["item_id"]; - - LLViewerInventoryItem* item = (LLViewerInventoryItem*)gInventory.getItem(item_id); - if(!item) - { - LL_WARNS() << "Inventory item for " << mVFileID - << " is no longer in agent inventory." << LL_ENDL; - return; - } - - // Update viewer inventory item - LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item); - new_item->setAssetUUID(content["new_asset"].asUUID()); - gInventory.updateItem(new_item); - gInventory.notifyObservers(); - - LL_INFOS() << "Inventory item " << item->getName() << " saved into " - << content["new_asset"].asString() << LL_ENDL; - - LLInventoryType::EType inventory_type = new_item->getInventoryType(); - switch(inventory_type) - { - case LLInventoryType::IT_NOTECARD: - { - // Update the UI with the new asset. - LLPreviewNotecard* nc = LLFloaterReg::findTypedInstance<LLPreviewNotecard>("preview_notecard", LLSD(item_id)); - 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(content["new_asset"].asUUID(), LLAssetType::AT_NOTECARD); - } - nc->refreshFromInventory(new_item->getUUID()); - } - break; - } - case LLInventoryType::IT_LSL: - { - // Find our window and close it if requested. - LLPreviewLSL* preview = LLFloaterReg::findTypedInstance<LLPreviewLSL>("preview_script", LLSD(item_id)); - if (preview) - { - // Bytecode save completed - if (content["compiled"]) - { - preview->callbackLSLCompileSucceeded(); - } - else - { - preview->callbackLSLCompileFailed(content["errors"]); - } - } - break; - } - - case LLInventoryType::IT_GESTURE: - { - // If this gesture is active, then we need to update the in-memory - // active map with the new pointer. - if (LLGestureMgr::instance().isGestureActive(item_id)) - { - LLUUID asset_id = new_item->getAssetUUID(); - LLGestureMgr::instance().replaceGesture(item_id, asset_id); - gInventory.notifyObservers(); - } - - //gesture will have a new asset_id - LLPreviewGesture* previewp = LLFloaterReg::findTypedInstance<LLPreviewGesture>("preview_gesture", LLSD(item_id)); - if(previewp) - { - previewp->onUpdateSucceeded(); - } - - break; - } - case LLInventoryType::IT_WEARABLE: - default: - break; - } -} - - -LLUpdateTaskInventoryResponder::LLUpdateTaskInventoryResponder(const LLSD& post_data, - const LLUUID& vfile_id, - LLAssetType::EType asset_type) -: LLAssetUploadResponder(post_data, vfile_id, asset_type) -{ -} - -LLUpdateTaskInventoryResponder::LLUpdateTaskInventoryResponder(const LLSD& post_data, - const std::string& file_name, - LLAssetType::EType asset_type) -: LLAssetUploadResponder(post_data, file_name, asset_type) -{ -} - -LLUpdateTaskInventoryResponder::LLUpdateTaskInventoryResponder(const LLSD& post_data, - const std::string& file_name, - const LLUUID& queue_id, - LLAssetType::EType asset_type) -: LLAssetUploadResponder(post_data, file_name, asset_type), mQueueId(queue_id) -{ -} - -//virtual -void LLUpdateTaskInventoryResponder::uploadComplete(const LLSD& content) -{ - LL_INFOS() << "LLUpdateTaskInventoryResponder::result from capabilities" << LL_ENDL; - LLUUID item_id = mPostData["item_id"]; - LLUUID task_id = mPostData["task_id"]; - - dialog_refresh_all(); - - switch(mAssetType) - { - case LLAssetType::AT_NOTECARD: - { - // Update the UI with the new asset. - LLSD floater_key; - floater_key["taskid"] = task_id; - floater_key["itemid"] = item_id; - LLPreviewNotecard* nc = LLFloaterReg::findTypedInstance<LLPreviewNotecard>("preview_notecard", floater_key); - 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(content["new_asset"].asUUID(), - LLAssetType::AT_NOTECARD); - } - nc->setAssetId(content["new_asset"].asUUID()); - nc->refreshFromInventory(); - } - break; - } - case LLAssetType::AT_LSL_TEXT: - { - if(mQueueId.notNull()) - { - LLFloaterCompileQueue* queue = LLFloaterReg::findTypedInstance<LLFloaterCompileQueue>("compile_queue", mQueueId); - if(NULL != queue) - { - queue->removeItemByItemID(item_id); - } - } - else - { - LLSD floater_key; - floater_key["taskid"] = task_id; - floater_key["itemid"] = item_id; - LLLiveLSLEditor* preview = LLFloaterReg::findTypedInstance<LLLiveLSLEditor>("preview_scriptedit", floater_key); - if (preview) - { - // Bytecode save completed - if (content["compiled"]) - { - preview->callbackLSLCompileSucceeded(task_id, item_id, mPostData["is_script_running"]); - } - else - { - preview->callbackLSLCompileFailed(content["errors"]); - } - } - } - break; - } - default: - break; - } -} - - -///////////////////////////////////////////////////// -// LLNewAgentInventoryVariablePriceResponder::Impl // -///////////////////////////////////////////////////// -class LLNewAgentInventoryVariablePriceResponder::Impl -{ -public: - Impl( - const LLUUID& vfile_id, - LLAssetType::EType asset_type, - const LLSD& inventory_data) : - mVFileID(vfile_id), - mAssetType(asset_type), - mInventoryData(inventory_data), - mFileName("") - { - if (!gVFS->getExists(vfile_id, asset_type)) - { - LL_WARNS() - << "LLAssetUploadResponder called with nonexistant " - << "vfile_id " << vfile_id << LL_ENDL; - mVFileID.setNull(); - mAssetType = LLAssetType::AT_NONE; - } - } - - Impl( - const std::string& file_name, - LLAssetType::EType asset_type, - const LLSD& inventory_data) : - mFileName(file_name), - mAssetType(asset_type), - mInventoryData(inventory_data) - { - mVFileID.setNull(); - } - - std::string getFilenameOrIDString() const - { - return (mFileName.empty() ? mVFileID.asString() : mFileName); - } - - LLUUID getVFileID() const - { - return mVFileID; - } - - std::string getFilename() const - { - return mFileName; - } - - LLAssetType::EType getAssetType() const - { - return mAssetType; - } - - LLInventoryType::EType getInventoryType() const - { - return LLInventoryType::lookup( - mInventoryData["inventory_type"].asString()); - } - - std::string getInventoryTypeString() const - { - return mInventoryData["inventory_type"].asString(); - } - - LLUUID getFolderID() const - { - return mInventoryData["folder_id"].asUUID(); - } - - std::string getItemName() const - { - return mInventoryData["name"].asString(); - } - - std::string getItemDescription() const - { - return mInventoryData["description"].asString(); - } - - void displayCannotUploadReason(const std::string& reason) - { - LLSD args; - args["FILE"] = getFilenameOrIDString(); - args["REASON"] = reason; - - - LLNotificationsUtil::add("CannotUploadReason", args); - LLUploadDialog::modalUploadFinished(); - } - - void onApplicationLevelError(const LLSD& error) - { - static const std::string _IDENTIFIER = "identifier"; - - static const std::string _INSUFFICIENT_FUNDS = - "NewAgentInventory_InsufficientLindenDollarBalance"; - static const std::string _MISSING_REQUIRED_PARAMETER = - "NewAgentInventory_MissingRequiredParamater"; - static const std::string _INVALID_REQUEST_BODY = - "NewAgentInventory_InvalidRequestBody"; - static const std::string _RESOURCE_COST_DIFFERS = - "NewAgentInventory_ResourceCostDiffers"; - - static const std::string _MISSING_PARAMETER = "missing_parameter"; - static const std::string _INVALID_PARAMETER = "invalid_parameter"; - static const std::string _MISSING_RESOURCE = "missing_resource"; - static const std::string _INVALID_RESOURCE = "invalid_resource"; - - // TODO* Add the other error_identifiers - - std::string error_identifier = error[_IDENTIFIER].asString(); - - // TODO*: Pull these user visible strings from an xml file - // to be localized - if ( _INSUFFICIENT_FUNDS == error_identifier ) - { - displayCannotUploadReason("You do not have a sufficient L$ balance to complete this upload."); - } - else if ( _MISSING_REQUIRED_PARAMETER == error_identifier ) - { - // Missing parameters - if (error.has(_MISSING_PARAMETER) ) - { - std::string message = - "Upload request was missing required parameter '[P]'"; - LLStringUtil::replaceString( - message, - "[P]", - error[_MISSING_PARAMETER].asString()); - - displayCannotUploadReason(message); - } - else - { - std::string message = - "Upload request was missing a required parameter"; - displayCannotUploadReason(message); - } - } - else if ( _INVALID_REQUEST_BODY == error_identifier ) - { - // Invalid request body, check to see if - // a particular parameter was invalid - if ( error.has(_INVALID_PARAMETER) ) - { - std::string message = "Upload parameter '[P]' is invalid."; - LLStringUtil::replaceString( - message, - "[P]", - error[_INVALID_PARAMETER].asString()); - - // See if the server also responds with what resource - // is missing. - if ( error.has(_MISSING_RESOURCE) ) - { - message += "\nMissing resource '[R]'."; - - LLStringUtil::replaceString( - message, - "[R]", - error[_MISSING_RESOURCE].asString()); - } - else if ( error.has(_INVALID_RESOURCE) ) - { - message += "\nInvalid resource '[R]'."; - - LLStringUtil::replaceString( - message, - "[R]", - error[_INVALID_RESOURCE].asString()); - } - - displayCannotUploadReason(message); - } - else - { - std::string message = "Upload request was malformed"; - displayCannotUploadReason(message); - } - } - else if ( _RESOURCE_COST_DIFFERS == error_identifier ) - { - displayCannotUploadReason("The resource cost associated with this upload is not consistent with the server."); - } - else - { - displayCannotUploadReason("Unknown Error"); - } - } - - void onTransportError() - { - displayCannotUploadReason( - "The server is experiencing unexpected difficulties."); - } - - void onTransportError(const LLSD& error) - { - static const std::string _IDENTIFIER = "identifier"; - - static const std::string _SERVER_ERROR_AFTER_CHARGE = - "NewAgentInventory_ServerErrorAfterCharge"; - - std::string error_identifier = error[_IDENTIFIER].asString(); - - // TODO*: Pull the user visible strings from an xml file - // to be localized - - if ( _SERVER_ERROR_AFTER_CHARGE == error_identifier ) - { - displayCannotUploadReason( - "The server is experiencing unexpected difficulties. You may have been charged for the upload."); - } - else - { - displayCannotUploadReason( - "The server is experiencing unexpected difficulties."); - } - } - - bool uploadConfirmationCallback( - const LLSD& notification, - const LLSD& response, - LLPointer<LLNewAgentInventoryVariablePriceResponder> responder) - { - S32 option; - std::string confirmation_url; - - option = LLNotificationsUtil::getSelectedOption( - notification, - response); - - confirmation_url = - notification["payload"]["confirmation_url"].asString(); - - // Yay! We are confirming or cancelling our upload - switch(option) - { - case 0: - { - confirmUpload(confirmation_url, responder); - } - break; - case 1: - default: - break; - } - - return false; - } - - void confirmUpload( - const std::string& confirmation_url, - LLPointer<LLNewAgentInventoryVariablePriceResponder> responder) - { - if ( getFilename().empty() ) - { - // we have no filename, use virtual file ID instead - LLHTTPClient::postFile( - confirmation_url, - getVFileID(), - getAssetType(), - responder); - } - else - { - LLHTTPClient::postFile( - confirmation_url, - getFilename(), - responder); - } - } - - -private: - std::string mFileName; - - LLSD mInventoryData; - LLAssetType::EType mAssetType; - LLUUID mVFileID; -}; - -/////////////////////////////////////////////// -// LLNewAgentInventoryVariablePriceResponder // -/////////////////////////////////////////////// -LLNewAgentInventoryVariablePriceResponder::LLNewAgentInventoryVariablePriceResponder( - const LLUUID& vfile_id, - LLAssetType::EType asset_type, - const LLSD& inventory_info) -{ - mImpl = new Impl( - vfile_id, - asset_type, - inventory_info); -} - -LLNewAgentInventoryVariablePriceResponder::LLNewAgentInventoryVariablePriceResponder( - const std::string& file_name, - LLAssetType::EType asset_type, - const LLSD& inventory_info) -{ - mImpl = new Impl( - file_name, - asset_type, - inventory_info); -} - -LLNewAgentInventoryVariablePriceResponder::~LLNewAgentInventoryVariablePriceResponder() -{ - delete mImpl; -} - -void LLNewAgentInventoryVariablePriceResponder::httpFailure() -{ - const LLSD& content = getContent(); - LL_WARNS("Upload") << dumpResponse() << LL_ENDL; - - static const std::string _ERROR = "error"; - if ( content.has(_ERROR) ) - { - mImpl->onTransportError(content[_ERROR]); - } - else - { - mImpl->onTransportError(); - } -} - -void LLNewAgentInventoryVariablePriceResponder::httpSuccess() -{ - const LLSD& content = getContent(); - if (!content.isMap()) - { - failureResult(HTTP_INTERNAL_ERROR, "Malformed response contents", content); - return; - } - // Parse out application level errors and the appropriate - // responses for them - static const std::string _ERROR = "error"; - static const std::string _STATE = "state"; - - static const std::string _COMPLETE = "complete"; - static const std::string _CONFIRM_UPLOAD = "confirm_upload"; - - static const std::string _UPLOAD_PRICE = "upload_price"; - static const std::string _RESOURCE_COST = "resource_cost"; - static const std::string _RSVP = "rsvp"; - - // Check for application level errors - if ( content.has(_ERROR) ) - { - LL_WARNS("Upload") << dumpResponse() << LL_ENDL; - onApplicationLevelError(content[_ERROR]); - return; - } - - std::string state = content[_STATE]; - LLAssetType::EType asset_type = mImpl->getAssetType(); - - if ( _COMPLETE == state ) - { - // rename file in VFS with new asset id - if (mImpl->getFilename().empty()) - { - // rename the file in the VFS to the actual asset id - // LL_INFOS() << "Changing uploaded asset UUID to " << content["new_asset"].asUUID() << LL_ENDL; - gVFS->renameFile( - mImpl->getVFileID(), - asset_type, - content["new_asset"].asUUID(), - asset_type); - } - - on_new_single_inventory_upload_complete( - asset_type, - mImpl->getInventoryType(), - mImpl->getInventoryTypeString(), - mImpl->getFolderID(), - mImpl->getItemName(), - mImpl->getItemDescription(), - content, - content[_UPLOAD_PRICE].asInteger()); - - // TODO* Add bulk (serial) uploading or add - // a super class of this that does so - } - else if ( _CONFIRM_UPLOAD == state ) - { - showConfirmationDialog( - content[_UPLOAD_PRICE].asInteger(), - content[_RESOURCE_COST].asInteger(), - content[_RSVP].asString()); - } - else - { - LL_WARNS("Upload") << dumpResponse() << LL_ENDL; - onApplicationLevelError(""); - } -} - -void LLNewAgentInventoryVariablePriceResponder::onApplicationLevelError( - const LLSD& error) -{ - mImpl->onApplicationLevelError(error); -} - -void LLNewAgentInventoryVariablePriceResponder::showConfirmationDialog( - S32 upload_price, - S32 resource_cost, - const std::string& confirmation_url) -{ - if ( 0 == upload_price ) - { - // don't show confirmation dialog for free uploads, I mean, - // they're free! - - // The creating of a new instrusive_ptr(this) - // creates a new boost::intrusive_ptr - // which is a copy of this. This code is required because - // 'this' is always of type Class* and not the intrusive_ptr, - // and thus, a reference to 'this' is not registered - // by using just plain 'this'. - - // Since LLNewAgentInventoryVariablePriceResponder is a - // reference counted class, it is possible (since the - // reference to a plain 'this' would be missed here) that, - // when using plain ol' 'this', that this object - // would be deleted before the callback is triggered - // and cause sadness. - mImpl->confirmUpload( - confirmation_url, - LLPointer<LLNewAgentInventoryVariablePriceResponder>(this)); - } - else - { - LLSD substitutions; - LLSD payload; - - substitutions["PRICE"] = upload_price; - - payload["confirmation_url"] = confirmation_url; - - // The creating of a new instrusive_ptr(this) - // creates a new boost::intrusive_ptr - // which is a copy of this. This code is required because - // 'this' is always of type Class* and not the intrusive_ptr, - // and thus, a reference to 'this' is not registered - // by using just plain 'this'. - - // Since LLNewAgentInventoryVariablePriceResponder is a - // reference counted class, it is possible (since the - // reference to a plain 'this' would be missed here) that, - // when using plain ol' 'this', that this object - // would be deleted before the callback is triggered - // and cause sadness. - LLNotificationsUtil::add( - "UploadCostConfirmation", - substitutions, - payload, - boost::bind( - &LLNewAgentInventoryVariablePriceResponder::Impl::uploadConfirmationCallback, - mImpl, - _1, - _2, - LLPointer<LLNewAgentInventoryVariablePriceResponder>(this))); - } -} - - diff --git a/indra/newview/llassetuploadresponders.h b/indra/newview/llassetuploadresponders.h deleted file mode 100644 index 7fbebc7481..0000000000 --- a/indra/newview/llassetuploadresponders.h +++ /dev/null @@ -1,151 +0,0 @@ -/** - * @file llassetuploadresponders.h - * @brief Processes responses received for asset upload requests. - * - * $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_LLASSETUPLOADRESPONDER_H -#define LL_LLASSETUPLOADRESPONDER_H - -#include "llhttpclient.h" - -// Abstract class for supporting asset upload -// via capabilities -class LLAssetUploadResponder : public LLHTTPClient::Responder -{ -protected: - LOG_CLASS(LLAssetUploadResponder); -public: - LLAssetUploadResponder(const LLSD& post_data, - const LLUUID& vfile_id, - LLAssetType::EType asset_type); - LLAssetUploadResponder(const LLSD& post_data, - const std::string& file_name, - LLAssetType::EType asset_type); - ~LLAssetUploadResponder(); - -protected: - virtual void httpFailure(); - virtual void httpSuccess(); - -public: - virtual void uploadUpload(const LLSD& content); - virtual void uploadComplete(const LLSD& content); - virtual void uploadFailure(const LLSD& content); - -protected: - LLSD mPostData; - LLAssetType::EType mAssetType; - LLUUID mVFileID; - std::string mFileName; -}; - -// TODO*: Remove this once deprecated -class LLNewAgentInventoryResponder : public LLAssetUploadResponder -{ - LOG_CLASS(LLNewAgentInventoryResponder); -public: - LLNewAgentInventoryResponder( - const LLSD& post_data, - const LLUUID& vfile_id, - LLAssetType::EType asset_type); - LLNewAgentInventoryResponder( - const LLSD& post_data, - const std::string& file_name, - LLAssetType::EType asset_type); - virtual void uploadComplete(const LLSD& content); - virtual void uploadFailure(const LLSD& content); -protected: - virtual void httpFailure(); -}; - -// 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.) -// the functions onApplicationLevelError and showConfirmationDialog. -class LLNewAgentInventoryVariablePriceResponder : - public LLHTTPClient::Responder -{ - LOG_CLASS(LLNewAgentInventoryVariablePriceResponder); -public: - LLNewAgentInventoryVariablePriceResponder( - const LLUUID& vfile_id, - LLAssetType::EType asset_type, - const LLSD& inventory_info); - - LLNewAgentInventoryVariablePriceResponder( - const std::string& file_name, - LLAssetType::EType asset_type, - const LLSD& inventory_info); - virtual ~LLNewAgentInventoryVariablePriceResponder(); - -private: - /* virtual */ void httpFailure(); - /* virtual */ void httpSuccess(); - -public: - virtual void onApplicationLevelError( - const LLSD& error); - virtual void showConfirmationDialog( - S32 upload_price, - S32 resource_cost, - const std::string& confirmation_url); - -private: - class Impl; - Impl* mImpl; -}; - -class LLUpdateAgentInventoryResponder : public LLAssetUploadResponder -{ -public: - LLUpdateAgentInventoryResponder(const LLSD& post_data, - const LLUUID& vfile_id, - LLAssetType::EType asset_type); - LLUpdateAgentInventoryResponder(const LLSD& post_data, - const std::string& file_name, - LLAssetType::EType asset_type); - virtual void uploadComplete(const LLSD& content); -}; - -class LLUpdateTaskInventoryResponder : public LLAssetUploadResponder -{ -public: - LLUpdateTaskInventoryResponder(const LLSD& post_data, - const LLUUID& vfile_id, - LLAssetType::EType asset_type); - LLUpdateTaskInventoryResponder(const LLSD& post_data, - const std::string& file_name, - LLAssetType::EType asset_type); - LLUpdateTaskInventoryResponder(const LLSD& post_data, - const std::string& file_name, - const LLUUID& queue_id, - LLAssetType::EType asset_type); - - virtual void uploadComplete(const LLSD& content); - -private: - LLUUID mQueueId; -}; - -#endif // LL_LLASSETUPLOADRESPONDER_H diff --git a/indra/newview/llavatarrenderinfoaccountant.cpp b/indra/newview/llavatarrenderinfoaccountant.cpp index 2760a97bda..2be3e8546f 100644 --- a/indra/newview/llavatarrenderinfoaccountant.cpp +++ b/indra/newview/llavatarrenderinfoaccountant.cpp @@ -28,17 +28,14 @@ // Precompiled header #include "llviewerprecompiledheaders.h" +// associated header +#include "llavatarrenderinfoaccountant.h" +#include "llavatarrendernotifier.h" // STL headers // std headers // external library headers // other Linden headers #include "llcharacter.h" -#include "httprequest.h" -#include "httphandler.h" -#include "httpresponse.h" -#include "llcorehttputil.h" -#include "llappcorehttp.h" -#include "llavatarrendernotifier.h" #include "lltimer.h" #include "llviewercontrol.h" #include "llviewermenu.h" @@ -46,9 +43,10 @@ #include "llviewerregion.h" #include "llvoavatar.h" #include "llworld.h" -// associated header -#include "llavatarrenderinfoaccountant.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 @@ -60,329 +58,286 @@ static const std::string KEY_IDENTIFIER = "identifier"; static const std::string KEY_MESSAGE = "message"; static const std::string KEY_ERROR = "error"; - static const F32 SECS_BETWEEN_REGION_SCANS = 5.f; // Scan the region list every 5 seconds static const F32 SECS_BETWEEN_REGION_REQUEST = 15.0; // Look for new avs every 15 seconds static const F32 SECS_BETWEEN_REGION_REPORTS = 60.0; // Update each region every 60 seconds -// The policy class for HTTP traffic; this is the right value for all capability requests. -static LLCore::HttpRequest::policy_t http_policy(LLAppCoreHttp::AP_REPORTING); - -// Priority for HTTP requests. Use 0U. -static LLCore::HttpRequest::priority_t http_priority(0U); - +//========================================================================= LLAvatarRenderInfoAccountant::LLAvatarRenderInfoAccountant() - : mHttpRequest(new LLCore::HttpRequest) - , mHttpHeaders(new LLCore::HttpHeaders) - , mHttpOptions(new LLCore::HttpOptions) { - mHttpOptions->setTransferTimeout(SECS_BETWEEN_REGION_SCANS); - - mHttpHeaders->append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_LLSD_XML); - mHttpHeaders->append(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_LLSD_XML); } LLAvatarRenderInfoAccountant::~LLAvatarRenderInfoAccountant() { - mHttpOptions->release(); - mHttpHeaders->release(); - // delete mHttpRequest; ??? } -// HTTP responder class for GET request for avatar render weight information -class LLAvatarRenderInfoGetHandler : public LLCore::HttpHandler + +void LLAvatarRenderInfoAccountant::avatarRenderInfoGetCoro(std::string url, U64 regionHandle) { -private: - LOG_CLASS(LLAvatarRenderInfoGetHandler); - -public: - LLAvatarRenderInfoGetHandler() : LLCore::HttpHandler() - { - } + 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->getAndSuspend(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; + } + + regionp->getRenderInfoRequestTimer().resetWithExpiry(SECS_BETWEEN_REGION_REQUEST); + + 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 & avatar_render_info = result[KEY_AGENTS]; + if (avatar_render_info.isMap()) + { + if ( avatar_render_info.has(KEY_REPORTING_COMPLEXITY_LIMIT) + && avatar_render_info.has(KEY_OVER_COMPLEXITY_LIMIT)) + { + U32 reporting = avatar_render_info[KEY_REPORTING_COMPLEXITY_LIMIT].asInteger(); + U32 overlimit = avatar_render_info[KEY_OVER_COMPLEXITY_LIMIT].asInteger(); - void onCompleted(LLCore::HttpHandle handle, - LLCore::HttpResponse* response) - { - LLCore::HttpStatus status = response->getStatus(); - if (status) + LL_DEBUGS("AvatarRenderInfo") << "complexity limit: "<<reporting<<" reporting, "<<overlimit<<" over limit"<<LL_ENDL; + + LLAvatarRenderNotifier::getInstance()->updateNotificationRegion(reporting, overlimit); + } + + if (avatar_render_info.has(KEY_AGENTS)) { - LL_DEBUGS("AvatarRenderInfo") << "response"<<LL_ENDL; - LLSD avatar_render_info; - if (LLCoreHttpUtil::responseToLLSD(response, false /* quiet logging */, - avatar_render_info)) - { - if (avatar_render_info.isMap()) - { - if ( avatar_render_info.has(KEY_REPORTING_COMPLEXITY_LIMIT) - && avatar_render_info.has(KEY_OVER_COMPLEXITY_LIMIT)) - { - U32 reporting = avatar_render_info[KEY_REPORTING_COMPLEXITY_LIMIT].asInteger(); - U32 overlimit = avatar_render_info[KEY_OVER_COMPLEXITY_LIMIT].asInteger(); - - LL_DEBUGS("AvatarRenderInfo") << "complexity limit: "<<reporting<<" reporting, "<<overlimit<<" over limit"<<LL_ENDL; - - LLAvatarRenderNotifier::getInstance()->updateNotificationRegion(reporting, overlimit); + const LLSD & agents = avatar_render_info[KEY_AGENTS]; + if (agents.isMap()) + { + for (LLSD::map_const_iterator agent_iter = agents.beginMap(); + agent_iter != agents.endMap(); + agent_iter++ + ) + { + LLUUID target_agent_id = LLUUID(agent_iter->first); + LLViewerObject* avatarp = gObjectList.findObject(target_agent_id); + if (avatarp && avatarp->isAvatar()) + { + const LLSD & agent_info_map = agent_iter->second; + if (agent_info_map.isMap()) + { + LL_DEBUGS("AvatarRenderInfo") << " 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()); + } + } + else + { + LL_WARNS("AvatarRenderInfo") << "agent entry invalid" + << " agent " << target_agent_id + << " map " << agent_info_map + << LL_ENDL; + } } - - if (avatar_render_info.has(KEY_AGENTS)) - { - const LLSD & agents = avatar_render_info[KEY_AGENTS]; - if (agents.isMap()) - { - for (LLSD::map_const_iterator agent_iter = agents.beginMap(); - agent_iter != agents.endMap(); - agent_iter++ - ) - { - LLUUID target_agent_id = LLUUID(agent_iter->first); - LLViewerObject* avatarp = gObjectList.findObject(target_agent_id); - if (avatarp && avatarp->isAvatar()) - { - const LLSD & agent_info_map = agent_iter->second; - if (agent_info_map.isMap()) - { - LL_DEBUGS("AvatarRenderInfo") << " 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()); - } - } - else - { - LL_WARNS("AvatarRenderInfo") << "agent entry invalid" - << " agent " << target_agent_id - << " map " << agent_info_map - << LL_ENDL; - } - } - else - { - LL_DEBUGS("AvatarRenderInfo") << "Unknown agent " << target_agent_id << LL_ENDL; - } - } // for agent_iter - } - else - { - LL_WARNS("AvatarRenderInfo") << "malformed get response agents avatar_render_info is not map" << LL_ENDL; - } - } // has "agents" - else if (avatar_render_info.has(KEY_ERROR)) - { - const LLSD & error = avatar_render_info[KEY_ERROR]; - LL_WARNS("AvatarRenderInfo") << "Avatar render info GET error: " - << error[KEY_IDENTIFIER] - << ": " << error[KEY_MESSAGE] - << LL_ENDL; - } - else - { - LL_WARNS("AvatarRenderInfo") << "no agent key in get response" << LL_ENDL; - } - } - else - { - LL_WARNS("AvatarRenderInfo") << "malformed get response is not map" << LL_ENDL; - } + else + { + LL_DEBUGS("AvatarRenderInfo") << "Unknown agent " << target_agent_id << LL_ENDL; + } + } // for agent_iter } - else - { - LL_WARNS("AvatarRenderInfo") << "malformed get response parse failure" << LL_ENDL; - } + else + { + LL_WARNS("AvatarRenderInfo") << "malformed get response agents avatar_render_info is not map" << LL_ENDL; + } + } // has "agents" + else if (avatar_render_info.has(KEY_ERROR)) + { + const LLSD & error = avatar_render_info[KEY_ERROR]; + LL_WARNS("AvatarRenderInfo") << "Avatar render info GET error: " + << error[KEY_IDENTIFIER] + << ": " << error[KEY_MESSAGE] + << LL_ENDL; } else { - // Something went wrong. Translate the status to - // a meaningful message. - LL_WARNS("AvatarRenderInfo") << "GET failed Status: " - << status.toTerseString() - << ", Reason: " << status.toString() - << LL_ENDL; - } - - delete this; // release the handler object - } -}; + LL_WARNS("AvatarRenderInfo") << "no agent key in get response" << LL_ENDL; + } + } + else + { + LL_WARNS("AvatarRenderInfo") << "malformed get response is not map" << LL_ENDL; + } + } // 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 LLAvatarRenderInfoPostHandler : public LLCore::HttpHandler +//------------------------------------------------------------------------- +void LLAvatarRenderInfoAccountant::avatarRenderInfoReportCoro(std::string url, U64 regionHandle) { - private: - LOG_CLASS(LLAvatarRenderInfoPostHandler); - - public: - LLAvatarRenderInfoPostHandler() : LLCore::HttpHandler() - { - } - - void onCompleted(LLCore::HttpHandle handle, - LLCore::HttpResponse* response) - { - LLCore::HttpStatus status = response->getStatus(); - if (status) - { - LL_DEBUGS("AvatarRenderInfo") << "post succeeded" << LL_ENDL; - } - else - { - // Something went wrong. Translate the status to - // a meaningful message. - LL_WARNS("AvatarRenderInfo") << "POST failed Status: " - << status.toTerseString() - << ", Reason: " << status.toString() - << LL_ENDL; - } - - delete this; // release the handler object - } -}; - + 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; + } + + LL_DEBUGS("AvatarRenderInfoAccountant") + << "Sending avatar render info for region " << regionp->getName() + << " to " << url << LL_ENDL; + + U32 num_avs = 0; + // Build the render info to POST to the region + 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->calculateUpdateRenderComplexity(); // Make sure the numbers are up-to-date + + LLSD info = LLSD::emptyMap(); + U32 avatar_complexity = avatar->getVisualComplexity(); + if (avatar_complexity > 0) + { + // the weight/complexity is unsigned, but LLSD only stores signed integers, + // so if it's over that (which would be ridiculously high), just store the maximum signed int value + info[KEY_WEIGHT] = (S32)(avatar_complexity < S32_MAX ? avatar_complexity : S32_MAX); + info[KEY_TOO_COMPLEX] = LLSD::Boolean(avatar->isTooComplex()); + agents[avatar->getID().asString()] = info; + + LL_DEBUGS("AvatarRenderInfo") << "Sending avatar render info for " << avatar->getID() + << ": " << info << LL_ENDL; + num_avs++; + } + } + iter++; + } + + // Reset this regions timer, moving to longer intervals if there are lots of avatars around + regionp->getRenderInfoReportTimer().resetWithExpiry(SECS_BETWEEN_REGION_REPORTS + (2.f * num_avs)); + + if (num_avs == 0) + return; // nothing to report + + LLSD report = LLSD::emptyMap(); + report[KEY_AGENTS] = agents; + + regionp = NULL; + LLSD result = httpAdapter->postAndSuspend(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 (result.isMap()) + { + if (result.has(KEY_ERROR)) + { + const LLSD & error = result[KEY_ERROR]; + LL_WARNS("AvatarRenderInfoAccountant") << "POST error: " + << error[KEY_IDENTIFIER] + << ": " << error[KEY_MESSAGE] + << " from region " << regionp->getName() + << LL_ENDL; + } + else + { + LL_DEBUGS("AvatarRenderInfoAccountant") + << "POST result for region " << regionp->getName() + << ": " << result + << LL_ENDL; + } + } + else + { + LL_WARNS("AvatarRenderInfoAccountant") << "Malformed POST response from region '" << regionp->getName() + << LL_ENDL; + } +} // Send request for avatar weights in one region // called when the mRenderInfoScanTimer expires (forced when entering a new region) void LLAvatarRenderInfoAccountant::sendRenderInfoToRegion(LLViewerRegion * regionp) { - if ( regionp->getRenderInfoReportTimer().hasExpired() ) // Time to make request + std::string url = regionp->getCapability("AvatarRenderInfo"); + if ( !url.empty() // we have the capability + && regionp->getRenderInfoReportTimer().hasExpired() // Time to make request) + ) { - U32 num_avs = 0; - - std::string url = regionp->getCapability("AvatarRenderInfo"); - if (!url.empty()) - { - // Build the render info to POST to the region - 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->calculateUpdateRenderComplexity(); // Make sure the numbers are up-to-date - - LLSD info = LLSD::emptyMap(); - U32 avatar_complexity = avatar->getVisualComplexity(); - if (avatar_complexity > 0) - { - // the weight/complexity is unsigned, but LLSD only stores signed integers, - // so if it's over that (which would be ridiculously high), just store the maximum signed int value - info[KEY_WEIGHT] = (S32)(avatar_complexity < S32_MAX ? avatar_complexity : S32_MAX); - info[KEY_TOO_COMPLEX] = LLSD::Boolean(avatar->isTooComplex()); - agents[avatar->getID().asString()] = info; - - LL_DEBUGS("AvatarRenderInfo") << "Sending avatar render info for " << avatar->getID() - << ": " << info << LL_ENDL; - num_avs++; - } - } - iter++; - } - - if (num_avs > 0) - { - LLSD report = LLSD::emptyMap(); - report[KEY_AGENTS] = agents; - - LLCore::HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); - LLAvatarRenderInfoPostHandler* handler = new LLAvatarRenderInfoPostHandler; - - handle = LLCoreHttpUtil::requestPostWithLLSD(mHttpRequest, - http_policy, - http_priority, - url, - report, - mHttpOptions, - mHttpHeaders, - handler); - if (LLCORE_HTTP_HANDLE_INVALID == handle) - { - LLCore::HttpStatus status(mHttpRequest->getStatus()); - LL_WARNS("AvatarRenderInfo") << "HTTP POST request failed" - << " Status: " << status.toTerseString() - << " Reason: '" << status.toString() << "'" - << LL_ENDL; - delete handler; - } - else - { - LL_DEBUGS("AvatarRenderInfo") << "Sent render costs for " << num_avs - << " avatars to region " << regionp->getName() - << LL_ENDL; - - - } - } - else - { - LL_DEBUGS("AvatarRenderInfo") << "no agent info to send" << LL_ENDL; - } - } - else - { - LL_WARNS("AvatarRenderInfo") << "AvatarRenderInfo cap is empty" << LL_ENDL; - } - - // Reset this regions timer, moving to longer intervals if there are lots of avatars around - regionp->getRenderInfoReportTimer().resetWithExpiry(SECS_BETWEEN_REGION_REPORTS + (2.f * num_avs)); + std::string coroname = + LLCoros::instance().launch("LLAvatarRenderInfoAccountant::avatarRenderInfoReportCoro", + boost::bind(&LLAvatarRenderInfoAccountant::avatarRenderInfoReportCoro, url, regionp->getHandle())); } } - - - -// static -// Send request for one region, no timer checks +// Send request for avatar weights in one region +// called when the mRenderInfoScanTimer expires (forced when entering a new region) void LLAvatarRenderInfoAccountant::getRenderInfoFromRegion(LLViewerRegion * regionp) { - if (regionp->getRenderInfoRequestTimer().hasExpired()) + std::string url = regionp->getCapability("AvatarRenderInfo"); + if ( !url.empty() + && regionp->getRenderInfoRequestTimer().hasExpired() + ) { - std::string url = regionp->getCapability("AvatarRenderInfo"); - if (!url.empty()) - { - - LLAvatarRenderInfoGetHandler* handler = new LLAvatarRenderInfoGetHandler; - // First send a request to get the latest data - LLCore::HttpHandle handle = mHttpRequest->requestGet(http_policy, - http_priority, - url, - NULL, - NULL, - handler); - if (LLCORE_HTTP_HANDLE_INVALID != handle) - { - LL_DEBUGS("AvatarRenderInfo") << "Requested avatar render info for region " - << regionp->getName() - << LL_ENDL; - } - else - { - LL_WARNS("AvatarRenderInfo") << "Failed to launch HTTP GET request. Try again." - << LL_ENDL; - delete handler; - } - } - else - { - LL_WARNS("AvatarRenderInfo") << "no AvatarRenderInfo cap for " << regionp->getName() << LL_ENDL; - } - - regionp->getRenderInfoRequestTimer().resetWithExpiry(SECS_BETWEEN_REGION_REQUEST); + LL_DEBUGS("AvatarRenderInfo") + << "Requesting avatar render info for region " << regionp->getName() + << " from " << url + << LL_ENDL; + + // First send a request to get the latest data + std::string coroname = + LLCoros::instance().launch("LLAvatarRenderInfoAccountant::avatarRenderInfoGetCoro", + boost::bind(&LLAvatarRenderInfoAccountant::avatarRenderInfoGetCoro, url, regionp->getHandle())); } } - // static // Called every frame - send render weight requests to every region void LLAvatarRenderInfoAccountant::idle() { - mHttpRequest->update(0); // give any pending http operations a chance to call completion methods - if (mRenderInfoScanTimer.hasExpired()) { LL_DEBUGS("AvatarRenderInfo") << "Scanning regions for render info updates" diff --git a/indra/newview/llavatarrenderinfoaccountant.h b/indra/newview/llavatarrenderinfoaccountant.h index 8117c18f4d..870ef4f394 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 @@ -56,10 +59,8 @@ class LLAvatarRenderInfoAccountant : public LLSingleton<LLAvatarRenderInfoAccoun // further limited by per region Request and Report timers LLFrameTimer mRenderInfoScanTimer; - // - LLCore::HttpRequest* mHttpRequest; - LLCore::HttpHeaders* mHttpHeaders; - LLCore::HttpOptions* mHttpOptions; + 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 100644 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 100644 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/llcaphttpsender.cpp b/indra/newview/llcaphttpsender.cpp deleted file mode 100644 index b2524d14f8..0000000000 --- a/indra/newview/llcaphttpsender.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/** - * @file llcaphttpsender.cpp - * @brief Abstracts details of sending messages via UntrustedMessage cap. - * - * $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 "llcaphttpsender.h" - -#include "llhost.h" - -LLCapHTTPSender::LLCapHTTPSender(const std::string& cap) : - mCap(cap) -{ -} - -//virtual -void LLCapHTTPSender::send(const LLHost& host, const std::string& message, - const LLSD& body, - LLHTTPClient::ResponderPtr response) const -{ - LL_INFOS() << "LLCapHTTPSender::send: message " << message - << " to host " << host << LL_ENDL; - LLSD llsd; - llsd["message"] = message; - llsd["body"] = body; - LLHTTPClient::post(mCap, llsd, response); -} diff --git a/indra/newview/llcaphttpsender.h b/indra/newview/llcaphttpsender.h deleted file mode 100644 index e1f4c813f6..0000000000 --- a/indra/newview/llcaphttpsender.h +++ /dev/null @@ -1,48 +0,0 @@ -/** - * @file llcaphttpsender.h - * @brief Abstracts details of sending messages via the - * UntrustedMessage capability. - * - * $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_CAP_HTTP_SENDER_H -#define LL_CAP_HTTP_SENDER_H - -#include "llhttpsender.h" - -class LLCapHTTPSender : public LLHTTPSender -{ -public: - LLCapHTTPSender(const std::string& cap); - - /** @brief Send message via UntrustedMessage capability with body, - call response when done */ - virtual void send(const LLHost& host, - const std::string& message, const LLSD& body, - LLHTTPClient::ResponderPtr response) const; - -private: - std::string mCap; -}; - -#endif // LL_CAP_HTTP_SENDER_H diff --git a/indra/newview/llclassifiedstatsresponder.cpp b/indra/newview/llclassifiedstatsresponder.cpp deleted file mode 100644 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 100644 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/llcompilequeue.cpp b/indra/newview/llcompilequeue.cpp index d9fd4509a5..219bcf0eb0 100644 --- a/indra/newview/llcompilequeue.cpp +++ b/indra/newview/llcompilequeue.cpp @@ -37,8 +37,6 @@ #include "llcompilequeue.h" #include "llagent.h" -#include "llassetuploadqueue.h" -#include "llassetuploadresponders.h" #include "llchat.h" #include "llfloaterreg.h" #include "llviewerwindow.h" @@ -59,11 +57,54 @@ #include "lltrans.h" #include "llselectmgr.h" -#include "llexperienceassociationresponder.h" #include "llexperiencecache.h" -// *TODO: This should be separated into the script queue, and the floater views of that queue. -// There should only be one floater class that can view any queue type +#include "llviewerassetupload.h" +#include "llcorehttputil.h" + +// *NOTE$: A minor specialization of LLScriptAssetUpload, it does not require a buffer +// (and does not save a buffer to the vFS) and it finds the compile queue window and +// displays a compiling message. +class LLQueuedScriptAssetUpload : public LLScriptAssetUpload +{ +public: + LLQueuedScriptAssetUpload(LLUUID taskId, LLUUID itemId, LLUUID assetId, TargetType_t targetType, + bool isRunning, std::string scriptName, LLUUID queueId, LLUUID exerienceId, taskUploadFinish_f finish) : + LLScriptAssetUpload(taskId, itemId, targetType, isRunning, + exerienceId, std::string(), finish), + mScriptName(scriptName), + mQueueId(queueId) + { + setAssetId(assetId); + } + + virtual LLSD prepareUpload() + { + /* *NOTE$: The parent class (LLScriptAssetUpload will attempt to save + * the script buffer into to the VFS. Since the resource is already in + * the VFS we don't want to do that. Just put a compiling message in + * the window and move on + */ + LLFloaterCompileQueue* queue = LLFloaterReg::findTypedInstance<LLFloaterCompileQueue>("compile_queue", LLSD(mQueueId)); + if (queue) + { + std::string message = std::string("Compiling \"") + getScriptName() + std::string("\"..."); + + queue->getChild<LLScrollListCtrl>("queue output")->addSimpleElement(message, ADD_BOTTOM); + } + + return LLSD().with("success", LLSD::Boolean(true)); + } + + + std::string getScriptName() const { return mScriptName; } + +private: + void setScriptName(const std::string &scriptName) { mScriptName = scriptName; } + + LLUUID mQueueId; + std::string mScriptName; +}; ///---------------------------------------------------------------------------- /// Local function declarations, constants, enums, and typedefs @@ -127,7 +168,7 @@ void LLFloaterScriptQueue::inventoryChanged(LLViewerObject* viewer_object, //which it internally stores. //If we call this further down in the function, calls to handleInventory - //and nextObject may update the interally stored viewer object causing + //and nextObject may update the internally stored viewer object causing //the removal of the incorrect listener from an incorrect object. //Fixes SL-6119:Recompile scripts fails to complete @@ -242,81 +283,17 @@ BOOL LLFloaterScriptQueue::startQueue() return nextObject(); } -class CompileQueueExperienceResponder : public LLHTTPClient::Responder -{ -public: - CompileQueueExperienceResponder(const LLUUID& parent):mParent(parent) - { - } - - LLUUID mParent; - - /*virtual*/ void httpSuccess() - { - sendResult(getContent()); - } - /*virtual*/ void httpFailure() - { - sendResult(LLSD()); - } - void sendResult(const LLSD& content) - { - LLFloaterCompileQueue* queue = LLFloaterReg::findTypedInstance<LLFloaterCompileQueue>("compile_queue", mParent); - if(!queue) - return; - - queue->experienceIdsReceived(content["experience_ids"]); - } -}; - - - ///---------------------------------------------------------------------------- /// Class LLFloaterCompileQueue ///---------------------------------------------------------------------------- - -class LLCompileFloaterUploadQueueSupplier : public LLAssetUploadQueueSupplier -{ -public: - - LLCompileFloaterUploadQueueSupplier(const LLUUID& queue_id) : - mQueueId(queue_id) - { - } - - virtual LLAssetUploadQueue* get() const - { - LLFloaterCompileQueue* queue = LLFloaterReg::findTypedInstance<LLFloaterCompileQueue>("compile_queue", LLSD(mQueueId)); - if(NULL == queue) - { - return NULL; - } - return queue->getUploadQueue(); - } - - virtual void log(std::string message) const - { - LLFloaterCompileQueue* queue = LLFloaterReg::findTypedInstance<LLFloaterCompileQueue>("compile_queue", LLSD(mQueueId)); - if(NULL == queue) - { - return; - } - - queue->getChild<LLScrollListCtrl>("queue output")->addSimpleElement(message, ADD_BOTTOM); - } - -private: - LLUUID mQueueId; -}; - LLFloaterCompileQueue::LLFloaterCompileQueue(const LLSD& key) : LLFloaterScriptQueue(key) { setTitle(LLTrans::getString("CompileQueueTitle")); setStartString(LLTrans::getString("CompileQueueStart")); - mUploadQueue = new LLAssetUploadQueue(new LLCompileFloaterUploadQueueSupplier(key.asUUID())); +// mUploadQueue = new LLAssetUploadQueue(new LLCompileFloaterUploadQueueSupplier(key.asUUID())); } LLFloaterCompileQueue::~LLFloaterCompileQueue() @@ -380,8 +357,8 @@ void LLFloaterCompileQueue::handleInventory(LLViewerObject *viewer_object, LLScriptQueueData* datap = new LLScriptQueueData(getKey().asUUID(), viewer_object->getID(), itemp); - ExperienceAssociationResponder::fetchAssociatedExperience(itemp->getParentUUID(), itemp->getUUID(), - boost::bind(LLFloaterCompileQueue::requestAsset, datap, _1)); + LLExperienceCache::instance().fetchAssociatedExperience(itemp->getParentUUID(), itemp->getUUID(), + boost::bind(&LLFloaterCompileQueue::requestAsset, datap, _1)); } } } @@ -423,6 +400,37 @@ void LLFloaterCompileQueue::requestAsset( LLScriptQueueData* datap, const LLSD& (void*)datap); } +/*static*/ +void LLFloaterCompileQueue::finishLSLUpload(LLUUID itemId, LLUUID taskId, LLUUID newAssetId, LLSD response, std::string scriptName, LLUUID queueId) +{ + + LLFloaterCompileQueue* queue = LLFloaterReg::findTypedInstance<LLFloaterCompileQueue>("compile_queue", LLSD(queueId)); + if (queue) + { + // Bytecode save completed + if (response["compiled"]) + { + std::string message = std::string("Compilation of \"") + scriptName + std::string("\" succeeded"); + + queue->getChild<LLScrollListCtrl>("queue output")->addSimpleElement(message, ADD_BOTTOM); + LL_INFOS() << message << LL_ENDL; + } + else + { + LLSD compile_errors = response["errors"]; + for (LLSD::array_const_iterator line = compile_errors.beginArray(); + line < compile_errors.endArray(); line++) + { + std::string str = line->asString(); + str.erase(std::remove(str.begin(), str.end(), '\n'), str.end()); + + queue->getChild<LLScrollListCtrl>("queue output")->addSimpleElement(str, ADD_BOTTOM); + } + LL_INFOS() << response["errors"] << LL_ENDL; + } + + } +} // This is the callback for when each script arrives // static @@ -441,36 +449,21 @@ void LLFloaterCompileQueue::scriptArrived(LLVFS *vfs, const LLUUID& asset_id, std::string buffer; if(queue && (0 == status)) { - //LL_INFOS() << "ITEM NAME 3: " << data->mScriptName << LL_ENDL; - - // Dump this into a file on the local disk so we can compile it. - std::string filename; - LLVFile file(vfs, asset_id, type); - std::string uuid_str; - asset_id.toString(uuid_str); - filename = gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_str) + llformat(".%s",LLAssetType::lookup(type)); - - const bool is_running = true; - LLViewerObject* object = gObjectList.findObject(data->mTaskId); - if (object) - { - std::string url = object->getRegion()->getCapability("UpdateScriptTask"); - if(!url.empty()) - { - // Read script source in to buffer. - U32 script_size = file.getSize(); - U8* script_data = new U8[script_size]; - file.read(script_data, script_size); - - queue->mUploadQueue->queue(filename, data->mTaskId, - data->mItem->getUUID(), is_running, queue->mMono, queue->getKey().asUUID(), - script_data, script_size, data->mItem->getName(), data->mExperienceId); - } - else - { - buffer = LLTrans::getString("CompileQueueServiceUnavailable") + (": ") + data->mItem->getName(); - } - } + LLViewerObject* object = gObjectList.findObject(data->mTaskId); + if (object) + { + std::string url = object->getRegion()->getCapability("UpdateScriptTask"); + std::string scriptName = data->mItem->getName(); + + LLBufferedAssetUploadInfo::taskUploadFinish_f proc = boost::bind(&LLFloaterCompileQueue::finishLSLUpload, _1, _2, _3, _4, + scriptName, data->mQueueID); + + LLResourceUploadInfo::ptr_t uploadInfo(new LLQueuedScriptAssetUpload(data->mTaskId, data->mItem->getUUID(), asset_id, + (queue->mMono) ? LLScriptAssetUpload::MONO : LLScriptAssetUpload::LSL2, + true, scriptName, data->mQueueID, data->mExperienceId, proc)); + + LLViewerAssetUpload::EnqueueInventoryUpload(url, uploadInfo); + } } else { @@ -653,14 +646,29 @@ BOOL LLFloaterCompileQueue::startQueue() std::string lookup_url=region->getCapability("GetCreatorExperiences"); if(!lookup_url.empty()) { - LLHTTPClient::get(lookup_url, new CompileQueueExperienceResponder(getKey().asUUID())); - return TRUE; + LLCoreHttpUtil::HttpCoroutineAdapter::completionCallback_t success = + boost::bind(&LLFloaterCompileQueue::processExperienceIdResults, _1, getKey().asUUID()); + + LLCoreHttpUtil::HttpCoroutineAdapter::completionCallback_t failure = + boost::bind(&LLFloaterCompileQueue::processExperienceIdResults, LLSD(), getKey().asUUID()); + + LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpGet(lookup_url, + success, failure); + return TRUE; } } return nextObject(); } +/*static*/ +void LLFloaterCompileQueue::processExperienceIdResults(LLSD result, LLUUID parent) +{ + LLFloaterCompileQueue* queue = LLFloaterReg::findTypedInstance<LLFloaterCompileQueue>("compile_queue", parent); + if (!queue) + return; + queue->experienceIdsReceived(result["experience_ids"]); +} void LLFloaterNotRunQueue::handleInventory(LLViewerObject* viewer_obj, LLInventoryObject::object_list_t* inv) diff --git a/indra/newview/llcompilequeue.h b/indra/newview/llcompilequeue.h index 54842bb302..cee8efe9b0 100644 --- a/indra/newview/llcompilequeue.h +++ b/indra/newview/llcompilequeue.h @@ -118,8 +118,6 @@ struct LLCompileQueueData mQueueID(q_id), mItemId(item_id) {} }; -class LLAssetUploadQueue; - class LLFloaterCompileQueue : public LLFloaterScriptQueue { friend class LLFloaterReg; @@ -131,8 +129,6 @@ public: // remove any object in mScriptScripts with the matching uuid. void removeItemByItemID(const LLUUID& item_id); - LLAssetUploadQueue* getUploadQueue() { return mUploadQueue; } - void experienceIdsReceived( const LLSD& content ); BOOL hasExperience(const LLUUID& id)const; @@ -147,6 +143,8 @@ protected: static void requestAsset(struct LLScriptQueueData* datap, const LLSD& experience); + static void finishLSLUpload(LLUUID itemId, LLUUID taskId, LLUUID newAssetId, LLSD response, std::string scriptName, LLUUID queueId); + // This is the callback for when each script arrives static void scriptArrived(LLVFS *vfs, const LLUUID& asset_id, LLAssetType::EType type, @@ -157,7 +155,8 @@ protected: LLViewerInventoryItem::item_array_t mCurrentScripts; private: - LLAssetUploadQueue* mUploadQueue; + static void processExperienceIdResults(LLSD result, LLUUID parent); + uuid_list_t mExperienceIds; }; diff --git a/indra/newview/llestateinfomodel.cpp b/indra/newview/llestateinfomodel.cpp index 78d619a315..8f2eb41307 100644 --- 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->postAndSuspend(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 100644 --- 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..7178042b32 100644 --- a/indra/newview/lleventpoll.cpp +++ b/indra/newview/lleventpoll.cpp @@ -30,261 +30,256 @@ #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() + { + mDone = true; + + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t adapter = mAdapter.lock(); + if (adapter) + { + LL_INFOS() << "requesting stop for event poll coroutine <" << mCounter << ">" << LL_ENDL; + // cancel the yielding operation if any. + adapter->cancelSuspendedOperation(); + } + else + { + LL_INFOS() << "Coroutine for poll <" << mCounter << "> previously stopped. No action taken." << LL_ENDL; + } + } + + 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->postAndSuspend(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(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_OPERATION_TIMEDOUT)) + { // A standard timeout response we get this when there are no events. + LL_INFOS("LLEventPollImpl") << "All is very quiet on target server. It may have gone idle?" << LL_ENDL; + 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("LLEventPollImpl") << "Canceling coroutine" << LL_ENDL; + break; + } + else if (!status.isHttpStatus()) + { + /// Some LLCore or LIBCurl error was returned. This is unlikely to be recoverable + LL_WARNS("LLEventPollImpl") << "Critical error from poll request returned from libraries. 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. + ++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; + + llcoro::suspendUntilTimeout(waitToRetry); + + 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..e2afd9226b 100644 --- a/indra/newview/lleventpoll.h +++ b/indra/newview/lleventpoll.h @@ -27,10 +27,23 @@ #ifndef LL_LLEVENTPOLL_H #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 +53,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/llexperienceassociationresponder.cpp b/indra/newview/llexperienceassociationresponder.cpp deleted file mode 100644 index f62ca7d75f..0000000000 --- a/indra/newview/llexperienceassociationresponder.cpp +++ /dev/null @@ -1,116 +0,0 @@ -/** - * @file llexperienceassociationresponder.cpp - * @brief llexperienceassociationresponder implementation. This class combines - * a lookup for a script association and an experience details request. The first - * is always async, but the second may be cached locally. - * - * $LicenseInfo:firstyear=2013&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2013, 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 "llexperienceassociationresponder.h" -#include "llexperiencecache.h" -#include "llviewerobjectlist.h" -#include "llviewerregion.h" -#include "llagent.h" - -ExperienceAssociationResponder::ExperienceAssociationResponder(ExperienceAssociationResponder::callback_t callback):mCallback(callback) -{ - ref(); -} - -void ExperienceAssociationResponder::fetchAssociatedExperience( const LLUUID& object_id, const LLUUID& item_id, callback_t callback ) -{ - LLSD request; - request["object-id"]=object_id; - request["item-id"]=item_id; - fetchAssociatedExperience(request, callback); -} - -void ExperienceAssociationResponder::fetchAssociatedExperience(LLSD& request, callback_t callback) -{ - LLViewerObject* object = gObjectList.findObject(request["object-id"]); - if (!object) - { - LL_DEBUGS() << "Object with ID " << request["object-id"] << " not found via gObjectList.findObject() in fetchAssociatedExperience" << LL_ENDL; - LL_DEBUGS() << "Using gAgent.getRegion() instead of object->getRegion()" << LL_ENDL; - } - LLViewerRegion* region = NULL; - if (object) - { - region = object->getRegion(); - } - else - { - region = gAgent.getRegion(); - } - if (region) - { - std::string lookup_url=region->getCapability("GetMetadata"); - if(!lookup_url.empty()) - { - LLSD fields; - fields.append("experience"); - request["fields"] = fields; - LLHTTPClient::post(lookup_url, request, new ExperienceAssociationResponder(callback)); - } - } - else - { - LL_WARNS() << "Failed to lookup region in fetchAssociatedExperience. Fetch request not sent." << LL_ENDL; - } -} - -void ExperienceAssociationResponder::httpFailure() -{ - LLSD msg; - msg["error"]=(LLSD::Integer)getStatus(); - msg["message"]=getReason(); - LL_INFOS("ExperienceAssociation") << "Failed to look up associated experience: " << getStatus() << ": " << getReason() << LL_ENDL; - - sendResult(msg); - -} -void ExperienceAssociationResponder::httpSuccess() -{ - if(!getContent().has("experience")) - { - - LLSD msg; - msg["message"]="no experience"; - msg["error"]=-1; - sendResult(msg); - return; - } - - LLExperienceCache::get(getContent()["experience"].asUUID(), boost::bind(&ExperienceAssociationResponder::sendResult, this, _1)); - -} - -void ExperienceAssociationResponder::sendResult( const LLSD& experience ) -{ - mCallback(experience); - unref(); -} - - - diff --git a/indra/newview/llexperienceassociationresponder.h b/indra/newview/llexperienceassociationresponder.h deleted file mode 100644 index 2bdc3d251b..0000000000 --- a/indra/newview/llexperienceassociationresponder.h +++ /dev/null @@ -1,58 +0,0 @@ -#include "llhttpclient.h" -#include "llsd.h" -/** - * @file llexperienceassociationresponder.h - * @brief llexperienceassociationresponder and related class definitions - * - * $LicenseInfo:firstyear=2013&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2013, 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_LLEXPERIENCEASSOCIATIONRESPONDER_H -#define LL_LLEXPERIENCEASSOCIATIONRESPONDER_H - -#include "llhttpclient.h" -#include "llsd.h" - -class ExperienceAssociationResponder : public LLHTTPClient::Responder -{ -public: - typedef boost::function<void(const LLSD& experience)> callback_t; - - ExperienceAssociationResponder(callback_t callback); - - /*virtual*/ void httpSuccess(); - /*virtual*/ void httpFailure(); - - static void fetchAssociatedExperience(const LLUUID& object_it, const LLUUID& item_id, callback_t callback); - -private: - static void fetchAssociatedExperience(LLSD& request, callback_t callback); - - void sendResult(const LLSD& experience); - - callback_t mCallback; - -}; - -#endif // LL_LLEXPERIENCEASSOCIATIONRESPONDER_H diff --git a/indra/newview/llfacebookconnect.cpp b/indra/newview/llfacebookconnect.cpp index 28319564e4..1de4102dba 100644 --- 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,349 @@ 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); + + LLSD result = httpAdapter->putAndSuspend(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(); - } + LLSD result = httpAdapter->postAndSuspend(httpRequest, getFacebookConnectURL(route, true), share, httpOpts, get_headers()); - /* 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")); - } - } -}; + 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->postAndSuspend(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); + + httpOpts->setFollowRedirects(false); + + LLSD result = httpAdapter->deleteAndSuspend(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->getAndSuspend(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->getAndSuspend(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->getAndSuspend(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 +541,32 @@ 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()); + setConnectionState(LLFacebookConnect::FB_CONNECTION_IN_PROGRESS); + + 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); + setConnectionState(LLFacebookConnect::FB_DISCONNECTING); + + 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,15 +574,16 @@ 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) { + setConnectionState(LLFacebookConnect::FB_POSTING); + LLSD body; if (!location.empty()) { @@ -511,80 +606,39 @@ 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) { + setConnectionState(LLFacebookConnect::FB_POSTING); + 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]; - } + setConnectionState(LLFacebookConnect::FB_POSTING); - 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()); + + setConnectionState(LLFacebookConnect::FB_POSTING); + + 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 7f72ddd804..063a3a521f 100644 --- 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" @@ -495,95 +495,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) - { - } - - - 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; -}; + const std::string base = gSavedSettings.getString("FeatureManagerHTTPTable"); -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->getRawAndSuspend(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 " << path << LL_ENDL; + + S32 size = raw.size(); + if (size > 0) + { + // write to file + LLAPRFile out(path, 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 100644 --- 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/llfilepicker.cpp b/indra/newview/llfilepicker.cpp index ef50594feb..a7236d1778 100644 --- a/indra/newview/llfilepicker.cpp +++ b/indra/newview/llfilepicker.cpp @@ -40,6 +40,10 @@ #include "llwindowsdl.h" // for some X/GTK utils to help with filepickers #endif // LL_SDL +#if LL_LINUX || LL_SOLARIS +#include "llhttpconstants.h" // file picker uses some of thes constants on Linux +#endif + // // Globals // diff --git a/indra/newview/llflickrconnect.cpp b/indra/newview/llflickrconnect.cpp index b75660ea00..c0ca5b8cf8 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,322 @@ 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->putAndSuspend(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); + + LLSD result = httpAdapter->postAndSuspend(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->postAndSuspend(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->deleteAndSuspend(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->getAndSuspend(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")); + } + } + else + { + 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() - { - // 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; -}; + +} /////////////////////////////////////////////////////////////////////////////// // -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->getAndSuspend(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 +435,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 +468,20 @@ 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()); + + setConnectionState(LLFlickrConnect::FLICKR_POSTING); + + 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"; + setConnectionState(LLFlickrConnect::FLICKR_POSTING); - // 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 c5d637d1fc..bababca652 100644 --- a/indra/newview/llfloaterabout.cpp +++ b/indra/newview/llfloaterabout.cpp @@ -50,7 +50,6 @@ // Linden library includes #include "llaudioengine.h" #include "llbutton.h" -#include "llcurl.h" #include "llglheaders.h" #include "llfloater.h" #include "llfloaterreg.h" @@ -63,6 +62,7 @@ #include "stringize.h" #include "llsdutil_math.h" #include "lleventapi.h" +#include "llcorehttputil.h" #if LL_WINDOWS #include "lldxhardware.h" @@ -72,18 +72,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 @@ -118,6 +106,9 @@ private: // listener name for update checks static const std::string sCheckUpdateListenerName; + + static void startFetchServerReleaseNotes(); + static void handleServerReleaseNotes(LLSD results); }; @@ -157,7 +148,7 @@ BOOL LLFloaterAbout::postBuild() { // start fetching server release notes URL setSupportText(LLTrans::getString("RetrievingData")); - LLServerReleaseNotesURLFetcher::startFetch(); + startFetchServerReleaseNotes(); } else // not logged in { @@ -220,6 +211,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: @@ -356,35 +391,3 @@ void LLFloaterAboutUtil::checkUpdatesAndNotify() LLFloaterAbout::setUpdateListener(); } -///---------------------------------------------------------------------------- -/// 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; - - 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 100644 --- 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 100644 --- 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..72892b47a4 100644 --- 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->getAndSuspend(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 100644 --- 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 100644 --- 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 100644 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 100644 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/llfloaterexperienceprofile.cpp b/indra/newview/llfloaterexperienceprofile.cpp index fd226d656c..2c9a8e64b7 100644 --- a/indra/newview/llfloaterexperienceprofile.cpp +++ b/indra/newview/llfloaterexperienceprofile.cpp @@ -36,7 +36,6 @@ #include "llexpandabletextbox.h" #include "llexperiencecache.h" #include "llfloaterreg.h" -#include "llhttpclient.h" #include "lllayoutstack.h" #include "lllineeditor.h" #include "llnotificationsutil.h" @@ -99,7 +98,7 @@ public: if(params.size() != 2 || params[1].asString() != "profile") return false; - LLExperienceCache::get(params[0].asUUID(), boost::bind(&LLExperienceHandler::experienceCallback, this, _1)); + LLExperienceCache::instance().get(params[0].asUUID(), boost::bind(&LLExperienceHandler::experienceCallback, this, _1)); return true; } @@ -139,175 +138,20 @@ LLFloaterExperienceProfile::~LLFloaterExperienceProfile() } -template<class T> -class HandleResponder : public LLHTTPClient::Responder -{ -public: - HandleResponder(const LLHandle<T>& parent):mParent(parent){} - LLHandle<T> mParent; - - virtual void httpFailure() - { - LL_WARNS() << "HandleResponder failed with code: " << getStatus() << ", reason: " << getReason() << LL_ENDL; - } -}; - -class ExperienceUpdateResponder : public HandleResponder<LLFloaterExperienceProfile> -{ -public: - ExperienceUpdateResponder(const LLHandle<LLFloaterExperienceProfile>& parent):HandleResponder<LLFloaterExperienceProfile>(parent) - { - } - - virtual void httpSuccess() - { - LLFloaterExperienceProfile* parent=mParent.get(); - if(parent) - { - parent->onSaveComplete(getContent()); - } - } -}; - - - -class ExperiencePreferencesResponder : public LLHTTPClient::Responder -{ -public: - ExperiencePreferencesResponder(const LLUUID& single = LLUUID::null):mId(single) - { - } - - bool sendSingle(const LLSD& content, const LLSD& permission, const char* name) - { - if(!content.has(name)) - return false; - - LLEventPump& pump = LLEventPumps::instance().obtain("experience_permission"); - const LLSD& list = content[name]; - LLSD::array_const_iterator it = list.beginArray(); - while(it != list.endArray()) - { - if(it->asUUID() == mId) - { - LLSD message; - message[it->asString()] = permission; - message["experience"] = mId; - pump.post(message); - return true; - } - ++it; - } - return false; - } - - bool hasPermission(const LLSD& content, const char* name) - { - if(!content.has(name)) - return false; - - const LLSD& list = content[name]; - LLSD::array_const_iterator it = list.beginArray(); - while(it != list.endArray()) - { - if(it->asUUID() == mId) - { - return true; - } - ++it; - } - return false; - } - - const char* getPermission(const LLSD& content) - { - if(hasPermission(content, "experiences")) - { - return "Allow"; - } - else if(hasPermission(content, "blocked")) - { - return "Block"; - } - return "Forget"; - } - - - virtual void httpSuccess() - { - if(mId.notNull()) - { - post(getPermission(getContent())); - return; - } - LLEventPumps::instance().obtain("experience_permission").post(getContent()); - } - - void post( const char* perm ) - { - LLSD experience; - LLSD message; - experience["permission"]=perm; - message["experience"] = mId; - message[mId.asString()] = experience; - LLEventPumps::instance().obtain("experience_permission").post(message); - } - -private: - LLUUID mId; -}; - - -class IsAdminResponder : public HandleResponder<LLFloaterExperienceProfile> -{ -public: - IsAdminResponder(const LLHandle<LLFloaterExperienceProfile>& parent):HandleResponder<LLFloaterExperienceProfile>(parent) - { - } - - virtual void httpSuccess() - { - LLFloaterExperienceProfile* parent = mParent.get(); - if(!parent) - return; - - bool enabled = true; - LLViewerRegion* region = gAgent.getRegion(); - if (!region) - { - enabled = false; - } - else - { - std::string url=region->getCapability("UpdateExperience"); - if(url.empty()) - enabled = false; - } - if(enabled && getContent()["status"].asBoolean()) - { - parent->getChild<LLLayoutPanel>(PNL_TOP)->setVisible(TRUE); - parent->getChild<LLButton>(BTN_EDIT)->setVisible(TRUE); - } - } -}; - BOOL LLFloaterExperienceProfile::postBuild() { if (mExperienceId.notNull()) { - LLExperienceCache::fetch(mExperienceId, true); - LLExperienceCache::get(mExperienceId, boost::bind(&LLFloaterExperienceProfile::experienceCallback, + LLExperienceCache::instance().fetch(mExperienceId, true); + LLExperienceCache::instance().get(mExperienceId, boost::bind(&LLFloaterExperienceProfile::experienceCallback, getDerivedHandle<LLFloaterExperienceProfile>(), _1)); LLViewerRegion* region = gAgent.getRegion(); if (region) { - std::string lookup_url=region->getCapability("IsExperienceAdmin"); - if(!lookup_url.empty()) - { - LLHTTPClient::get(lookup_url+"?experience_id="+mExperienceId.asString(), new IsAdminResponder(getDerivedHandle<LLFloaterExperienceProfile>())); - } + LLExperienceCache::instance().getExperienceAdmin(mExperienceId, boost::bind( + &LLFloaterExperienceProfile::experienceIsAdmin, getDerivedHandle<LLFloaterExperienceProfile>(), _1)); } } @@ -384,23 +228,13 @@ void LLFloaterExperienceProfile::onClickSave() doSave(NOTHING); } - void LLFloaterExperienceProfile::onClickPermission(const char* perm) { LLViewerRegion* region = gAgent.getRegion(); if (!region) return; - - std::string lookup_url=region->getCapability("ExperiencePreferences"); - if(lookup_url.empty()) - return; - LLSD permission; - LLSD data; - permission["permission"]=perm; - - data[mExperienceId.asString()]=permission; - LLHTTPClient::put(lookup_url, data, new ExperiencePreferencesResponder(mExperienceId)); - + LLExperienceCache::instance().setExperiencePermission(mExperienceId, perm, boost::bind( + &LLFloaterExperienceProfile::experiencePermissionResults, mExperienceId, _1)); } @@ -410,11 +244,8 @@ void LLFloaterExperienceProfile::onClickForget() if (!region) return; - std::string lookup_url=region->getCapability("ExperiencePreferences"); - if(lookup_url.empty()) - return; - - LLHTTPClient::del(lookup_url+"?"+mExperienceId.asString(), new ExperiencePreferencesResponder(mExperienceId)); + LLExperienceCache::instance().forgetExperiencePermission(mExperienceId, boost::bind( + &LLFloaterExperienceProfile::experiencePermissionResults, mExperienceId, _1)); } bool LLFloaterExperienceProfile::setMaturityString( U8 maturity, LLTextBox* child, LLComboBox* combo ) @@ -561,11 +392,8 @@ void LLFloaterExperienceProfile::refreshExperience( const LLSD& experience ) LLViewerRegion* region = gAgent.getRegion(); if (region) { - std::string lookup_url=region->getCapability("ExperiencePreferences"); - if(!lookup_url.empty()) - { - LLHTTPClient::get(lookup_url+"?"+mExperienceId.asString(), new ExperiencePreferencesResponder(mExperienceId)); - } + LLExperienceCache::instance().getExperiencePermission(mExperienceId, boost::bind( + &LLFloaterExperienceProfile::experiencePermissionResults, mExperienceId, _1)); } } @@ -745,15 +573,9 @@ void LLFloaterExperienceProfile::doSave( int success_action ) if (!region) return; - std::string url=region->getCapability("UpdateExperience"); - if(url.empty()) - return; - - mPackage.erase(LLExperienceCache::QUOTA); - mPackage.erase(LLExperienceCache::EXPIRES); - mPackage.erase(LLExperienceCache::AGENT_ID); - - LLHTTPClient::post(url, mPackage, new ExperienceUpdateResponder(getDerivedHandle<LLFloaterExperienceProfile>())); + LLExperienceCache::instance().updateExperience(mPackage, boost::bind( + &LLFloaterExperienceProfile::experienceUpdateResult, + getDerivedHandle<LLFloaterExperienceProfile>(), _1)); } void LLFloaterExperienceProfile::onSaveComplete( const LLSD& content ) @@ -811,8 +633,8 @@ void LLFloaterExperienceProfile::onSaveComplete( const LLSD& content ) } refreshExperience(*it); - LLExperienceCache::insert(*it); - LLExperienceCache::fetch(id, true); + LLExperienceCache::instance().insert(*it); + LLExperienceCache::instance().fetch(id, true); if(mSaveCompleteAction==VIEW) { @@ -1021,3 +843,76 @@ void LLFloaterExperienceProfile::onReportExperience() { LLFloaterReporter::showFromExperience(mExperienceId); } + +/*static*/ +bool LLFloaterExperienceProfile::hasPermission(const LLSD& content, const std::string &name, const LLUUID &test) +{ + if (!content.has(name)) + return false; + + const LLSD& list = content[name]; + LLSD::array_const_iterator it = list.beginArray(); + while (it != list.endArray()) + { + if (it->asUUID() == test) + { + return true; + } + ++it; + } + return false; +} + +/*static*/ +void LLFloaterExperienceProfile::experiencePermissionResults(LLUUID exprienceId, LLSD result) +{ + std::string permission("Forget"); + if (hasPermission(result, "experiences", exprienceId)) + permission = "Allow"; + else if (hasPermission(result, "blocked", exprienceId)) + permission = "Block"; + + LLSD experience; + LLSD message; + experience["permission"] = permission; + message["experience"] = exprienceId; + message[exprienceId.asString()] = experience; + + LLEventPumps::instance().obtain("experience_permission").post(message); +} + +/*static*/ +void LLFloaterExperienceProfile::experienceIsAdmin(LLHandle<LLFloaterExperienceProfile> handle, const LLSD &result) +{ + LLFloaterExperienceProfile* parent = handle.get(); + if (!parent) + return; + + bool enabled = true; + LLViewerRegion* region = gAgent.getRegion(); + if (!region) + { + enabled = false; + } + else + { + std::string url = region->getCapability("UpdateExperience"); + if (url.empty()) + enabled = false; + } + if (enabled && result["status"].asBoolean()) + { + parent->getChild<LLLayoutPanel>(PNL_TOP)->setVisible(TRUE); + parent->getChild<LLButton>(BTN_EDIT)->setVisible(TRUE); + } +} + +/*static*/ +void LLFloaterExperienceProfile::experienceUpdateResult(LLHandle<LLFloaterExperienceProfile> handle, const LLSD &result) +{ + LLFloaterExperienceProfile* parent = handle.get(); + if (parent) + { + parent->onSaveComplete(result); + } +} diff --git a/indra/newview/llfloaterexperienceprofile.h b/indra/newview/llfloaterexperienceprofile.h index 14e033d240..1394418d91 100644 --- a/indra/newview/llfloaterexperienceprofile.h +++ b/indra/newview/llfloaterexperienceprofile.h @@ -101,6 +101,11 @@ protected: bool mDirty; bool mForceClose; bool mPostEdit; // edit experience after opening and updating it +private: + static bool hasPermission(const LLSD& content, const std::string &name, const LLUUID &test); + static void experiencePermissionResults(LLUUID exprienceId, LLSD result); + static void experienceIsAdmin(LLHandle<LLFloaterExperienceProfile> handle, const LLSD &result); + static void experienceUpdateResult(LLHandle<LLFloaterExperienceProfile> handle, const LLSD &result); }; #endif // LL_LLFLOATEREXPERIENCEPROFILE_H diff --git a/indra/newview/llfloaterexperiences.cpp b/indra/newview/llfloaterexperiences.cpp index 63f1fab823..fdc1b47334 100644 --- a/indra/newview/llfloaterexperiences.cpp +++ b/indra/newview/llfloaterexperiences.cpp @@ -32,7 +32,6 @@ #include "llevents.h" #include "llexperiencecache.h" #include "llfloaterregioninfo.h" -#include "llhttpclient.h" #include "llnotificationsutil.h" #include "llpanelexperiencelog.h" #include "llpanelexperiencepicker.h" @@ -43,59 +42,6 @@ #define SHOW_RECENT_TAB (0) - -class LLExperienceListResponder : public LLHTTPClient::Responder -{ -public: - typedef std::map<std::string, std::string> NameMap; - typedef boost::function<void(LLPanelExperiences*, const LLSD&)> Callback; - LLExperienceListResponder(const LLHandle<LLFloaterExperiences>& parent, NameMap& nameMap, const std::string& errorMessage="ErrorMessage"):mParent(parent),mErrorMessage(errorMessage) - { - mNameMap.swap(nameMap); - } - - Callback mCallback; - LLHandle<LLFloaterExperiences> mParent; - NameMap mNameMap; - const std::string mErrorMessage; - /*virtual*/ void httpSuccess() - { - if(mParent.isDead()) - return; - - LLFloaterExperiences* parent=mParent.get(); - LLTabContainer* tabs = parent->getChild<LLTabContainer>("xp_tabs"); - - NameMap::iterator it = mNameMap.begin(); - while(it != mNameMap.end()) - { - if(getContent().has(it->first)) - { - LLPanelExperiences* tab = (LLPanelExperiences*)tabs->getPanelByName(it->second); - if(tab) - { - const LLSD& ids = getContent()[it->first]; - tab->setExperienceList(ids); - if(!mCallback.empty()) - { - mCallback(tab, getContent()); - } - } - } - ++it; - } - } - - /*virtual*/ void httpFailure() - { - LLSD subs; - subs["ERROR_MESSAGE"] = getReason(); - LLNotificationsUtil::add(mErrorMessage, subs); - } -}; - - - LLFloaterExperiences::LLFloaterExperiences(const LLSD& data) :LLFloater(data) { @@ -198,26 +144,20 @@ void LLFloaterExperiences::refreshContents() if (region) { - LLExperienceListResponder::NameMap nameMap; - std::string lookup_url=region->getCapability("GetExperiences"); - if(!lookup_url.empty()) - { - nameMap["experiences"]="Allowed_Experiences_Tab"; - nameMap["blocked"]="Blocked_Experiences_Tab"; - LLHTTPClient::get(lookup_url, new LLExperienceListResponder(getDerivedHandle<LLFloaterExperiences>(), nameMap)); - } + NameMap_t tabMap; + LLHandle<LLFloaterExperiences> handle = getDerivedHandle<LLFloaterExperiences>(); + + tabMap["experiences"]="Allowed_Experiences_Tab"; + tabMap["blocked"]="Blocked_Experiences_Tab"; + tabMap["experience_ids"]="Owned_Experiences_Tab"; + + retrieveExperienceList(region->getCapability("GetExperiences"), handle, tabMap); updateInfo("GetAdminExperiences","Admin_Experiences_Tab"); updateInfo("GetCreatorExperiences","Contrib_Experiences_Tab"); - lookup_url = region->getCapability("AgentExperiences"); - if(!lookup_url.empty()) - { - nameMap["experience_ids"]="Owned_Experiences_Tab"; - LLExperienceListResponder* responder = new LLExperienceListResponder(getDerivedHandle<LLFloaterExperiences>(), nameMap, "ExperienceAcquireFailed"); - responder->mCallback = boost::bind(&LLFloaterExperiences::checkPurchaseInfo, this, _1, _2); - LLHTTPClient::get(lookup_url, responder); - } + retrieveExperienceList(region->getCapability("AgentExperiences"), handle, tabMap, + "ExperienceAcquireFailed", boost::bind(&LLFloaterExperiences::checkPurchaseInfo, this, _1, _2)); } } @@ -329,29 +269,31 @@ void LLFloaterExperiences::checkAndOpen(LLPanelExperiences* panel, const LLSD& c } } -void LLFloaterExperiences::updateInfo(std::string experiences, std::string tab) +void LLFloaterExperiences::updateInfo(std::string experienceCap, std::string tab) { LLViewerRegion* region = gAgent.getRegion(); if (region) { - LLExperienceListResponder::NameMap nameMap; - std::string lookup_url = region->getCapability(experiences); - if(!lookup_url.empty()) - { - nameMap["experience_ids"]=tab; - LLHTTPClient::get(lookup_url, new LLExperienceListResponder(getDerivedHandle<LLFloaterExperiences>(), nameMap)); - } - } + NameMap_t tabMap; + LLHandle<LLFloaterExperiences> handle = getDerivedHandle<LLFloaterExperiences>(); + + tabMap["experience_ids"] = tab; + + retrieveExperienceList(region->getCapability(experienceCap), handle, tabMap); + } } -void LLFloaterExperiences::sendPurchaseRequest() +void LLFloaterExperiences::sendPurchaseRequest() { - LLViewerRegion* region = gAgent.getRegion(); - std::string url = region->getCapability("AgentExperiences"); - if(!url.empty()) - { - LLSD content; + LLViewerRegion* region = gAgent.getRegion(); + + if (region) + { + NameMap_t tabMap; const std::string tab_owned_name = "Owned_Experiences_Tab"; + LLHandle<LLFloaterExperiences> handle = getDerivedHandle<LLFloaterExperiences>(); + + tabMap["experience_ids"] = tab_owned_name; // extract ids for experiences that we already have LLTabContainer* tabs = getChild<LLTabContainer>("xp_tabs"); @@ -362,15 +304,114 @@ void LLFloaterExperiences::sendPurchaseRequest() tab_owned->getExperienceIdsList(mPrepurchaseIds); } - LLExperienceListResponder::NameMap nameMap; - nameMap["experience_ids"] = tab_owned_name; - LLExperienceListResponder* responder = new LLExperienceListResponder(getDerivedHandle<LLFloaterExperiences>(), nameMap, "ExperienceAcquireFailed"); - responder->mCallback = boost::bind(&LLFloaterExperiences::checkAndOpen, this, _1, _2); - LLHTTPClient::post(url, content, responder); - } + requestNewExperience(region->getCapability("AgentExperiences"), handle, tabMap, "ExperienceAcquireFailed", + boost::bind(&LLFloaterExperiences::checkAndOpen, this, _1, _2)); + } } LLFloaterExperiences* LLFloaterExperiences::findInstance() { return LLFloaterReg::findTypedInstance<LLFloaterExperiences>("experiences"); } + + +void LLFloaterExperiences::retrieveExperienceList(const std::string &url, + const LLHandle<LLFloaterExperiences> &hparent, const NameMap_t &tabMapping, + const std::string &errorNotify, Callback_t cback) + +{ + invokationFn_t getFn = boost::bind( + // Humans ignore next line. It is just a cast to specify which LLCoreHttpUtil::HttpCoroutineAdapter routine overload. + static_cast<LLSD(LLCoreHttpUtil::HttpCoroutineAdapter::*)(LLCore::HttpRequest::ptr_t, const std::string &, LLCore::HttpOptions::ptr_t, LLCore::HttpHeaders::ptr_t)> + //---- + // _1 -> httpAdapter + // _2 -> httpRequest + // _3 -> url + // _4 -> httpOptions + // _5 -> httpHeaders + (&LLCoreHttpUtil::HttpCoroutineAdapter::getAndSuspend), _1, _2, _3, _4, _5); + + LLCoros::instance().launch("LLFloaterExperiences::retrieveExperienceList", + boost::bind(&LLFloaterExperiences::retrieveExperienceListCoro, + url, hparent, tabMapping, errorNotify, cback, getFn)); + +} + +void LLFloaterExperiences::requestNewExperience(const std::string &url, + const LLHandle<LLFloaterExperiences> &hparent, const NameMap_t &tabMapping, + const std::string &errorNotify, Callback_t cback) +{ + invokationFn_t postFn = boost::bind( + // Humans ignore next line. It is just a cast to specify which LLCoreHttpUtil::HttpCoroutineAdapter routine overload. + static_cast<LLSD(LLCoreHttpUtil::HttpCoroutineAdapter::*)(LLCore::HttpRequest::ptr_t, const std::string &, const LLSD &, LLCore::HttpOptions::ptr_t, LLCore::HttpHeaders::ptr_t)> + //---- + // _1 -> httpAdapter + // _2 -> httpRequest + // _3 -> url + // _4 -> httpOptions + // _5 -> httpHeaders + (&LLCoreHttpUtil::HttpCoroutineAdapter::postAndSuspend), _1, _2, _3, LLSD(), _4, _5); + + LLCoros::instance().launch("LLFloaterExperiences::requestNewExperience", + boost::bind(&LLFloaterExperiences::retrieveExperienceListCoro, + url, hparent, tabMapping, errorNotify, cback, postFn)); + +} + + +void LLFloaterExperiences::retrieveExperienceListCoro(std::string url, + LLHandle<LLFloaterExperiences> hparent, NameMap_t tabMapping, + std::string errorNotify, Callback_t cback, invokationFn_t invoker) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("retrieveExperienceListCoro", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpOptions::ptr_t httpOptions(new LLCore::HttpOptions); + LLCore::HttpHeaders::ptr_t httpHeaders(new LLCore::HttpHeaders); + + + if (url.empty()) + { + LL_WARNS() << "retrieveExperienceListCoro called with empty capability!" << LL_ENDL; + return; + } + + LLSD result = invoker(httpAdapter, httpRequest, url, httpOptions, httpHeaders); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) + { + LLSD subs; + subs["ERROR_MESSAGE"] = status.getType(); + LLNotificationsUtil::add(errorNotify, subs); + + return; + } + + if (hparent.isDead()) + return; + + LLFloaterExperiences* parent = hparent.get(); + LLTabContainer* tabs = parent->getChild<LLTabContainer>("xp_tabs"); + + for (NameMap_t::iterator it = tabMapping.begin(); it != tabMapping.end(); ++it) + { + if (result.has(it->first)) + { + LLPanelExperiences* tab = (LLPanelExperiences*)tabs->getPanelByName(it->second); + if (tab) + { + const LLSD& ids = result[it->first]; + tab->setExperienceList(ids); + if (!cback.empty()) + { + cback(tab, result); + } + } + } + } + +} diff --git a/indra/newview/llfloaterexperiences.h b/indra/newview/llfloaterexperiences.h index 06fd6c93d6..6d0559af7c 100644 --- a/indra/newview/llfloaterexperiences.h +++ b/indra/newview/llfloaterexperiences.h @@ -28,6 +28,7 @@ #define LL_LLFLOATEREXPERIENCES_H #include "llfloater.h" +#include "llcorehttputil.h" class LLPanelExperiences; @@ -41,6 +42,9 @@ public: virtual void onOpen(const LLSD& key); static LLFloaterExperiences* findInstance(); protected: + typedef std::map<std::string, std::string> NameMap_t; + typedef boost::function<void(LLPanelExperiences*, const LLSD&)> Callback_t; + void clearFromRecent(const LLSD& ids); void resizeToTabs(); /*virtual*/ BOOL postBuild(); @@ -49,13 +53,23 @@ protected: LLPanelExperiences* addTab(const std::string& name, bool select); bool updatePermissions(const LLSD& permission); - void sendPurchaseRequest(); + void sendPurchaseRequest(); void checkPurchaseInfo(LLPanelExperiences* panel, const LLSD& content)const; void checkAndOpen(LLPanelExperiences* panel, const LLSD& content) const; void updateInfo(std::string experiences, std::string tab); + void retrieveExperienceList(const std::string &url, const LLHandle<LLFloaterExperiences> &hparent, const NameMap_t &tabMapping, + const std::string &errorNotify = std::string("ErrorMessage"), Callback_t cback = Callback_t()); + + void requestNewExperience(const std::string &url, const LLHandle<LLFloaterExperiences> &hparent, const NameMap_t &tabMapping, + const std::string &errorNotify, Callback_t cback); + private: + typedef boost::function < LLSD(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t, LLCore::HttpRequest::ptr_t, + const std::string, LLCore::HttpOptions::ptr_t, LLCore::HttpHeaders::ptr_t) > invokationFn_t; + static void retrieveExperienceListCoro(std::string url, LLHandle<LLFloaterExperiences> hparent, + NameMap_t tabMapping, std::string errorNotify, Callback_t cback, invokationFn_t invoker); std::vector<LLUUID> mPrepurchaseIds; }; diff --git a/indra/newview/llfloatergodtools.cpp b/indra/newview/llfloatergodtools.cpp index 37774fbc5c..adc7f71586 100644 --- a/indra/newview/llfloatergodtools.cpp +++ b/indra/newview/llfloatergodtools.cpp @@ -115,7 +115,7 @@ void LLFloaterGodTools::refreshAll() LLFloaterGodTools::LLFloaterGodTools(const LLSD& key) : LLFloater(key), - mCurrentHost(LLHost::invalid), + mCurrentHost(LLHost()), mUpdateTimer() { mFactoryMap["grid"] = LLCallbackMap(createPanelGrid, this); @@ -180,7 +180,7 @@ void LLFloaterGodTools::updatePopup(LLCoordGL center, MASK mask) // virtual void LLFloaterGodTools::draw() { - if (mCurrentHost == LLHost::invalid) + if (mCurrentHost == LLHost()) { if (mUpdateTimer.getElapsedTimeF32() > SECONDS_BETWEEN_UPDATE_REQUESTS) { @@ -325,7 +325,7 @@ void LLFloaterGodTools::sendRegionInfoRequest() { if (mPanelRegionTools) mPanelRegionTools->clearAllWidgets(); if (mPanelObjectTools) mPanelObjectTools->clearAllWidgets(); - mCurrentHost = LLHost::invalid; + mCurrentHost = LLHost(); mUpdateTimer.reset(); LLMessageSystem* msg = gMessageSystem; diff --git a/indra/newview/llfloaterhoverheight.cpp b/indra/newview/llfloaterhoverheight.cpp index 8908626de6..003a22fa04 100644 --- 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 100644 --- 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/llfloatermarketplacelistings.cpp b/indra/newview/llfloatermarketplacelistings.cpp index b2d36479cd..18f0bc4498 100644 --- a/indra/newview/llfloatermarketplacelistings.cpp +++ b/indra/newview/llfloatermarketplacelistings.cpp @@ -572,7 +572,7 @@ void LLFloaterMarketplaceListings::updateView() std::string title; std::string tooltip; - const LLSD& subs = getMarketplaceStringSubstitutions(); + const LLSD& subs = LLMarketplaceData::getMarketplaceStringSubstitutions(); // Update the top message or flip to the tabs and folders view // *TODO : check those messages and create better appropriate ones in strings.xml diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp index 459c598f6d..a6a9838a3c 100644 --- a/indra/newview/llfloatermodelpreview.cpp +++ b/indra/newview/llfloatermodelpreview.cpp @@ -3719,6 +3719,10 @@ BOOL LLModelPreview::render() if (regen) { genBuffers(mPreviewLOD, skin_weight); + { + LL_INFOS() << "Vertex Buffer[" << mPreviewLOD << "]" << " is EMPTY!!!" << LL_ENDL; + regen = TRUE; + } } if (!skin_weight) @@ -4487,6 +4491,15 @@ void LLFloaterModelPreview::onPermissionsReceived(const LLSD& result) // BAP HACK: handle "" for case that MeshUploadFlag cap is broken. mHasUploadPerm = (("" == upload_status) || ("valid" == upload_status)); + if (!mHasUploadPerm) + { + LL_WARNS() << "Upload permission set to false because upload_status=\"" << upload_status << "\"" << LL_ENDL; + } + else if (mHasUploadPerm && mUploadModelUrl.empty()) + { + LL_WARNS() << "Upload permission set to true but uploadModelUrl is empty!" << LL_ENDL; + } + // isModelUploadAllowed() includes mHasUploadPerm mUploadBtn->setEnabled(isModelUploadAllowed()); getChild<LLTextBox>("warning_title")->setVisible(!mHasUploadPerm); diff --git a/indra/newview/llfloatermodeluploadbase.cpp b/indra/newview/llfloatermodeluploadbase.cpp index 22a8ac4705..0fe97fd610 100644 --- 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->getAndSuspend(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 100644 --- 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..135bbb335e 100644 --- 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 LLNewFileResourceUploadInfo( + 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/llfloateroutbox.cpp b/indra/newview/llfloateroutbox.cpp deleted file mode 100644 index b7b1634a5f..0000000000 --- a/indra/newview/llfloateroutbox.cpp +++ /dev/null @@ -1,623 +0,0 @@ -/** - * @file llfloateroutbox.cpp - * @brief Implementation of the merchant outbox window - * - * $LicenseInfo:firstyear=2001&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 "llfloateroutbox.h" - -#include "llfloaterreg.h" -#include "llfolderview.h" -#include "llinventorybridge.h" -#include "llinventorymodelbackgroundfetch.h" -#include "llinventoryobserver.h" -#include "llinventorypanel.h" -#include "llmarketplacefunctions.h" -#include "llnotificationhandler.h" -#include "llnotificationmanager.h" -#include "llnotificationsutil.h" -#include "lltextbox.h" -#include "lltransientfloatermgr.h" -#include "lltrans.h" -#include "llviewernetwork.h" -#include "llwindowshade.h" - - -///---------------------------------------------------------------------------- -/// LLOutboxNotification class -///---------------------------------------------------------------------------- - -LLNotificationsUI::LLOutboxNotification::LLOutboxNotification() - : LLSystemNotificationHandler("Outbox", "outbox") -{ -} - -bool LLNotificationsUI::LLOutboxNotification::processNotification(const LLNotificationPtr& notify) -{ - LLFloaterOutbox* outbox_floater = LLFloaterReg::getTypedInstance<LLFloaterOutbox>("outbox"); - - outbox_floater->showNotification(notify); - - return false; -} - -void LLNotificationsUI::LLOutboxNotification::onDelete(LLNotificationPtr p) -{ - LLNotificationsUI::LLNotificationHandler * notification_handler = dynamic_cast<LLNotificationsUI::LLNotificationHandler*>(LLNotifications::instance().getChannel("AlertModal").get()); - if (notification_handler) - { - notification_handler->onDelete(p); - } -} - -///---------------------------------------------------------------------------- -/// LLOutboxAddedObserver helper class -///---------------------------------------------------------------------------- - -class LLOutboxAddedObserver : public LLInventoryCategoryAddedObserver -{ -public: - LLOutboxAddedObserver(LLFloaterOutbox * outboxFloater) - : LLInventoryCategoryAddedObserver() - , mOutboxFloater(outboxFloater) - { - } - - void done() - { - for (cat_vec_t::iterator it = mAddedCategories.begin(); it != mAddedCategories.end(); ++it) - { - LLViewerInventoryCategory* added_category = *it; - - LLFolderType::EType added_category_type = added_category->getPreferredType(); - - if (added_category_type == LLFolderType::FT_OUTBOX) - { - mOutboxFloater->initializeMarketPlace(); - } - } - } - -private: - LLFloaterOutbox * mOutboxFloater; -}; - -///---------------------------------------------------------------------------- -/// LLFloaterOutbox -///---------------------------------------------------------------------------- - -LLFloaterOutbox::LLFloaterOutbox(const LLSD& key) - : LLFloater(key) - , mCategoriesObserver(NULL) - , mCategoryAddedObserver(NULL) - , mImportBusy(false) - , mImportButton(NULL) - , mInventoryFolderCountText(NULL) - , mInventoryImportInProgress(NULL) - , mInventoryPlaceholder(NULL) - , mInventoryText(NULL) - , mInventoryTitle(NULL) - , mOutboxId(LLUUID::null) - , mOutboxItemCount(0) - , mOutboxTopLevelDropZone(NULL) - , mWindowShade(NULL) -{ -} - -LLFloaterOutbox::~LLFloaterOutbox() -{ - if (mCategoriesObserver && gInventory.containsObserver(mCategoriesObserver)) - { - gInventory.removeObserver(mCategoriesObserver); - } - delete mCategoriesObserver; - - if (mCategoryAddedObserver && gInventory.containsObserver(mCategoryAddedObserver)) - { - gInventory.removeObserver(mCategoryAddedObserver); - } - delete mCategoryAddedObserver; -} - -BOOL LLFloaterOutbox::postBuild() -{ - mInventoryFolderCountText = getChild<LLTextBox>("outbox_folder_count"); - mInventoryImportInProgress = getChild<LLView>("import_progress_indicator"); - mInventoryPlaceholder = getChild<LLView>("outbox_inventory_placeholder_panel"); - mInventoryText = mInventoryPlaceholder->getChild<LLTextBox>("outbox_inventory_placeholder_text"); - mInventoryTitle = mInventoryPlaceholder->getChild<LLTextBox>("outbox_inventory_placeholder_title"); - - mImportButton = getChild<LLButton>("outbox_import_btn"); - mImportButton->setCommitCallback(boost::bind(&LLFloaterOutbox::onImportButtonClicked, this)); - - mOutboxTopLevelDropZone = getChild<LLPanel>("outbox_generic_drag_target"); - - LLFocusableElement::setFocusReceivedCallback(boost::bind(&LLFloaterOutbox::onFocusReceived, this)); - - // Observe category creation to catch outbox creation (moot if already existing) - mCategoryAddedObserver = new LLOutboxAddedObserver(this); - gInventory.addObserver(mCategoryAddedObserver); - - // Setup callbacks for importer - LLMarketplaceInventoryImporter& importer = LLMarketplaceInventoryImporter::instance(); - importer.setInitializationErrorCallback(boost::bind(&LLFloaterOutbox::initializationReportError, this, _1, _2)); - importer.setStatusChangedCallback(boost::bind(&LLFloaterOutbox::importStatusChanged, this, _1)); - importer.setStatusReportCallback(boost::bind(&LLFloaterOutbox::importReportResults, this, _1, _2)); - - return TRUE; -} - -void LLFloaterOutbox::cleanOutbox() -{ - // Note: we cannot delete the mOutboxInventoryPanel as that point - // as this is called through callback observers of the panel itself. - // Doing so would crash rapidly. - - // Invalidate the outbox data - mOutboxId.setNull(); - mOutboxItemCount = 0; -} - -void LLFloaterOutbox::onClose(bool app_quitting) -{ - if (mWindowShade) - { - delete mWindowShade; - - mWindowShade = NULL; - } -} - -void LLFloaterOutbox::onOpen(const LLSD& key) -{ - // - // Initialize the Market Place or go update the outbox - // - if (LLMarketplaceInventoryImporter::getInstance()->getMarketPlaceStatus() == MarketplaceStatusCodes::MARKET_PLACE_NOT_INITIALIZED) - { - initializeMarketPlace(); - } - else - { - setupOutbox(); - } - - // - // Update the floater view - // - updateView(); - - // - // Trigger fetch of outbox contents - // - fetchOutboxContents(); -} - -void LLFloaterOutbox::onFocusReceived() -{ - fetchOutboxContents(); -} - -void LLFloaterOutbox::fetchOutboxContents() -{ - if (mOutboxId.notNull()) - { - LLInventoryModelBackgroundFetch::instance().start(mOutboxId); - } -} - -void LLFloaterOutbox::setupOutbox() -{ - if (LLMarketplaceInventoryImporter::getInstance()->getMarketPlaceStatus() != MarketplaceStatusCodes::MARKET_PLACE_MERCHANT) - { - // If we are *not* a merchant or we have no market place connection established yet, do nothing - return; - } - - // We are a merchant. Get the outbox, create it if needs be. - LLUUID outbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, true); - if (outbox_id.isNull()) - { - // We should never get there unless the inventory fails badly - LL_ERRS() << "Inventory problem: failure to create the outbox for a merchant!" << LL_ENDL; - return; - } - - // Consolidate Merchant Outbox - // We shouldn't have to do that but with a client/server system relying on a "well known folder" convention, things get messy and conventions get broken down eventually - gInventory.consolidateForType(outbox_id, LLFolderType::FT_OUTBOX); - - if (outbox_id == mOutboxId) - { - LL_WARNS() << "Inventory warning: Merchant outbox already set" << LL_ENDL; - return; - } - mOutboxId = outbox_id; - - // No longer need to observe new category creation - if (mCategoryAddedObserver && gInventory.containsObserver(mCategoryAddedObserver)) - { - gInventory.removeObserver(mCategoryAddedObserver); - delete mCategoryAddedObserver; - mCategoryAddedObserver = NULL; - } - llassert(!mCategoryAddedObserver); - - // Create observer for outbox modifications : clear the old one and create a new one - if (mCategoriesObserver && gInventory.containsObserver(mCategoriesObserver)) - { - gInventory.removeObserver(mCategoriesObserver); - delete mCategoriesObserver; - } - mCategoriesObserver = new LLInventoryCategoriesObserver(); - gInventory.addObserver(mCategoriesObserver); - mCategoriesObserver->addCategory(mOutboxId, boost::bind(&LLFloaterOutbox::onOutboxChanged, this)); - llassert(mCategoriesObserver); - - // Set up the outbox inventory view - LLInventoryPanel* inventory_panel = mOutboxInventoryPanel.get(); - if (inventory_panel) - { - delete inventory_panel; - } - inventory_panel = LLUICtrlFactory::createFromFile<LLInventoryPanel>("panel_outbox_inventory.xml", mInventoryPlaceholder->getParent(), LLInventoryPanel::child_registry_t::instance()); - mOutboxInventoryPanel = inventory_panel->getInventoryPanelHandle(); - llassert(mOutboxInventoryPanel.get() != NULL); - - // Reshape the inventory to the proper size - LLRect inventory_placeholder_rect = mInventoryPlaceholder->getRect(); - inventory_panel->setShape(inventory_placeholder_rect); - - // Set the sort order newest to oldest - inventory_panel->getFolderViewModel()->setSorter(LLInventoryFilter::SO_FOLDERS_BY_NAME); - inventory_panel->getFilter().markDefault(); - - // Get the content of the outbox - fetchOutboxContents(); -} - -void LLFloaterOutbox::initializeMarketPlace() -{ - // - // Initialize the marketplace import API - // - LLMarketplaceInventoryImporter& importer = LLMarketplaceInventoryImporter::instance(); - if (!importer.isInitialized()) - { - importer.initialize(); - } -} - -void LLFloaterOutbox::setStatusString(const std::string& statusString) -{ - llassert(mInventoryFolderCountText != NULL); - - mInventoryFolderCountText->setText(statusString); -} - -void LLFloaterOutbox::updateFolderCount() -{ - if (mOutboxInventoryPanel.get() && mOutboxId.notNull()) - { - U32 item_count = 0; - - if (mOutboxId.notNull()) - { - LLInventoryModel::cat_array_t * cats; - LLInventoryModel::item_array_t * items; - gInventory.getDirectDescendentsOf(mOutboxId, cats, items); - - item_count = cats->size() + items->size(); - } - - mOutboxItemCount = item_count; - } - else - { - // If there's no outbox, the number of items in it should be set to 0 for consistency - mOutboxItemCount = 0; - } - - if (!mImportBusy) - { - updateFolderCountStatus(); - } -} - -void LLFloaterOutbox::updateFolderCountStatus() -{ - if (mOutboxInventoryPanel.get() && mOutboxId.notNull()) - { - switch (mOutboxItemCount) - { - case 0: setStatusString(getString("OutboxFolderCount0")); break; - case 1: setStatusString(getString("OutboxFolderCount1")); break; - default: - { - std::string item_count_str = llformat("%d", mOutboxItemCount); - - LLStringUtil::format_map_t args; - args["[NUM]"] = item_count_str; - - setStatusString(getString("OutboxFolderCountN", args)); - break; - } - } - } - - mImportButton->setEnabled(mOutboxItemCount > 0); -} - -void LLFloaterOutbox::updateView() -{ - updateFolderCount(); - LLInventoryPanel* panel = mOutboxInventoryPanel.get(); - - if (mOutboxItemCount > 0) - { - panel->setVisible(TRUE); - mInventoryPlaceholder->setVisible(FALSE); - mOutboxTopLevelDropZone->setVisible(TRUE); - } - else - { - if (panel) - { - panel->setVisible(FALSE); - } - - // Show the drop zone if there is an outbox folder - mOutboxTopLevelDropZone->setVisible(mOutboxId.notNull()); - - mInventoryPlaceholder->setVisible(TRUE); - - std::string outbox_text; - std::string outbox_title; - std::string outbox_tooltip; - - const LLSD& subs = getMarketplaceStringSubstitutions(); - U32 mkt_status = LLMarketplaceInventoryImporter::getInstance()->getMarketPlaceStatus(); - - if (mOutboxId.notNull()) - { - // Does the outbox needs recreation? - if ((mOutboxInventoryPanel.get() == NULL) || !gInventory.getCategory(mOutboxId)) - { - setupOutbox(); - } - // "Outbox is empty!" message strings - outbox_text = LLTrans::getString("InventoryOutboxNoItems", subs); - outbox_title = LLTrans::getString("InventoryOutboxNoItemsTitle"); - outbox_tooltip = LLTrans::getString("InventoryOutboxNoItemsTooltip"); - } - else if (mkt_status <= MarketplaceStatusCodes::MARKET_PLACE_INITIALIZING) - { - // "Initializing!" message strings - outbox_text = LLTrans::getString("InventoryOutboxInitializing", subs); - outbox_title = LLTrans::getString("InventoryOutboxInitializingTitle"); - outbox_tooltip = LLTrans::getString("InventoryOutboxInitializingTooltip"); - } - else if (mkt_status == MarketplaceStatusCodes::MARKET_PLACE_NOT_MERCHANT) - { - // "Not a merchant!" message strings - outbox_text = LLTrans::getString("InventoryOutboxNotMerchant", subs); - outbox_title = LLTrans::getString("InventoryOutboxNotMerchantTitle"); - outbox_tooltip = LLTrans::getString("InventoryOutboxNotMerchantTooltip"); - } - else - { - // "Errors!" message strings - outbox_text = LLTrans::getString("InventoryOutboxError", subs); - outbox_title = LLTrans::getString("InventoryOutboxErrorTitle"); - outbox_tooltip = LLTrans::getString("InventoryOutboxErrorTooltip"); - } - - mInventoryText->setValue(outbox_text); - mInventoryTitle->setValue(outbox_title); - mInventoryPlaceholder->getParent()->setToolTip(outbox_tooltip); - } -} - -bool isAccepted(EAcceptance accept) -{ - return (accept >= ACCEPT_YES_COPY_SINGLE); -} - -BOOL LLFloaterOutbox::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg) -{ - if ((mOutboxInventoryPanel.get() == NULL) || - (mWindowShade && mWindowShade->isShown()) || - LLMarketplaceInventoryImporter::getInstance()->isImportInProgress() || - mOutboxId.isNull()) - { - return FALSE; - } - - LLView * handled_view = childrenHandleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg); - BOOL handled = (handled_view != NULL); - - // Determine if the mouse is inside the inventory panel itself or just within the floater - bool pointInInventoryPanel = false; - bool pointInInventoryPanelChild = false; - LLInventoryPanel* panel = mOutboxInventoryPanel.get(); - LLFolderView* root_folder = panel->getRootFolder(); - if (panel->getVisible()) - { - S32 inv_x, inv_y; - localPointToOtherView(x, y, &inv_x, &inv_y, panel); - - pointInInventoryPanel = panel->getRect().pointInRect(inv_x, inv_y); - - LLView * inventory_panel_child_at_point = panel->childFromPoint(inv_x, inv_y, true); - pointInInventoryPanelChild = (inventory_panel_child_at_point != root_folder); - } - - // Pass all drag and drop for this floater to the outbox inventory control - if (!handled || !isAccepted(*accept)) - { - // Handle the drag and drop directly to the root of the outbox if we're not in the inventory panel - // (otherwise the inventory panel itself will handle the drag and drop operation, without any override) - if (!pointInInventoryPanel) - { - handled = root_folder->handleDragAndDropToThisFolder(mask, drop, cargo_type, cargo_data, accept, tooltip_msg); - } - - mOutboxTopLevelDropZone->setBackgroundVisible(handled && !drop && isAccepted(*accept)); - } - else - { - mOutboxTopLevelDropZone->setBackgroundVisible(!pointInInventoryPanelChild); - } - - return handled; -} - -BOOL LLFloaterOutbox::handleHover(S32 x, S32 y, MASK mask) -{ - mOutboxTopLevelDropZone->setBackgroundVisible(FALSE); - - return LLFloater::handleHover(x, y, mask); -} - -void LLFloaterOutbox::onMouseLeave(S32 x, S32 y, MASK mask) -{ - mOutboxTopLevelDropZone->setBackgroundVisible(FALSE); - - LLFloater::onMouseLeave(x, y, mask); -} - -void LLFloaterOutbox::onImportButtonClicked() -{ - if (mOutboxInventoryPanel.get()) - { - mOutboxInventoryPanel.get()->clearSelection(); - } - - mImportBusy = LLMarketplaceInventoryImporter::instance().triggerImport(); -} - -void LLFloaterOutbox::onOutboxChanged() -{ - LLViewerInventoryCategory* category = gInventory.getCategory(mOutboxId); - if (mOutboxId.notNull() && category) - { - fetchOutboxContents(); - updateView(); - } - else - { - cleanOutbox(); - } -} - -void LLFloaterOutbox::importReportResults(U32 status, const LLSD& content) -{ - if (status == MarketplaceErrorCodes::IMPORT_DONE) - { - LLNotificationsUtil::add("OutboxImportComplete"); - } - else if (status == MarketplaceErrorCodes::IMPORT_DONE_WITH_ERRORS) - { - const LLSD& subs = getMarketplaceStringSubstitutions(); - - LLNotificationsUtil::add("OutboxImportHadErrors", subs); - } - else - { - char status_string[16]; - sprintf(status_string, "%d", status); - - LLSD subs; - subs["[ERROR_CODE]"] = status_string; - - LLNotificationsUtil::add("OutboxImportFailed", subs); - } - - updateView(); -} - -void LLFloaterOutbox::importStatusChanged(bool inProgress) -{ - if (mOutboxId.isNull() && (LLMarketplaceInventoryImporter::getInstance()->getMarketPlaceStatus() == MarketplaceStatusCodes::MARKET_PLACE_MERCHANT)) - { - setupOutbox(); - } - - if (inProgress) - { - if (mImportBusy) - { - setStatusString(getString("OutboxImporting")); - } - else - { - setStatusString(getString("OutboxInitializing")); - } - - mImportBusy = true; - mImportButton->setEnabled(false); - mInventoryImportInProgress->setVisible(true); - } - else - { - setStatusString(""); - mImportBusy = false; - mImportButton->setEnabled(mOutboxItemCount > 0); - mInventoryImportInProgress->setVisible(false); - } - - updateView(); -} - -void LLFloaterOutbox::initializationReportError(U32 status, const LLSD& content) -{ - if (status >= MarketplaceErrorCodes::IMPORT_BAD_REQUEST) - { - char status_string[16]; - sprintf(status_string, "%d", status); - - LLSD subs; - subs["[ERROR_CODE]"] = status_string; - - LLNotificationsUtil::add("OutboxInitFailed", subs); - } - - updateView(); -} - -void LLFloaterOutbox::showNotification(const LLNotificationPtr& notification) -{ - LLNotificationsUI::LLNotificationHandler * notification_handler = dynamic_cast<LLNotificationsUI::LLNotificationHandler*>(LLNotifications::instance().getChannel("AlertModal").get()); - llassert(notification_handler); - - notification_handler->processNotification(notification); -} - - - diff --git a/indra/newview/llfloateroutbox.h b/indra/newview/llfloateroutbox.h deleted file mode 100644 index 2cf69fc3cc..0000000000 --- a/indra/newview/llfloateroutbox.h +++ /dev/null @@ -1,116 +0,0 @@ -/** - * @file llfloateroutbox.h - * @brief Implementation of the merchant outbox window - * - * $LicenseInfo:firstyear=2001&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 - * ABILITY 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_LLFLOATEROUTBOX_H -#define LL_LLFLOATEROUTBOX_H - -#include "llfloater.h" -#include "llfoldertype.h" -#include "llinventoryfilter.h" -#include "llnotificationptr.h" - - -class LLButton; -class LLInventoryCategoriesObserver; -class LLInventoryCategoryAddedObserver; -class LLInventoryPanel; -class LLLoadingIndicator; -class LLNotification; -class LLTextBox; -class LLView; -class LLWindowShade; - - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLFloaterOutbox -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -class LLFloaterOutbox : public LLFloater -{ -public: - LLFloaterOutbox(const LLSD& key); - ~LLFloaterOutbox(); - - void initializeMarketPlace(); - - // virtuals - BOOL postBuild(); - BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg); - - void showNotification(const LLNotificationPtr& notification); - - BOOL handleHover(S32 x, S32 y, MASK mask); - void onMouseLeave(S32 x, S32 y, MASK mask); - -protected: - void setupOutbox(); - void cleanOutbox(); - void fetchOutboxContents(); - - void importReportResults(U32 status, const LLSD& content); - void importStatusChanged(bool inProgress); - void initializationReportError(U32 status, const LLSD& content); - - void onClose(bool app_quitting); - void onOpen(const LLSD& key); - - void onFocusReceived(); - - void onImportButtonClicked(); - void onOutboxChanged(); - - void setStatusString(const std::string& statusString); - - void updateFolderCount(); - void updateFolderCountStatus(); - void updateView(); - -private: - LLInventoryCategoriesObserver * mCategoriesObserver; - LLInventoryCategoryAddedObserver * mCategoryAddedObserver; - - bool mImportBusy; - LLButton * mImportButton; - - LLTextBox * mInventoryFolderCountText; - LLView * mInventoryImportInProgress; - LLView * mInventoryPlaceholder; - LLTextBox * mInventoryText; - LLTextBox * mInventoryTitle; - - LLUUID mOutboxId; - LLHandle<LLInventoryPanel> mOutboxInventoryPanel; - U32 mOutboxItemCount; - LLPanel * mOutboxTopLevelDropZone; - - LLWindowShade * mWindowShade; -}; - -#endif // LL_LLFLOATEROUTBOX_H diff --git a/indra/newview/llfloaterperms.cpp b/indra/newview/llfloaterperms.cpp index 04a818c2c0..b0b2770c6e 100644 --- a/indra/newview/llfloaterperms.cpp +++ b/indra/newview/llfloaterperms.cpp @@ -37,6 +37,9 @@ #include "llnotificationsutil.h" #include "llsdserialize.h" #include "llvoavatar.h" +#include "llcorehttputil.h" +#include "lleventfilter.h" +#include "lleventcoro.h" LLFloaterPerms::LLFloaterPerms(const LLSD& seed) : LLFloater(seed) @@ -167,82 +170,7 @@ void LLFloaterPermsDefault::onCommitCopy(const LLSD& user_data) } const int MAX_HTTP_RETRIES = 5; -LLFloaterPermsRequester* LLFloaterPermsRequester::sPermsRequester = NULL; - -LLFloaterPermsRequester::LLFloaterPermsRequester(const std::string url, const LLSD report, - int maxRetries) - : mRetriesCount(0), mMaxRetries(maxRetries), mUrl(url), mReport(report) -{} - -//static -void LLFloaterPermsRequester::init(const std::string url, const LLSD report, int maxRetries) -{ - if (sPermsRequester == NULL) { - sPermsRequester = new LLFloaterPermsRequester(url, report, maxRetries); - } -} - -//static -void LLFloaterPermsRequester::finalize() -{ - if (sPermsRequester != NULL) - { - delete sPermsRequester; - sPermsRequester = NULL; - } -} - -//static -LLFloaterPermsRequester* LLFloaterPermsRequester::instance() -{ - return sPermsRequester; -} - -void LLFloaterPermsRequester::start() -{ - ++mRetriesCount; - LLHTTPClient::post(mUrl, mReport, new LLFloaterPermsResponder()); -} - -bool LLFloaterPermsRequester::retry() -{ - if (++mRetriesCount < mMaxRetries) - { - LLHTTPClient::post(mUrl, mReport, new LLFloaterPermsResponder()); - return true; - } - return false; -} - -void LLFloaterPermsResponder::httpFailure() -{ - if (!LLFloaterPermsRequester::instance() || !LLFloaterPermsRequester::instance()->retry()) - { - LLFloaterPermsRequester::finalize(); - 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 LLFloaterPermsResponder::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 = ""; - LL_INFOS("ObjectPermissionsFloater") << "Default permissions successfully sent to simulator" << LL_ENDL; -} - -std::string LLFloaterPermsResponder::sPreviousReason; +const float RETRY_TIMEOUT = 5.0; void LLFloaterPermsDefault::sendInitialPerms() { @@ -259,23 +187,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; - } - LLFloaterPermsRequester::init(object_url, report, MAX_HTTP_RETRIES); - LLFloaterPermsRequester::instance()->start(); + LLCoros::instance().launch("LLFloaterPermsDefault::updateCapCoro", + boost::bind(&LLFloaterPermsDefault::updateCapCoro, object_url)); } else { @@ -283,6 +196,69 @@ void LLFloaterPermsDefault::updateCap() } } +/*static*/ +void LLFloaterPermsDefault::updateCapCoro(std::string url) +{ + int retryCount = 0; + 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; + } + + while (true) + { + ++retryCount; + LLSD result = httpAdapter->postAndSuspend(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); + } + + llcoro::suspendUntilTimeout(RETRY_TIMEOUT); + if (retryCount < MAX_HTTP_RETRIES) + continue; + + LL_WARNS("ObjectPermissionsFloater") << "Unable to send default permissions. Giving up for now." << LL_ENDL; + return; + } + break; + } + + // 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 d3b52c1fe5..e866b6de7d 100644 --- a/indra/newview/llfloaterperms.h +++ b/indra/newview/llfloaterperms.h @@ -29,7 +29,8 @@ #define LL_LLFLOATERPERMPREFS_H #include "llfloater.h" -#include "llhttpclient.h" +#include "lleventcoro.h" +#include "llcoros.h" class LLFloaterPerms : public LLFloater { @@ -81,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]; @@ -90,36 +93,4 @@ private: bool mNextOwnerTransfer[CAT_LAST]; }; -class LLFloaterPermsRequester -{ -public: - LLFloaterPermsRequester(const std::string url, const LLSD report, int maxRetries); - - static void init(const std::string url, const LLSD report, int maxRetries); - static void finalize(); - static LLFloaterPermsRequester* instance(); - - void start(); - bool retry(); - -private: - int mRetriesCount; - int mMaxRetries; - const std::string mUrl; - const LLSD mReport; -public: - static LLFloaterPermsRequester* sPermsRequester; -}; - -class LLFloaterPermsResponder : public LLHTTPClient::Responder -{ -public: - LLFloaterPermsResponder() : LLHTTPClient::Responder() {} -private: - static std::string sPreviousReason; - - void httpFailure(); - void httpSuccess(); -}; - #endif diff --git a/indra/newview/llfloaterregiondebugconsole.cpp b/indra/newview/llfloaterregiondebugconsole.cpp index 40757a4d04..271fb2f9a3 100644 --- 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 100644 --- 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 16566bea73..aed3294189 100644 --- 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 { @@ -2305,36 +2281,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(); @@ -3642,29 +3588,6 @@ void LLPanelRegionExperiences::processResponse( const LLSD& content ) } - -class LLRegionExperienceResponder : public LLHTTPClient::Responder -{ -public: - typedef boost::function<void (const LLSD&)> callback_t; - - callback_t mCallback; - - LLRegionExperienceResponder(callback_t callback) : mCallback(callback) { } - -protected: - /*virtual*/ void httpSuccess() - { - mCallback(getContent()); - } - - /*virtual*/ void httpFailure() - { - LL_WARNS() << "experience responder failed [status:" << getStatus() << "]: " << getContent() << LL_ENDL; - } -}; - - // Used for both access add and remove operations, depending on the flag // passed in (ESTATE_EXPERIENCE_ALLOWED_ADD, ESTATE_EXPERIENCE_ALLOWED_REMOVE, etc.) // static @@ -3747,6 +3670,13 @@ void LLPanelRegionExperiences::infoCallback(LLHandle<LLPanelRegionExperiences> h } } +/*static*/ +std::string LLPanelRegionExperiences::regionCapabilityQuery(LLViewerRegion* region, const std::string &cap) +{ + // region->getHandle() How to get a region * from a handle? + + return region->getCapability(cap); +} bool LLPanelRegionExperiences::refreshFromRegion(LLViewerRegion* region) { @@ -3771,13 +3701,10 @@ bool LLPanelRegionExperiences::refreshFromRegion(LLViewerRegion* region) mTrusted->loading(); mTrusted->setReadonly(!allow_modify); - std::string url = region->getCapability("RegionExperiences"); - if (!url.empty()) - { - LLHTTPClient::get(url, new LLRegionExperienceResponder(boost::bind(&LLPanelRegionExperiences::infoCallback, - getDerivedHandle<LLPanelRegionExperiences>(), _1))); - } - return LLPanelRegionInfo::refreshFromRegion(region); + LLExperienceCache::instance().getRegionExperiences(boost::bind(&LLPanelRegionExperiences::regionCapabilityQuery, region, _1), + boost::bind(&LLPanelRegionExperiences::infoCallback, getDerivedHandle<LLPanelRegionExperiences>(), _1)); + + return LLPanelRegionInfo::refreshFromRegion(region); } LLSD LLPanelRegionExperiences::addIds(LLPanelExperienceListEditor* panel) @@ -3795,18 +3722,15 @@ LLSD LLPanelRegionExperiences::addIds(LLPanelExperienceListEditor* panel) BOOL LLPanelRegionExperiences::sendUpdate() { LLViewerRegion* region = gAgent.getRegion(); - std::string url = region->getCapability("RegionExperiences"); - if (!url.empty()) - { - LLSD content; - content["allowed"]=addIds(mAllowed); - content["blocked"]=addIds(mBlocked); - content["trusted"]=addIds(mTrusted); + LLSD content; - LLHTTPClient::post(url, content, new LLRegionExperienceResponder(boost::bind(&LLPanelRegionExperiences::infoCallback, - getDerivedHandle<LLPanelRegionExperiences>(), _1))); - } + content["allowed"]=addIds(mAllowed); + content["blocked"]=addIds(mBlocked); + content["trusted"]=addIds(mTrusted); + + LLExperienceCache::instance().setRegionExperiences(boost::bind(&LLPanelRegionExperiences::regionCapabilityQuery, region, _1), + content, boost::bind(&LLPanelRegionExperiences::infoCallback, getDerivedHandle<LLPanelRegionExperiences>(), _1)); return TRUE; } diff --git a/indra/newview/llfloaterregioninfo.h b/indra/newview/llfloaterregioninfo.h index e7b49d8553..3c74618fff 100644 --- 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); @@ -476,6 +479,8 @@ public: private: void refreshRegionExperiences(); + static std::string regionCapabilityQuery(LLViewerRegion* region, const std::string &cap); + LLPanelExperienceListEditor* setupList(const char* control_name, U32 add_id, U32 remove_id); static LLSD addIds( LLPanelExperienceListEditor* panel ); diff --git a/indra/newview/llfloaterreporter.cpp b/indra/newview/llfloaterreporter.cpp index 99a5cf8002..ed6f4ede9f 100644 --- a/indra/newview/llfloaterreporter.cpp +++ b/indra/newview/llfloaterreporter.cpp @@ -78,12 +78,66 @@ #include "lluictrlfactory.h" #include "llviewernetwork.h" -#include "llassetuploadresponders.h" #include "llagentui.h" #include "lltrans.h" #include "llexperiencecache.h" +#include "llcorehttputil.h" +#include "llviewerassetupload.h" + + +//========================================================================= +//----------------------------------------------------------------------------- +// Support classes +//----------------------------------------------------------------------------- +class LLARScreenShotUploader : public LLResourceUploadInfo +{ +public: + LLARScreenShotUploader(LLSD report, LLUUID assetId, LLAssetType::EType assetType); + + virtual LLSD prepareUpload(); + virtual LLSD generatePostBody(); + virtual S32 getEconomyUploadCost(); + virtual LLUUID finishUpload(LLSD &result); + + virtual bool showInventoryPanel() const { return false; } + virtual std::string getDisplayName() const { return "Abuse Report"; } + +private: + + LLSD mReport; +}; + +LLARScreenShotUploader::LLARScreenShotUploader(LLSD report, LLUUID assetId, LLAssetType::EType assetType) : + LLResourceUploadInfo(assetId, assetType, "Abuse Report"), + mReport(report) +{ +} + +LLSD LLARScreenShotUploader::prepareUpload() +{ + return LLSD().with("success", LLSD::Boolean(true)); +} + +LLSD LLARScreenShotUploader::generatePostBody() +{ // The report was pregenerated and passed in the constructor. + return mReport; +} + +S32 LLARScreenShotUploader::getEconomyUploadCost() +{ // Abuse report screen shots do not cost anything to upload. + return 0; +} + +LLUUID LLARScreenShotUploader::finishUpload(LLSD &result) +{ + /* *TODO$: Report success or failure. Carried over from previous todo on responder*/ + return LLUUID::null; +} + + +//========================================================================= //----------------------------------------------------------------------------- // Globals //----------------------------------------------------------------------------- @@ -216,7 +270,7 @@ void LLFloaterReporter::getExperienceInfo(const LLUUID& experience_id) if (LLUUID::null != mExperienceID) { - const LLSD& experience = LLExperienceCache::get(mExperienceID); + const LLSD& experience = LLExperienceCache::instance().get(mExperienceID); std::stringstream desc; if(experience.isDefined()) @@ -708,59 +762,25 @@ void LLFloaterReporter::sendReportViaLegacy(const LLSD & report) msg->sendReliable(regionp->getHost()); } -class LLUserReportScreenshotResponder : public LLAssetUploadResponder +void LLFloaterReporter::finishedARPost(const LLSD &) { -public: - LLUserReportScreenshotResponder(const LLSD & post_data, - const LLUUID & vfile_id, - LLAssetType::EType asset_type): - LLAssetUploadResponder(post_data, vfile_id, asset_type) - { - } - void uploadFailed(const LLSD& content) - { - // *TODO pop up a dialog so the user knows their report screenshot didn't make it - LLUploadDialog::modalUploadFinished(); - } - void uploadComplete(const LLSD& content) - { - // we don't care about what the server returns from this post, just clean up the UI - LLUploadDialog::modalUploadFinished(); - } -}; + LLUploadDialog::modalUploadFinished(); -class LLUserReportResponder : public LLHTTPClient::Responder -{ - LOG_CLASS(LLUserReportResponder); -public: - LLUserReportResponder(): LLHTTPClient::Responder() {} - -private: - void httpCompleted() - { - if (!isGoodStatus()) - { - // *TODO do some user messaging here - LL_WARNS("UserReport") << dumpResponse() << LL_ENDL; - } - // we don't care about what the server returns - LLUploadDialog::modalUploadFinished(); - } -}; +} void LLFloaterReporter::sendReportViaCaps(std::string url, std::string sshot_url, const LLSD& report) { if(getChild<LLUICtrl>("screen_check")->getValue().asBoolean() && !sshot_url.empty()) - { + { // try to upload screenshot - LLHTTPClient::post(sshot_url, report, new LLUserReportScreenshotResponder(report, - mResourceDatap->mAssetInfo.mUuid, - mResourceDatap->mAssetInfo.mType)); + LLResourceUploadInfo::ptr_t uploadInfo(new LLARScreenShotUploader(report, mResourceDatap->mAssetInfo.mUuid, mResourceDatap->mAssetInfo.mType)); + LLViewerAssetUpload::EnqueueInventoryUpload(sshot_url, uploadInfo); } else { - // screenshot not wanted or we don't have screenshot cap - LLHTTPClient::post(url, report, new LLUserReportResponder()); + LLCoreHttpUtil::HttpCoroutineAdapter::completionCallback_t proc = boost::bind(&LLFloaterReporter::finishedARPost, _1); + LLUploadDialog::modalUploadDialog("Abuse Report"); + LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost(url, report, proc, proc); } } diff --git a/indra/newview/llfloaterreporter.h b/indra/newview/llfloaterreporter.h index d857528f10..1aff07bd37 100644 --- a/indra/newview/llfloaterreporter.h +++ b/indra/newview/llfloaterreporter.h @@ -122,6 +122,8 @@ private: void setFromAvatarID(const LLUUID& avatar_id); void onAvatarNameCache(const LLUUID& avatar_id, const LLAvatarName& av_name); + static void finishedARPost(const LLSD &); + private: EReportType mReportType; LLUUID mObjectID; diff --git a/indra/newview/llfloaterscriptlimits.cpp b/indra/newview/llfloaterscriptlimits.cpp index 5fbdd75e97..7b8fc5b35b 100644 --- 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->postAndSuspend(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->getAndSuspend(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->getAndSuspend(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->getAndSuspend(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 100644 --- 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 4cb1ca6cc0..1743d0fc69 100644 --- 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,6 +43,8 @@ #include "llvfile.h" #include "message.h" #include "llstartup.h" // login_alert_done +#include "llcorehttputil.h" +#include "llfloaterreg.h" LLFloaterTOS::LLFloaterTOS(const LLSD& data) : LLModalDialog( data["message"].asString() ), @@ -56,57 +56,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); @@ -208,9 +157,6 @@ void LLFloaterTOS::setSiteIsAlive( bool alive ) LLFloaterTOS::~LLFloaterTOS() { - // tell the responder we're not here anymore - if ( gResponsePtr ) - gResponsePtr->setParent( 0 ); } // virtual @@ -271,9 +217,12 @@ void LLFloaterTOS::handleMediaEvent(LLPluginClassMedia* /*self*/, EMediaEvent ev if(!mLoadingScreenLoaded) { mLoadingScreenLoaded = true; + std::string url(getString("real_url")); + + LLHandle<LLFloater> handle = getHandle(); - gResponsePtr = LLIamHere::build( this ); - LLHTTPClient::get( getString( "real_url" ), gResponsePtr ); + LLCoros::instance().launch("LLFloaterTOS::testSiteIsAliveCoro", + boost::bind(&LLFloaterTOS::testSiteIsAliveCoro, handle, url)); } else if(mRealNavigateBegun) { @@ -285,3 +234,39 @@ void LLFloaterTOS::handleMediaEvent(LLPluginClassMedia* /*self*/, EMediaEvent ev } } +void LLFloaterTOS::testSiteIsAliveCoro(LLHandle<LLFloater> handle, 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("testSiteIsAliveCoro") << "Generic POST for " << url << LL_ENDL; + + LLSD result = httpAdapter->getAndSuspend(httpRequest, url); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (handle.isDead()) + { + LL_WARNS("testSiteIsAliveCoro") << "Dialog canceled before response." << LL_ENDL; + return; + } + + LLFloaterTOS *that = dynamic_cast<LLFloaterTOS *>(handle.get()); + + if (that) + that->setSiteIsAlive(static_cast<bool>(status)); + else + { + LL_WARNS("testSiteIsAliveCoro") << "Handle was not a TOS floater." << LL_ENDL; + } +} + + diff --git a/indra/newview/llfloatertos.h b/indra/newview/llfloatertos.h index 47126d06a6..e70c9f24af 100644 --- 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: + static void testSiteIsAliveCoro(LLHandle<LLFloater> handle, std::string url); std::string mMessage; bool mLoadingScreenLoaded; bool mSiteAlive; bool mRealNavigateBegun; std::string mReplyPumpName; + + }; #endif // LL_LLFLOATERTOS_H diff --git a/indra/newview/llfloatertranslationsettings.cpp b/indra/newview/llfloatertranslationsettings.cpp index 965d29b09c..b1316e386d 100644 --- a/indra/newview/llfloatertranslationsettings.cpp +++ b/indra/newview/llfloatertranslationsettings.cpp @@ -42,41 +42,6 @@ #include "llnotificationsutil.h" #include "llradiogroup.h" -class EnteredKeyVerifier : public LLTranslate::KeyVerificationReceiver -{ -public: - EnteredKeyVerifier(LLTranslate::EService service, bool alert) - : LLTranslate::KeyVerificationReceiver(service) - , mAlert(alert) - { - } - -private: - /*virtual*/ void setVerificationStatus(bool ok) - { - LLFloaterTranslationSettings* floater = - LLFloaterReg::getTypedInstance<LLFloaterTranslationSettings>("prefs_translation"); - - if (!floater) - { - LL_WARNS() << "Cannot find translation settings floater" << LL_ENDL; - return; - } - - switch (getService()) - { - case LLTranslate::SERVICE_BING: - floater->setBingVerified(ok, mAlert); - break; - case LLTranslate::SERVICE_GOOGLE: - floater->setGoogleVerified(ok, mAlert); - break; - } - } - - bool mAlert; -}; - LLFloaterTranslationSettings::LLFloaterTranslationSettings(const LLSD& key) : LLFloater(key) , mMachineTranslationCB(NULL) @@ -231,11 +196,34 @@ void LLFloaterTranslationSettings::updateControlsEnabledState() mOKBtn->setEnabled(!on || service_verified); } +/*static*/ +void LLFloaterTranslationSettings::setVerificationStatus(int service, bool ok, bool alert) +{ + LLFloaterTranslationSettings* floater = + LLFloaterReg::getTypedInstance<LLFloaterTranslationSettings>("prefs_translation"); + + if (!floater) + { + LL_WARNS() << "Cannot find translation settings floater" << LL_ENDL; + return; + } + + switch (service) + { + case LLTranslate::SERVICE_BING: + floater->setBingVerified(ok, alert); + break; + case LLTranslate::SERVICE_GOOGLE: + floater->setGoogleVerified(ok, alert); + break; + } +} + + void LLFloaterTranslationSettings::verifyKey(int service, const std::string& key, bool alert) { - LLTranslate::KeyVerificationReceiverPtr receiver = - new EnteredKeyVerifier((LLTranslate::EService) service, alert); - LLTranslate::verifyKey(receiver, key); + LLTranslate::verifyKey(static_cast<LLTranslate::EService>(service), key, + boost::bind(&LLFloaterTranslationSettings::setVerificationStatus, _1, _2, alert)); } void LLFloaterTranslationSettings::onEditorFocused(LLFocusableElement* control) diff --git a/indra/newview/llfloatertranslationsettings.h b/indra/newview/llfloatertranslationsettings.h index b9bfd6265a..2a15eacded 100644 --- a/indra/newview/llfloatertranslationsettings.h +++ b/indra/newview/llfloatertranslationsettings.h @@ -61,6 +61,8 @@ private: void onBtnGoogleVerify(); void onBtnOK(); + static void setVerificationStatus(int service, bool alert, bool ok); + LLCheckBoxCtrl* mMachineTranslationCB; LLComboBox* mLanguageCombo; LLLineEditor* mBingAPIKeyEditor; diff --git a/indra/newview/llfloaterurlentry.cpp b/indra/newview/llfloaterurlentry.cpp index e02e8eeb5a..f2efef0c33 100644 --- 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->getAndSuspend(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 100644 --- 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 012a4fdb2f..7545112ab9 100644 --- a/indra/newview/llgroupmgr.cpp +++ b/indra/newview/llgroupmgr.cpp @@ -41,7 +41,6 @@ #include "llui.h" #include "message.h" #include "roles_constants.h" -#include "llhttpclient.h" #include "lltransactiontypes.h" #include "llstatusbar.h" #include "lleconomy.h" @@ -53,6 +52,7 @@ #include "lltrans.h" #include "llviewerregion.h" #include <boost/regex.hpp> +#include "llcorehttputil.h" #if LL_MSVC #pragma warning(push) @@ -830,9 +830,9 @@ void LLGroupMgrGroupData::banMemberById(const LLUUID& participant_uuid) // LLGroupMgr // -LLGroupMgr::LLGroupMgr() +LLGroupMgr::LLGroupMgr(): + mMemberRequestInFlight(false) { - mLastGroupMembersRequestFrame = 0; } LLGroupMgr::~LLGroupMgr() @@ -1933,49 +1933,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->getAndSuspend(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->postAndSuspend(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) @@ -1997,37 +2042,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? @@ -2065,43 +2100,42 @@ void LLGroupMgr::processGroupBanRequest(const LLSD& content) LLGroupMgr::getInstance()->notifyObservers(GC_BANLIST); } -// Responder class for capability group management -class GroupMemberDataResponder : public LLHTTPClient::Responder +void LLGroupMgr::groupMembersRequestCoro(std::string url, LLUUID groupId) { - LOG_CLASS(GroupMemberDataResponder); -public: - GroupMemberDataResponder() {} - virtual ~GroupMemberDataResponder() {} + 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); -private: - /* virtual */ void httpSuccess(); - /* virtual */ void httpFailure(); - LLSD mMemberData; -}; + mMemberRequestInFlight = true; -void GroupMemberDataResponder::httpFailure() -{ - LL_WARNS("GrpMgr") << "Error receiving group member data " - << dumpResponse() << LL_ENDL; -} + LLSD postData = LLSD::emptyMap(); + postData["group_id"] = groupId; -void GroupMemberDataResponder::httpSuccess() -{ - const LLSD& content = getContent(); - if (!content.isMap()) - { - failureResult(HTTP_INTERNAL_ERROR, "Malformed response contents", content); - return; - } - LLGroupMgr::processCapGroupMembersRequest(content); -} + LLSD result = httpAdapter->postAndSuspend(httpRequest, url, postData, httpOpts); + 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; + 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(); @@ -2130,20 +2164,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? @@ -2155,7 +2182,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; @@ -2261,7 +2288,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; @@ -2270,11 +2297,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 5307c4de92..e5ce768035 100644 --- 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; @@ -365,6 +367,7 @@ public: BAN_UPDATE = 4 }; + public: LLGroupMgr(); ~LLGroupMgr(); @@ -399,15 +402,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); @@ -430,6 +431,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); @@ -445,7 +454,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 100644 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 100644 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..6a2daeeb90 100644 --- a/indra/newview/llhttpretrypolicy.cpp +++ b/indra/newview/llhttpretrypolicy.cpp @@ -25,9 +25,23 @@ */ #include "llviewerprecompiledheaders.h" - #include "llhttpretrypolicy.h" +namespace +{ + // Moved from httpconstants.h... only used in this file. + bool isHttpServerErrorStatus(S32 status) + { + // Status 499 is sometimes used for re-interpreted status 2xx errors. + // Allow retry of these, since we don't have enough information in this + // context to know if this will always fail. + if (HTTP_INTERNAL_ERROR == status) return true; + + // Check for status 5xx. + return((500 <= status) && (status < 600)); + } +} + LLAdaptiveRetryPolicy::LLAdaptiveRetryPolicy(F32 min_delay, F32 max_delay, F32 backoff_factor, U32 max_retries, bool retry_on_4xx): mMinDelay(min_delay), mMaxDelay(max_delay), @@ -56,7 +70,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 +99,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) @@ -140,3 +154,34 @@ bool LLAdaptiveRetryPolicy::shouldRetry(F32& seconds_to_wait) const seconds_to_wait = mShouldRetry ? (F32) mRetryTimer.getRemainingTimeF32() : F32_MAX; return mShouldRetry; } + +// Moved from httpconstants. Only used by this file. +// Parses 'Retry-After' header contents and returns seconds until retry should occur. +/*static*/ +bool LLAdaptiveRetryPolicy::getSecondsUntilRetryAfter(const std::string& retry_after, F32& seconds_to_wait) +{ + // *TODO: This needs testing! Not in use yet. + // Examples of Retry-After headers: + // Retry-After: Fri, 31 Dec 1999 23:59:59 GMT + // Retry-After: 120 + + // Check for number of seconds version, first: + char* end = 0; + // Parse as double + double seconds = std::strtod(retry_after.c_str(), &end); + if (end != 0 && *end == 0) + { + // Successful parse + seconds_to_wait = (F32)seconds; + return true; + } + + // Parse rfc1123 date. + time_t date = curl_getdate(retry_after.c_str(), NULL); + if (-1 == date) return false; + + seconds_to_wait = (F64)date - LLTimer::getTotalSeconds(); + + return true; +} + diff --git a/indra/newview/llhttpretrypolicy.h b/indra/newview/llhttpretrypolicy.h index cf79e0b401..af07b4afec 100644 --- a/indra/newview/llhttpretrypolicy.h +++ b/indra/newview/llhttpretrypolicy.h @@ -76,10 +76,12 @@ public: // virtual bool shouldRetry(F32& seconds_to_wait) const; + static bool getSecondsUntilRetryAfter(const std::string& retry_after, F32& seconds_to_wait); + 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 b8b6bdaa11..951389b856 100644 --- 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->postAndSuspend(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->postAndSuspend(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)); @@ -1262,12 +1388,12 @@ void LLIMModel::sendMessage(const std::string& utf8_text, info = LLAvatarTracker::instance().getBuddyInfo(other_participant_id); U8 offline = (!info || info->isOnline()) ? IM_ONLINE : IM_OFFLINE; - - if((offline == IM_OFFLINE) && (LLVoiceClient::getInstance()->isOnlineSIP(other_participant_id))) - { - // User is online through the OOW connector, but not with a regular viewer. Try to send the message via SLVoice. - sent = LLVoiceClient::getInstance()->sendTextMessage(other_participant_id, utf8_text); - } + // Old call to send messages to SLim client, no longer supported. + //if((offline == IM_OFFLINE) && (LLVoiceClient::getInstance()->isOnlineSIP(other_participant_id))) + //{ + // // User is online through the OOW connector, but not with a regular viewer. Try to send the message via SLVoice. + // sent = LLVoiceClient::getInstance()->sendTextMessage(other_participant_id, utf8_text); + //} if(!sent) { @@ -1465,54 +1591,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( @@ -1549,20 +1627,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 { @@ -1580,97 +1648,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) @@ -2496,15 +2473,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") @@ -2539,10 +2510,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."); } } @@ -2589,15 +2560,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; @@ -2627,10 +2592,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."); } } @@ -3687,15 +3651,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 100644 --- 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/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 9782c792c9..1782653439 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -126,11 +126,6 @@ bool isRemoveAction(const std::string& action) return ("take_off" == action || "detach" == action); } -bool isMarketplaceCopyAction(const std::string& action) -{ - return (("copy_to_outbox" == action) || ("move_to_outbox" == action)); -} - bool isMarketplaceSendAction(const std::string& action) { return ("send_to_marketplace" == action); @@ -797,8 +792,8 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id, } } - // Don't allow items to be pasted directly into the COF or the inbox/outbox - if (!isCOFFolder() && !isInboxFolder() && !isOutboxFolder()) + // Don't allow items to be pasted directly into the COF or the inbox + if (!isCOFFolder() && !isInboxFolder()) { items.push_back(std::string("Paste")); } @@ -836,10 +831,6 @@ void LLInvFVBridge::buildContextMenu(LLMenuGL& menu, U32 flags) { addTrashContextMenuOptions(items, disabled_items); } - else if(isOutboxFolder()) - { - addOutboxContextMenuOptions(flags, items, disabled_items); - } else { items.push_back(std::string("Share")); @@ -932,19 +923,6 @@ void LLInvFVBridge::addOpenRightClickMenuOption(menuentry_vec_t &items) items.push_back(std::string("Open")); } -void LLInvFVBridge::addOutboxContextMenuOptions(U32 flags, - menuentry_vec_t &items, - menuentry_vec_t &disabled_items) -{ - items.push_back(std::string("Rename")); - items.push_back(std::string("Delete")); - - if ((flags & FIRST_SELECTED_ITEM) == 0) - { - disabled_items.push_back(std::string("Rename")); - } -} - void LLInvFVBridge::addMarketplaceContextMenuOptions(U32 flags, menuentry_vec_t &items, menuentry_vec_t &disabled_items) @@ -1194,41 +1172,6 @@ BOOL LLInvFVBridge::isMarketplaceListingsFolder() const return gInventory.isObjectDescendentOf(mUUID, folder_id); } -BOOL LLInvFVBridge::isOutboxFolder() const -{ - const LLUUID outbox_id = getOutboxFolder(); - - if (outbox_id.isNull()) - { - return FALSE; - } - - return gInventory.isObjectDescendentOf(mUUID, outbox_id); -} - -BOOL LLInvFVBridge::isOutboxFolderDirectParent() const -{ - BOOL outbox_is_parent = FALSE; - - const LLInventoryCategory *cat = gInventory.getCategory(mUUID); - - if (cat) - { - const LLUUID outbox_id = getOutboxFolder(); - - outbox_is_parent = (outbox_id.notNull() && (outbox_id == cat->getParentUUID())); - } - - return outbox_is_parent; -} - -const LLUUID LLInvFVBridge::getOutboxFolder() const -{ - const LLUUID outbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); - - return outbox_id; -} - BOOL LLInvFVBridge::isItemPermissive() const { return FALSE; @@ -1485,56 +1428,6 @@ bool LLInvFVBridge::canListOnMarketplace() const return true; } -// *TODO : Suppress canListOnOutboxNow() once we deprecate Merchant Outbox completely -bool LLInvFVBridge::canListOnOutboxNow() const -{ - bool can_list = true; - - // Do not allow listing while import is in progress - if (LLMarketplaceInventoryImporter::instanceExists()) - { - can_list = !LLMarketplaceInventoryImporter::instance().isImportInProgress(); - } - - const LLInventoryObject* obj = getInventoryObject(); - can_list &= (obj != NULL); - - if (can_list) - { - const LLUUID& object_id = obj->getLinkedUUID(); - can_list = object_id.notNull(); - - if (can_list) - { - LLFolderViewFolder * object_folderp = mInventoryPanel.get() ? mInventoryPanel.get()->getFolderByID(object_id) : NULL; - if (object_folderp) - { - can_list = !static_cast<LLFolderBridge*>(object_folderp->getViewModelItem())->isLoading(); - } - } - - if (can_list) - { - // Get outbox id - const LLUUID & outbox_id = getInventoryModel()->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); - LLFolderViewItem * outbox_itemp = mInventoryPanel.get() ? mInventoryPanel.get()->getItemByID(outbox_id) : NULL; - - if (outbox_itemp) - { - MASK mask = 0x0; - BOOL drop = FALSE; - EDragAndDropType cargo_type = LLViewerAssetType::lookupDragAndDropType(obj->getActualType()); - void * cargo_data = (void *) obj; - std::string tooltip_msg; - - can_list = outbox_itemp->getViewModelItem()->dragOrDrop(mask, drop, cargo_type, cargo_data, tooltip_msg); - } - } - } - - return can_list; -} - bool LLInvFVBridge::canListOnMarketplaceNow() const { bool can_list = true; @@ -2438,13 +2331,10 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, const LLUUID &cat_id = inv_cat->getUUID(); const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, false); - const LLUUID &outbox_id = model->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); const LLUUID from_folder_uuid = inv_cat->getParentUUID(); const BOOL move_is_into_current_outfit = (mUUID == current_outfit_id); - const BOOL move_is_into_outbox = model->isObjectDescendentOf(mUUID, outbox_id); - const BOOL move_is_from_outbox = model->isObjectDescendentOf(cat_id, outbox_id); const BOOL move_is_into_marketplacelistings = model->isObjectDescendentOf(mUUID, marketplacelistings_id); const BOOL move_is_from_marketplacelistings = model->isObjectDescendentOf(cat_id, marketplacelistings_id); @@ -2596,9 +2486,9 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, } } - if (is_movable && (move_is_into_outbox || move_is_into_marketplacelistings)) + if (is_movable && move_is_into_marketplacelistings) { - const LLViewerInventoryCategory * master_folder = (move_is_into_outbox ? model->getFirstDescendantOf(outbox_id, mUUID) : model->getFirstDescendantOf(marketplacelistings_id, mUUID)); + const LLViewerInventoryCategory * master_folder = model->getFirstDescendantOf(marketplacelistings_id, mUUID); LLViewerInventoryCategory * dest_folder = getCategory(); S32 bundle_size = (drop ? 1 : LLToolDragAndDrop::instance().getCargoCount()); is_movable = can_move_folder_to_marketplace(master_folder, dest_folder, inv_cat, tooltip_msg, bundle_size); @@ -2709,10 +2599,6 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, BOOL append = true; LLAppearanceMgr::instance().wearInventoryCategory(inv_cat, false, append); } - else if (move_is_into_outbox && !move_is_from_outbox) - { - copy_folder_to_outbox(inv_cat, mUUID, cat_id, LLToolDragAndDrop::getOperationId()); - } else if (move_is_into_marketplacelistings) { move_folder_to_marketplacelistings(inv_cat, mUUID); @@ -2763,7 +2649,7 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, } else if (LLToolDragAndDrop::SOURCE_WORLD == source) { - if (move_is_into_outbox || move_is_into_marketplacelistings) + if (move_is_into_marketplacelistings) { tooltip_msg = LLTrans::getString("TooltipOutboxNotInInventory"); accept = FALSE; @@ -2775,7 +2661,7 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, } else if (LLToolDragAndDrop::SOURCE_LIBRARY == source) { - if (move_is_into_outbox || move_is_into_marketplacelistings) + if (move_is_into_marketplacelistings) { tooltip_msg = LLTrans::getString("TooltipOutboxNotInInventory"); accept = FALSE; @@ -3559,7 +3445,6 @@ void LLFolderBridge::perform_pasteFromClipboard() if (model && isClipboardPasteable()) { const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, false); - const LLUUID &outbox_id = model->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); const LLUUID &favorites_id = model->findCategoryUUIDForType(LLFolderType::FT_FAVORITE, false); const LLUUID &my_outifts_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS, false); @@ -3567,7 +3452,6 @@ void LLFolderBridge::perform_pasteFromClipboard() const BOOL move_is_into_current_outfit = (mUUID == current_outfit_id); const BOOL move_is_into_my_outfits = (mUUID == my_outifts_id) || model->isObjectDescendentOf(mUUID, my_outifts_id); const BOOL move_is_into_outfit = move_is_into_my_outfits || (getCategory() && getCategory()->getPreferredType()==LLFolderType::FT_OUTFIT); - const BOOL move_is_into_outbox = model->isObjectDescendentOf(mUUID, outbox_id); const BOOL move_is_into_marketplacelistings = model->isObjectDescendentOf(mUUID, marketplacelistings_id); const BOOL move_is_into_favorites = (mUUID == favorites_id); @@ -3575,10 +3459,10 @@ void LLFolderBridge::perform_pasteFromClipboard() LLClipboard::instance().pasteFromClipboard(objects); LLViewerInventoryCategory * dest_folder = getCategory(); - if (move_is_into_outbox || move_is_into_marketplacelistings) + if (move_is_into_marketplacelistings) { std::string error_msg; - const LLViewerInventoryCategory * master_folder = (move_is_into_outbox ? model->getFirstDescendantOf(outbox_id, mUUID) : model->getFirstDescendantOf(marketplacelistings_id, mUUID)); + const LLViewerInventoryCategory * master_folder = model->getFirstDescendantOf(marketplacelistings_id, mUUID); int index = 0; for (std::vector<LLUUID>::const_iterator iter = objects.begin(); iter != objects.end(); ++iter) { @@ -3757,17 +3641,15 @@ void LLFolderBridge::pasteLinkFromClipboard() if(model) { const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, false); - const LLUUID &outbox_id = model->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); const LLUUID &my_outifts_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS, false); const BOOL move_is_into_current_outfit = (mUUID == current_outfit_id); const BOOL move_is_into_my_outfits = (mUUID == my_outifts_id) || model->isObjectDescendentOf(mUUID, my_outifts_id); const BOOL move_is_into_outfit = move_is_into_my_outfits || (getCategory() && getCategory()->getPreferredType()==LLFolderType::FT_OUTFIT); - const BOOL move_is_into_outbox = model->isObjectDescendentOf(mUUID, outbox_id); const BOOL move_is_into_marketplacelistings = model->isObjectDescendentOf(mUUID, marketplacelistings_id); - if (move_is_into_outbox || move_is_into_marketplacelistings) + if (move_is_into_marketplacelistings) { // Notify user of failure somehow -- play error sound? modal dialog? return; @@ -3888,10 +3770,6 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items items.clear(); // clear any items that used to exist addTrashContextMenuOptions(items, disabled_items); } - else if(isOutboxFolder()) - { - addOutboxContextMenuOptions(flags, items, disabled_items); - } else if(isAgentInventory()) // do not allow creating in library { LLViewerInventoryCategory *cat = getCategory(); @@ -3899,7 +3777,7 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items // Not sure what the right thing is to do here. if (!isCOFFolder() && cat && (cat->getPreferredType() != LLFolderType::FT_OUTFIT)) { - if (!isInboxFolder() && !isOutboxFolder()) // don't allow creation in inbox or outbox + if (!isInboxFolder()) // don't allow creation in inbox { // Do not allow to create 2-level subfolder in the Calling Card/Friends folder. EXT-694. if (!LLFriendCardsManager::instance().isCategoryInFriendFolder(cat)) @@ -3961,7 +3839,7 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items disabled_items.push_back(std::string("Delete System Folder")); } - if (!isOutboxFolder() && !isMarketplaceListingsFolder()) + if (!isMarketplaceListingsFolder()) { items.push_back(std::string("Share")); if (!canShare()) @@ -4007,7 +3885,6 @@ void LLFolderBridge::buildContextMenuFolderOptions(U32 flags, menuentry_vec_t& if (trash_id == mUUID) return; if (isItemInTrash()) return; if (!isAgentInventory()) return; - if (isOutboxFolder()) return; if (!isItemRemovable()) { @@ -4601,7 +4478,6 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, false); const LLUUID &favorites_id = model->findCategoryUUIDForType(LLFolderType::FT_FAVORITE, false); const LLUUID &landmarks_id = model->findCategoryUUIDForType(LLFolderType::FT_LANDMARK, false); - const LLUUID &outbox_id = model->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); const LLUUID &my_outifts_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS, false); const LLUUID from_folder_uuid = inv_item->getParentUUID(); @@ -4611,8 +4487,6 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, const BOOL move_is_into_my_outfits = (mUUID == my_outifts_id) || model->isObjectDescendentOf(mUUID, my_outifts_id); const BOOL move_is_into_outfit = move_is_into_my_outfits || (getCategory() && getCategory()->getPreferredType()==LLFolderType::FT_OUTFIT); const BOOL move_is_into_landmarks = (mUUID == landmarks_id) || model->isObjectDescendentOf(mUUID, landmarks_id); - const BOOL move_is_into_outbox = model->isObjectDescendentOf(mUUID, outbox_id); - const BOOL move_is_from_outbox = model->isObjectDescendentOf(inv_item->getUUID(), outbox_id); const BOOL move_is_into_marketplacelistings = model->isObjectDescendentOf(mUUID, marketplacelistings_id); const BOOL move_is_from_marketplacelistings = model->isObjectDescendentOf(inv_item->getUUID(), marketplacelistings_id); @@ -4688,9 +4562,9 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, { accept = can_move_to_landmarks(inv_item); } - else if (user_confirm && (move_is_into_outbox || move_is_into_marketplacelistings)) + else if (user_confirm && move_is_into_marketplacelistings) { - const LLViewerInventoryCategory * master_folder = (move_is_into_outbox ? model->getFirstDescendantOf(outbox_id, mUUID) : model->getFirstDescendantOf(marketplacelistings_id, mUUID)); + const LLViewerInventoryCategory * master_folder = model->getFirstDescendantOf(marketplacelistings_id, mUUID); LLViewerInventoryCategory * dest_folder = getCategory(); accept = can_move_item_to_marketplace(master_folder, dest_folder, inv_item, tooltip_msg, LLToolDragAndDrop::instance().getCargoCount() - LLToolDragAndDrop::instance().getCargoIndex()); } @@ -4775,19 +4649,6 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, { dropToOutfit(inv_item, move_is_into_current_outfit); } - // MERCHANT OUTBOX folder - // Move the item - else if (move_is_into_outbox) - { - if (move_is_from_outbox) - { - move_item_within_outbox(inv_item, mUUID, LLToolDragAndDrop::getOperationId()); - } - else - { - copy_item_to_outbox(inv_item, mUUID, LLUUID::null, LLToolDragAndDrop::getOperationId()); - } - } // MARKETPLACE LISTINGS folder // Move the item else if (move_is_into_marketplacelistings) @@ -4874,7 +4735,7 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, { accept = FALSE; } - else if (move_is_into_outbox || move_is_into_marketplacelistings) + else if (move_is_into_marketplacelistings) { tooltip_msg = LLTrans::getString("TooltipOutboxNotInInventory"); accept = FALSE; @@ -4912,7 +4773,7 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, } else if(LLToolDragAndDrop::SOURCE_NOTECARD == source) { - if (move_is_into_outbox || move_is_into_marketplacelistings) + if (move_is_into_marketplacelistings) { tooltip_msg = LLTrans::getString("TooltipOutboxNotInInventory"); accept = FALSE; @@ -4946,7 +4807,7 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, { accept = TRUE; - if (move_is_into_outbox || move_is_into_marketplacelistings) + if (move_is_into_marketplacelistings) { tooltip_msg = LLTrans::getString("TooltipOutboxNotInInventory"); accept = FALSE; @@ -5115,10 +4976,6 @@ void LLTextureBridge::buildContextMenu(LLMenuGL& menu, U32 flags) { addTrashContextMenuOptions(items, disabled_items); } - else if(isOutboxFolder()) - { - addOutboxContextMenuOptions(flags, items, disabled_items); - } else if (isMarketplaceListingsFolder()) { addMarketplaceContextMenuOptions(flags, items, disabled_items); @@ -5188,11 +5045,7 @@ void LLSoundBridge::buildContextMenu(LLMenuGL& menu, U32 flags) menuentry_vec_t items; menuentry_vec_t disabled_items; - if (isOutboxFolder()) - { - addOutboxContextMenuOptions(flags, items, disabled_items); - } - else if (isMarketplaceListingsFolder()) + if (isMarketplaceListingsFolder()) { addMarketplaceContextMenuOptions(flags, items, disabled_items); items.push_back(std::string("Properties")); @@ -5269,11 +5122,7 @@ void LLLandmarkBridge::buildContextMenu(LLMenuGL& menu, U32 flags) menuentry_vec_t disabled_items; LL_DEBUGS() << "LLLandmarkBridge::buildContextMenu()" << LL_ENDL; - if(isOutboxFolder()) - { - addOutboxContextMenuOptions(flags, items, disabled_items); - } - else if (isMarketplaceListingsFolder()) + if (isMarketplaceListingsFolder()) { addMarketplaceContextMenuOptions(flags, items, disabled_items); items.push_back(std::string("Properties")); @@ -5567,10 +5416,6 @@ void LLCallingCardBridge::buildContextMenu(LLMenuGL& menu, U32 flags) { addTrashContextMenuOptions(items, disabled_items); } - else if(isOutboxFolder()) - { - items.push_back(std::string("Delete")); - } else if (isMarketplaceListingsFolder()) { addMarketplaceContextMenuOptions(flags, items, disabled_items); @@ -5859,10 +5704,6 @@ void LLGestureBridge::buildContextMenu(LLMenuGL& menu, U32 flags) { addTrashContextMenuOptions(items, disabled_items); } - else if(isOutboxFolder()) - { - items.push_back(std::string("Delete")); - } else if (isMarketplaceListingsFolder()) { addMarketplaceContextMenuOptions(flags, items, disabled_items); @@ -5919,11 +5760,7 @@ void LLAnimationBridge::buildContextMenu(LLMenuGL& menu, U32 flags) menuentry_vec_t disabled_items; LL_DEBUGS() << "LLAnimationBridge::buildContextMenu()" << LL_ENDL; - if(isOutboxFolder()) - { - items.push_back(std::string("Delete")); - } - else if (isMarketplaceListingsFolder()) + if (isMarketplaceListingsFolder()) { addMarketplaceContextMenuOptions(flags, items, disabled_items); items.push_back(std::string("Properties")); @@ -6184,10 +6021,6 @@ void LLObjectBridge::buildContextMenu(LLMenuGL& menu, U32 flags) { addTrashContextMenuOptions(items, disabled_items); } - else if(isOutboxFolder()) - { - items.push_back(std::string("Delete")); - } else if (isMarketplaceListingsFolder()) { addMarketplaceContextMenuOptions(flags, items, disabled_items); @@ -6408,10 +6241,6 @@ void LLWearableBridge::buildContextMenu(LLMenuGL& menu, U32 flags) { addTrashContextMenuOptions(items, disabled_items); } - else if(isOutboxFolder()) - { - items.push_back(std::string("Delete")); - } else if (isMarketplaceListingsFolder()) { addMarketplaceContextMenuOptions(flags, items, disabled_items); @@ -6714,10 +6543,6 @@ void LLMeshBridge::buildContextMenu(LLMenuGL& menu, U32 flags) items.push_back(std::string("Restore Item")); } - else if(isOutboxFolder()) - { - addOutboxContextMenuOptions(flags, items, disabled_items); - } else if (isMarketplaceListingsFolder()) { addMarketplaceContextMenuOptions(flags, items, disabled_items); diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h index 03e19cc4da..9053c61171 100644 --- a/indra/newview/llinventorybridge.h +++ b/indra/newview/llinventorybridge.h @@ -77,7 +77,6 @@ public: bool canShare() const; bool canListOnMarketplace() const; - bool canListOnOutboxNow() const; bool canListOnMarketplaceNow() const; //-------------------------------------------------------------------- @@ -145,9 +144,6 @@ protected: virtual void addDeleteContextMenuOptions(menuentry_vec_t &items, menuentry_vec_t &disabled_items); virtual void addOpenRightClickMenuOption(menuentry_vec_t &items); - virtual void addOutboxContextMenuOptions(U32 flags, - menuentry_vec_t &items, - menuentry_vec_t &disabled_items); virtual void addMarketplaceContextMenuOptions(U32 flags, menuentry_vec_t &items, menuentry_vec_t &disabled_items); @@ -164,9 +160,7 @@ protected: BOOL isCOFFolder() const; // true if COF or descendant of BOOL isInboxFolder() const; // true if COF or descendant of marketplace inbox - BOOL isOutboxFolderDirectParent() const; BOOL isMarketplaceListingsFolder() const; // true if descendant of Marketplace listings folder - const LLUUID getOutboxFolder() const; virtual BOOL isItemPermissive() const; static void changeItemParent(LLInventoryModel* model, @@ -182,10 +176,6 @@ protected: BOOL callback_cutToClipboard(const LLSD& notification, const LLSD& response); BOOL perform_cutToClipboard(); -public: - BOOL isOutboxFolder() const; // true if COF or descendant of marketplace outbox - -protected: LLHandle<LLInventoryPanel> mInventoryPanel; LLFolderView* mRoot; const LLUUID mUUID; // item id diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index bb1d026d9c..e3cb4d57ef 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -782,11 +782,6 @@ void reset_inventory_filter() } } -void open_outbox() -{ - LLFloaterReg::showInstance("outbox"); -} - void open_marketplace_listings() { LLFloaterReg::showInstance("marketplace_listings"); @@ -808,157 +803,6 @@ LLUUID create_folder_for_item(LLInventoryItem* item, const LLUUID& destFolderId) return created_folder_id; } -void move_to_outbox_cb_action(const LLSD& payload) -{ - LLViewerInventoryItem * viitem = gInventory.getItem(payload["item_id"].asUUID()); - LLUUID dest_folder_id = payload["dest_folder_id"].asUUID(); - - if (viitem) - { - // when moving item directly into outbox create folder with that name - if (dest_folder_id == gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false)) - { - dest_folder_id = create_folder_for_item(viitem, dest_folder_id); - } - - LLUUID parent = viitem->getParentUUID(); - - gInventory.changeItemParent( - viitem, - dest_folder_id, - false); - - LLUUID top_level_folder = payload["top_level_folder"].asUUID(); - - if (top_level_folder != LLUUID::null) - { - LLViewerInventoryCategory* category; - - while (parent.notNull()) - { - LLInventoryModel::cat_array_t* cat_array; - LLInventoryModel::item_array_t* item_array; - gInventory.getDirectDescendentsOf(parent,cat_array,item_array); - - LLUUID next_parent; - - category = gInventory.getCategory(parent); - - if (!category) break; - - next_parent = category->getParentUUID(); - - if (cat_array->empty() && item_array->empty()) - { - gInventory.removeCategory(parent); - } - - if (parent == top_level_folder) - { - break; - } - - parent = next_parent; - } - } - - open_outbox(); - } -} - -void copy_item_to_outbox(LLInventoryItem* inv_item, LLUUID dest_folder, const LLUUID& top_level_folder, S32 operation_id) -{ - // Collapse links directly to items/folders - LLViewerInventoryItem * viewer_inv_item = (LLViewerInventoryItem *) inv_item; - LLViewerInventoryCategory * linked_category = viewer_inv_item->getLinkedCategory(); - if (linked_category != NULL) - { - copy_folder_to_outbox(linked_category, dest_folder, top_level_folder, operation_id); - } - else - { - LLViewerInventoryItem * linked_item = viewer_inv_item->getLinkedItem(); - if (linked_item != NULL) - { - inv_item = (LLInventoryItem *) linked_item; - } - - // Check for copy permissions - if (inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) - { - // when moving item directly into outbox create folder with that name - if (dest_folder == gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false)) - { - dest_folder = create_folder_for_item(inv_item, dest_folder); - } - - copy_inventory_item(gAgent.getID(), - inv_item->getPermissions().getOwner(), - inv_item->getUUID(), - dest_folder, - inv_item->getName(), - LLPointer<LLInventoryCallback>(NULL)); - - open_outbox(); - } - else - { - LLSD payload; - payload["item_id"] = inv_item->getUUID(); - payload["dest_folder_id"] = dest_folder; - payload["top_level_folder"] = top_level_folder; - payload["operation_id"] = operation_id; - - LLMarketplaceInventoryNotifications::addNoCopyNotification(payload, move_to_outbox_cb_action); - } - } -} - -void move_item_within_outbox(LLInventoryItem* inv_item, LLUUID dest_folder, S32 operation_id) -{ - // when moving item directly into outbox create folder with that name - if (dest_folder == gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false)) - { - dest_folder = create_folder_for_item(inv_item, dest_folder); - } - - LLViewerInventoryItem * viewer_inv_item = (LLViewerInventoryItem *) inv_item; - - gInventory.changeItemParent( - viewer_inv_item, - dest_folder, - false); -} - -void copy_folder_to_outbox(LLInventoryCategory* inv_cat, const LLUUID& dest_folder, const LLUUID& top_level_folder, S32 operation_id) -{ - LLUUID new_folder_id = gInventory.createNewCategory(dest_folder, LLFolderType::FT_NONE, inv_cat->getName()); - gInventory.notifyObservers(); - - LLInventoryModel::cat_array_t* cat_array; - LLInventoryModel::item_array_t* item_array; - gInventory.getDirectDescendentsOf(inv_cat->getUUID(),cat_array,item_array); - - // copy the vector because otherwise the iterator won't be happy if we delete from it - LLInventoryModel::item_array_t item_array_copy = *item_array; - - for (LLInventoryModel::item_array_t::iterator iter = item_array_copy.begin(); iter != item_array_copy.end(); iter++) - { - LLInventoryItem* item = *iter; - copy_item_to_outbox(item, new_folder_id, top_level_folder, operation_id); - } - - LLInventoryModel::cat_array_t cat_array_copy = *cat_array; - - for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(); iter != cat_array_copy.end(); iter++) - { - LLViewerInventoryCategory* category = *iter; - copy_folder_to_outbox(category, new_folder_id, top_level_folder, operation_id); - } - - open_outbox(); -} - ///---------------------------------------------------------------------------- // Marketplace functions // @@ -975,7 +819,7 @@ S32 depth_nesting_in_marketplace(LLUUID cur_uuid) { return -1; } - // If not a descendent of the marketplace listings root, then the nesting depth is -1 by definition + // If not a descendant of the marketplace listings root, then the nesting depth is -1 by definition if (!gInventory.isObjectDescendentOf(cur_uuid, marketplace_listings_uuid)) { return -1; @@ -1605,7 +1449,7 @@ bool sort_alpha(const LLViewerInventoryCategory* cat1, const LLViewerInventoryCa void dump_trace(std::string& message, S32 depth, LLError::ELevel log_level) { - LL_INFOS("SLM") << "validate_marketplacelistings : error = "<< log_level << ", depth = " << depth << ", message = " << message << LL_ENDL; + LL_INFOS() << "validate_marketplacelistings : error = "<< log_level << ", depth = " << depth << ", message = " << message << LL_ENDL; } // Make all relevant business logic checks on the marketplace listings starting with the folder as argument. diff --git a/indra/newview/llinventoryfunctions.h b/indra/newview/llinventoryfunctions.h index b93bf9a163..649db4032d 100644 --- a/indra/newview/llinventoryfunctions.h +++ b/indra/newview/llinventoryfunctions.h @@ -75,10 +75,6 @@ void copy_inventory_category(LLInventoryModel* model, LLViewerInventoryCategory* // Generates a string containing the path to the item specified by item_id. void append_path(const LLUUID& id, std::string& path); -void copy_item_to_outbox(LLInventoryItem* inv_item, LLUUID dest_folder, const LLUUID& top_level_folder, S32 operation_id); -void move_item_within_outbox(LLInventoryItem* inv_item, LLUUID dest_folder, S32 operation_id); -void copy_folder_to_outbox(LLInventoryCategory* inv_cat, const LLUUID& dest_folder, const LLUUID& top_level_folder, S32 operation_id); - typedef boost::function<void(std::string& validation_message, S32 depth, LLError::ELevel log_level)> validation_callback_t; bool can_move_item_to_marketplace(const LLInventoryCategory* root_folder, LLInventoryCategory* dest_folder, LLInventoryItem* inv_item, std::string& tooltip_msg, S32 bundle_size = 1, bool from_paste = false); diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index e123a3e68a..bdbe253723 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -149,8 +149,8 @@ LLInventoryModel::LLInventoryModel() mObservers(), mHttpRequestFG(NULL), mHttpRequestBG(NULL), - mHttpOptions(NULL), - mHttpHeaders(NULL), + mHttpOptions(), + mHttpHeaders(), mHttpPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID), mHttpPriorityFG(0), mHttpPriorityBG(0), @@ -179,16 +179,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; @@ -525,59 +518,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 @@ -585,7 +525,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; @@ -617,7 +557,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. @@ -631,11 +571,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; } @@ -664,6 +601,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->postAndSuspend(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. @@ -2442,11 +2430,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); @@ -2468,7 +2456,7 @@ void LLInventoryModel::handleResponses(bool foreground) LLCore::HttpHandle LLInventoryModel::requestPost(bool foreground, const std::string & url, const LLSD & body, - LLCore::HttpHandler * handler, + const LLCore::HttpHandler::ptr_t &handler, const char * const message) { if (! mHttpRequestFG) @@ -2497,7 +2485,6 @@ LLCore::HttpHandle LLInventoryModel::requestPost(bool foreground, << ", Status: " << status.toTerseString() << " Reason: '" << status.toString() << "'" << LL_ENDL; - delete handler; } return handle; } @@ -4063,9 +4050,6 @@ void LLInventoryModel::FetchItemHttpHandler::onCompleted(LLCore::HttpHandle hand processData(body_llsd, response); } while (false); - - // Must delete on completion. - delete this; } void LLInventoryModel::FetchItemHttpHandler::processData(LLSD & content, LLCore::HttpResponse * response) diff --git a/indra/newview/llinventorymodel.h b/indra/newview/llinventorymodel.h index ac336e347c..e1e6db19eb 100644 --- a/indra/newview/llinventorymodel.h +++ b/indra/newview/llinventorymodel.h @@ -35,7 +35,6 @@ #include "llassettype.h" #include "llfoldertype.h" #include "llframetimer.h" -#include "llcurl.h" #include "lluuid.h" #include "llpermissionsflags.h" #include "llviewerinventory.h" @@ -46,6 +45,8 @@ #include "httpoptions.h" #include "httpheaders.h" #include "httphandler.h" +#include "lleventcoro.h" +#include "llcoros.h" class LLInventoryObserver; class LLInventoryObject; @@ -79,6 +80,9 @@ public: typedef std::vector<LLPointer<LLViewerInventoryItem> > item_array_t; typedef std::set<LLUUID> changed_items_t; + // Rider: This is using the old responder patter. It should be refactored to + // take advantage of coroutines. + // HTTP handler for individual item requests (inventory or library). // Background item requests are derived from this in the background // inventory system. All folder requests are also located there @@ -207,14 +211,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 +227,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 +344,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 +382,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 +413,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 +437,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 +445,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 ** ** @@ -560,15 +566,15 @@ public: LLCore::HttpHandle requestPost(bool foreground, const std::string & url, const LLSD & body, - LLCore::HttpHandler * handler, + const LLCore::HttpHandler::ptr_t &handler, const char * const message); 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/llinventorymodelbackgroundfetch.cpp b/indra/newview/llinventorymodelbackgroundfetch.cpp index 40edb13a80..4a77edc565 100644 --- a/indra/newview/llinventorymodelbackgroundfetch.cpp +++ b/indra/newview/llinventorymodelbackgroundfetch.cpp @@ -513,7 +513,7 @@ void LLInventoryModelBackgroundFetch::bulkFetch() if (! url.empty()) { - BGFolderHttpHandler * handler(new BGFolderHttpHandler(folder_request_body, recursive_cats)); + LLCore::HttpHandler::ptr_t handler(new BGFolderHttpHandler(folder_request_body, recursive_cats)); gInventory.requestPost(false, url, folder_request_body, handler, "Inventory Folder"); } } @@ -524,7 +524,7 @@ void LLInventoryModelBackgroundFetch::bulkFetch() if (! url.empty()) { - BGFolderHttpHandler * handler(new BGFolderHttpHandler(folder_request_body_lib, recursive_cats)); + LLCore::HttpHandler::ptr_t handler(new BGFolderHttpHandler(folder_request_body_lib, recursive_cats)); gInventory.requestPost(false, url, folder_request_body_lib, handler, "Library Folder"); } } @@ -540,7 +540,7 @@ void LLInventoryModelBackgroundFetch::bulkFetch() { LLSD body; body["items"] = item_request_body; - BGItemHttpHandler * handler(new BGItemHttpHandler(body)); + LLCore::HttpHandler::ptr_t handler(new BGItemHttpHandler(body)); gInventory.requestPost(false, url, body, handler, "Inventory Item"); } } @@ -553,7 +553,7 @@ void LLInventoryModelBackgroundFetch::bulkFetch() { LLSD body; body["items"] = item_request_body_lib; - BGItemHttpHandler * handler(new BGItemHttpHandler(body)); + LLCore::HttpHandler::ptr_t handler(new BGItemHttpHandler(body)); gInventory.requestPost(false, url, body, handler, "Library Item"); } } @@ -647,9 +647,6 @@ void BGFolderHttpHandler::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRes processData(body_llsd, response); } while (false); - - // Must delete on completion. - delete this; } diff --git a/indra/newview/llinventoryobserver.cpp b/indra/newview/llinventoryobserver.cpp index d81401b59b..6c81378622 100644 --- a/indra/newview/llinventoryobserver.cpp +++ b/indra/newview/llinventoryobserver.cpp @@ -237,7 +237,7 @@ void fetch_items_from_llsd(const LLSD& items_llsd) if (!url.empty()) { body[i]["agent_id"] = gAgent.getID(); - LLInventoryModel::FetchItemHttpHandler * handler(new LLInventoryModel::FetchItemHttpHandler(body[i])); + LLCore::HttpHandler::ptr_t handler(new LLInventoryModel::FetchItemHttpHandler(body[i])); gInventory.requestPost(true, url, body[i], handler, (i ? "Library Item" : "Inventory Item")); continue; } diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp index 5194cba891..02d378bc51 100644 --- a/indra/newview/llinventorypanel.cpp +++ b/indra/newview/llinventorypanel.cpp @@ -1437,16 +1437,20 @@ BOOL LLInventoryPanel::handleKeyHere( KEY key, MASK mask ) // Open selected items if enter key hit on the inventory panel if (mask == MASK_NONE) { - //Don't allow attaching or opening items from Merchant Outbox - LLFolderViewItem* folder_item = mFolderRoot.get()->getCurSelectedItem(); - if(folder_item) - { - LLInvFVBridge* bridge = (LLInvFVBridge*)folder_item->getViewModelItem(); - if(bridge && bridge->isOutboxFolder() && (bridge->getInventoryType() != LLInventoryType::IT_CATEGORY)) - { - return handled; - } - } + +// @TODO$: Rider: This code is dead with Outbox, however should something similar be +// done for VMM? +// +// //Don't allow attaching or opening items from Merchant Outbox +// LLFolderViewItem* folder_item = mFolderRoot.get()->getCurSelectedItem(); +// if(folder_item) +// { +// LLInvFVBridge* bridge = (LLInvFVBridge*)folder_item->getViewModelItem(); +// if(bridge && bridge->is() && (bridge->getInventoryType() != LLInventoryType::IT_CATEGORY)) +// { +// return handled; +// } +// } LLInventoryAction::doToSelected(mInventory, mFolderRoot.get(), "open"); handled = TRUE; diff --git a/indra/newview/llmaniprotate.cpp b/indra/newview/llmaniprotate.cpp index ed40483029..3a0f96cd37 100644 --- a/indra/newview/llmaniprotate.cpp +++ b/indra/newview/llmaniprotate.cpp @@ -67,9 +67,6 @@ const F32 RADIUS_PIXELS = 100.f; // size in screen space const F32 SQ_RADIUS = RADIUS_PIXELS * RADIUS_PIXELS; const F32 WIDTH_PIXELS = 8; const S32 CIRCLE_STEPS = 100; -const F32 DELTA = F_TWO_PI / CIRCLE_STEPS; -const F32 SIN_DELTA = sin( DELTA ); -const F32 COS_DELTA = cos( DELTA ); const F32 MAX_MANIP_SELECT_DISTANCE = 100.f; const F32 SNAP_ANGLE_INCREMENT = 5.625f; const F32 SNAP_ANGLE_DETENTE = SNAP_ANGLE_INCREMENT; diff --git a/indra/newview/llmarketplacefunctions.cpp b/indra/newview/llmarketplacefunctions.cpp index d5bfe1df4a..dfa33b37ef 100644 --- a/indra/newview/llmarketplacefunctions.cpp +++ b/indra/newview/llmarketplacefunctions.cpp @@ -30,7 +30,6 @@ #include "llagent.h" #include "llbufferstream.h" -#include "llhttpclient.h" #include "llinventoryfunctions.h" #include "llinventoryobserver.h" #include "llnotificationsutil.h" @@ -42,609 +41,133 @@ #include "llviewermedia.h" #include "llviewernetwork.h" #include "llviewerregion.h" - #include "reader.h" // JSON #include "writer.h" // JSON +#include "lleventcoro.h" +#include "llcoros.h" +#include "llcorehttputil.h" +#include "llsdutil.h" // // Helpers // -static std::string getMarketplaceDomain() -{ - std::string domain = "secondlife.com"; - - if (!LLGridManager::getInstance()->isInProductionGrid()) - { - const std::string& grid_id = LLGridManager::getInstance()->getGridId(); - const std::string& grid_id_lower = utf8str_tolower(grid_id); - - if (grid_id_lower == "damballah") - { - domain = "secondlife-staging.com"; - } - else - { - domain = llformat("%s.lindenlab.com", grid_id_lower.c_str()); - } - } - - return domain; -} - -static std::string getMarketplaceURL(const std::string& urlStringName) -{ - LLStringUtil::format_map_t domain_arg; - domain_arg["[MARKETPLACE_DOMAIN_NAME]"] = getMarketplaceDomain(); - - std::string marketplace_url = LLTrans::getString(urlStringName, domain_arg); - - return marketplace_url; -} - -LLSD getMarketplaceStringSubstitutions() -{ - std::string marketplace_url = getMarketplaceURL("MarketplaceURL"); - std::string marketplace_url_create = getMarketplaceURL("MarketplaceURL_CreateStore"); - std::string marketplace_url_dashboard = getMarketplaceURL("MarketplaceURL_Dashboard"); - std::string marketplace_url_imports = getMarketplaceURL("MarketplaceURL_Imports"); - std::string marketplace_url_info = getMarketplaceURL("MarketplaceURL_LearnMore"); - - LLSD marketplace_sub_map; - - marketplace_sub_map["[MARKETPLACE_URL]"] = marketplace_url; - marketplace_sub_map["[MARKETPLACE_CREATE_STORE_URL]"] = marketplace_url_create; - marketplace_sub_map["[MARKETPLACE_LEARN_MORE_URL]"] = marketplace_url_info; - marketplace_sub_map["[MARKETPLACE_DASHBOARD_URL]"] = marketplace_url_dashboard; - marketplace_sub_map["[MARKETPLACE_IMPORTS_URL]"] = marketplace_url_imports; - - return marketplace_sub_map; -} - -// Get the version folder: if there is only one subfolder, we will use it as a version folder -LLUUID getVersionFolderIfUnique(const LLUUID& folder_id) -{ - LLUUID version_id = LLUUID::null; - LLInventoryModel::cat_array_t* categories; - LLInventoryModel::item_array_t* items; - gInventory.getDirectDescendentsOf(folder_id, categories, items); - if (categories->size() == 1) - { - version_id = categories->begin()->get()->getUUID(); - } - else - { - LLNotificationsUtil::add("AlertMerchantListingActivateRequired"); - } - return version_id; -} +namespace { -/////////////////////////////////////////////////////////////////////////////// -// SLM Responders -void log_SLM_warning(const std::string& request, U32 status, const std::string& reason, const std::string& code, const std::string& description) -{ - LL_WARNS("SLM") << "SLM API : Responder to " << request << ". status : " << status << ", reason : " << reason << ", code : " << code << ", description : " << description << LL_ENDL; - if ((status == 422) && (description == "[\"You must have an English description to list the product\", \"You must choose a category for your product before it can be listed\", \"Listing could not change state.\", \"Price can't be blank\"]")) - { - // Unprocessable Entity : Special case that error as it is a frequent answer when trying to list an incomplete listing - LLNotificationsUtil::add("MerchantUnprocessableEntity"); - } - else - { - // Prompt the user with the warning (so they know why things are failing) - LLSD subs; - subs["[ERROR_REASON]"] = reason; - // We do show long descriptions in the alert (unlikely to be readable). The description string will be in the log though. - subs["[ERROR_DESCRIPTION]"] = (description.length() <= 512 ? description : ""); - LLNotificationsUtil::add("MerchantTransactionFailed", subs); - } -} -void log_SLM_infos(const std::string& request, U32 status, const std::string& body) -{ - if (gSavedSettings.getBOOL("MarketplaceListingsLogging")) + static std::string getMarketplaceDomain() { - LL_INFOS("SLM") << "SLM API : Responder to " << request << ". status : " << status << ", body or description : " << body << LL_ENDL; - } -} -void log_SLM_infos(const std::string& request, const std::string& url, const std::string& body) -{ - if (gSavedSettings.getBOOL("MarketplaceListingsLogging")) - { - LL_INFOS("SLM") << "SLM API : Sending " << request << ". url : " << url << ", body : " << body << LL_ENDL; - } -} + std::string domain = "secondlife.com"; -class LLSLMGetMerchantResponder : public LLHTTPClient::Responder -{ - LOG_CLASS(LLSLMGetMerchantResponder); -public: - - LLSLMGetMerchantResponder() {} - -protected: - virtual void httpFailure() - { - if (HTTP_NOT_FOUND == getStatus()) - { - log_SLM_infos("Get /merchant", getStatus(), "User is not a merchant"); - LLMarketplaceData::instance().setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_NOT_MERCHANT); - } - else if (HTTP_SERVICE_UNAVAILABLE == getStatus()) + if (!LLGridManager::getInstance()->isInProductionGrid()) { - log_SLM_infos("Get /merchant", getStatus(), "Merchant is not migrated"); - LLMarketplaceData::instance().setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_NOT_MIGRATED_MERCHANT); - } - else - { - log_SLM_warning("Get /merchant", getStatus(), getReason(), getContent().get("error_code"), getContent().get("error_description")); - LLMarketplaceData::instance().setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_CONNECTION_FAILURE); - } - } - - virtual void httpSuccess() - { - log_SLM_infos("Get /merchant", getStatus(), "User is a merchant"); - LLMarketplaceData::instance().setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_MERCHANT); - } - -}; + const std::string& grid_id = LLGridManager::getInstance()->getGridId(); + const std::string& grid_id_lower = utf8str_tolower(grid_id); -class LLSLMGetListingsResponder : public LLHTTPClient::Responder -{ - LOG_CLASS(LLSLMGetListingsResponder); -public: - - LLSLMGetListingsResponder(const LLUUID& folder_id) - { - mExpectedFolderId = folder_id; - } - - virtual void completedRaw(const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer) - { - LLMarketplaceData::instance().setUpdating(mExpectedFolderId,false); - - LLBufferStream istr(channels, buffer.get()); - std::stringstream strstrm; - strstrm << istr.rdbuf(); - const std::string body = strstrm.str(); - - if (!isGoodStatus()) - { - log_SLM_warning("Get /listings", getStatus(), getReason(), "", body); - LLMarketplaceData::instance().setSLMDataFetched(MarketplaceFetchCodes::MARKET_FETCH_FAILED); - update_marketplace_category(mExpectedFolderId, false); - gInventory.notifyObservers(); - return; - } - - Json::Value root; - Json::Reader reader; - if (!reader.parse(body,root)) - { - log_SLM_warning("Get /listings", getStatus(), "Json parsing failed", reader.getFormatedErrorMessages(), body); - LLMarketplaceData::instance().setSLMDataFetched(MarketplaceFetchCodes::MARKET_FETCH_FAILED); - update_marketplace_category(mExpectedFolderId, false); - gInventory.notifyObservers(); - return; - } - - log_SLM_infos("Get /listings", getStatus(), body); - - // Extract the info from the Json string - Json::ValueIterator it = root["listings"].begin(); - - while (it != root["listings"].end()) - { - Json::Value listing = *it; - - int listing_id = listing["id"].asInt(); - bool is_listed = listing["is_listed"].asBool(); - std::string edit_url = listing["edit_url"].asString(); - std::string folder_uuid_string = listing["inventory_info"]["listing_folder_id"].asString(); - std::string version_uuid_string = listing["inventory_info"]["version_folder_id"].asString(); - int count = listing["inventory_info"]["count_on_hand"].asInt(); - - LLUUID folder_id(folder_uuid_string); - LLUUID version_id(version_uuid_string); - if (folder_id.notNull()) + if (grid_id_lower == "damballah") + { + domain = "secondlife-staging.com"; + } + else { - LLMarketplaceData::instance().addListing(folder_id,listing_id,version_id,is_listed,edit_url,count); + domain = llformat("%s.lindenlab.com", grid_id_lower.c_str()); } - it++; } - - // Update all folders under the root - LLMarketplaceData::instance().setSLMDataFetched(MarketplaceFetchCodes::MARKET_FETCH_DONE); - update_marketplace_category(mExpectedFolderId, false); - gInventory.notifyObservers(); - } -private: - LLUUID mExpectedFolderId; -}; -class LLSLMCreateListingsResponder : public LLHTTPClient::Responder -{ - LOG_CLASS(LLSLMCreateListingsResponder); -public: - - LLSLMCreateListingsResponder(const LLUUID& folder_id) - { - mExpectedFolderId = folder_id; + return domain; } - - virtual void completedRaw(const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer) + + static std::string getMarketplaceURL(const std::string& urlStringName) { - LLMarketplaceData::instance().setUpdating(mExpectedFolderId,false); - - LLBufferStream istr(channels, buffer.get()); - std::stringstream strstrm; - strstrm << istr.rdbuf(); - const std::string body = strstrm.str(); - - if (!isGoodStatus()) - { - log_SLM_warning("Post /listings", getStatus(), getReason(), "", body); - update_marketplace_category(mExpectedFolderId, false); - gInventory.notifyObservers(); - return; - } - - Json::Value root; - Json::Reader reader; - if (!reader.parse(body,root)) - { - log_SLM_warning("Post /listings", getStatus(), "Json parsing failed", reader.getFormatedErrorMessages(), body); - update_marketplace_category(mExpectedFolderId, false); - gInventory.notifyObservers(); - return; - } + LLStringUtil::format_map_t domain_arg; + domain_arg["[MARKETPLACE_DOMAIN_NAME]"] = getMarketplaceDomain(); - log_SLM_infos("Post /listings", getStatus(), body); + std::string marketplace_url = LLTrans::getString(urlStringName, domain_arg); - // Extract the info from the Json string - Json::ValueIterator it = root["listings"].begin(); - - while (it != root["listings"].end()) - { - Json::Value listing = *it; - - int listing_id = listing["id"].asInt(); - bool is_listed = listing["is_listed"].asBool(); - std::string edit_url = listing["edit_url"].asString(); - std::string folder_uuid_string = listing["inventory_info"]["listing_folder_id"].asString(); - std::string version_uuid_string = listing["inventory_info"]["version_folder_id"].asString(); - int count = listing["inventory_info"]["count_on_hand"].asInt(); - - LLUUID folder_id(folder_uuid_string); - LLUUID version_id(version_uuid_string); - LLMarketplaceData::instance().addListing(folder_id,listing_id,version_id,is_listed,edit_url,count); - update_marketplace_category(folder_id, false); - gInventory.notifyObservers(); - it++; - } + return marketplace_url; } -private: - LLUUID mExpectedFolderId; -}; -class LLSLMGetListingResponder : public LLHTTPClient::Responder -{ - LOG_CLASS(LLSLMGetListingResponder); -public: - - LLSLMGetListingResponder(const LLUUID& folder_id) + // Get the version folder: if there is only one subfolder, we will use it as a version folder + LLUUID getVersionFolderIfUnique(const LLUUID& folder_id) { - mExpectedFolderId = folder_id; - } - - virtual void completedRaw(const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer) - { - LLMarketplaceData::instance().setUpdating(mExpectedFolderId,false); - - LLBufferStream istr(channels, buffer.get()); - std::stringstream strstrm; - strstrm << istr.rdbuf(); - const std::string body = strstrm.str(); - - if (!isGoodStatus()) - { - if (getStatus() == 404) - { - // That listing does not exist -> delete its record from the local SLM data store - LLMarketplaceData::instance().deleteListing(mExpectedFolderId, false); - } - else - { - log_SLM_warning("Get /listing", getStatus(), getReason(), "", body); - } - update_marketplace_category(mExpectedFolderId, false); - gInventory.notifyObservers(); - return; - } - - Json::Value root; - Json::Reader reader; - if (!reader.parse(body,root)) + LLUUID version_id = LLUUID::null; + LLInventoryModel::cat_array_t* categories; + LLInventoryModel::item_array_t* items; + gInventory.getDirectDescendentsOf(folder_id, categories, items); + if (categories->size() == 1) { - log_SLM_warning("Get /listing", getStatus(), "Json parsing failed", reader.getFormatedErrorMessages(), body); - update_marketplace_category(mExpectedFolderId, false); - gInventory.notifyObservers(); - return; + version_id = categories->begin()->get()->getUUID(); } - - log_SLM_infos("Get /listing", getStatus(), body); - - // Extract the info from the Json string - Json::ValueIterator it = root["listings"].begin(); - - while (it != root["listings"].end()) + else { - Json::Value listing = *it; - - int listing_id = listing["id"].asInt(); - bool is_listed = listing["is_listed"].asBool(); - std::string edit_url = listing["edit_url"].asString(); - std::string folder_uuid_string = listing["inventory_info"]["listing_folder_id"].asString(); - std::string version_uuid_string = listing["inventory_info"]["version_folder_id"].asString(); - int count = listing["inventory_info"]["count_on_hand"].asInt(); - - LLUUID folder_id(folder_uuid_string); - LLUUID version_id(version_uuid_string); - - // Update that listing - LLMarketplaceData::instance().setListingID(folder_id, listing_id, false); - LLMarketplaceData::instance().setVersionFolderID(folder_id, version_id, false); - LLMarketplaceData::instance().setActivationState(folder_id, is_listed, false); - LLMarketplaceData::instance().setListingURL(folder_id, edit_url, false); - LLMarketplaceData::instance().setCountOnHand(folder_id,count,false); - update_marketplace_category(folder_id, false); - gInventory.notifyObservers(); - - it++; + LLNotificationsUtil::add("AlertMerchantListingActivateRequired"); } + return version_id; } -private: - LLUUID mExpectedFolderId; -}; -class LLSLMUpdateListingsResponder : public LLHTTPClient::Responder -{ - LOG_CLASS(LLSLMUpdateListingsResponder); -public: - - LLSLMUpdateListingsResponder(const LLUUID& folder_id, bool expected_listed_state, const LLUUID& expected_version_id) - { - mExpectedFolderId = folder_id; - mExpectedListedState = expected_listed_state; - mExpectedVersionUUID = expected_version_id; - } - - virtual void completedRaw(const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer) + /////////////////////////////////////////////////////////////////////////////// + // SLM Reporters + void log_SLM_warning(const std::string& request, U32 status, const std::string& reason, const std::string& code, const LLSD& result) { - LLMarketplaceData::instance().setUpdating(mExpectedFolderId,false); - LLBufferStream istr(channels, buffer.get()); - std::stringstream strstrm; - strstrm << istr.rdbuf(); - const std::string body = strstrm.str(); - - if (!isGoodStatus()) - { - log_SLM_warning("Put /listing", getStatus(), getReason(), "", body); - update_marketplace_category(mExpectedFolderId, false); - gInventory.notifyObservers(); - return; - } - - Json::Value root; - Json::Reader reader; - if (!reader.parse(body,root)) + LL_WARNS("SLM") << "SLM API : Responder to " << request << ". status : " << status << ", reason : " << reason << ", code : " << code << ", description : " << ll_pretty_print_sd(result) << LL_ENDL; + if ((status == 422) && (result.has(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_CONTENT) && + result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_CONTENT].isArray() && + result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_CONTENT].size() > 4)) { - log_SLM_warning("Put /listing", getStatus(), "Json parsing failed", reader.getFormatedErrorMessages(), body); - update_marketplace_category(mExpectedFolderId, false); - gInventory.notifyObservers(); - return; + // Unprocessable Entity : Special case that error as it is a frequent answer when trying to list an incomplete listing + LLNotificationsUtil::add("MerchantUnprocessableEntity"); } - - log_SLM_infos("Put /listing", getStatus(), body); - - // Extract the info from the Json string - Json::ValueIterator it = root["listings"].begin(); - - while (it != root["listings"].end()) + else { - Json::Value listing = *it; - - int listing_id = listing["id"].asInt(); - bool is_listed = listing["is_listed"].asBool(); - std::string edit_url = listing["edit_url"].asString(); - std::string folder_uuid_string = listing["inventory_info"]["listing_folder_id"].asString(); - std::string version_uuid_string = listing["inventory_info"]["version_folder_id"].asString(); - int count = listing["inventory_info"]["count_on_hand"].asInt(); - - LLUUID folder_id(folder_uuid_string); - LLUUID version_id(version_uuid_string); - - // Update that listing - LLMarketplaceData::instance().setListingID(folder_id, listing_id, false); - LLMarketplaceData::instance().setVersionFolderID(folder_id, version_id, false); - LLMarketplaceData::instance().setActivationState(folder_id, is_listed, false); - LLMarketplaceData::instance().setListingURL(folder_id, edit_url, false); - LLMarketplaceData::instance().setCountOnHand(folder_id,count,false); - update_marketplace_category(folder_id, false); - gInventory.notifyObservers(); - - // Show a notification alert if what we got is not what we expected - // (this actually doesn't result in an error status from the SLM API protocol) - if ((mExpectedListedState != is_listed) || (mExpectedVersionUUID != version_id)) + // Prompt the user with the warning (so they know why things are failing) + LLSD subs; + subs["[ERROR_REASON]"] = reason; + // We do show long descriptions in the alert (unlikely to be readable). The description string will be in the log though. + std::string description; + if (result.has(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_CONTENT)) { - LLSD subs; - subs["[URL]"] = edit_url; - LLNotificationsUtil::add("AlertMerchantListingNotUpdated", subs); + LLSD content = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_CONTENT]; + if (content.isArray()) + { + for (LLSD::array_iterator it = content.beginArray(); it != content.endArray(); ++it) + { + if (!description.empty()) + description += "\n"; + description += (*it).asString(); + } + } + else + { + description = content.asString(); + } } - - it++; + else + { + description = result.asString(); + } + subs["[ERROR_DESCRIPTION]"] = description; + LLNotificationsUtil::add("MerchantTransactionFailed", subs); } - } -private: - LLUUID mExpectedFolderId; - bool mExpectedListedState; - LLUUID mExpectedVersionUUID; -}; -class LLSLMAssociateListingsResponder : public LLHTTPClient::Responder -{ - LOG_CLASS(LLSLMAssociateListingsResponder); -public: - - LLSLMAssociateListingsResponder(const LLUUID& folder_id, const LLUUID& source_folder_id) - { - mExpectedFolderId = folder_id; - mSourceFolderId = source_folder_id; } - - virtual void completedRaw(const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer) + + void log_SLM_infos(const std::string& request, U32 status, const std::string& body) { - LLMarketplaceData::instance().setUpdating(mExpectedFolderId,false); - LLMarketplaceData::instance().setUpdating(mSourceFolderId,false); - - LLBufferStream istr(channels, buffer.get()); - std::stringstream strstrm; - strstrm << istr.rdbuf(); - const std::string body = strstrm.str(); - - if (!isGoodStatus()) - { - log_SLM_warning("Put /associate_inventory", getStatus(), getReason(), "", body); - update_marketplace_category(mExpectedFolderId, false); - update_marketplace_category(mSourceFolderId, false); - gInventory.notifyObservers(); - return; - } - - Json::Value root; - Json::Reader reader; - if (!reader.parse(body,root)) - { - log_SLM_warning("Put /associate_inventory", getStatus(), "Json parsing failed", reader.getFormatedErrorMessages(), body); - update_marketplace_category(mExpectedFolderId, false); - update_marketplace_category(mSourceFolderId, false); - gInventory.notifyObservers(); - return; - } - - log_SLM_infos("Put /associate_inventory", getStatus(), body); - - // Extract the info from the Json string - Json::ValueIterator it = root["listings"].begin(); - - while (it != root["listings"].end()) + if (gSavedSettings.getBOOL("MarketplaceListingsLogging")) { - Json::Value listing = *it; - - int listing_id = listing["id"].asInt(); - bool is_listed = listing["is_listed"].asBool(); - std::string edit_url = listing["edit_url"].asString(); - std::string folder_uuid_string = listing["inventory_info"]["listing_folder_id"].asString(); - std::string version_uuid_string = listing["inventory_info"]["version_folder_id"].asString(); - int count = listing["inventory_info"]["count_on_hand"].asInt(); - - LLUUID folder_id(folder_uuid_string); - LLUUID version_id(version_uuid_string); - - // Check that the listing ID is not already associated to some other record - LLUUID old_listing = LLMarketplaceData::instance().getListingFolder(listing_id); - if (old_listing.notNull()) - { - // If it is already used, unlist the old record (we can't have 2 listings with the same listing ID) - LLMarketplaceData::instance().deleteListing(old_listing); - } - - // Add the new association - LLMarketplaceData::instance().addListing(folder_id,listing_id,version_id,is_listed,edit_url,count); - update_marketplace_category(folder_id, false); - gInventory.notifyObservers(); - - // The stock count needs to be updated with the new local count now - LLMarketplaceData::instance().updateCountOnHand(folder_id,1); - - it++; + LL_INFOS("SLM") << "SLM API : Responder to " << request << ". status : " << status << ", body or description : " << body << LL_ENDL; } - - // Always update the source folder so its widget updates - update_marketplace_category(mSourceFolderId, false); } -private: - LLUUID mExpectedFolderId; // This is the folder now associated with the id. - LLUUID mSourceFolderId; // This is the folder initially associated with the id. Can be LLUUI::null -}; -class LLSLMDeleteListingsResponder : public LLHTTPClient::Responder -{ - LOG_CLASS(LLSLMDeleteListingsResponder); -public: - - LLSLMDeleteListingsResponder(const LLUUID& folder_id) + void log_SLM_infos(const std::string& request, U32 status, const LLSD& body) { - mExpectedFolderId = folder_id; + log_SLM_infos(request, status, std::string(ll_pretty_print_sd(body))); } - - virtual void completedRaw(const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer) - { - LLMarketplaceData::instance().setUpdating(mExpectedFolderId,false); - - LLBufferStream istr(channels, buffer.get()); - std::stringstream strstrm; - strstrm << istr.rdbuf(); - const std::string body = strstrm.str(); - - if (!isGoodStatus()) - { - log_SLM_warning("Delete /listing", getStatus(), getReason(), "", body); - update_marketplace_category(mExpectedFolderId, false); - gInventory.notifyObservers(); - return; - } - - Json::Value root; - Json::Reader reader; - if (!reader.parse(body,root)) - { - log_SLM_warning("Delete /listing", getStatus(), "Json parsing failed", reader.getFormatedErrorMessages(), body); - update_marketplace_category(mExpectedFolderId, false); - gInventory.notifyObservers(); - return; - } - - log_SLM_infos("Delete /listing", getStatus(), body); - - // Extract the info from the Json string - Json::ValueIterator it = root["listings"].begin(); - - while (it != root["listings"].end()) - { - Json::Value listing = *it; - - int listing_id = listing["id"].asInt(); - LLUUID folder_id = LLMarketplaceData::instance().getListingFolder(listing_id); - LLMarketplaceData::instance().deleteListing(folder_id); - - it++; - } - } -private: - LLUUID mExpectedFolderId; -}; -// SLM Responders End -/////////////////////////////////////////////////////////////////////////////// +} + +#if 1 namespace LLMarketplaceImport { // Basic interface for this namespace @@ -669,105 +192,133 @@ namespace LLMarketplaceImport static S32 sImportResultStatus = 0; static LLSD sImportResults = LLSD::emptyMap(); - static LLTimer slmGetTimer; - static LLTimer slmPostTimer; - // Responders - - class LLImportPostResponder : public LLHTTPClient::Responder - { - LOG_CLASS(LLImportPostResponder); - public: - LLImportPostResponder() : LLCurl::Responder() {} - protected: - /* virtual */ void httpCompleted() - { - slmPostTimer.stop(); - - if (gSavedSettings.getBOOL("InventoryOutboxLogging")) - { - LL_INFOS() << " SLM [timer:" << slmPostTimer.getElapsedTimeF32() << "] " - << dumpResponse() << LL_ENDL; - } - - S32 status = getStatus(); - if ((status == MarketplaceErrorCodes::IMPORT_REDIRECT) || - (status == MarketplaceErrorCodes::IMPORT_AUTHENTICATION_ERROR) || - // MAINT-2301 : we determined we can safely ignore that error in that context - (status == MarketplaceErrorCodes::IMPORT_JOB_TIMEOUT)) - { - if (gSavedSettings.getBOOL("InventoryOutboxLogging")) - { - LL_INFOS() << " SLM POST : Ignoring time out status and treating it as success" << LL_ENDL; - } - status = MarketplaceErrorCodes::IMPORT_DONE; - } - - if (status >= 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 = (status == MarketplaceErrorCodes::IMPORT_DONE); - sImportPostPending = false; - sImportResultStatus = status; - sImportId = getContent(); - } - }; - - class LLImportGetResponder : public LLHTTPClient::Responder - { - LOG_CLASS(LLImportGetResponder); - public: - LLImportGetResponder() : LLCurl::Responder() {} - - protected: - /* virtual */ void httpCompleted() - { - const std::string& set_cookie_string = getResponseHeader(HTTP_IN_HEADER_SET_COOKIE); - - if (!set_cookie_string.empty()) - { - sMarketplaceCookie = set_cookie_string; - } - - slmGetTimer.stop(); - - if (gSavedSettings.getBOOL("InventoryOutboxLogging")) - { - LL_INFOS() << " SLM [timer:" << slmGetTimer.getElapsedTimeF32() << "] " - << dumpResponse() << LL_ENDL; - } - - // 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 - S32 status = getStatus(); - if ((status >= MarketplaceErrorCodes::IMPORT_BAD_REQUEST) && - (status != MarketplaceErrorCodes::IMPORT_DONE_WITH_ERRORS) && - (status != 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") && (status >= MarketplaceErrorCodes::IMPORT_BAD_REQUEST)) + 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->postAndSuspend(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 GET : Got error status = " << status << ", but marketplace cookie not cleared." << LL_ENDL; + LL_INFOS() << " SLM POST : Ignoring time out status and treating it as success" << LL_ENDL; } + httpCode = MarketplaceErrorCodes::IMPORT_DONE; + } - sImportInProgress = (status == MarketplaceErrorCodes::IMPORT_PROCESSING); - sImportGetPending = false; - sImportResultStatus = status; - sImportResults = getContent(); - } - }; + 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; + + } + + 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->getAndSuspend(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; + + + } // Basic API @@ -818,20 +369,10 @@ namespace LLMarketplaceImport sImportGetPending = true; std::string url = getInventoryImportURL(); - - if (gSavedSettings.getBOOL("InventoryOutboxLogging")) - { - LL_INFOS() << " SLM GET: establishMarketplaceSessionCookie, LLHTTPClient::get, url = " << url << LL_ENDL; - LLSD headers = LLViewerMedia::getHeaders(); - std::stringstream str; - LLSDSerialize::toPrettyXML(headers, str); - LL_INFOS() << " SLM GET: headers " << LL_ENDL; - LL_INFOS() << str.str() << LL_ENDL; - } - slmGetTimer.start(); - LLHTTPClient::get(url, new LLImportGetResponder(), LLViewerMedia::getHeaders()); - + LLCoros::instance().launch("marketplaceGetCoro", + boost::bind(&marketplaceGetCoro, url, false)); + return true; } @@ -848,26 +389,9 @@ namespace LLMarketplaceImport url += sImportId.asString(); - // Make the headers for the post - LLSD headers = LLSD::emptyMap(); - headers[HTTP_OUT_HEADER_ACCEPT] = "*/*"; - headers[HTTP_OUT_HEADER_COOKIE] = sMarketplaceCookie; - // *TODO: Why are we setting Content-Type for a GET request? - headers[HTTP_OUT_HEADER_CONTENT_TYPE] = HTTP_CONTENT_LLSD_XML; - headers[HTTP_OUT_HEADER_USER_AGENT] = LLViewerMedia::getCurrentUserAgent(); - - if (gSavedSettings.getBOOL("InventoryOutboxLogging")) - { - LL_INFOS() << " SLM GET: pollStatus, LLHTTPClient::get, url = " << url << LL_ENDL; - std::stringstream str; - LLSDSerialize::toPrettyXML(headers, str); - LL_INFOS() << " SLM GET: headers " << LL_ENDL; - LL_INFOS() << str.str() << LL_ENDL; - } - - slmGetTimer.start(); - LLHTTPClient::get(url, new LLImportGetResponder(), headers); - + LLCoros::instance().launch("marketplaceGetCoro", + boost::bind(&marketplaceGetCoro, url, true)); + return true; } @@ -886,35 +410,17 @@ namespace LLMarketplaceImport std::string url = getInventoryImportURL(); - // Make the headers for the post - LLSD headers = LLSD::emptyMap(); - headers[HTTP_OUT_HEADER_ACCEPT] = "*/*"; - headers[HTTP_OUT_HEADER_CONNECTION] = "Keep-Alive"; - headers[HTTP_OUT_HEADER_COOKIE] = sMarketplaceCookie; - headers[HTTP_OUT_HEADER_CONTENT_TYPE] = HTTP_CONTENT_XML; - headers[HTTP_OUT_HEADER_USER_AGENT] = LLViewerMedia::getCurrentUserAgent(); - - if (gSavedSettings.getBOOL("InventoryOutboxLogging")) - { - LL_INFOS() << " SLM POST: triggerImport, LLHTTPClient::post, url = " << url << LL_ENDL; - std::stringstream str; - LLSDSerialize::toPrettyXML(headers, str); - LL_INFOS() << " SLM POST: headers " << LL_ENDL; - LL_INFOS() << str.str() << LL_ENDL; - } + LLCoros::instance().launch("marketplacePostCoro", + boost::bind(&marketplacePostCoro, url)); - slmPostTimer.start(); - LLHTTPClient::post(url, LLSD(), new LLImportPostResponder(), headers); - return true; } } - +#endif // // Interface class // - static const F32 MARKET_IMPORTER_UPDATE_FREQUENCY = 1.0f; //static @@ -1212,6 +718,26 @@ LLMarketplaceData::~LLMarketplaceData() gInventory.removeObserver(mInventoryObserver); } + +LLSD LLMarketplaceData::getMarketplaceStringSubstitutions() +{ + std::string marketplace_url = getMarketplaceURL("MarketplaceURL"); + std::string marketplace_url_create = getMarketplaceURL("MarketplaceURL_CreateStore"); + std::string marketplace_url_dashboard = getMarketplaceURL("MarketplaceURL_Dashboard"); + std::string marketplace_url_imports = getMarketplaceURL("MarketplaceURL_Imports"); + std::string marketplace_url_info = getMarketplaceURL("MarketplaceURL_LearnMore"); + + LLSD marketplace_sub_map; + + marketplace_sub_map["[MARKETPLACE_URL]"] = marketplace_url; + marketplace_sub_map["[MARKETPLACE_CREATE_STORE_URL]"] = marketplace_url_create; + marketplace_sub_map["[MARKETPLACE_LEARN_MORE_URL]"] = marketplace_url_info; + marketplace_sub_map["[MARKETPLACE_DASHBOARD_URL]"] = marketplace_url_dashboard; + marketplace_sub_map["[MARKETPLACE_IMPORTS_URL]"] = marketplace_url_imports; + + return marketplace_sub_map; +} + void LLMarketplaceData::initializeSLM(const status_updated_signal_t::slot_type& cb) { if (mStatusUpdatedSignal == NULL) @@ -1227,15 +753,60 @@ void LLMarketplaceData::initializeSLM(const status_updated_signal_t::slot_type& } else { - // Initiate SLM connection and set responder - std::string url = getSLMConnectURL("/merchant"); - if (url != "") + mMarketPlaceStatus = MarketplaceStatusCodes::MARKET_PLACE_INITIALIZING; + + LLCoros::instance().launch("getMerchantStatus", + boost::bind(&LLMarketplaceData::getMerchantStatusCoro, this)); + } +} + +void LLMarketplaceData::getMerchantStatusCoro() +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("getMerchantStatusCoro", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); + + httpOpts->setFollowRedirects(true); + + std::string url = getSLMConnectURL("/merchant"); + if (url.empty()) + { + LL_INFOS("Marketplace") << "No marketplace capability on Sim" << LL_ENDL; + } + + LLSD result = httpAdapter->getAndSuspend(httpRequest, url, httpOpts); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) + { + S32 httpCode = status.getType(); + + if (httpCode == HTTP_NOT_FOUND) + { + log_SLM_infos("Get /merchant", httpCode, std::string("User is not a merchant")); + setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_NOT_MERCHANT); + } + else if (httpCode == HTTP_SERVICE_UNAVAILABLE) + { + log_SLM_infos("Get /merchant", httpCode, std::string("Merchant is not migrated")); + setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_NOT_MIGRATED_MERCHANT); + } + else { - mMarketPlaceStatus = MarketplaceStatusCodes::MARKET_PLACE_INITIALIZING; - log_SLM_infos("LLHTTPClient::get", url, ""); - LLHTTPClient::get(url, new LLSLMGetMerchantResponder(), LLSD()); + std::string err_code = result["error_code"].asString(); + //std::string err_description = result["error_description"].asString(); + log_SLM_warning("Get /merchant", httpCode, status.toString(), err_code, result["error_description"]); + setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_CONNECTION_FAILURE); } + return; } + + log_SLM_infos("Get /merchant", status.getType(), std::string("User is a merchant")); + setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_MERCHANT); } void LLMarketplaceData::setDataFetchedSignal(const status_updated_signal_t::slot_type& cb) @@ -1250,151 +821,434 @@ void LLMarketplaceData::setDataFetchedSignal(const status_updated_signal_t::slot // Get/Post/Put requests to the SLM Server using the SLM API void LLMarketplaceData::getSLMListings() { - LLSD headers = LLSD::emptyMap(); - headers["Accept"] = "application/json"; - headers["Content-Type"] = "application/json"; - - // Send request + const LLUUID marketplaceFolderId = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + setUpdating(marketplaceFolderId, true); + + LLCoros::instance().launch("getSLMListings", + boost::bind(&LLMarketplaceData::getSLMListingsCoro, this, marketplaceFolderId)); +} + +void LLMarketplaceData::getSLMListingsCoro(LLUUID folderId) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("getMerchantStatusCoro", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpHeaders::ptr_t httpHeaders(new LLCore::HttpHeaders); + + httpHeaders->append("Accept", "application/json"); + httpHeaders->append("Content-Type", "application/json"); + std::string url = getSLMConnectURL("/listings"); - log_SLM_infos("LLHTTPClient::get", url, ""); - const LLUUID marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); - setUpdating(marketplacelistings_id,true); - LLHTTPClient::get(url, new LLSLMGetListingsResponder(marketplacelistings_id), headers); + + LLSD result = httpAdapter->getJsonAndSuspend(httpRequest, url, httpHeaders); + + setUpdating(folderId, false); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) + { + log_SLM_warning("Get /listings", status.getType(), status.toString(), "", result); + setSLMDataFetched(MarketplaceFetchCodes::MARKET_FETCH_FAILED); + update_marketplace_category(folderId, false); + gInventory.notifyObservers(); + return; + } + + log_SLM_infos("Get /listings", static_cast<U32>(status.getType()), result); + + // Extract the info from the results + for (LLSD::array_iterator it = result["listings"].beginArray(); + it != result["listings"].endArray(); ++it) + { + LLSD listing = *it; + + int listingId = listing["id"].asInteger(); + bool isListed = listing["is_listed"].asBoolean(); + std::string editUrl = listing["edit_url"].asString(); + LLUUID folderUuid = listing["inventory_info"]["listing_folder_id"].asUUID(); + LLUUID versionUuid = listing["inventory_info"]["version_folder_id"].asUUID(); + int count = listing["inventory_info"]["count_on_hand"].asInteger(); + + if (folderUuid.notNull()) + { + addListing(folderUuid, listingId, versionUuid, isListed, editUrl, count); + } + } + + // Update all folders under the root + setSLMDataFetched(MarketplaceFetchCodes::MARKET_FETCH_DONE); + update_marketplace_category(folderId, false); + gInventory.notifyObservers(); } -void LLMarketplaceData::getSLMListing(S32 listing_id) +void LLMarketplaceData::getSLMListing(S32 listingId) { - LLSD headers = LLSD::emptyMap(); - headers["Accept"] = "application/json"; - headers["Content-Type"] = "application/json"; - - // Send request - std::string url = getSLMConnectURL("/listing/") + llformat("%d",listing_id); - log_SLM_infos("LLHTTPClient::get", url, ""); - LLUUID folder_id = LLMarketplaceData::instance().getListingFolder(listing_id); - setUpdating(folder_id,true); - LLHTTPClient::get(url, new LLSLMGetListingResponder(folder_id), headers); + LLUUID folderId = getListingFolder(listingId); + setUpdating(folderId, true); + + LLCoros::instance().launch("getSingleListingCoro", + boost::bind(&LLMarketplaceData::getSingleListingCoro, this, listingId, folderId)); +} + +void LLMarketplaceData::getSingleListingCoro(S32 listingId, LLUUID folderId) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("getMerchantStatusCoro", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpHeaders::ptr_t httpHeaders(new LLCore::HttpHeaders); + + httpHeaders->append("Accept", "application/json"); + httpHeaders->append("Content-Type", "application/json"); + + std::string url = getSLMConnectURL("/listing/") + llformat("%d", listingId); + + LLSD result = httpAdapter->getJsonAndSuspend(httpRequest, url, httpHeaders); + + setUpdating(folderId, false); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) + { + if (status.getType() == HTTP_NOT_FOUND) + { + // That listing does not exist -> delete its record from the local SLM data store + deleteListing(folderId, false); + } + else + { + log_SLM_warning("Get /listing", status.getType(), status.toString(), "", result); + } + + update_marketplace_category(folderId, false); + gInventory.notifyObservers(); + return; + } + + log_SLM_infos("Get /listings", static_cast<U32>(status.getType()), result); + + + // Extract the info from the results + for (LLSD::array_iterator it = result["listings"].beginArray(); + it != result["listings"].endArray(); ++it) + { + LLSD listing = *it; + + int resListingId = listing["id"].asInteger(); + bool isListed = listing["is_listed"].asBoolean(); + std::string editUrl = listing["edit_url"].asString(); + LLUUID folderUuid = listing["inventory_info"]["listing_folder_id"].asUUID(); + LLUUID versionUuid = listing["inventory_info"]["version_folder_id"].asUUID(); + int count = listing["inventory_info"]["count_on_hand"].asInteger(); + + // Update that listing + setListingID(folderUuid, resListingId, false); + setVersionFolderID(folderUuid, versionUuid, false); + setActivationState(folderUuid, isListed, false); + setListingURL(folderUuid, editUrl, false); + setCountOnHand(folderUuid, count, false); + update_marketplace_category(folderUuid, false); + gInventory.notifyObservers(); + } } void LLMarketplaceData::createSLMListing(const LLUUID& folder_id, const LLUUID& version_id, S32 count) { - LLSD headers = LLSD::emptyMap(); - headers["Accept"] = "application/json"; - headers["Content-Type"] = "application/json"; - - // Build the json message - Json::Value root; - Json::FastWriter writer; - - LLViewerInventoryCategory* category = gInventory.getCategory(folder_id); - root["listing"]["name"] = category->getName(); - root["listing"]["inventory_info"]["listing_folder_id"] = folder_id.asString(); - root["listing"]["inventory_info"]["version_folder_id"] = version_id.asString(); - root["listing"]["inventory_info"]["count_on_hand"] = count; - - std::string json_str = writer.write(root); - - // postRaw() takes ownership of the buffer and releases it later. - size_t size = json_str.size(); - U8 *data = new U8[size]; - memcpy(data, (U8*)(json_str.c_str()), size); - - // Send request - std::string url = getSLMConnectURL("/listings"); - log_SLM_infos("LLHTTPClient::postRaw", url, json_str); - setUpdating(folder_id,true); - LLHTTPClient::postRaw(url, data, size, new LLSLMCreateListingsResponder(folder_id), headers); + setUpdating(folder_id, true); + LLCoros::instance().launch("createSLMListingCoro", + boost::bind(&LLMarketplaceData::createSLMListingCoro, this, folder_id, version_id, count)); } -void LLMarketplaceData::updateSLMListing(const LLUUID& folder_id, S32 listing_id, const LLUUID& version_id, bool is_listed, S32 count) +void LLMarketplaceData::createSLMListingCoro(LLUUID folderId, LLUUID versionId, S32 count) { - LLSD headers = LLSD::emptyMap(); - headers["Accept"] = "application/json"; - headers["Content-Type"] = "application/json"; + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("getMerchantStatusCoro", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpHeaders::ptr_t httpHeaders(new LLCore::HttpHeaders); - Json::Value root; - Json::FastWriter writer; + httpHeaders->append("Accept", "application/json"); + httpHeaders->append("Content-Type", "application/json"); - // Note : auto unlist if the count is 0 (out of stock) - if (is_listed && (count == 0)) + LLViewerInventoryCategory* category = gInventory.getCategory(folderId); + LLSD invInfo; + invInfo["listing_folder_id"] = folderId; + invInfo["version_folder_id"] = versionId; + invInfo["count_on_hand"] = count; + LLSD listing; + listing["name"] = category->getName(); + listing["inventory_info"] = invInfo; + LLSD postData; + postData["listing"] = listing; + + std::string url = getSLMConnectURL("/listings"); + + LLSD result = httpAdapter->postJsonAndSuspend(httpRequest, url, postData, httpHeaders); + + setUpdating(folderId, false); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) { - is_listed = false; - LLNotificationsUtil::add("AlertMerchantStockFolderEmpty"); + log_SLM_warning("Post /listings", status.getType(), status.toString(), "", result); + update_marketplace_category(folderId, false); + gInventory.notifyObservers(); + return; } - // Note : we're assuming that sending unchanged info won't break anything server side... - root["listing"]["id"] = listing_id; - root["listing"]["is_listed"] = is_listed; - root["listing"]["inventory_info"]["listing_folder_id"] = folder_id.asString(); - root["listing"]["inventory_info"]["version_folder_id"] = version_id.asString(); - root["listing"]["inventory_info"]["count_on_hand"] = count; - - std::string json_str = writer.write(root); - - // postRaw() takes ownership of the buffer and releases it later. - size_t size = json_str.size(); - U8 *data = new U8[size]; - memcpy(data, (U8*)(json_str.c_str()), size); + log_SLM_infos("Post /listings", status.getType(), result); + + // Extract the info from the results + for (LLSD::array_iterator it = result["listings"].beginArray(); + it != result["listings"].endArray(); ++it) + { + LLSD listing = *it; + + int listingId = listing["id"].asInteger(); + bool isListed = listing["is_listed"].asBoolean(); + std::string editUrl = listing["edit_url"].asString(); + LLUUID folderUuid = listing["inventory_info"]["listing_folder_id"].asUUID(); + LLUUID versionUuid = listing["inventory_info"]["version_folder_id"].asUUID(); + int count = listing["inventory_info"]["count_on_hand"].asInteger(); + + addListing(folderUuid, listingId, versionUuid, isListed, editUrl, count); + update_marketplace_category(folderUuid, false); + gInventory.notifyObservers(); + } + +} + +void LLMarketplaceData::updateSLMListing(const LLUUID& folder_id, S32 listing_id, const LLUUID& version_id, bool is_listed, S32 count) +{ + setUpdating(folder_id, true); + LLCoros::instance().launch("updateSLMListingCoro", + boost::bind(&LLMarketplaceData::updateSLMListingCoro, this, folder_id, listing_id, version_id, is_listed, count)); +} + +void LLMarketplaceData::updateSLMListingCoro(LLUUID folderId, S32 listingId, LLUUID versionId, bool isListed, S32 count) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("getMerchantStatusCoro", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpHeaders::ptr_t httpHeaders(new LLCore::HttpHeaders); + + httpHeaders->append("Accept", "application/json"); + httpHeaders->append("Content-Type", "application/json"); - // Send request - std::string url = getSLMConnectURL("/listing/") + llformat("%d",listing_id); - log_SLM_infos("LLHTTPClient::putRaw", url, json_str); - setUpdating(folder_id,true); - LLHTTPClient::putRaw(url, data, size, new LLSLMUpdateListingsResponder(folder_id, is_listed, version_id), headers); + LLSD invInfo; + invInfo["listing_folder_id"] = folderId; + invInfo["version_folder_id"] = versionId; + invInfo["count_on_hand"] = count; + LLSD listing; + listing["inventory_info"] = invInfo; + listing["id"] = listingId; + listing["is_listed"] = isListed; + LLSD postData; + postData["listing"] = listing; + + std::string url = getSLMConnectURL("/listing/") + llformat("%d", listingId); + LLSD result = httpAdapter->putJsonAndSuspend(httpRequest, url, postData, httpHeaders); + + setUpdating(folderId, false); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) + { + log_SLM_warning("Put /listing", status.getType(), status.toString(), "", result); + update_marketplace_category(folderId, false); + gInventory.notifyObservers(); + return; + } + + log_SLM_infos("Put /listing", status.getType(), result); + + // Extract the info from the Json string + for (LLSD::array_iterator it = result["listings"].beginArray(); + it != result["listings"].endArray(); ++it) + { + LLSD listing = *it; + + int listing_id = listing["id"].asInteger(); + bool is_listed = listing["is_listed"].asBoolean(); + std::string edit_url = listing["edit_url"].asString(); + LLUUID folderUuid = listing["inventory_info"]["listing_folder_id"].asUUID(); + LLUUID versionUuid = listing["inventory_info"]["version_folder_id"].asUUID(); + int onHand = listing["inventory_info"]["count_on_hand"].asInteger(); + + // Update that listing + setListingID(folderUuid, listing_id, false); + setVersionFolderID(folderUuid, versionUuid, false); + setActivationState(folderUuid, is_listed, false); + setListingURL(folderUuid, edit_url, false); + setCountOnHand(folderUuid, onHand, false); + update_marketplace_category(folderUuid, false); + gInventory.notifyObservers(); + + // Show a notification alert if what we got is not what we expected + // (this actually doesn't result in an error status from the SLM API protocol) + if ((isListed != is_listed) || (versionId != versionUuid)) + { + LLSD subs; + subs["[URL]"] = edit_url; + LLNotificationsUtil::add("AlertMerchantListingNotUpdated", subs); + } + } + } void LLMarketplaceData::associateSLMListing(const LLUUID& folder_id, S32 listing_id, const LLUUID& version_id, const LLUUID& source_folder_id) { - LLSD headers = LLSD::emptyMap(); - headers["Accept"] = "application/json"; - headers["Content-Type"] = "application/json"; - - Json::Value root; - Json::FastWriter writer; - - // Note : we're assuming that sending unchanged info won't break anything server side... - root["listing"]["id"] = listing_id; - root["listing"]["inventory_info"]["listing_folder_id"] = folder_id.asString(); - root["listing"]["inventory_info"]["version_folder_id"] = version_id.asString(); - - std::string json_str = writer.write(root); - - // postRaw() takes ownership of the buffer and releases it later. - size_t size = json_str.size(); - U8 *data = new U8[size]; - memcpy(data, (U8*)(json_str.c_str()), size); - - // Send request - std::string url = getSLMConnectURL("/associate_inventory/") + llformat("%d",listing_id); - log_SLM_infos("LLHTTPClient::putRaw", url, json_str); - setUpdating(folder_id,true); - setUpdating(source_folder_id,true); - LLHTTPClient::putRaw(url, data, size, new LLSLMAssociateListingsResponder(folder_id,source_folder_id), headers); + setUpdating(folder_id, true); + setUpdating(source_folder_id, true); + LLCoros::instance().launch("associateSLMListingCoro", + boost::bind(&LLMarketplaceData::associateSLMListingCoro, this, folder_id, listing_id, version_id, source_folder_id)); } -void LLMarketplaceData::deleteSLMListing(S32 listing_id) +void LLMarketplaceData::associateSLMListingCoro(LLUUID folderId, S32 listingId, LLUUID versionId, LLUUID sourceFolderId) { - LLSD headers = LLSD::emptyMap(); - headers["Accept"] = "application/json"; - headers["Content-Type"] = "application/json"; - - // Send request - std::string url = getSLMConnectURL("/listing/") + llformat("%d",listing_id); - log_SLM_infos("LLHTTPClient::del", url, ""); - LLUUID folder_id = LLMarketplaceData::instance().getListingFolder(listing_id); - setUpdating(folder_id,true); - LLHTTPClient::del(url, new LLSLMDeleteListingsResponder(folder_id), headers); + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("getMerchantStatusCoro", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpHeaders::ptr_t httpHeaders(new LLCore::HttpHeaders); + + httpHeaders->append("Accept", "application/json"); + httpHeaders->append("Content-Type", "application/json"); + + LLSD invInfo; + invInfo["listing_folder_id"] = folderId; + invInfo["version_folder_id"] = versionId; + LLSD listing; + listing["id"] = listingId; + listing["inventory_info"] = invInfo; + LLSD postData; + postData["listing"] = listing; + + // Send request + std::string url = getSLMConnectURL("/associate_inventory/") + llformat("%d", listingId); + + LLSD result = httpAdapter->putJsonAndSuspend(httpRequest, url, postData, httpHeaders); + + setUpdating(folderId, false); + setUpdating(sourceFolderId, false); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) + { + log_SLM_warning("Put /associate_inventory", status.getType(), status.toString(), "", result); + update_marketplace_category(folderId, false); + update_marketplace_category(sourceFolderId, false); + gInventory.notifyObservers(); + return; + } + + log_SLM_infos("Put /associate_inventory", status.getType(), result); + + for (LLSD::array_iterator it = result["listings"].beginArray(); + it != result["listings"].endArray(); ++it) + { + LLSD listing = *it; + + int listing_id = listing["id"].asInteger(); + bool is_listed = listing["is_listed"].asBoolean(); + std::string edit_url = listing["edit_url"].asString(); + LLUUID folder_uuid = listing["inventory_info"]["listing_folder_id"].asUUID(); + LLUUID version_uuid = listing["inventory_info"]["version_folder_id"].asUUID(); + int count = listing["inventory_info"]["count_on_hand"].asInteger(); + + // Check that the listing ID is not already associated to some other record + LLUUID old_listing = LLMarketplaceData::instance().getListingFolder(listing_id); + if (old_listing.notNull()) + { + // If it is already used, unlist the old record (we can't have 2 listings with the same listing ID) + deleteListing(old_listing); + } + + // Add the new association + addListing(folder_uuid, listing_id, version_uuid, is_listed, edit_url, count); + update_marketplace_category(folder_uuid, false); + gInventory.notifyObservers(); + + // The stock count needs to be updated with the new local count now + updateCountOnHand(folder_uuid, 1); + } + + // Always update the source folder so its widget updates + update_marketplace_category(sourceFolderId, false); +} + +void LLMarketplaceData::deleteSLMListing(S32 listingId) +{ + LLCoros::instance().launch("deleteSLMListingCoro", + boost::bind(&LLMarketplaceData::deleteSLMListingCoro, this, listingId)); +} + +void LLMarketplaceData::deleteSLMListingCoro(S32 listingId) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("getMerchantStatusCoro", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpHeaders::ptr_t httpHeaders(new LLCore::HttpHeaders); + + httpHeaders->append("Accept", "application/json"); + httpHeaders->append("Content-Type", "application/json"); + + std::string url = getSLMConnectURL("/listing/") + llformat("%d", listingId); + LLUUID folderId = getListingFolder(listingId); + + setUpdating(folderId, true); + + LLSD result = httpAdapter->deleteJsonAndSuspend(httpRequest, url, httpHeaders); + + setUpdating(folderId, false); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) + { + log_SLM_warning("Delete /listing", status.getType(), status.toString(), "", result); + update_marketplace_category(folderId, false); + gInventory.notifyObservers(); + return; + } + + log_SLM_infos("Delete /listing", status.getType(), result); + + for (LLSD::array_iterator it = result["listings"].beginArray(); + it != result["listings"].endArray(); ++it) + { + LLSD listing = *it; + + int listing_id = listing["id"].asInteger(); + LLUUID folder_id = LLMarketplaceData::instance().getListingFolder(listing_id); + deleteListing(folder_id); + } + } std::string LLMarketplaceData::getSLMConnectURL(const std::string& route) { - std::string url(""); + std::string url; LLViewerRegion *regionp = gAgent.getRegion(); if (regionp) { // Get DirectDelivery cap url = regionp->getCapability("DirectDelivery"); - if (url != "") + if (!url.empty()) { url += route; } @@ -1983,5 +1837,3 @@ bool LLMarketplaceData::setListingURL(const LLUUID& folder_id, const std::string return true; } - - diff --git a/indra/newview/llmarketplacefunctions.h b/indra/newview/llmarketplacefunctions.h index f8e7ed4364..9d795c6ced 100644 --- a/indra/newview/llmarketplacefunctions.h +++ b/indra/newview/llmarketplacefunctions.h @@ -37,8 +37,6 @@ #include "llstring.h" -LLSD getMarketplaceStringSubstitutions(); - namespace MarketplaceErrorCodes { @@ -183,6 +181,8 @@ class LLSLMDeleteListingsResponder; class LLMarketplaceData : public LLSingleton<LLMarketplaceData> { + friend class LLSingleton < LLMarketplaceData > ; + public: friend class LLSLMGetMerchantResponder; friend class LLSLMGetListingsResponder; @@ -192,9 +192,8 @@ public: friend class LLSLMAssociateListingsResponder; friend class LLSLMDeleteListingsResponder; - LLMarketplaceData(); - virtual ~LLMarketplaceData(); - + static LLSD getMarketplaceStringSubstitutions(); + // Public SLM API : Initialization and status typedef boost::signals2::signal<void ()> status_updated_signal_t; void initializeSLM(const status_updated_signal_t::slot_type& cb); @@ -241,8 +240,11 @@ public: // Used to decide when to run a validation on listing folders void setValidationWaiting(const LLUUID& folder_id, S32 count); void decrementValidationWaiting(const LLUUID& folder_id, S32 count = 1); - + private: + LLMarketplaceData(); + virtual ~LLMarketplaceData(); + // Modify Marketplace data set : each method returns true if the function succeeds, false if error // Used internally only by SLM Responders when data are received from the SLM Server bool addListing(const LLUUID& folder_id, S32 listing_id, const LLUUID& version_id, bool is_listed, const std::string& edit_url, S32 count); @@ -261,6 +263,14 @@ private: void deleteSLMListing(S32 listing_id); std::string getSLMConnectURL(const std::string& route); + void getMerchantStatusCoro(); + void getSLMListingsCoro(LLUUID folderId); + void getSingleListingCoro(S32 listingId, LLUUID folderId); + void createSLMListingCoro(LLUUID folderId, LLUUID versionId, S32 count); + void updateSLMListingCoro(LLUUID folderId, S32 listingId, LLUUID versionId, bool isListed, S32 count); + void associateSLMListingCoro(LLUUID folderId, S32 listingId, LLUUID versionId, LLUUID sourceFolderId); + void deleteSLMListingCoro(S32 listingId); + // Handling Marketplace connection and inventory connection U32 mMarketPlaceStatus; status_updated_signal_t* mStatusUpdatedSignal; diff --git a/indra/newview/llmaterialmgr.cpp b/indra/newview/llmaterialmgr.cpp index a1f6a01aa0..f996557c17 100644 --- 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; - LLMaterialsResponder(const std::string& pMethod, const std::string& pCapabilityURL, CallbackFunction pCallback); - virtual ~LLMaterialsResponder(); + LLMaterialHttpHandler(const std::string& method, CallbackFunction cback); - virtual void httpSuccess(); - virtual void httpFailure(); + virtual ~LLMaterialHttpHandler(); + +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,48 +575,63 @@ void LLMaterialMgr::onIdle(void*) { instancep->processPutQueue(); } + + instancep->mHttpRequest->update(0L); } -void LLMaterialMgr::processGetQueue() +/*static*/ +void LLMaterialMgr::CapsRecvForRegion(const LLUUID& regionId, LLUUID regionTest, std::string pumpname) { - get_queue_t::iterator loopRegionQueue = mGetQueue.begin(); - while (mGetQueue.end() != loopRegionQueue) - { - get_queue_t::iterator itRegionQueue = loopRegionQueue++; - - const LLUUID& region_id = itRegionQueue->first; - if (isGetAllPending(region_id)) - { - continue; - } - - LLViewerRegion* regionp = LLWorld::instance().getRegionFromID(region_id); - if (!regionp) - { - LL_WARNS("Materials") << "Unknown region with id " << region_id.asString() << LL_ENDL; - mGetQueue.erase(itRegionQueue); - continue; - } - else if (!regionp->capabilitiesReceived() || regionp->materialsCapThrottled()) - { - continue; - } - else if (mGetAllRequested.end() == mGetAllRequested.find(region_id)) - { - LL_DEBUGS("Materials") << "calling getAll for " << regionp->getName() << LL_ENDL; - getAll(region_id); - continue; - } - - const std::string capURL = regionp->getCapability(MATERIALS_CAPABILITY_NAME); - if (capURL.empty()) - { - LL_WARNS("Materials") << "Capability '" << MATERIALS_CAPABILITY_NAME - << "' is not defined on region '" << regionp->getName() << "'" << LL_ENDL; - mGetQueue.erase(itRegionQueue); - continue; - } + if (regionId == regionTest) + { + LLEventPumps::instance().obtain(pumpname).post(LLSD()); + } +} +void LLMaterialMgr::processGetQueue() +{ + get_queue_t::iterator loopRegionQueue = mGetQueue.begin(); + while (mGetQueue.end() != loopRegionQueue) + { +#if 1 + //* $TODO: This block is screaming to be turned into a coroutine. + // see processGetQueueCoro() below. + // + get_queue_t::iterator itRegionQueue = loopRegionQueue++; + + LLUUID region_id = itRegionQueue->first; + if (isGetAllPending(region_id)) + { + continue; + } + + LLViewerRegion* regionp = LLWorld::instance().getRegionFromID(region_id); + if (!regionp) + { + LL_WARNS("Materials") << "Unknown region with id " << region_id.asString() << LL_ENDL; + mGetQueue.erase(itRegionQueue); + continue; + } + else if (!regionp->capabilitiesReceived() || regionp->materialsCapThrottled()) + { + continue; + } + else if (mGetAllRequested.end() == mGetAllRequested.find(region_id)) + { + LL_DEBUGS("Materials") << "calling getAll for " << regionp->getName() << LL_ENDL; + getAll(region_id); + continue; + } + + const std::string capURL = regionp->getCapability(MATERIALS_CAPABILITY_NAME); + if (capURL.empty()) + { + LL_WARNS("Materials") << "Capability '" << MATERIALS_CAPABILITY_NAME + << "' is not defined on region '" << regionp->getName() << "'" << LL_ENDL; + mGetQueue.erase(itRegionQueue); + continue; + } + LLSD materialsData = LLSD::emptyArray(); material_queue_t& materials = itRegionQueue->second; @@ -611,6 +647,7 @@ void LLMaterialMgr::processGetQueue() if (materials.empty()) { mGetQueue.erase(itRegionQueue); + // $TODO*: We may be able to issue a continue here. Research. } std::string materialString = zip_llsd(materialsData); @@ -629,12 +666,123 @@ 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." + LLCore::HttpHandler::ptr_t 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) + { + LLCore::HttpStatus status = mHttpRequest->getStatus(); + LL_ERRS("Meterials") << "Failed to execute material POST. Status = " << + status.toULong() << "\"" << status.toString() << "\"" << LL_ENDL; + } + regionp->resetMaterialsCapThrottle(); } +#endif +} + +void LLMaterialMgr::processGetQueueCoro() +{ +#if 0 + get_queue_t::iterator itRegionQueue = loopRegionQueue++; + + const LLUUID& region_id = itRegionQueue->first; + if (isGetAllPending(region_id)) + { + continue; + } + + LLViewerRegion* regionp = LLWorld::instance().getRegionFromID(region_id); + if (!regionp) + { + LL_WARNS("Materials") << "Unknown region with id " << region_id.asString() << LL_ENDL; + mGetQueue.erase(itRegionQueue); + continue; + } + else if (!regionp->capabilitiesReceived() || regionp->materialsCapThrottled()) + { + continue; + } + else if (mGetAllRequested.end() == mGetAllRequested.find(region_id)) + { + LL_DEBUGS("Materials") << "calling getAll for " << regionp->getName() << LL_ENDL; + getAll(region_id); + continue; + } + + const std::string capURL = regionp->getCapability(MATERIALS_CAPABILITY_NAME); + if (capURL.empty()) + { + LL_WARNS("Materials") << "Capability '" << MATERIALS_CAPABILITY_NAME + << "' is not defined on region '" << regionp->getName() << "'" << LL_ENDL; + mGetQueue.erase(itRegionQueue); + continue; + } + + LLSD materialsData = LLSD::emptyArray(); + + material_queue_t& materials = itRegionQueue->second; + U32 max_entries = regionp->getMaxMaterialsPerTransaction(); + material_queue_t::iterator loopMaterial = materials.begin(); + while ((materials.end() != loopMaterial) && (materialsData.size() < max_entries)) + { + material_queue_t::iterator itMaterial = loopMaterial++; + materialsData.append((*itMaterial).asLLSD()); + materials.erase(itMaterial); + markGetPending(region_id, *itMaterial); + } + if (materials.empty()) + { + mGetQueue.erase(itRegionQueue); + } + + std::string materialString = zip_llsd(materialsData); + + S32 materialSize = materialString.size(); + if (materialSize <= 0) + { + LL_ERRS("Materials") << "cannot zip LLSD binary content" << LL_ENDL; + return; + } + + LLSD::Binary materialBinary; + materialBinary.resize(materialSize); + memcpy(materialBinary.data(), materialString.data(), materialSize); + + LLSD postData = LLSD::emptyMap(); + postData[MATERIALS_CAP_ZIP_FIELD] = materialBinary; + + 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; + + 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(); +#endif + } void LLMaterialMgr::processGetAllQueue() @@ -645,36 +793,87 @@ void LLMaterialMgr::processGetAllQueue() getall_queue_t::iterator itRegion = loopRegion++; const LLUUID& region_id = *itRegion; - LLViewerRegion* regionp = LLWorld::instance().getRegionFromID(region_id); - if (regionp == NULL) - { - LL_WARNS("Materials") << "Unknown region with id " << region_id.asString() << LL_ENDL; - clearGetQueues(region_id); // Invalidates region_id - continue; - } - else if (!regionp->capabilitiesReceived() || regionp->materialsCapThrottled()) - { - continue; - } - std::string capURL = regionp->getCapability(MATERIALS_CAPABILITY_NAME); - if (capURL.empty()) - { - LL_WARNS("Materials") << "Capability '" << MATERIALS_CAPABILITY_NAME - << "' is not defined on the current region '" << regionp->getName() << "'" << LL_ENDL; - clearGetQueues(region_id); // Invalidates region_id - continue; - } + LLCoros::instance().launch("LLMaterialMgr::processGetAllQueueCoro", boost::bind(&LLMaterialMgr::processGetAllQueueCoro, + this, region_id)); - 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); - regionp->resetMaterialsCapThrottle(); - mGetAllPending.insert(std::pair<LLUUID, F64>(region_id, LLFrameTimer::getTotalSeconds())); + mGetAllPending.insert(std::pair<LLUUID, F64>(region_id, LLFrameTimer::getTotalSeconds())); mGetAllQueue.erase(itRegion); // Invalidates region_id } } +void LLMaterialMgr::processGetAllQueueCoro(LLUUID regionId) +{ + LLViewerRegion* regionp = LLWorld::instance().getRegionFromID(regionId); + if (regionp == NULL) + { + LL_WARNS("Materials") << "Unknown region with id " << regionId.asString() << LL_ENDL; + clearGetQueues(regionId); // Invalidates region_id + return; + } + else if (!regionp->capabilitiesReceived()) + { + LLEventStream capsRecv("waitForCaps", true); + + regionp->setCapabilitiesReceivedCallback( + boost::bind(&LLMaterialMgr::CapsRecvForRegion, + _1, regionId, capsRecv.getName())); + + llcoro::suspendUntilEventOn(capsRecv); + + // reget the region from the region ID since it may have gone away while waiting. + regionp = LLWorld::instance().getRegionFromID(regionId); + if (!regionp) + { + LL_WARNS("Materials") << "Region with ID " << regionId << " is no longer valid." << LL_ENDL; + return; + } + } + else if (regionp->materialsCapThrottled()) + { + // TODO: + // Figure out how to handle the throttle. + } + + std::string capURL = regionp->getCapability(MATERIALS_CAPABILITY_NAME); + if (capURL.empty()) + { + LL_WARNS("Materials") << "Capability '" << MATERIALS_CAPABILITY_NAME + << "' is not defined on the current region '" << regionp->getName() << "'" << LL_ENDL; + clearGetQueues(regionId); // Invalidates region_id + return; + } + + LL_DEBUGS("Materials") << "GET all for region " << regionId << "url " << capURL << LL_ENDL; + + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter( + new LLCoreHttpUtil::HttpCoroutineAdapter("processGetAllQueue", LLCore::HttpRequest::DEFAULT_POLICY_ID)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest()); + + LLSD result = httpAdapter->getAndSuspend(httpRequest, capURL); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) + { + onGetAllResponse(false, LLSD(), regionId); + } + else + { + onGetAllResponse(true, result, regionId); + } + + // reget the region from the region ID since it may have gone away while waiting. + regionp = LLWorld::instance().getRegionFromID(regionId); + if (!regionp) + { + LL_WARNS("Materials") << "Region with ID " << regionId << " is no longer valid." << LL_ENDL; + return; + } + regionp->resetMaterialsCapThrottle(); +} + void LLMaterialMgr::processPutQueue() { typedef std::map<LLViewerRegion*, LLSD> regionput_request_map; @@ -696,34 +895,34 @@ void LLMaterialMgr::processPutQueue() { LLViewerRegion* regionp = objectp->getRegion(); if ( !regionp ) - { + { LL_WARNS("Materials") << "Object region is NULL" << LL_ENDL; mPutQueue.erase(itQueue); - } + } else if ( regionp->capabilitiesReceived() && !regionp->materialsCapThrottled()) { - LLSD& facesData = requests[regionp]; - - facematerial_map_t& face_map = itQueue->second; - U32 max_entries = regionp->getMaxMaterialsPerTransaction(); - facematerial_map_t::iterator itFace = face_map.begin(); - while ( (face_map.end() != itFace) && (facesData.size() < max_entries) ) - { - LLSD faceData = LLSD::emptyMap(); - faceData[MATERIALS_CAP_FACE_FIELD] = static_cast<LLSD::Integer>(itFace->first); - faceData[MATERIALS_CAP_OBJECT_ID_FIELD] = static_cast<LLSD::Integer>(objectp->getLocalID()); - if (!itFace->second.isNull()) - { - faceData[MATERIALS_CAP_MATERIAL_FIELD] = itFace->second.asLLSD(); - } - facesData.append(faceData); - face_map.erase(itFace++); - } - if (face_map.empty()) - { - mPutQueue.erase(itQueue); - } - } + LLSD& facesData = requests[regionp]; + + facematerial_map_t& face_map = itQueue->second; + U32 max_entries = regionp->getMaxMaterialsPerTransaction(); + facematerial_map_t::iterator itFace = face_map.begin(); + while ( (face_map.end() != itFace) && (facesData.size() < max_entries) ) + { + LLSD faceData = LLSD::emptyMap(); + faceData[MATERIALS_CAP_FACE_FIELD] = static_cast<LLSD::Integer>(itFace->first); + faceData[MATERIALS_CAP_OBJECT_ID_FIELD] = static_cast<LLSD::Integer>(objectp->getLocalID()); + if (!itFace->second.isNull()) + { + faceData[MATERIALS_CAP_MATERIAL_FIELD] = itFace->second.asLLSD(); + } + facesData.append(faceData); + face_map.erase(itFace++); + } + if (face_map.empty()) + { + mPutQueue.erase(itQueue); + } + } } } @@ -755,8 +954,22 @@ 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); + + LLCore::HttpHandler::ptr_t 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) + { + LLCore::HttpStatus status = mHttpRequest->getStatus(); + LL_ERRS("Meterials") << "Failed to execute material PUT. Status = " << + status.toULong() << "\"" << status.toString() << "\"" << LL_ENDL; + } + regionp->resetMaterialsCapThrottle(); } else @@ -769,6 +982,7 @@ void LLMaterialMgr::processPutQueue() void LLMaterialMgr::clearGetQueues(const LLUUID& region_id) { mGetQueue.erase(region_id); + for (get_pending_map_t::iterator itPending = mGetPending.begin(); itPending != mGetPending.end();) { if (region_id == itPending->first.first) diff --git a/indra/newview/llmaterialmgr.h b/indra/newview/llmaterialmgr.h index e83f1f4e01..36dd0904b6 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; @@ -64,24 +67,20 @@ protected: const LLMaterialPtr setMaterial(const LLUUID& region_id, const LLMaterialID& material_id, const LLSD& material_data); static void onIdle(void*); + + static void CapsRecvForRegion(const LLUUID& regionId, LLUUID regionTest, std::string pumpname); + void processGetQueue(); + void processGetQueueCoro(); void onGetResponse(bool success, const LLSD& content, const LLUUID& region_id); void processGetAllQueue(); + void processGetAllQueueCoro(LLUUID regionId); void onGetAllResponse(bool success, const LLSD& content, const LLUUID& region_id); void processPutQueue(); 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 +107,39 @@ 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; + uuid_set_t mRegionGets; + 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..bd8f464acd 100644 --- 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,16 @@ void LLMediaDataClient::serviceQueue() trackRequest(request); // and make the post - LLHTTPClient::post(url, sd_payload, request->createResponder()); + LLCore::HttpHandler::ptr_t handler = request->createHandler(); + LLCore::HttpHandle handle = LLCoreHttpUtil::requestPostWithLLSD(mHttpRequest, mHttpPolicy, 0, + url, sd_payload, mHttpOpts, mHttpHeaders, handler); + + if (handle == LLCORE_HTTP_HANDLE_INVALID) + { + LLCore::HttpStatus status = mHttpRequest->getStatus(); + LL_WARNS("LLMediaDataClient") << "'" << url << "' request POST failed. Reason " + << status.toTerseString() << " \"" << status.toString() << "\"" << LL_ENDL; + } } else { @@ -332,13 +377,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 +444,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 +466,7 @@ BOOL LLMediaDataClient::RetryTimer::tick() } // Release the ref to the request. - mRequest = NULL; + mRequest.reset(); // Don't fire again return TRUE; @@ -490,7 +539,7 @@ void LLMediaDataClient::Request::reEnqueue() { if(mMDC) { - mMDC->enqueue(this); + mMDC->enqueue(shared_from_this()); } } @@ -533,13 +582,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 +600,61 @@ 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; + + 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; - LL_DEBUGS("LLMediaDataClientResponse") << *mRequest << " " << dumpResponse() << 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 +665,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 +709,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 +734,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 +750,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 +783,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 +819,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 +830,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 +850,7 @@ bool LLObjectMediaDataClient::processQueueTimer() LL_DEBUGS("LLMediaDataClientQueue") << "after sort, SORTED queue is: " << mQueue << LL_ENDL; serviceQueue(); + serviceHttp(); swapCurrentQueue(); @@ -824,7 +859,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 +876,16 @@ LLSD LLObjectMediaDataClient::RequestGet::getPayload() const return result; } -LLMediaDataClient::Responder *LLObjectMediaDataClient::RequestGet::createResponder() +LLCore::HttpHandler::ptr_t LLObjectMediaDataClient::RequestGet::createHandler() { - return new LLObjectMediaDataClient::Responder(this); + return LLCore::HttpHandler::ptr_t(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 +912,58 @@ LLSD LLObjectMediaDataClient::RequestUpdate::getPayload() const return result; } -LLMediaDataClient::Responder *LLObjectMediaDataClient::RequestUpdate::createResponder() +LLCore::HttpHandler::ptr_t LLObjectMediaDataClient::RequestUpdate::createHandler() { // This just uses the base class's responder. - return new LLMediaDataClient::Responder(this); + return LLCore::HttpHandler::ptr_t(new LLMediaDataClient::Handler(shared_from_this())); } - -/*virtual*/ -void LLObjectMediaDataClient::Responder::httpSuccess() +void LLObjectMediaDataClient::Handler::onSuccess(LLCore::HttpResponse * response, const LLSD &content) { - getRequest()->stopTracking(); + 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]); + } - 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; - } - - // 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 +976,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 +995,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 +1014,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 +1028,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 +1047,67 @@ LLSD LLObjectMediaNavigateClient::RequestNavigate::getPayload() const return result; } -LLMediaDataClient::Responder *LLObjectMediaNavigateClient::RequestNavigate::createResponder() +LLCore::HttpHandler::ptr_t LLObjectMediaNavigateClient::RequestNavigate::createHandler() { - return new LLObjectMediaNavigateClient::Responder(this); + return LLCore::HttpHandler::ptr_t(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..58f8bad3e4 100644 --- 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 LLCore::HttpHandler::ptr_t 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*/ LLCore::HttpHandler::ptr_t createHandler(); }; class RequestUpdate: public Request @@ -317,7 +332,7 @@ public: public: RequestUpdate(LLMediaDataClientObject *obj, LLMediaDataClient *mdc); /*virtual*/ LLSD getPayload() const; - /*virtual*/ Responder *createResponder(); + /*virtual*/ LLCore::HttpHandler::ptr_t 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*/ LLCore::HttpHandler::ptr_t 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 1841d066a2..9387bd0adb 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -1,4 +1,4 @@ -/** +/** * @file llmeshrepository.cpp * @brief Mesh repository implementation. * @@ -36,7 +36,6 @@ #include "llappviewer.h" #include "llbufferstream.h" #include "llcallbacklist.h" -#include "llcurl.h" #include "lldatapacker.h" #include "lldeadmantimer.h" #include "llfloatermodelpreview.h" @@ -73,6 +72,10 @@ #include "llfasttimer.h" #include "llcorehttputil.h" #include "lltrans.h" +#include "llstatusbar.h" +#include "llinventorypanel.h" +#include "lluploaddialog.h" +#include "llfloaterreg.h" #include "boost/lexical_cast.hpp" @@ -389,6 +392,19 @@ U32 LLMeshRepository::sMaxLockHoldoffs = 0; LLDeadmanTimer LLMeshRepository::sQuiescentTimer(15.0, false); // true -> gather cpu metrics +namespace { + // The NoOpDeletor is used when passing certain objects (generally the LLMeshUploadThread) + // in a smart pointer below for passage into the LLCore::Http libararies. + // When the smart pointer is destroyed, no action will be taken since we + // do not in these cases want the object to be destroyed at the end of the call. + // + // *NOTE$: Yes! It is "Deletor" + // http://english.stackexchange.com/questions/4733/what-s-the-rule-for-adding-er-vs-or-when-nouning-a-verb + // "delete" derives from Latin "deletus" + + void NoOpDeletor(LLCore::HttpHandler *) + { /*NoOp*/ } +} static S32 dump_num = 0; std::string make_dump_name(std::string prefix, S32 num) @@ -413,6 +429,17 @@ static unsigned int metrics_teleport_start_count = 0; boost::signals2::connection metrics_teleport_started_signal; static void teleport_started(); +void on_new_single_inventory_upload_complete( + LLAssetType::EType asset_type, + LLInventoryType::EType inventory_type, + const std::string inventory_type_string, + const LLUUID& item_folder_id, + const std::string& item_name, + const std::string& item_description, + const LLSD& server_response, + S32 upload_price); + + //get the number of bytes resident in memory for given volume U32 get_volume_memory_size(const LLVolume* volume) { @@ -524,9 +551,12 @@ S32 LLMeshRepoThread::sRequestWaterLevel = 0; // LLMeshPhysicsShapeHandler // LLMeshUploadThread -class LLMeshHandlerBase : public LLCore::HttpHandler +class LLMeshHandlerBase : public LLCore::HttpHandler, + public boost::enable_shared_from_this<LLMeshHandlerBase> { public: + typedef boost::shared_ptr<LLMeshHandlerBase> ptr_t; + LOG_CLASS(LLMeshHandlerBase); LLMeshHandlerBase(U32 offset, U32 requested_bytes) : LLCore::HttpHandler(), @@ -774,9 +804,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), @@ -789,13 +819,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); @@ -810,29 +840,10 @@ LLMeshRepoThread::~LLMeshRepoThread() << ", Max Lock Holdoffs: " << LLMeshRepository::sMaxLockHoldoffs << LL_ENDL; - for (http_request_set::iterator iter(mHttpRequestSet.begin()); - iter != mHttpRequestSet.end(); - ++iter) - { - 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; @@ -1160,7 +1171,7 @@ void LLMeshRepoThread::constructUrl(LLUUID mesh_id, std::string * url, int * ver // Thread: repo LLCore::HttpHandle LLMeshRepoThread::getByteRange(const std::string & url, int cap_version, size_t offset, size_t len, - LLCore::HttpHandler * handler) + const LLCore::HttpHandler::ptr_t &handler) { // Also used in lltexturefetch.cpp static LLCachedControl<bool> disable_range_req(gSavedSettings, "HttpRangeRequestsDisable", false); @@ -1274,7 +1285,7 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id) if (!http_url.empty()) { - LLMeshSkinInfoHandler * handler = new LLMeshSkinInfoHandler(mesh_id, offset, size); + LLMeshHandlerBase::ptr_t handler(new LLMeshSkinInfoHandler(mesh_id, offset, size)); LLCore::HttpHandle handle = getByteRange(http_url, cap_version, offset, size, handler); if (LLCORE_HTTP_HANDLE_INVALID == handle) { @@ -1282,7 +1293,6 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id) << ". Reason: " << mHttpStatus.toString() << " (" << mHttpStatus.toTerseString() << ")" << LL_ENDL; - delete handler; ret = false; } else @@ -1368,7 +1378,7 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id) if (!http_url.empty()) { - LLMeshDecompositionHandler * handler = new LLMeshDecompositionHandler(mesh_id, offset, size); + LLMeshHandlerBase::ptr_t handler(new LLMeshDecompositionHandler(mesh_id, offset, size)); LLCore::HttpHandle handle = getByteRange(http_url, cap_version, offset, size, handler); if (LLCORE_HTTP_HANDLE_INVALID == handle) { @@ -1376,7 +1386,6 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id) << ". Reason: " << mHttpStatus.toString() << " (" << mHttpStatus.toTerseString() << ")" << LL_ENDL; - delete handler; ret = false; } else @@ -1461,7 +1470,7 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id) if (!http_url.empty()) { - LLMeshPhysicsShapeHandler * handler = new LLMeshPhysicsShapeHandler(mesh_id, offset, size); + LLMeshHandlerBase::ptr_t handler(new LLMeshPhysicsShapeHandler(mesh_id, offset, size)); LLCore::HttpHandle handle = getByteRange(http_url, cap_version, offset, size, handler); if (LLCORE_HTTP_HANDLE_INVALID == handle) { @@ -1469,7 +1478,6 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id) << ". Reason: " << mHttpStatus.toString() << " (" << mHttpStatus.toTerseString() << ")" << LL_ENDL; - delete handler; ret = false; } else @@ -1560,7 +1568,7 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params) //within the first 4KB //NOTE -- this will break of headers ever exceed 4KB - LLMeshHeaderHandler * handler = new LLMeshHeaderHandler(mesh_params, 0, MESH_HEADER_SIZE); + LLMeshHandlerBase::ptr_t handler(new LLMeshHeaderHandler(mesh_params, 0, MESH_HEADER_SIZE)); LLCore::HttpHandle handle = getByteRange(http_url, cap_version, 0, MESH_HEADER_SIZE, handler); if (LLCORE_HTTP_HANDLE_INVALID == handle) { @@ -1568,7 +1576,6 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params) << ". Reason: " << mHttpStatus.toString() << " (" << mHttpStatus.toTerseString() << ")" << LL_ENDL; - delete handler; retval = false; } else @@ -1644,7 +1651,7 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod) if (!http_url.empty()) { - LLMeshLODHandler * handler = new LLMeshLODHandler(mesh_params, lod, offset, size); + LLMeshHandlerBase::ptr_t handler(new LLMeshLODHandler(mesh_params, lod, offset, size)); LLCore::HttpHandle handle = getByteRange(http_url, cap_version, offset, size, handler); if (LLCORE_HTTP_HANDLE_INVALID == handle) { @@ -1652,7 +1659,6 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod) << ". Reason: " << mHttpStatus.toString() << " (" << mHttpStatus.toTerseString() << ")" << LL_ENDL; - delete handler; retval = false; } else @@ -1918,11 +1924,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; @@ -1930,16 +1936,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; } @@ -2367,6 +2363,164 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures) } } + for (instance_map::iterator iter = mInstance.begin(); iter != mInstance.end(); ++iter) + { + LLMeshUploadData data; + data.mBaseModel = iter->first; + + if (!data.mBaseModel->mSubmodelID) + { + // These were handled above already... + // + continue; + } + + LLModelInstance& first_instance = *(iter->second.begin()); + for (S32 i = 0; i < 5; i++) + { + data.mModel[i] = first_instance.mLOD[i]; + } + + if (mesh_index.find(data.mBaseModel) == mesh_index.end()) + { + // Have not seen this model before - create a new mesh_list entry for it. + if (model_name.empty()) + { + model_name = data.mBaseModel->getName(); + } + + if (model_metric.empty()) + { + model_metric = data.mBaseModel->getMetric(); + } + + std::stringstream ostr; + + LLModel::Decomposition& decomp = + data.mModel[LLModel::LOD_PHYSICS].notNull() ? + data.mModel[LLModel::LOD_PHYSICS]->mPhysics : + data.mBaseModel->mPhysics; + + decomp.mBaseHull = mHullMap[data.mBaseModel]; + + LLSD mesh_header = LLModel::writeModel( + ostr, + data.mModel[LLModel::LOD_PHYSICS], + data.mModel[LLModel::LOD_HIGH], + data.mModel[LLModel::LOD_MEDIUM], + data.mModel[LLModel::LOD_LOW], + data.mModel[LLModel::LOD_IMPOSTOR], + decomp, + mUploadSkin, + mUploadJoints, + FALSE, + FALSE, + data.mBaseModel->mSubmodelID); + + data.mAssetData = ostr.str(); + std::string str = ostr.str(); + + res["mesh_list"][mesh_num] = LLSD::Binary(str.begin(),str.end()); + mesh_index[data.mBaseModel] = mesh_num; + mesh_num++; + } + + // For all instances that use this model + for (instance_list::iterator instance_iter = iter->second.begin(); + instance_iter != iter->second.end(); + ++instance_iter) + { + + LLModelInstance& instance = *instance_iter; + + LLSD instance_entry; + + for (S32 i = 0; i < 5; i++) + { + data.mModel[i] = instance.mLOD[i]; + } + + LLVector3 pos, scale; + LLQuaternion rot; + LLMatrix4 transformation = instance.mTransform; + decomposeMeshMatrix(transformation,pos,rot,scale); + instance_entry["position"] = ll_sd_from_vector3(pos); + instance_entry["rotation"] = ll_sd_from_quaternion(rot); + instance_entry["scale"] = ll_sd_from_vector3(scale); + + instance_entry["material"] = LL_MCODE_WOOD; + instance_entry["physics_shape_type"] = (U8)(LLViewerObject::PHYSICS_SHAPE_NONE); + instance_entry["mesh"] = mesh_index[data.mBaseModel]; + + instance_entry["face_list"] = LLSD::emptyArray(); + + // We want to be able to allow more than 8 materials... + // + S32 end = llmin((S32)instance.mMaterial.size(), instance.mModel->getNumVolumeFaces()) ; + + for (S32 face_num = 0; face_num < end; face_num++) + { + LLImportMaterial& material = instance.mMaterial[data.mBaseModel->mMaterialList[face_num]]; + LLSD face_entry = LLSD::emptyMap(); + + LLViewerFetchedTexture *texture = NULL; + + if (material.mDiffuseMapFilename.size()) + { + texture = FindViewerTexture(material); + } + + if ((texture != NULL) && + (textures.find(texture) == textures.end())) + { + textures.insert(texture); + } + + std::stringstream texture_str; + if (texture != NULL && include_textures && mUploadTextures) + { + if(texture->hasSavedRawImage()) + { + LLPointer<LLImageJ2C> upload_file = + LLViewerTextureList::convertToUploadFile(texture->getSavedRawImage()); + + if (!upload_file.isNull() && upload_file->getDataSize()) + { + texture_str.write((const char*) upload_file->getData(), upload_file->getDataSize()); + } + } + } + + if (texture != NULL && + mUploadTextures && + texture_index.find(texture) == texture_index.end()) + { + texture_index[texture] = texture_num; + std::string str = texture_str.str(); + res["texture_list"][texture_num] = LLSD::Binary(str.begin(),str.end()); + texture_num++; + } + + // Subset of TextureEntry fields. + if (texture != NULL && mUploadTextures) + { + face_entry["image"] = texture_index[texture]; + face_entry["scales"] = 1.0; + face_entry["scalet"] = 1.0; + face_entry["offsets"] = 0.0; + face_entry["offsett"] = 0.0; + face_entry["imagerot"] = 0.0; + } + face_entry["diffuse_color"] = ll_sd_from_color4(material.mDiffuseColor); + face_entry["fullbright"] = material.mFullbright; + instance_entry["face_list"][face_num] = face_entry; + } + + res["instance_list"][instance_num] = instance_entry; + instance_num++; + } + } + if (model_name.empty()) model_name = "mesh model"; result["name"] = model_name; if (model_metric.empty()) model_metric = "MUT_Unspecified"; @@ -2465,7 +2619,7 @@ void LLMeshUploadThread::doWholeModelUpload() body, mHttpOptions, mHttpHeaders, - this); + LLCore::HttpHandler::ptr_t(this, &NoOpDeletor)); if (LLCORE_HTTP_HANDLE_INVALID == handle) { mHttpStatus = mHttpRequest->getStatus(); @@ -2516,7 +2670,7 @@ void LLMeshUploadThread::requestWholeModelFee() mModelData, mHttpOptions, mHttpHeaders, - this); + LLCore::HttpHandler::ptr_t(this, &NoOpDeletor)); if (LLCORE_HTTP_HANDLE_INVALID == handle) { mHttpStatus = mHttpRequest->getStatus(); @@ -2957,8 +3111,7 @@ void LLMeshHandlerBase::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRespo // Release handler common_exit: - gMeshRepo.mThread->mHttpRequestSet.erase(this); - delete this; // Must be last statement + gMeshRepo.mThread->mHttpRequestSet.erase(this->shared_from_this()); } @@ -4732,3 +4885,122 @@ void teleport_started() LLMeshRepository::metricsStart(); } + +void on_new_single_inventory_upload_complete( + LLAssetType::EType asset_type, + LLInventoryType::EType inventory_type, + const std::string inventory_type_string, + const LLUUID& item_folder_id, + const std::string& item_name, + const std::string& item_description, + const LLSD& server_response, + S32 upload_price) +{ + bool success = false; + + if (upload_price > 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", upload_price); + LLNotificationsUtil::add("UploadPayment", args); + } + + if (item_folder_id.notNull()) + { + U32 everyone_perms = PERM_NONE; + U32 group_perms = PERM_NONE; + U32 next_owner_perms = PERM_ALL; + if (server_response.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. + everyone_perms = server_response["new_everyone_mask"].asInteger(); + group_perms = server_response["new_group_mask"].asInteger(); + next_owner_perms = server_response["new_next_owner_mask"].asInteger(); + } + else + { + // The server doesn't provide creation perms + // so use old assumption-based perms. + if (inventory_type_string != "snapshot") + { + next_owner_perms = 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, + everyone_perms, + group_perms, + next_owner_perms); + + U32 inventory_item_flags = 0; + if (server_response.has("inventory_flags")) + { + inventory_item_flags = (U32)server_response["inventory_flags"].asInteger(); + if (inventory_item_flags != 0) + { + LL_INFOS() << "inventory_item_flags " << inventory_item_flags << LL_ENDL; + } + } + S32 creation_date_now = time_corrected(); + LLPointer<LLViewerInventoryItem> item = new LLViewerInventoryItem( + server_response["new_inventory_item"].asUUID(), + item_folder_id, + new_perms, + server_response["new_asset"].asUUID(), + asset_type, + inventory_type, + item_name, + item_description, + LLSaleInfo::DEFAULT, + inventory_item_flags, + creation_date_now); + + gInventory.updateItem(item); + gInventory.notifyObservers(); + 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( + server_response["new_inventory_item"].asUUID(), + 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 + LLUploadDialog::modalUploadFinished(); + + // Let the Snapshot floater know we have finished uploading a snapshot to inventory. + LLFloater* floater_snapshot = LLFloaterReg::findInstance("snapshot"); + if (asset_type == LLAssetType::AT_TEXTURE && floater_snapshot) + { + floater_snapshot->notify(LLSD().with("set-finished", LLSD().with("ok", success).with("msg", "inventory"))); + } +} diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index 688cd01a87..d35c44397b 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -38,6 +38,7 @@ #include "httpoptions.h" #include "httpheaders.h" #include "httphandler.h" +#include "llthread.h" #define LLCONVEXDECOMPINTER_STATIC 1 @@ -274,15 +275,15 @@ 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; LLCore::HttpRequest::priority_t mHttpPriority; - typedef std::set<LLCore::HttpHandler *> http_request_set; + typedef std::set<LLCore::HttpHandler::ptr_t> http_request_set; http_request_set mHttpRequestSet; // Outstanding HTTP requests std::string mGetMeshCapability; @@ -350,7 +351,7 @@ private: // Threads: Repo thread only LLCore::HttpHandle getByteRange(const std::string & url, int cap_version, size_t offset, size_t len, - LLCore::HttpHandler * handler); + const LLCore::HttpHandler::ptr_t &handler); }; @@ -447,8 +448,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/llnotificationhandler.h b/indra/newview/llnotificationhandler.h index 3e7f05b5e1..7a183cb298 100644 --- a/indra/newview/llnotificationhandler.h +++ b/indra/newview/llnotificationhandler.h @@ -277,22 +277,6 @@ protected: virtual void initChannel() {}; }; -/** - * Handler for outbox notifications - */ -class LLOutboxNotification : public LLSystemNotificationHandler -{ -public: - LLOutboxNotification(); - virtual ~LLOutboxNotification() {}; - virtual void onChange(LLNotificationPtr p) { } - virtual void onDelete(LLNotificationPtr p); - virtual bool processNotification(const LLNotificationPtr& p); - -protected: - virtual void initChannel() {}; -}; - class LLHandlerUtil { public: diff --git a/indra/newview/llnotificationmanager.cpp b/indra/newview/llnotificationmanager.cpp index 152581c5a0..a6f20a9f27 100644 --- a/indra/newview/llnotificationmanager.cpp +++ b/indra/newview/llnotificationmanager.cpp @@ -61,7 +61,6 @@ void LLNotificationManager::init() mChannels.push_back(new LLOfferHandler()); mChannels.push_back(new LLHintHandler()); mChannels.push_back(new LLBrowserNotification()); - mChannels.push_back(new LLOutboxNotification()); mChannels.push_back(new LLIMHandler()); mChatHandler = boost::shared_ptr<LLFloaterIMNearbyChatHandler>(new LLFloaterIMNearbyChatHandler()); @@ -70,7 +69,7 @@ void LLNotificationManager::init() //-------------------------------------------------------------------------- void LLNotificationManager::onChat(const LLChat& msg, const LLSD &args) { - if(mChatHandler) - mChatHandler->processChat(msg, args); - } + if(mChatHandler) + mChatHandler->processChat(msg, args); +} diff --git a/indra/newview/llpanelclassified.cpp b/indra/newview/llpanelclassified.cpp index 878f1af9ef..5d1ae4ff10 100644 --- 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 100644 --- 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/llpanelexperiencelisteditor.cpp b/indra/newview/llpanelexperiencelisteditor.cpp index 7c07301762..32ec4930ab 100644 --- a/indra/newview/llpanelexperiencelisteditor.cpp +++ b/indra/newview/llpanelexperiencelisteditor.cpp @@ -185,7 +185,7 @@ void LLPanelExperienceListEditor::onItems() columns[0]["value"] = getString("loading"); mItems->addElement(item); - LLExperienceCache::get(experience, boost::bind(&LLPanelExperienceListEditor::experienceDetailsCallback, + LLExperienceCache::instance().get(experience, boost::bind(&LLPanelExperienceListEditor::experienceDetailsCallback, getDerivedHandle<LLPanelExperienceListEditor>(), _1)); } diff --git a/indra/newview/llpanelexperiencelog.cpp b/indra/newview/llpanelexperiencelog.cpp index 30576a8d67..44b4728df7 100644 --- a/indra/newview/llpanelexperiencelog.cpp +++ b/indra/newview/llpanelexperiencelog.cpp @@ -146,7 +146,7 @@ void LLPanelExperienceLog::refresh() } const LLSD event = dayArray[i]; LLUUID id = event[LLExperienceCache::EXPERIENCE_ID].asUUID(); - const LLSD& experience = LLExperienceCache::get(id); + const LLSD& experience = LLExperienceCache::instance().get(id); if(experience.isUndefined()){ waiting = true; waiting_id = id; @@ -175,7 +175,7 @@ void LLPanelExperienceLog::refresh() { mEventList->deleteAllItems(); mEventList->setCommentText(getString("loading")); - LLExperienceCache::get(waiting_id, boost::bind(&LLPanelExperienceLog::refresh, this)); + LLExperienceCache::instance().get(waiting_id, boost::bind(&LLPanelExperienceLog::refresh, this)); } else { diff --git a/indra/newview/llpanelexperiencepicker.cpp b/indra/newview/llpanelexperiencepicker.cpp index 43dc7569a4..a7f2dbafa2 100644 --- a/indra/newview/llpanelexperiencepicker.cpp +++ b/indra/newview/llpanelexperiencepicker.cpp @@ -59,41 +59,6 @@ const static std::string columnSpace = " "; static LLPanelInjector<LLPanelExperiencePicker> t_panel_status("llpanelexperiencepicker"); -class LLExperienceSearchResponder : public LLHTTPClient::Responder -{ -public: - LLUUID mQueryID; - LLHandle<LLPanelExperiencePicker> mParent; - - LLExperienceSearchResponder(const LLUUID& id, const LLHandle<LLPanelExperiencePicker>& parent) : mQueryID(id), mParent(parent) { } - -protected: - /*virtual*/ void httpSuccess() - { - if(mParent.isDead()) - return; - - LLPanelExperiencePicker* panel =mParent.get(); - if (panel) - { - panel->processResponse(mQueryID, getContent()); - } - } - - /*virtual*/ void httpFailure() - { - if(mParent.isDead()) - return; - - LLPanelExperiencePicker* panel =mParent.get(); - if (panel) - { - panel->processResponse(mQueryID, LLSD()); - } - LL_WARNS() << "experience picker failed [status:" << getStatus() << "]: " << getContent() << LL_ENDL; - } -}; - LLPanelExperiencePicker::LLPanelExperiencePicker() :LLPanel() { @@ -161,7 +126,7 @@ void LLPanelExperiencePicker::onBtnFind() LLUUID experience_id(exp_id); if (!experience_id.isNull()) { - const LLSD& experience_details = LLExperienceCache::get(experience_id); + const LLSD& experience_details = LLExperienceCache::instance().get(experience_id); if(!experience_details.isUndefined()) { std::string experience_name_string = experience_details[LLExperienceCache::NAME].asString(); @@ -180,7 +145,7 @@ void LLPanelExperiencePicker::onBtnFind() getChildView(BTN_RIGHT)->setEnabled(FALSE); getChildView(BTN_LEFT)->setEnabled(FALSE); - LLExperienceCache::get(experience_id, boost::bind(&LLPanelExperiencePicker::onBtnFind, this)); + LLExperienceCache::instance().get(experience_id, boost::bind(&LLPanelExperiencePicker::onBtnFind, this)); return; } } @@ -204,17 +169,11 @@ void LLPanelExperiencePicker::find() { std::string text = getChild<LLUICtrl>(TEXT_EDIT)->getValue().asString(); mQueryID.generate(); - std::ostringstream url; - LLViewerRegion* region = gAgent.getRegion(); - std::string cap = region->getCapability("FindExperienceByName"); - if (!cap.empty()) - { - url << cap << "?page=" << mCurrentPage << "&page_size=30&query=" << LLURI::escape(text); - LLHTTPClient::get(url.str(), new LLExperienceSearchResponder(mQueryID, getDerivedHandle<LLPanelExperiencePicker>())); + LLExperienceCache::instance().findExperienceByName(text, mCurrentPage, + boost::bind(&LLPanelExperiencePicker::findResults, getDerivedHandle<LLPanelExperiencePicker>(), mQueryID, _1)); - } - getChild<LLScrollListCtrl>(LIST_RESULTS)->deleteAllItems(); + getChild<LLScrollListCtrl>(LIST_RESULTS)->deleteAllItems(); getChild<LLScrollListCtrl>(LIST_RESULTS)->setCommentText(getString("searching")); getChildView(BTN_OK)->setEnabled(FALSE); @@ -224,6 +183,19 @@ void LLPanelExperiencePicker::find() getChildView(BTN_LEFT)->setEnabled(FALSE); } +/*static*/ +void LLPanelExperiencePicker::findResults(LLHandle<LLPanelExperiencePicker> hparent, LLUUID queryId, LLSD foundResult) +{ + if (hparent.isDead()) + return; + + LLPanelExperiencePicker* panel = hparent.get(); + if (panel) + { + panel->processResponse(queryId, foundResult); + } +} + bool LLPanelExperiencePicker::isSelectButtonEnabled() { LLScrollListCtrl* list=getChild<LLScrollListCtrl>(LIST_RESULTS); @@ -273,13 +245,6 @@ void LLPanelExperiencePicker::processResponse( const LLUUID& query_id, const LLS mResponse = content; - const LLSD& experiences=mResponse["experience_keys"]; - LLSD::array_const_iterator it = experiences.beginArray(); - for ( ; it != experiences.endArray(); ++it) - { - LLExperienceCache::insert(*it); - } - getChildView(BTN_RIGHT)->setEnabled(content.has("next_page_url")); getChildView(BTN_LEFT)->setEnabled(content.has("previous_page_url")); diff --git a/indra/newview/llpanelexperiencepicker.h b/indra/newview/llpanelexperiencepicker.h index e39ffed70b..97aa04cf4c 100644 --- a/indra/newview/llpanelexperiencepicker.h +++ b/indra/newview/llpanelexperiencepicker.h @@ -74,8 +74,9 @@ private: void getSelectedExperienceIds( const LLScrollListCtrl* results, uuid_vec_t &experience_ids ); void setAllowMultiple(bool allow_multiple); - void find(); + static void findResults(LLHandle<LLPanelExperiencePicker> hparent, LLUUID queryId, LLSD foundResult); + bool isSelectButtonEnabled(); void processResponse( const LLUUID& query_id, const LLSD& content ); diff --git a/indra/newview/llpanelgroupexperiences.cpp b/indra/newview/llpanelgroupexperiences.cpp index 2d7690895f..a88a55ab22 100644 --- a/indra/newview/llpanelgroupexperiences.cpp +++ b/indra/newview/llpanelgroupexperiences.cpp @@ -31,45 +31,16 @@ #include "lluictrlfactory.h" #include "roles_constants.h" -#include "llhttpclient.h" #include "llagent.h" #include "llviewerregion.h" #include "llflatlistview.h" #include "llpanelexperiences.h" #include "llsd.h" - +#include "llexperiencecache.h" static LLPanelInjector<LLPanelGroupExperiences> t_panel_group_experiences("panel_group_experiences"); -class LLGroupExperienceResponder : public LLHTTPClient::Responder -{ -public: - LLHandle<LLPanelGroupExperiences> mHandle; - - LLGroupExperienceResponder(LLHandle<LLPanelGroupExperiences> handle) : mHandle(handle) { } - -protected: - /*virtual*/ void httpSuccess() - { - if (mHandle.isDead()) - { - return; - } - - LLPanelGroupExperiences* panel = mHandle.get(); - if (panel) - { - panel->setExperienceList(getContent().get("experience_ids")); - } - } - - /*virtual*/ void httpFailure() - { - LL_WARNS() << "experience responder failed [status:" << getStatus() << "]: " << getContent() << LL_ENDL; - } -}; - LLPanelGroupExperiences::LLPanelGroupExperiences() : LLPanelGroupTab(), mExperiencesList(NULL) { @@ -101,14 +72,8 @@ void LLPanelGroupExperiences::activate() return; } - // search for experiences owned by the current group - std::string url = (gAgent.getRegion()) ? gAgent.getRegion()->getCapability("GroupExperiences") : LLStringUtil::null; - if (!url.empty()) - { - url += "?" + getGroupID().asString(); - - LLHTTPClient::get(url, new LLGroupExperienceResponder(getDerivedHandle<LLPanelGroupExperiences>())); - } + LLExperienceCache::instance().getGroupExperiences(getGroupID(), + boost::bind(&LLPanelGroupExperiences::groupExperiencesResults, getDerivedHandle<LLPanelGroupExperiences>(), _1)); } void LLPanelGroupExperiences::setGroupID(const LLUUID& id) @@ -141,3 +106,19 @@ void LLPanelGroupExperiences::setExperienceList(const LLSD& experiences) mExperiencesList->addItem(item, public_key); } } + +/*static*/ +void LLPanelGroupExperiences::groupExperiencesResults(LLHandle<LLPanelGroupExperiences> handle, const LLSD &experiences) +{ + if (handle.isDead()) + { + return; + } + + LLPanelGroupExperiences* panel = handle.get(); + if (panel) + { + panel->setExperienceList(experiences); + } + +} diff --git a/indra/newview/llpanelgroupexperiences.h b/indra/newview/llpanelgroupexperiences.h index ae1ecc1ac5..7c79f77332 100644 --- a/indra/newview/llpanelgroupexperiences.h +++ b/indra/newview/llpanelgroupexperiences.h @@ -48,6 +48,9 @@ public: protected: LLFlatListView* mExperiencesList; + +private: + static void groupExperiencesResults(LLHandle<LLPanelGroupExperiences>, const LLSD &); }; #endif diff --git a/indra/newview/llpanellandmarks.cpp b/indra/newview/llpanellandmarks.cpp index 1d73d4bd6e..cd1dc0f070 100644 --- 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 8374eea2e0..f8a5bbb036 100644 --- a/indra/newview/llpanellogin.cpp +++ b/indra/newview/llpanellogin.cpp @@ -40,7 +40,6 @@ #include "llcheckboxctrl.h" #include "llcommandhandler.h" // for secondlife:///app/login/ #include "llcombobox.h" -#include "llcurl.h" #include "llviewercontrol.h" #include "llfloaterpreference.h" #include "llfocusmgr.h" @@ -59,7 +58,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" @@ -436,17 +434,10 @@ void LLPanelLogin::show(const LLRect &rect, void (*callback)(S32 option, void* user_data), void* callback_data) { - // instance management - if (LLPanelLogin::sInstance) - { - LL_WARNS("AppInit") << "Duplicate instance of login view deleted" << LL_ENDL; - // Don't leave bad pointer in gFocusMgr - gFocusMgr.setDefaultKeyboardFocus(NULL); - - delete LLPanelLogin::sInstance; - } - - new LLPanelLogin(rect, callback, callback_data); + if (!LLPanelLogin::sInstance) + { + new LLPanelLogin(rect, callback, callback_data); + } if( !gFocusMgr.getKeyboardFocus() ) { diff --git a/indra/newview/llpanelme.cpp b/indra/newview/llpanelme.cpp index cedd3025fc..55e4ffff5e 100644 --- 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 32b72fdd68..0c70aa87c2 100644 --- 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/llpanelsnapshotpostcard.cpp b/indra/newview/llpanelsnapshotpostcard.cpp index 8e37b1418c..e4f39aac04 100644 --- a/indra/newview/llpanelsnapshotpostcard.cpp +++ b/indra/newview/llpanelsnapshotpostcard.cpp @@ -40,6 +40,7 @@ #include "llpostcard.h" #include "llviewercontrol.h" // gSavedSettings #include "llviewerwindow.h" +#include "llviewerregion.h" #include <boost/regex.hpp> @@ -67,7 +68,8 @@ private: /*virtual*/ void updateControls(const LLSD& info); bool missingSubjMsgAlertCallback(const LLSD& notification, const LLSD& response); - void sendPostcard(); + static void sendPostcardFinished(LLSD result); + void sendPostcard(); void onMsgFormFocusRecieved(); void onFormatComboCommit(LLUICtrl* ctrl); @@ -166,24 +168,44 @@ bool LLPanelSnapshotPostcard::missingSubjMsgAlertCallback(const LLSD& notificati } -void LLPanelSnapshotPostcard::sendPostcard() +void LLPanelSnapshotPostcard::sendPostcardFinished(LLSD result) { - std::string to(getChild<LLUICtrl>("to_form")->getValue().asString()); - std::string subject(getChild<LLUICtrl>("subject_form")->getValue().asString()); + LL_WARNS() << result << LL_ENDL; - LLSD postcard = LLSD::emptyMap(); - postcard["pos-global"] = LLFloaterSnapshot::getPosTakenGlobal().getValue(); - postcard["to"] = to; - postcard["from"] = mAgentEmail; - postcard["name"] = getChild<LLUICtrl>("name_form")->getValue().asString(); - postcard["subject"] = subject; - postcard["msg"] = getChild<LLUICtrl>("msg_form")->getValue().asString(); - LLPostCard::send(LLFloaterSnapshot::getImageData(), postcard); + std::string state = result["state"].asString(); - // Give user feedback of the event. - gViewerWindow->playSnapshotAnimAndSound(); + LLPostCard::reportPostResult((state == "complete")); +} - LLFloaterSnapshot::postSave(); + +void LLPanelSnapshotPostcard::sendPostcard() +{ + // upload the image + std::string url = gAgent.getRegion()->getCapability("SendPostcard"); + if (!url.empty()) + { + LLResourceUploadInfo::ptr_t uploadInfo(new LLPostcardUploadInfo( + mAgentEmail, + getChild<LLUICtrl>("name_form")->getValue().asString(), + getChild<LLUICtrl>("to_form")->getValue().asString(), + getChild<LLUICtrl>("subject_form")->getValue().asString(), + getChild<LLUICtrl>("msg_form")->getValue().asString(), + LLFloaterSnapshot::getPosTakenGlobal(), + LLFloaterSnapshot::getImageData(), + boost::bind(&LLPanelSnapshotPostcard::sendPostcardFinished, _4))); + + LLViewerAssetUpload::EnqueueInventoryUpload(url, uploadInfo); + } + else + { + LL_WARNS() << "Postcards unavailable in this region." << LL_ENDL; + } + + + // Give user feedback of the event. + gViewerWindow->playSnapshotAnimAndSound(); + + LLFloaterSnapshot::postSave(); } void LLPanelSnapshotPostcard::onMsgFormFocusRecieved() diff --git a/indra/newview/llpanelvoicedevicesettings.cpp b/indra/newview/llpanelvoicedevicesettings.cpp index 3946d6a63b..07f8045546 100644 --- a/indra/newview/llpanelvoicedevicesettings.cpp +++ b/indra/newview/llpanelvoicedevicesettings.cpp @@ -51,7 +51,7 @@ LLPanelVoiceDeviceSettings::LLPanelVoiceDeviceSettings() mCtrlOutputDevices = NULL; mInputDevice = gSavedSettings.getString("VoiceInputAudioDevice"); mOutputDevice = gSavedSettings.getString("VoiceOutputAudioDevice"); - mDevicesUpdated = FALSE; + mDevicesUpdated = FALSE; //obsolete mUseTuningMode = true; // grab "live" mic volume level @@ -80,6 +80,10 @@ BOOL LLPanelVoiceDeviceSettings::postBuild() mLocalizedDeviceNames[DEFAULT_DEVICE] = getString("default_text"); mLocalizedDeviceNames["No Device"] = getString("name_no_device"); mLocalizedDeviceNames["Default System Device"] = getString("name_default_system_device"); + + mCtrlOutputDevices->setMouseDownCallback(boost::bind(&LLPanelVoiceDeviceSettings::onOutputDevicesClicked, this)); + mCtrlInputDevices->setMouseDownCallback(boost::bind(&LLPanelVoiceDeviceSettings::onInputDevicesClicked, this)); + return TRUE; } @@ -226,9 +230,8 @@ void LLPanelVoiceDeviceSettings::refresh() mCtrlOutputDevices->add(getLocalizedDeviceName(mOutputDevice), mOutputDevice, ADD_BOTTOM); mCtrlOutputDevices->setValue(mOutputDevice); } - mDevicesUpdated = FALSE; } - else if (!mDevicesUpdated) + else if (LLVoiceClient::getInstance()->deviceSettingsUpdated()) { LLVoiceDeviceList::const_iterator iter; @@ -272,7 +275,6 @@ void LLPanelVoiceDeviceSettings::refresh() mOutputDevice = DEFAULT_DEVICE; } } - mDevicesUpdated = TRUE; } } @@ -281,7 +283,6 @@ void LLPanelVoiceDeviceSettings::initialize() mInputDevice = gSavedSettings.getString("VoiceInputAudioDevice"); mOutputDevice = gSavedSettings.getString("VoiceOutputAudioDevice"); mMicVolume = gSavedSettings.getF32("AudioLevelMic"); - mDevicesUpdated = FALSE; // ask for new device enumeration LLVoiceClient::getInstance()->refreshDeviceLists(); @@ -314,8 +315,8 @@ void LLPanelVoiceDeviceSettings::onCommitInputDevice() { if(LLVoiceClient::getInstance()) { - LLVoiceClient::getInstance()->setCaptureDevice( - mCtrlInputDevices->getValue().asString()); + mInputDevice = mCtrlInputDevices->getValue().asString(); + LLVoiceClient::getInstance()->setRenderDevice(mInputDevice); } } @@ -323,7 +324,18 @@ void LLPanelVoiceDeviceSettings::onCommitOutputDevice() { if(LLVoiceClient::getInstance()) { - LLVoiceClient::getInstance()->setRenderDevice( - mCtrlInputDevices->getValue().asString()); + + mOutputDevice = mCtrlOutputDevices->getValue().asString(); + LLVoiceClient::getInstance()->setRenderDevice(mOutputDevice); } } + +void LLPanelVoiceDeviceSettings::onOutputDevicesClicked() +{ + LLVoiceClient::getInstance()->refreshDeviceLists(false); // fill in the pop up menus again if needed. +} + +void LLPanelVoiceDeviceSettings::onInputDevicesClicked() +{ + LLVoiceClient::getInstance()->refreshDeviceLists(false); // fill in the pop up menus again if needed. +} diff --git a/indra/newview/llpanelvoicedevicesettings.h b/indra/newview/llpanelvoicedevicesettings.h index 83464f476a..355bc02b05 100644 --- a/indra/newview/llpanelvoicedevicesettings.h +++ b/indra/newview/llpanelvoicedevicesettings.h @@ -53,6 +53,8 @@ protected: void onCommitInputDevice(); void onCommitOutputDevice(); + void onOutputDevicesClicked(); + void onInputDevicesClicked(); F32 mMicVolume; std::string mInputDevice; diff --git a/indra/newview/llpathfindingcharacterlist.cpp b/indra/newview/llpathfindingcharacterlist.cpp index 12340cebfa..d63daaa59b 100644 --- a/indra/newview/llpathfindingcharacterlist.cpp +++ b/indra/newview/llpathfindingcharacterlist.cpp @@ -40,14 +40,14 @@ //--------------------------------------------------------------------------- LLPathfindingCharacterList::LLPathfindingCharacterList() - : LLPathfindingObjectList() + : LLPathfindingObjectList() { } LLPathfindingCharacterList::LLPathfindingCharacterList(const LLSD& pCharacterListData) - : LLPathfindingObjectList() + : LLPathfindingObjectList() { - parseCharacterListData(pCharacterListData); + parseCharacterListData(pCharacterListData); } LLPathfindingCharacterList::~LLPathfindingCharacterList() @@ -56,14 +56,16 @@ LLPathfindingCharacterList::~LLPathfindingCharacterList() void LLPathfindingCharacterList::parseCharacterListData(const LLSD& pCharacterListData) { - LLPathfindingObjectMap &objectMap = getObjectMap(); + LLPathfindingObjectMap &objectMap = getObjectMap(); - for (LLSD::map_const_iterator characterDataIter = pCharacterListData.beginMap(); - characterDataIter != pCharacterListData.endMap(); ++characterDataIter) - { - const std::string& uuid(characterDataIter->first); - const LLSD& characterData = characterDataIter->second; - LLPathfindingObjectPtr character(new LLPathfindingCharacter(uuid, characterData)); - objectMap.insert(std::pair<std::string, LLPathfindingObjectPtr>(uuid, character)); - } + for (LLSD::map_const_iterator characterDataIter = pCharacterListData.beginMap(); + characterDataIter != pCharacterListData.endMap(); ++characterDataIter) + { + if (characterDataIter->second.isUndefined()) + continue; + const std::string& uuid(characterDataIter->first); + const LLSD& characterData = characterDataIter->second; + LLPathfindingObjectPtr character(new LLPathfindingCharacter(uuid, characterData)); + objectMap.insert(std::pair<std::string, LLPathfindingObjectPtr>(uuid, character)); + } } diff --git a/indra/newview/llpathfindingmanager.cpp b/indra/newview/llpathfindingmanager.cpp index 4977a72dc6..711a869e82 100644 --- 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->getAndSuspend(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->postAndSuspend(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->getAndSuspend(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->postAndSuspend(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->getAndSuspend(httpRequest, url); + } + else + { + result = httpAdapter->putAndSuspend(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->getAndSuspend(httpRequest, url); + } + else + { + result = httpAdapter->putAndSuspend(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->getAndSuspend(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 100644 --- 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/llpostcard.cpp b/indra/newview/llpostcard.cpp index 5987044bff..2e639b56eb 100644 --- a/indra/newview/llpostcard.cpp +++ b/indra/newview/llpostcard.cpp @@ -36,84 +36,36 @@ #include "llagent.h" #include "llassetstorage.h" -#include "llassetuploadresponders.h" +#include "llviewerassetupload.h" /////////////////////////////////////////////////////////////////////////////// -// misc -static void postcard_upload_callback(const LLUUID& asset_id, void *user_data, S32 result, LLExtStat ext_status) +LLPostcardUploadInfo::LLPostcardUploadInfo(std::string emailFrom, std::string nameFrom, std::string emailTo, + std::string subject, std::string message, LLVector3d globalPosition, + LLPointer<LLImageFormatted> image, invnUploadFinish_f finish) : + LLBufferedAssetUploadInfo(LLUUID::null, image, finish), + mEmailFrom(emailFrom), + mNameFrom(nameFrom), + mEmailTo(emailTo), + mSubject(subject), + mMessage(message), + mGlobalPosition(globalPosition) { - LLSD* postcard_data = (LLSD*)user_data; - - if (result) - { - // TODO: display the error messages in UI - LL_WARNS() << "Failed to send postcard: " << LLAssetStorage::getErrorString(result) << LL_ENDL; - LLPostCard::reportPostResult(false); - } - else - { - // only create the postcard once the upload succeeds - - // request the postcard - const LLSD& data = *postcard_data; - LLMessageSystem* msg = gMessageSystem; - msg->newMessage("SendPostcard"); - msg->nextBlock("AgentData"); - msg->addUUID("AgentID", gAgent.getID()); - msg->addUUID("SessionID", gAgent.getSessionID()); - msg->addUUID("AssetID", data["asset-id"].asUUID()); - msg->addVector3d("PosGlobal", LLVector3d(data["pos-global"])); - msg->addString("To", data["to"]); - msg->addString("From", data["from"]); - msg->addString("Name", data["name"]); - msg->addString("Subject", data["subject"]); - msg->addString("Msg", data["msg"]); - msg->addBOOL("AllowPublish", FALSE); - msg->addBOOL("MaturePublish", FALSE); - gAgent.sendReliableMessage(); - - LLPostCard::reportPostResult(true); - } - - delete postcard_data; } - -/////////////////////////////////////////////////////////////////////////////// -// LLPostcardSendResponder - -class LLPostcardSendResponder : public LLAssetUploadResponder +LLSD LLPostcardUploadInfo::generatePostBody() { - LOG_CLASS(LLPostcardSendResponder); - -public: - LLPostcardSendResponder(const LLSD &post_data, - const LLUUID& vfile_id, - LLAssetType::EType asset_type): - LLAssetUploadResponder(post_data, vfile_id, asset_type) - { - } - - /*virtual*/ void httpFailure() - { - LL_WARNS() << "Sending postcard failed, status: " << getStatus() << LL_ENDL; - LLPostCard::reportPostResult(false); - } - - /*virtual*/ void uploadComplete(const LLSD& content) - { - LL_INFOS() << "Postcard sent" << LL_ENDL; - LL_DEBUGS("Snapshots") << "content: " << content << LL_ENDL; - LLPostCard::reportPostResult(true); - } + LLSD postcard = LLSD::emptyMap(); + postcard["pos-global"] = mGlobalPosition.getValue(); + postcard["to"] = mEmailTo; + postcard["from"] = mEmailFrom; + postcard["name"] = mNameFrom; + postcard["subject"] = mSubject; + postcard["msg"] = mMessage; + + return postcard; +} - /*virtual*/ void uploadFailure(const LLSD& content) - { - LL_WARNS() << "Sending postcard failed: " << content << LL_ENDL; - LLPostCard::reportPostResult(false); - } -}; /////////////////////////////////////////////////////////////////////////////// // LLPostCard @@ -121,38 +73,6 @@ public: LLPostCard::result_callback_t LLPostCard::mResultCallback; // static -void LLPostCard::send(LLPointer<LLImageFormatted> image, const LLSD& postcard_data) -{ - LLTransactionID transaction_id; - LLAssetID asset_id; - - transaction_id.generate(); - asset_id = transaction_id.makeAssetID(gAgent.getSecureSessionID()); - LLVFile::writeFile(image->getData(), image->getDataSize(), gVFS, asset_id, LLAssetType::AT_IMAGE_JPEG); - - // upload the image - std::string url = gAgent.getRegion()->getCapability("SendPostcard"); - if (!url.empty()) - { - LL_INFOS() << "Sending postcard via capability" << LL_ENDL; - // the capability already encodes: agent ID, region ID - LL_DEBUGS("Snapshots") << "url: " << url << LL_ENDL; - LL_DEBUGS("Snapshots") << "body: " << postcard_data << LL_ENDL; - LL_DEBUGS("Snapshots") << "data size: " << image->getDataSize() << LL_ENDL; - LLHTTPClient::post(url, postcard_data, - new LLPostcardSendResponder(postcard_data, asset_id, LLAssetType::AT_IMAGE_JPEG)); - } - else - { - LL_INFOS() << "Sending postcard" << LL_ENDL; - LLSD* data = new LLSD(postcard_data); - (*data)["asset-id"] = asset_id; - gAssetStorage->storeAssetData(transaction_id, LLAssetType::AT_IMAGE_JPEG, - &postcard_upload_callback, (void *)data, FALSE); - } -} - -// static void LLPostCard::reportPostResult(bool ok) { if (mResultCallback) diff --git a/indra/newview/llpostcard.h b/indra/newview/llpostcard.h index 0eb118b906..24157be636 100644 --- a/indra/newview/llpostcard.h +++ b/indra/newview/llpostcard.h @@ -29,7 +29,12 @@ #include "llimage.h" #include "lluuid.h" +#include "llviewerassetupload.h" +/// *TODO$: this LLPostCard class is a hold over and should be removed. Right now +/// all it does is hold a pointer to a call back function which is invoked by +/// llpanelsnapshotpostcard's finish function. (and all that call back does is +/// set the status in the floater. class LLPostCard { LOG_CLASS(LLPostCard); @@ -37,7 +42,6 @@ class LLPostCard public: typedef boost::function<void(bool ok)> result_callback_t; - static void send(LLPointer<LLImageFormatted> image, const LLSD& postcard_data); static void setPostResultCallback(result_callback_t cb) { mResultCallback = cb; } static void reportPostResult(bool ok); @@ -45,4 +49,24 @@ private: static result_callback_t mResultCallback; }; + +class LLPostcardUploadInfo : public LLBufferedAssetUploadInfo +{ +public: + LLPostcardUploadInfo(std::string emailFrom, std::string nameFrom, std::string emailTo, + std::string subject, std::string message, LLVector3d globalPosition, + LLPointer<LLImageFormatted> image, invnUploadFinish_f finish); + + virtual LLSD generatePostBody(); +private: + std::string mEmailFrom; + std::string mNameFrom; + std::string mEmailTo; + std::string mSubject; + std::string mMessage; + LLVector3d mGlobalPosition; + +}; + + #endif // LL_LLPOSTCARD_H diff --git a/indra/newview/llpreviewgesture.cpp b/indra/newview/llpreviewgesture.cpp index c378738b05..ff9a70d05c 100644 --- a/indra/newview/llpreviewgesture.cpp +++ b/indra/newview/llpreviewgesture.cpp @@ -31,7 +31,6 @@ #include "llanimstatelabels.h" #include "llanimationstates.h" #include "llappviewer.h" // gVFS -#include "llassetuploadresponders.h" #include "llcheckboxctrl.h" #include "llcombobox.h" #include "lldatapacker.h" @@ -52,6 +51,7 @@ #include "llviewerobjectlist.h" #include "llviewerregion.h" #include "llviewerstats.h" +#include "llviewerassetupload.h" std::string NONE_LABEL; std::string SHIFT_LABEL; @@ -1015,6 +1015,27 @@ struct LLSaveInfo }; +void LLPreviewGesture::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 +1049,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(&LLPreviewGesture::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/llpreviewgesture.h b/indra/newview/llpreviewgesture.h index 7ce5706a0d..3ba4f56295 100644 --- a/indra/newview/llpreviewgesture.h +++ b/indra/newview/llpreviewgesture.h @@ -132,6 +132,7 @@ protected: static void onDonePreview(LLMultiGesture* gesture, void* data); + static void finishInventoryUpload(LLUUID itemId, LLUUID newAssetId); private: // LLPreview contains mDescEditor LLLineEditor* mTriggerEditor; diff --git a/indra/newview/llpreviewnotecard.cpp b/indra/newview/llpreviewnotecard.cpp index f100c996b3..20c43bc432 100644 --- a/indra/newview/llpreviewnotecard.cpp +++ b/indra/newview/llpreviewnotecard.cpp @@ -31,7 +31,6 @@ #include "llinventory.h" #include "llagent.h" -#include "llassetuploadresponders.h" #include "lldraghandle.h" #include "llviewerwindow.h" #include "llbutton.h" @@ -56,6 +55,7 @@ #include "llappviewer.h" // app_abort_quit() #include "lllineeditor.h" #include "lluictrlfactory.h" +#include "llviewerassetupload.h" ///---------------------------------------------------------------------------- /// Class LLPreviewNotecard @@ -232,7 +232,7 @@ void LLPreviewNotecard::loadAsset() } else { - LLHost source_sim = LLHost::invalid; + LLHost source_sim = LLHost(); LLSD* user_data = new LLSD(); if (mObjectUUID.notNull()) { @@ -407,6 +407,35 @@ struct LLSaveNotecardInfo } }; +void LLPreviewNotecard::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"); @@ -419,14 +448,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)) { @@ -435,52 +456,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(&LLPreviewNotecard::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(&LLPreviewNotecard::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/llpreviewnotecard.h b/indra/newview/llpreviewnotecard.h index 1cf08dedd6..ba571995f6 100644 --- a/indra/newview/llpreviewnotecard.h +++ b/indra/newview/llpreviewnotecard.h @@ -95,6 +95,8 @@ protected: bool handleSaveChangesDialog(const LLSD& notification, const LLSD& response); bool handleConfirmDeleteDialog(const LLSD& notification, const LLSD& response); + static void finishInventoryUpload(LLUUID itemId, LLUUID newAssetId, LLUUID newItemId); + protected: LLViewerTextEditor* mEditor; LLButton* mSaveBtn; diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp index 9c115ee711..89f01c58c6 100644 --- a/indra/newview/llpreviewscript.cpp +++ b/indra/newview/llpreviewscript.cpp @@ -29,7 +29,6 @@ #include "llpreviewscript.h" #include "llassetstorage.h" -#include "llassetuploadresponders.h" #include "llbutton.h" #include "llcheckboxctrl.h" #include "llcombobox.h" @@ -87,7 +86,7 @@ #include "llfloatergotoline.h" #include "llexperiencecache.h" #include "llfloaterexperienceprofile.h" -#include "llexperienceassociationresponder.h" +#include "llviewerassetupload.h" const std::string HELLO_LSL = "default\n" @@ -117,26 +116,6 @@ static bool have_script_upload_cap(LLUUID& object_id) return object && (! object->getRegion()->getCapability("UpdateScriptTask").empty()); } - -class ExperienceResponder : public LLHTTPClient::Responder -{ -public: - ExperienceResponder(const LLHandle<LLLiveLSLEditor>& parent):mParent(parent) - { - } - - LLHandle<LLLiveLSLEditor> mParent; - - /*virtual*/ void httpSuccess() - { - LLLiveLSLEditor* parent = mParent.get(); - if(!parent) - return; - - parent->setExperienceIds(getContent()["experience_ids"]); - } -}; - /// --------------------------------------------------------------------------- /// LLLiveLSLFile /// --------------------------------------------------------------------------- @@ -1345,7 +1324,7 @@ void LLLiveLSLEditor::buildExperienceList() position = ADD_TOP; } - const LLSD& experience = LLExperienceCache::get(id); + const LLSD& experience = LLExperienceCache::instance().get(id); if(experience.isUndefined()) { mExperiences->add(getString("loading"), id, position); @@ -1364,7 +1343,7 @@ void LLLiveLSLEditor::buildExperienceList() if(!foundAssociated ) { - const LLSD& experience = LLExperienceCache::get(associated); + const LLSD& experience = LLExperienceCache::instance().get(associated); if(experience.isDefined()) { std::string experience_name_string = experience[LLExperienceCache::NAME].asString(); @@ -1385,7 +1364,7 @@ void LLLiveLSLEditor::buildExperienceList() if(last.notNull()) { mExperiences->setEnabled(FALSE); - LLExperienceCache::get(last, boost::bind(&LLLiveLSLEditor::buildExperienceList, this)); + LLExperienceCache::instance().get(last, boost::bind(&LLLiveLSLEditor::buildExperienceList, this)); } else { @@ -1417,11 +1396,23 @@ void LLLiveLSLEditor::requestExperiences() std::string lookup_url=region->getCapability("GetCreatorExperiences"); if(!lookup_url.empty()) { - LLHTTPClient::get(lookup_url, new ExperienceResponder(getDerivedHandle<LLLiveLSLEditor>())); + LLCoreHttpUtil::HttpCoroutineAdapter::completionCallback_t success = + boost::bind(&LLLiveLSLEditor::receiveExperienceIds, _1, getDerivedHandle<LLLiveLSLEditor>()); + + LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpGet(lookup_url, success); } } } +/*static*/ +void LLLiveLSLEditor::receiveExperienceIds(LLSD result, LLHandle<LLLiveLSLEditor> hparent) +{ + LLLiveLSLEditor* parent = hparent.get(); + if (!parent) + return; + + parent->setExperienceIds(result["experience_ids"]); +} /// --------------------------------------------------------------------------- @@ -1572,7 +1563,7 @@ void LLPreviewLSL::loadAsset() if (gAgent.isGodlike() || (is_copyable && (is_modifiable || is_library))) { LLUUID* new_uuid = new LLUUID(mItemUUID); - gAssetStorage->getInvItemAsset(LLHost::invalid, + gAssetStorage->getInvItemAsset(LLHost(), gAgent.getID(), gAgent.getSessionID(), item->getPermissions().getOwner(), @@ -1642,59 +1633,63 @@ void LLPreviewLSL::onSave(void* userdata, BOOL close_after_save) self->saveIfNeeded(); } +/*static*/ +void LLPreviewLSL::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*/) { - // LL_INFOS() << "LLPreviewLSL::saveIfNeeded()" << LL_ENDL; - if(!mScriptEd->hasChanged()) - { - return; - } + if (!mScriptEd->hasChanged()) + { + return; + } - mPendingUploads = 0; - mScriptEd->mErrorList->deleteAllItems(); - mScriptEd->mEditor->makePristine(); + mPendingUploads = 0; + mScriptEd->mErrorList->deleteAllItems(); + mScriptEd->mEditor->makePristine(); - // 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"; + if (sync) + { + mScriptEd->sync(); + } - mScriptEd->writeToFile(filename); + 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(&LLPreviewLSL::finishedLSLUpload, _1, _4); - if (sync) - { - mScriptEd->sync(); - } + LLResourceUploadInfo::ptr_t uploadInfo(new LLScriptAssetUpload(mItemUUID, buffer, proc)); - 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()) - { - uploadAssetViaCaps(url, filename, mItemUUID); - } - } + LLViewerAssetUpload::EnqueueInventoryUpload(url, uploadInfo); + } + } } -void LLPreviewLSL::uploadAssetViaCaps(const std::string& url, - const std::string& filename, - const LLUUID& item_id) -{ - LL_INFOS() << "Update Agent Inventory via capability" << LL_ENDL; - LLSD body; - body["item_id"] = item_id; - body["target"] = "lsl2"; - LLHTTPClient::post(url, body, new LLUpdateAgentInventoryResponder(body, filename, LLAssetType::AT_LSL_TEXT)); -} // static void LLPreviewLSL::onSaveComplete(const LLUUID& asset_uuid, void* user_data, S32 status, LLExtStat ext_status) // StoreAssetData callback (fixed) @@ -1951,8 +1946,9 @@ void LLLiveLSLEditor::loadAsset() if(item) { - ExperienceAssociationResponder::fetchAssociatedExperience(item->getParentUUID(), item->getUUID(), boost::bind(&LLLiveLSLEditor::setAssociatedExperience, getDerivedHandle<LLLiveLSLEditor>(), _1)); - + LLExperienceCache::instance().fetchAssociatedExperience(item->getParentUUID(), item->getUUID(), + boost::bind(&LLLiveLSLEditor::setAssociatedExperience, getDerivedHandle<LLLiveLSLEditor>(), _1)); + bool isGodlike = gAgent.isGodlike(); bool copyManipulate = gAgent.allowOperation(PERM_COPY, item->getPermissions(), GP_OBJECT_MANIPULATE); mIsModifiable = gAgent.allowOperation(PERM_MODIFY, item->getPermissions(), GP_OBJECT_MANIPULATE); @@ -2237,6 +2233,33 @@ LLLiveLSLSaveData::LLLiveLSLSaveData(const LLUUID& id, mItem = new LLViewerInventoryItem(item); } + +/*static*/ +void LLLiveLSLEditor::finishLSLUpload(LLUUID itemId, LLUUID taskId, LLUUID newAssetId, LLSD response, bool isRunning) +{ + LLSD floater_key; + floater_key["taskid"] = taskId; + floater_key["itemid"] = itemId; + + LLLiveLSLEditor* preview = LLFloaterReg::findTypedInstance<LLLiveLSLEditor>("preview_scriptedit", floater_key); + if (preview) + { + preview->mItem->setAssetUUID(newAssetId); + + // Bytecode save completed + if (response["compiled"]) + { + preview->callbackLSLCompileSucceeded(taskId, itemId, isRunning); + } + else + { + preview->callbackLSLCompileFailed(response["errors"]); + } + } + +} + + // virtual void LLLiveLSLEditor::saveIfNeeded(bool sync /*= true*/) { @@ -2247,7 +2270,7 @@ void LLLiveLSLEditor::saveIfNeeded(bool sync /*= true*/) return; } - if(mItem.isNull() || !mItem->isFinished()) + if (mItem.isNull() || !mItem->isFinished()) { // $NOTE: While the error message may not be exactly correct, // it's pretty close. @@ -2255,77 +2278,54 @@ void LLLiveLSLEditor::saveIfNeeded(bool sync /*= true*/) return; } - // get the latest info about it. We used to be losing the script - // name on save, because the viewer object version of the item, - // and the editor version would get out of synch. Here's a good - // place to synch them back up. - LLInventoryItem* inv_item = dynamic_cast<LLInventoryItem*>(object->getInventoryObject(mItemUUID)); - if(inv_item) - { - mItem->copyItem(inv_item); - } + // get the latest info about it. We used to be losing the script + // name on save, because the viewer object version of the item, + // and the editor version would get out of synch. Here's a good + // place to synch them back up. + LLInventoryItem* inv_item = dynamic_cast<LLInventoryItem*>(object->getInventoryObject(mItemUUID)); + if (inv_item) + { + mItem->copyItem(inv_item); + } - // Don't need to save if we're pristine - if(!mScriptEd->hasChanged()) - { - return; - } + // Don't need to save if we're pristine + if(!mScriptEd->hasChanged()) + { + return; + } - mPendingUploads = 0; + mPendingUploads = 0; - // save the script - mScriptEd->enableSave(FALSE); - mScriptEd->mEditor->makePristine(); - mScriptEd->mErrorList->deleteAllItems(); + // save the script + mScriptEd->enableSave(FALSE); + mScriptEd->mEditor->makePristine(); + mScriptEd->mErrorList->deleteAllItems(); + mScriptEd->mEditor->makePristine(); - // set up the save on the local machine. - mScriptEd->mEditor->makePristine(); - 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 = llformat("%s.lsl", filepath.c_str()); + if (sync) + { + mScriptEd->sync(); + } + bool isRunning = getChild<LLCheckBoxCtrl>("running")->get(); + getWindow()->incBusyCount(); + mPendingUploads++; - mItem->setAssetUUID(asset_id); - mItem->setTransactionID(tid); + std::string url = object->getRegion()->getCapability("UpdateScriptTask"); - mScriptEd->writeToFile(filename); + if (!url.empty()) + { + std::string buffer(mScriptEd->mEditor->getText()); + LLBufferedAssetUploadInfo::taskUploadFinish_f proc = boost::bind(&LLLiveLSLEditor::finishLSLUpload, _1, _2, _3, _4, isRunning); - if (sync) - { - mScriptEd->sync(); - } - - // save it out to asset server - std::string url = object->getRegion()->getCapability("UpdateScriptTask"); - getWindow()->incBusyCount(); - mPendingUploads++; - BOOL is_running = getChild<LLCheckBoxCtrl>( "running")->get(); - mIsSaving = TRUE; - if (!url.empty()) - { - uploadAssetViaCaps(url, filename, mObjectUUID, mItemUUID, is_running, mScriptEd->getAssociatedExperience()); - } -} - -void LLLiveLSLEditor::uploadAssetViaCaps(const std::string& url, - const std::string& filename, - const LLUUID& task_id, - const LLUUID& item_id, - BOOL is_running, - const LLUUID& experience_public_id ) -{ - LL_INFOS() << "Update Task Inventory via capability " << url << LL_ENDL; - LLSD body; - body["task_id"] = task_id; - body["item_id"] = item_id; - body["is_script_running"] = is_running; - body["target"] = monoChecked() ? "mono" : "lsl2"; - body["experience"] = experience_public_id; - LLHTTPClient::post(url, body, - new LLUpdateTaskInventoryResponder(body, filename, LLAssetType::AT_LSL_TEXT)); + LLResourceUploadInfo::ptr_t uploadInfo(new LLScriptAssetUpload(mObjectUUID, mItemUUID, + monoChecked() ? LLScriptAssetUpload::MONO : LLScriptAssetUpload::LSL2, + isRunning, mScriptEd->getAssociatedExperience(), buffer, proc)); + + LLViewerAssetUpload::EnqueueInventoryUpload(url, uploadInfo); + } } + void LLLiveLSLEditor::onSaveTextComplete(const LLUUID& asset_uuid, void* user_data, S32 status, LLExtStat ext_status) // StoreAssetData callback (fixed) { LLLiveLSLSaveData* data = (LLLiveLSLSaveData*)user_data; diff --git a/indra/newview/llpreviewscript.h b/indra/newview/llpreviewscript.h index d6ee692b10..fc2a56c0a4 100644 --- a/indra/newview/llpreviewscript.h +++ b/indra/newview/llpreviewscript.h @@ -203,9 +203,10 @@ protected: virtual void loadAsset(); /*virtual*/ void saveIfNeeded(bool sync = true); - void uploadAssetViaCaps(const std::string& url, - const std::string& filename, - const LLUUID& item_id); + + void uploadAssetLegacy(const std::string& filename, + const LLUUID& item_id, + const LLTransactionID& tid); static void onSearchReplace(void* userdata); static void onLoad(void* userdata); @@ -220,7 +221,7 @@ protected: protected: static void* createScriptEdPanel(void* userdata); - + static void finishedLSLUpload(LLUUID itemId, LLSD response); protected: // Can safely close only after both text and bytecode are uploaded @@ -267,12 +268,10 @@ private: virtual void loadAsset(); void loadAsset(BOOL is_new); /*virtual*/ void saveIfNeeded(bool sync = true); - void uploadAssetViaCaps(const std::string& url, - const std::string& filename, - const LLUUID& task_id, - const LLUUID& item_id, - BOOL is_running, - const LLUUID& experience_public_id); + void uploadAssetLegacy(const std::string& filename, + LLViewerObject* object, + const LLTransactionID& tid, + BOOL is_running); BOOL monoChecked() const; @@ -296,6 +295,9 @@ private: static void onMonoCheckboxClicked(LLUICtrl*, void* userdata); + static void finishLSLUpload(LLUUID itemId, LLUUID taskId, LLUUID newAssetId, LLSD response, bool isRunning); + static void receiveExperienceIds(LLSD result, LLHandle<LLLiveLSLEditor> parent); + private: bool mIsNew; //LLUUID mTransmitID; diff --git a/indra/newview/llproductinforequest.cpp b/indra/newview/llproductinforequest.cpp index e92bf4590d..b663df4aae 100644 --- 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->getAndSuspend(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 100644 --- 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/llprogressview.cpp b/indra/newview/llprogressview.cpp index 1257ee7f94..c17b86783d 100644 --- a/indra/newview/llprogressview.cpp +++ b/indra/newview/llprogressview.cpp @@ -175,6 +175,10 @@ void LLProgressView::setStartupComplete() void LLProgressView::setVisible(BOOL visible) { + if (!visible && mFadeFromLoginTimer.getStarted()) + { + mFadeFromLoginTimer.stop(); + } // hiding progress view if (getVisible() && !visible) { diff --git a/indra/newview/llremoteparcelrequest.cpp b/indra/newview/llremoteparcelrequest.cpp index 29dcc12f9e..055ccd5818 100644 --- 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->postAndSuspend(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 100644 --- 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/llsecapi.cpp b/indra/newview/llsecapi.cpp index 43bb7b1596..4f9f83b6f2 100644 --- a/indra/newview/llsecapi.cpp +++ b/indra/newview/llsecapi.cpp @@ -32,7 +32,6 @@ #include <openssl/evp.h> #include <openssl/err.h> #include <map> -#include "llhttpclient.h" @@ -99,46 +98,6 @@ std::ostream& operator <<(std::ostream& s, const LLCredential& cred) return s << (std::string)cred; } - -// secapiSSLCertVerifyCallback -// basic callback called when a cert verification is requested. -// calls SECAPI to validate the context -// not initialized in the above initialization function, due to unit tests -// see llappviewer - -int secapiSSLCertVerifyCallback(X509_STORE_CTX *ctx, void *param) -{ - LLURLRequest *req = (LLURLRequest *)param; - LLPointer<LLCertificateStore> store = gSecAPIHandler->getCertificateStore(""); - LLPointer<LLCertificateChain> chain = gSecAPIHandler->getCertificateChain(ctx); - LLSD validation_params = LLSD::emptyMap(); - LLURI uri(req->getURL()); - validation_params[CERT_HOSTNAME] = uri.hostName(); - try - { - // we rely on libcurl to validate the hostname, as libcurl does more extensive validation - // leaving our hostname validation call mechanism for future additions with respect to - // OS native (Mac keyring, windows CAPI) validation. - store->validate(VALIDATION_POLICY_SSL & (~VALIDATION_POLICY_HOSTNAME), chain, validation_params); - } - catch (LLCertValidationTrustException& cert_exception) - { - LL_WARNS("AppInit") << "Cert not trusted: " << cert_exception.getMessage() << LL_ENDL; - return 0; - } - catch (LLCertException& cert_exception) - { - LL_WARNS("AppInit") << "cert error " << cert_exception.getMessage() << LL_ENDL; - return 0; - } - catch (...) - { - LL_WARNS("AppInit") << "cert error " << LL_ENDL; - return 0; - } - return 1; -} - LLSD LLCredential::getLoginParams() { LLSD result = LLSD::emptyMap(); diff --git a/indra/newview/llsecapi.h b/indra/newview/llsecapi.h index c01d318f56..6fe3ee31cf 100644 --- a/indra/newview/llsecapi.h +++ b/indra/newview/llsecapi.h @@ -489,7 +489,4 @@ void registerSecHandler(const std::string& handler_type, extern LLPointer<LLSecAPIHandler> gSecAPIHandler; -int secapiSSLCertVerifyCallback(X509_STORE_CTX *ctx, void *param); - - #endif // LL_SECAPI_H diff --git a/indra/newview/llsidepanelinventory.cpp b/indra/newview/llsidepanelinventory.cpp index 6743a78dcb..6bcae1e858 100644 --- a/indra/newview/llsidepanelinventory.cpp +++ b/indra/newview/llsidepanelinventory.cpp @@ -37,7 +37,6 @@ #include "llfloatersidepanelcontainer.h" #include "llfoldertype.h" #include "llfolderview.h" -#include "llhttpclient.h" #include "llinventorybridge.h" #include "llinventoryfunctions.h" #include "llinventorymodel.h" diff --git a/indra/newview/llsidepaneliteminfo.cpp b/indra/newview/llsidepaneliteminfo.cpp index a7cfc173af..12cbff888d 100644 --- a/indra/newview/llsidepaneliteminfo.cpp +++ b/indra/newview/llsidepaneliteminfo.cpp @@ -44,7 +44,6 @@ #include "llviewercontrol.h" #include "llviewerinventory.h" #include "llviewerobjectlist.h" -#include "llexperienceassociationresponder.h" #include "llexperiencecache.h" #include "lltrans.h" @@ -328,7 +327,9 @@ void LLSidepanelItemInfo::refreshFromItem(LLViewerInventoryItem* item) LLTextBox* tb = getChild<LLTextBox>("LabelItemExperience"); tb->setText(getString("loading_experience")); tb->setVisible(TRUE); - ExperienceAssociationResponder::fetchAssociatedExperience(item->getParentUUID(), item->getUUID(), boost::bind(&LLSidepanelItemInfo::setAssociatedExperience, getDerivedHandle<LLSidepanelItemInfo>(), _1)); + + LLExperienceCache::instance().fetchAssociatedExperience(item->getParentUUID(), item->getUUID(), + boost::bind(&LLSidepanelItemInfo::setAssociatedExperience, getDerivedHandle<LLSidepanelItemInfo>(), _1)); } ////////////////////// diff --git a/indra/newview/llsnapshotlivepreview.cpp b/indra/newview/llsnapshotlivepreview.cpp index 8561a89ae5..623565817d 100644 --- a/indra/newview/llsnapshotlivepreview.cpp +++ b/indra/newview/llsnapshotlivepreview.cpp @@ -1032,21 +1032,18 @@ 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; - upload_new_resource(tid, // tid - LLAssetType::AT_TEXTURE, - "Snapshot : " + pos_string, - "Taken by " + who_took_it + " at " + pos_string, - 0, - LLFolderType::FT_SNAPSHOT_CATEGORY, - LLInventoryType::IT_SNAPSHOT, - PERM_ALL, // Note: Snapshots to inventory is a special case of content upload - LLFloaterPerms::getGroupPerms("Uploads"), // that is more permissive than other uploads - LLFloaterPerms::getEveryonePerms("Uploads"), - "Snapshot : " + pos_string, - callback, expected_upload_cost, userdata); + 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); + gViewerWindow->playSnapshotAnimAndSound(); } else diff --git a/indra/newview/llspeakers.cpp b/indra/newview/llspeakers.cpp index 44c980c96f..e7971028bf 100644 --- 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 // @@ -870,7 +827,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) @@ -894,10 +852,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->postAndSuspend(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 ) @@ -936,7 +934,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 100644 --- 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/llstartup.cpp b/indra/newview/llstartup.cpp index 8a027fdbfa..88fbd233b8 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -46,7 +46,6 @@ #include "llaudioengine_openal.h" #endif -#include "llares.h" #include "llavatarnamecache.h" #include "llexperiencecache.h" #include "lllandmark.h" @@ -56,7 +55,6 @@ #include "llerrorcontrol.h" #include "llfloaterreg.h" #include "llfocusmgr.h" -#include "llhttpsender.h" #include "llfloaterimsession.h" #include "lllocationhistory.h" #include "llimageworker.h" @@ -113,7 +111,6 @@ #include "llgroupmgr.h" #include "llhudeffecttrail.h" #include "llhudmanager.h" -#include "llhttpclient.h" #include "llimagebmp.h" #include "llinventorybridge.h" #include "llinventorymodel.h" @@ -292,20 +289,6 @@ void callback_cache_name(const LLUUID& id, const std::string& full_name, bool is // local classes // -namespace -{ - class LLNullHTTPSender : public LLHTTPSender - { - virtual void send(const LLHost& host, - const std::string& message, const LLSD& body, - LLHTTPClient::ResponderPtr response) const - { - LL_WARNS("AppInit") << " attemped to send " << message << " to " << host - << " with null sender" << LL_ENDL; - } - }; -} - void update_texture_fetch() { LLAppViewer::getTextureCache()->update(1); // unpauses the texture cache thread @@ -458,13 +441,6 @@ bool idle_startup() // Load the throttle settings gViewerThrottle.load(); - if (ll_init_ares() == NULL || !gAres->isInitialized()) - { - std::string diagnostic = "Could not start address resolution system"; - LL_WARNS("AppInit") << diagnostic << LL_ENDL; - LLAppViewer::instance()->earlyExit("LoginFailedNoNetwork", LLSD().with("DIAGNOSTIC", diagnostic)); - } - // // Initialize messaging system // @@ -509,8 +485,6 @@ bool idle_startup() port = gSavedSettings.getU32("ConnectionPort"); } - LLHTTPSender::setDefaultSender(new LLNullHTTPSender()); - // TODO parameterize const F32 circuit_heartbeat_interval = 5; const F32 circuit_timeout = 100; @@ -752,12 +726,9 @@ bool idle_startup() if (gLoginMenuBarView == NULL) { LL_DEBUGS("AppInit") << "initializing menu bar" << LL_ENDL; - display_startup(); initialize_edit_menu(); initialize_spellcheck_menu(); - display_startup(); init_menus(); - display_startup(); } if (show_connect_box) @@ -769,23 +740,17 @@ bool idle_startup() if (gUserCredential.isNull()) { LL_DEBUGS("AppInit") << "loading credentials from gLoginHandler" << LL_ENDL; - display_startup(); gUserCredential = gLoginHandler.initializeLoginInfo(); - display_startup(); } // Make sure the process dialog doesn't hide things - display_startup(); gViewerWindow->setShowProgress(FALSE); - display_startup(); // Show the login dialog login_show(); - display_startup(); // connect dialog is already shown, so fill in the names if (gUserCredential.notNull()) { LLPanelLogin::setFields( gUserCredential, gRememberPassword); } - display_startup(); LLPanelLogin::giveFocus(); // MAINT-3231 Show first run dialog only for Desura viewer @@ -811,22 +776,15 @@ bool idle_startup() LLStartUp::setStartupState( STATE_LOGIN_CLEANUP ); } - display_startup(); gViewerWindow->setNormalControlsVisible( FALSE ); - display_startup(); gLoginMenuBarView->setVisible( TRUE ); - display_startup(); gLoginMenuBarView->setEnabled( TRUE ); - display_startup(); show_debug_menus(); - display_startup(); // Hide the splash screen LLSplashScreen::hide(); - display_startup(); // Push our window frontmost gViewerWindow->getWindow()->show(); - display_startup(); // DEV-16927. The following code removes errant keystrokes that happen while the window is being // first made visible. @@ -834,9 +792,9 @@ bool idle_startup() MSG msg; while( PeekMessage( &msg, /*All hWnds owned by this thread */ NULL, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE ) ) { } - display_startup(); #endif - timeout.reset(); + display_startup(); + timeout.reset(); return FALSE; } @@ -2775,8 +2733,6 @@ void reset_login() gAgent.cleanup(); LLWorld::getInstance()->destroyClass(); - LLStartUp::setStartupState( STATE_LOGIN_SHOW ); - if ( gViewerWindow ) { // Hide menus and normal buttons gViewerWindow->setNormalControlsVisible( FALSE ); @@ -2786,6 +2742,7 @@ void reset_login() // Hide any other stuff LLFloaterReg::hideVisibleInstances(); + LLStartUp::setStartupState( STATE_BROWSER_INIT ); } //--------------------------------------------------------------------------- @@ -2835,9 +2792,11 @@ void LLStartUp::initNameCache() void LLStartUp::initExperiences() -{ - LLAppViewer::instance()->loadExperienceCache(); - LLExperienceCache::initClass(); +{ + // Should trigger loading the cache. + LLExperienceCache::instance().setCapabilityQuery( + boost::bind(&LLAgent::getRegionCapability, &gAgent, _1)); + LLExperienceLog::instance().initialize(); } diff --git a/indra/newview/llsyntaxid.cpp b/indra/newview/llsyntaxid.cpp index 802dff1ead..9e54c521b5 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->getAndSuspend(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 cb6a04a44e..1cc7473819 100644 --- 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" @@ -255,6 +254,20 @@ static const S32 HTTP_NONPIPE_REQUESTS_LOW_WATER = 20; static const S32 HTTP_REQUESTS_RANGE_END_MAX = 20000000; ////////////////////////////////////////////////////////////////////////////// +namespace +{ + // The NoOpDeletor is used when passing certain objects (the LLTextureFetchWorker and + // the LLTextureFetchDebugger) in a smart pointer below for passage into + // the LLCore::Http libararies. When the smart pointer is destroyed, no + // action will be taken since we do not in these cases want the object to + // be destroyed at the end of the call. + // + // *NOTE$: Yes! It is "Deletor" + // http://english.stackexchange.com/questions/4733/what-s-the-rule-for-adding-er-vs-or-when-nouning-a-verb + // "delete" derives from Latin "deletus" + void NoOpDeletor(LLCore::HttpHandler *) + { /*NoOp*/ } +} static const char* e_state_name[] = { @@ -808,16 +821,10 @@ public: * ownership of the copy and disposes of it * when done. */ - TFReqSendMetrics(const std::string & caps_url, - const LLUUID & session_id, - const LLUUID & agent_id, - LLViewerAssetStats * main_stats) - : LLTextureFetch::TFRequest(), - mCapsURL(caps_url), - mSessionID(session_id), - mAgentID(agent_id), - mMainStats(main_stats) - {} + TFReqSendMetrics(const std::string & caps_url, + const LLUUID & session_id, + const LLUUID & agent_id, + LLViewerAssetStats * main_stats); TFReqSendMetrics & operator=(const TFReqSendMetrics &); // Not defined virtual ~TFReqSendMetrics(); @@ -829,6 +836,9 @@ public: const LLUUID mSessionID; const LLUUID mAgentID; LLViewerAssetStats * mMainStats; + +private: + LLCore::HttpHandler::ptr_t mHandler; }; /* @@ -967,7 +977,7 @@ LLTextureFetchWorker::~LLTextureFetchWorker() if (mHttpActive) { // Issue a cancel on a live request... - mFetcher->getHttpRequest().requestCancel(mHttpHandle, NULL); + mFetcher->getHttpRequest().requestCancel(mHttpHandle, LLCore::HttpHandler::ptr_t()); } if (mCacheReadHandle != LLTextureCache::nullHandle() && mFetcher->mTextureCache) { @@ -1330,11 +1340,11 @@ bool LLTextureFetchWorker::doWork(S32 param) static LLCachedControl<bool> use_http(gSavedSettings, "ImagePipelineUseHTTP", true); -// if (mHost != LLHost::invalid) get_url = false; +// if (mHost.isInvalid()) get_url = false; if ( use_http && mCanUseHTTP && mUrl.empty())//get http url. { LLViewerRegion* region = NULL; - if (mHost == LLHost::invalid) + if (mHost.isInvalid()) region = gAgent.getRegion(); else region = LLWorld::getInstance()->getRegion(mHost); @@ -1559,7 +1569,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 @@ -1571,7 +1581,7 @@ bool LLTextureFetchWorker::doWork(S32 param) mUrl, options, mFetcher->mHttpHeaders, - this); + LLCore::HttpHandler::ptr_t(this, &NoOpDeletor)); } else { @@ -1584,7 +1594,7 @@ bool LLTextureFetchWorker::doWork(S32 param) : mRequestedSize, options, mFetcher->mHttpHeaders, - this); + LLCore::HttpHandler::ptr_t(this, &NoOpDeletor)); } if (LLCORE_HTTP_HANDLE_INVALID == mHttpHandle) { @@ -2518,11 +2528,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), @@ -2538,13 +2548,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; @@ -2578,30 +2588,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; @@ -3261,7 +3247,7 @@ void LLTextureFetch::sendRequestListToSimulators() { LLHost host = iter1->first; // invalid host = use agent host - if (host == LLHost::invalid) + if (host.isInvalid()) { host = gAgent.getRegionHost(); } @@ -3341,7 +3327,7 @@ void LLTextureFetch::sendRequestListToSimulators() iter1 != mCancelQueue.end(); ++iter1) { LLHost host = iter1->first; - if (host == LLHost::invalid) + if (host.isInvalid()) { host = gAgent.getRegionHost(); } @@ -3974,9 +3960,6 @@ public: } }; // end class AssetReportHandler -AssetReportHandler stats_handler; - - /** * Implements the 'Set Region' command. * @@ -3990,6 +3973,18 @@ TFReqSetRegion::doWork(LLTextureFetch *) return true; } +TFReqSendMetrics::TFReqSendMetrics(const std::string & caps_url, + const LLUUID & session_id, + const LLUUID & agent_id, + LLViewerAssetStats * main_stats): + LLTextureFetch::TFRequest(), + mCapsURL(caps_url), + mSessionID(session_id), + mAgentID(agent_id), + mMainStats(main_stats), + mHandler(new AssetReportHandler) +{} + TFReqSendMetrics::~TFReqSendMetrics() { @@ -4008,7 +4003,6 @@ bool TFReqSendMetrics::doWork(LLTextureFetch * fetcher) { static const U32 report_priority(1); - static LLCore::HttpHandler * const handler(fetcher->isQAMode() || true ? &stats_handler : NULL); //if (! gViewerAssetStatsThread1) // return true; @@ -4056,9 +4050,9 @@ TFReqSendMetrics::doWork(LLTextureFetch * fetcher) report_priority, mCapsURL, sd, - NULL, + LLCore::HttpOptions::ptr_t(), fetcher->getMetricsHeaders(), - handler); + mHandler); LLTextureFetch::svMetricsDataBreak = false; } else @@ -4175,7 +4169,7 @@ LLTextureFetchDebugger::LLTextureFetchDebugger(LLTextureFetch* fetcher, LLTextur mFetcher(fetcher), mTextureCache(cache), mImageDecodeThread(imagedecodethread), - mHttpHeaders(NULL), + mHttpHeaders(), mHttpPolicyClass(fetcher->getPolicyClass()), mNbCurlCompleted(0), mTempIndex(0), @@ -4189,11 +4183,6 @@ LLTextureFetchDebugger::~LLTextureFetchDebugger() mFetchingHistory.clear(); mStopDebug = TRUE; tryToStopDebug(); - if (mHttpHeaders) - { - mHttpHeaders->release(); - mHttpHeaders = NULL; - } } void LLTextureFetchDebugger::init() @@ -4238,7 +4227,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); } } @@ -4646,9 +4635,9 @@ S32 LLTextureFetchDebugger::fillCurlQueue() texture_url, 0, requestedSize, - NULL, + LLCore::HttpOptions::ptr_t(), mHttpHeaders, - this); + LLCore::HttpHandler::ptr_t(this, &NoOpDeletor)); if (LLCORE_HTTP_HANDLE_INVALID != handle) { mHandleToFetchIndex[handle] = i; diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h index dc9df71c67..072e6a3307 100644 --- a/indra/newview/lltexturefetch.h +++ b/indra/newview/lltexturefetch.h @@ -37,7 +37,6 @@ #include "lltextureinfo.h" #include "llapr.h" #include "llimageworker.h" -#include "llcurl.h" #include "httprequest.h" #include "httpoptions.h" #include "httpheaders.h" @@ -177,7 +176,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; } @@ -355,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 @@ -511,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/lltexturestats.cpp b/indra/newview/lltexturestats.cpp index ca42d710f8..8ded148e17 100644 --- a/indra/newview/lltexturestats.cpp +++ b/indra/newview/lltexturestats.cpp @@ -30,8 +30,8 @@ #include "llagent.h" #include "lltexturefetch.h" #include "lltexturestats.h" -#include "lltexturestatsuploader.h" #include "llviewerregion.h" +#include "llcorehttputil.h" void send_texture_stats_to_sim(const LLSD &texture_stats) { @@ -49,6 +49,16 @@ void send_texture_stats_to_sim(const LLSD &texture_stats) std::string texture_cap_url = gAgent.getRegion()->getCapability("TextureStats"); LL_INFOS() << "uploading texture stats data to simulator" << LL_ENDL; - LLTextureStatsUploader::uploadStatsToSimulator(texture_cap_url, texture_stats); + + if (texture_cap_url != "") + { + LLCoreHttpUtil::HttpCoroutineAdapter::messageHttpPost(texture_cap_url, texture_stats, + "Texture statistics posted to sim.", "Error posting texture statistics to sim"); + } + else + { + LL_INFOS() << "Not sending texture stats: " << texture_stats + << " as there is no cap url." << LL_ENDL; + } } diff --git a/indra/newview/lltexturestatsuploader.cpp b/indra/newview/lltexturestatsuploader.cpp deleted file mode 100644 index c4809bc8e7..0000000000 --- a/indra/newview/lltexturestatsuploader.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/** - * @file lltexturerstats.cpp - * @brief texture stats upload 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 "lltexturestatsuploader.h" - -#include "llhttpclient.h" - - -// static -void LLTextureStatsUploader::uploadStatsToSimulator(const std::string texture_cap_url, const LLSD &texture_stats) -{ - if ( texture_cap_url != "" ) - { - LLHTTPClient::post(texture_cap_url, texture_stats, NULL); - } - else - { - LL_INFOS() << "Not sending texture stats: " - << texture_stats - << " as there is no cap url." - << LL_ENDL; - } -} - diff --git a/indra/newview/lltexturestatsuploader.h b/indra/newview/lltexturestatsuploader.h deleted file mode 100644 index ac268c2516..0000000000 --- a/indra/newview/lltexturestatsuploader.h +++ /dev/null @@ -1,40 +0,0 @@ -/** - * @file lltexturestatsuploader.h - * @brief Class to send the texture stats to the simulatore - * - * $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 LL_LLTEXTURESTATSUPLOADER_H -#define LL_LLTEXTURESTATSUPLOADER_H - -#include "llappviewer.h" - -// utility functions to capture data on texture download speeds and send to simulator periodically - -class LLTextureStatsUploader -{ -public: - static void uploadStatsToSimulator(const std::string texture_cap_url, const LLSD &texture_stats); -}; - -#endif // LL_LLTEXTURESTATSUPLOADER_H diff --git a/indra/newview/lltooldraganddrop.cpp b/indra/newview/lltooldraganddrop.cpp index 78d9c7a3f4..98586e3b3d 100644 --- a/indra/newview/lltooldraganddrop.cpp +++ b/indra/newview/lltooldraganddrop.cpp @@ -821,21 +821,6 @@ void LLToolDragAndDrop::dragOrDrop( S32 x, S32 y, MASK mask, BOOL drop, if (!handled) { - // *TODO: Suppress the "outbox" case once "marketplace" is used everywhere for everyone - // Disallow drag and drop to 3D from the outbox - const LLUUID outbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); - if (outbox_id.notNull()) - { - for (S32 item_index = 0; item_index < (S32)mCargoIDs.size(); item_index++) - { - if (gInventory.isObjectDescendentOf(mCargoIDs[item_index], outbox_id)) - { - *acceptance = ACCEPT_NO; - mToolTipMsg = LLTrans::getString("TooltipOutboxDragToWorld"); - return; - } - } - } // Disallow drag and drop to 3D from the marketplace const LLUUID marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); if (marketplacelistings_id.notNull()) diff --git a/indra/newview/lltranslate.cpp b/indra/newview/lltranslate.cpp index c0ba0a1f39..1936e24761 100644 --- a/indra/newview/lltranslate.cpp +++ b/indra/newview/lltranslate.cpp @@ -35,31 +35,257 @@ #include "llui.h" #include "llversioninfo.h" #include "llviewercontrol.h" - +#include "llcoros.h" #include "reader.h" +#include "llcorehttputil.h" + + +/** +* Handler of an HTTP machine translation service. +* +* Derived classes know the service URL +* and how to parse the translation result. +*/ +class LLTranslationAPIHandler +{ +public: + typedef std::pair<std::string, std::string> LanguagePair_t; + + /** + * Get URL for translation of the given string. + * + * Sending HTTP GET request to the URL will initiate translation. + * + * @param[out] url Place holder for the result. + * @param from_lang Source language. Leave empty for auto-detection. + * @param to_lang Target language. + * @param text Text to translate. + */ + virtual std::string getTranslateURL( + const std::string &from_lang, + const std::string &to_lang, + const std::string &text) const = 0; + + /** + * Get URL to verify the given API key. + * + * Sending request to the URL verifies the key. + * Positive HTTP response (code 200) means that the key is valid. + * + * @param[out] url Place holder for the URL. + * @param[in] key Key to verify. + */ + virtual std::string getKeyVerificationURL( + const std::string &key) const = 0; + + /** + * Parse translation response. + * + * @param[in,out] status HTTP status. May be modified while parsing. + * @param body Response text. + * @param[out] translation Translated text. + * @param[out] detected_lang Detected source language. May be empty. + * @param[out] err_msg Error message (in case of error). + */ + virtual bool parseResponse( + int& status, + const std::string& body, + std::string& translation, + std::string& detected_lang, + std::string& err_msg) const = 0; + + /** + * @return if the handler is configured to function properly + */ + virtual bool isConfigured() const = 0; + + virtual void verifyKey(const std::string &key, LLTranslate::KeyVerificationResult_fn fnc) = 0; + virtual void translateMessage(LanguagePair_t fromTo, std::string msg, LLTranslate::TranslationSuccess_fn success, LLTranslate::TranslationFailure_fn failure); + + + virtual ~LLTranslationAPIHandler() {} + + void verifyKeyCoro(LLTranslate::EService service, std::string key, LLTranslate::KeyVerificationResult_fn fnc); + void translateMessageCoro(LanguagePair_t fromTo, std::string msg, LLTranslate::TranslationSuccess_fn success, LLTranslate::TranslationFailure_fn failure); +}; + +void LLTranslationAPIHandler::translateMessage(LanguagePair_t fromTo, std::string msg, LLTranslate::TranslationSuccess_fn success, LLTranslate::TranslationFailure_fn failure) +{ + LLCoros::instance().launch("Translation", boost::bind(&LLTranslationAPIHandler::translateMessageCoro, + this, fromTo, msg, success, failure)); + +} + + +void LLTranslationAPIHandler::verifyKeyCoro(LLTranslate::EService service, std::string key, LLTranslate::KeyVerificationResult_fn fnc) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("getMerchantStatusCoro", 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); + + + std::string user_agent = llformat("%s %d.%d.%d (%d)", + LLVersionInfo::getChannel().c_str(), + LLVersionInfo::getMajor(), + LLVersionInfo::getMinor(), + LLVersionInfo::getPatch(), + LLVersionInfo::getBuild()); + + httpHeaders->append(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_TEXT_PLAIN); + httpHeaders->append(HTTP_OUT_HEADER_USER_AGENT, user_agent); + + httpOpts->setFollowRedirects(true); + + std::string url = this->getKeyVerificationURL(key); + if (url.empty()) + { + LL_INFOS("Translate") << "No translation URL" << LL_ENDL; + return; + } + LLSD result = httpAdapter->getAndSuspend(httpRequest, url, httpOpts, httpHeaders); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + bool bOk = true; + if (!status) + bOk = false; + + if (!fnc.empty()) + fnc(service, bOk); +} + +void LLTranslationAPIHandler::translateMessageCoro(LanguagePair_t fromTo, std::string msg, + LLTranslate::TranslationSuccess_fn success, LLTranslate::TranslationFailure_fn failure) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("getMerchantStatusCoro", 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); + + + std::string user_agent = llformat("%s %d.%d.%d (%d)", + LLVersionInfo::getChannel().c_str(), + LLVersionInfo::getMajor(), + LLVersionInfo::getMinor(), + LLVersionInfo::getPatch(), + LLVersionInfo::getBuild()); + + httpHeaders->append(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_TEXT_PLAIN); + httpHeaders->append(HTTP_OUT_HEADER_USER_AGENT, user_agent); + + std::string url = this->getTranslateURL(fromTo.first, fromTo.second, msg); + if (url.empty()) + { + LL_INFOS("Translate") << "No translation URL" << LL_ENDL; + return; + } + + LLSD result = httpAdapter->getRawAndSuspend(httpRequest, url, httpOpts, httpHeaders); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + std::string translation, err_msg; + std::string detected_lang(fromTo.second); + + int parseResult = status.getType(); + const LLSD::Binary &rawBody = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_RAW].asBinary(); + std::string body(rawBody.begin(), rawBody.end()); + + if (this->parseResponse(parseResult, body, translation, detected_lang, err_msg)) + { + // Fix up the response + LLStringUtil::replaceString(translation, "<", "<"); + LLStringUtil::replaceString(translation, ">", ">"); + LLStringUtil::replaceString(translation, """, "\""); + LLStringUtil::replaceString(translation, "'", "'"); + LLStringUtil::replaceString(translation, "&", "&"); + LLStringUtil::replaceString(translation, "'", "'"); + + if (!success.empty()) + success(translation, detected_lang); + } + else + { + if (err_msg.empty()) + { + err_msg = LLTrans::getString("TranslationResponseParseError"); + } + + LL_WARNS() << "Translation request failed: " << err_msg << LL_ENDL; + if (!failure.empty()) + failure(status, err_msg); + } + + +} + +//========================================================================= +/// Google Translate v2 API handler. +class LLGoogleTranslationHandler : public LLTranslationAPIHandler +{ + LOG_CLASS(LLGoogleTranslationHandler); + +public: + /*virtual*/ std::string getTranslateURL( + const std::string &from_lang, + const std::string &to_lang, + const std::string &text) const; + /*virtual*/ std::string getKeyVerificationURL( + const std::string &key) const; + /*virtual*/ bool parseResponse( + int& status, + const std::string& body, + std::string& translation, + std::string& detected_lang, + std::string& err_msg) const; + /*virtual*/ bool isConfigured() const; + + /*virtual*/ void verifyKey(const std::string &key, LLTranslate::KeyVerificationResult_fn fnc); + +private: + static void parseErrorResponse( + const Json::Value& root, + int& status, + std::string& err_msg); + static bool parseTranslation( + const Json::Value& root, + std::string& translation, + std::string& detected_lang); + static std::string getAPIKey(); + +}; + +//------------------------------------------------------------------------- // virtual -void LLGoogleTranslationHandler::getTranslateURL( - std::string &url, +std::string LLGoogleTranslationHandler::getTranslateURL( const std::string &from_lang, const std::string &to_lang, const std::string &text) const { - url = std::string("https://www.googleapis.com/language/translate/v2?key=") + std::string url = std::string("https://www.googleapis.com/language/translate/v2?key=") + getAPIKey() + "&q=" + LLURI::escape(text) + "&target=" + to_lang; if (!from_lang.empty()) { url += "&source=" + from_lang; } + return url; } // virtual -void LLGoogleTranslationHandler::getKeyVerificationURL( - std::string& url, +std::string LLGoogleTranslationHandler::getKeyVerificationURL( const std::string& key) const { - url = std::string("https://www.googleapis.com/language/translate/v2/languages?key=") + std::string url = std::string("https://www.googleapis.com/language/translate/v2/languages?key=") + key + "&target=en"; + return url; } // virtual @@ -154,28 +380,66 @@ std::string LLGoogleTranslationHandler::getAPIKey() return gSavedSettings.getString("GoogleTranslateAPIKey"); } +/*virtual*/ +void LLGoogleTranslationHandler::verifyKey(const std::string &key, LLTranslate::KeyVerificationResult_fn fnc) +{ + LLCoros::instance().launch("Google /Verify Key", boost::bind(&LLTranslationAPIHandler::verifyKeyCoro, + this, LLTranslate::SERVICE_GOOGLE, key, fnc)); +} + + +//========================================================================= +/// Microsoft Translator v2 API handler. +class LLBingTranslationHandler : public LLTranslationAPIHandler +{ + LOG_CLASS(LLBingTranslationHandler); + +public: + /*virtual*/ std::string getTranslateURL( + const std::string &from_lang, + const std::string &to_lang, + const std::string &text) const; + /*virtual*/ std::string getKeyVerificationURL( + const std::string &key) const; + /*virtual*/ bool parseResponse( + int& status, + const std::string& body, + std::string& translation, + std::string& detected_lang, + std::string& err_msg) const; + /*virtual*/ bool isConfigured() const; + + /*virtual*/ void verifyKey(const std::string &key, LLTranslate::KeyVerificationResult_fn fnc); +private: + static std::string getAPIKey(); + static std::string getAPILanguageCode(const std::string& lang); + +}; + +//------------------------------------------------------------------------- // virtual -void LLBingTranslationHandler::getTranslateURL( - std::string &url, +std::string LLBingTranslationHandler::getTranslateURL( const std::string &from_lang, const std::string &to_lang, const std::string &text) const { - url = std::string("http://api.microsofttranslator.com/v2/Http.svc/Translate?appId=") + std::string url = std::string("http://api.microsofttranslator.com/v2/Http.svc/Translate?appId=") + getAPIKey() + "&text=" + LLURI::escape(text) + "&to=" + getAPILanguageCode(to_lang); if (!from_lang.empty()) { url += "&from=" + getAPILanguageCode(from_lang); } + return url; } + // virtual -void LLBingTranslationHandler::getKeyVerificationURL( - std::string& url, +std::string LLBingTranslationHandler::getKeyVerificationURL( const std::string& key) const { - url = std::string("http://api.microsofttranslator.com/v2/Http.svc/GetLanguagesForTranslate?appId=") + std::string url = std::string("http://api.microsofttranslator.com/v2/Http.svc/GetLanguagesForTranslate?appId=") + key; + return url; } // virtual @@ -242,96 +506,31 @@ std::string LLBingTranslationHandler::getAPILanguageCode(const std::string& lang return lang == "zh" ? "zh-CHT" : lang; // treat Chinese as Traditional Chinese } -LLTranslate::TranslationReceiver::TranslationReceiver(const std::string& from_lang, const std::string& to_lang) -: mFromLang(from_lang) -, mToLang(to_lang) -, mHandler(LLTranslate::getPreferredHandler()) +/*virtual*/ +void LLBingTranslationHandler::verifyKey(const std::string &key, LLTranslate::KeyVerificationResult_fn fnc) { + LLCoros::instance().launch("Bing /Verify Key", boost::bind(&LLTranslationAPIHandler::verifyKeyCoro, + this, LLTranslate::SERVICE_BING, key, fnc)); } -// virtual -void LLTranslate::TranslationReceiver::completedRaw( - const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer) +//========================================================================= +/*static*/ +void LLTranslate::translateMessage(const std::string &from_lang, const std::string &to_lang, + const std::string &mesg, TranslationSuccess_fn success, TranslationFailure_fn failure) { - LLBufferStream istr(channels, buffer.get()); - std::stringstream strstrm; - strstrm << istr.rdbuf(); - - const std::string body = strstrm.str(); - std::string translation, detected_lang, err_msg; - int status = getStatus(); - LL_DEBUGS("Translate") << "HTTP status: " << status << " " << getReason() << LL_ENDL; - LL_DEBUGS("Translate") << "Response body: " << body << LL_ENDL; - if (mHandler.parseResponse(status, body, translation, detected_lang, err_msg)) - { - // Fix up the response - LLStringUtil::replaceString(translation, "<", "<"); - LLStringUtil::replaceString(translation, ">",">"); - LLStringUtil::replaceString(translation, ""","\""); - LLStringUtil::replaceString(translation, "'","'"); - LLStringUtil::replaceString(translation, "&","&"); - LLStringUtil::replaceString(translation, "'","'"); - - handleResponse(translation, detected_lang); - } - else - { - if (err_msg.empty()) - { - err_msg = LLTrans::getString("TranslationResponseParseError"); - } + LLTranslationAPIHandler& handler = getPreferredHandler(); - LL_WARNS() << "Translation request failed: " << err_msg << LL_ENDL; - handleFailure(status, err_msg); - } + handler.translateMessage(LLTranslationAPIHandler::LanguagePair_t(from_lang, to_lang), mesg, success, failure); } -LLTranslate::KeyVerificationReceiver::KeyVerificationReceiver(EService service) -: mService(service) +/*static*/ +void LLTranslate::verifyKey(EService service, const std::string &key, KeyVerificationResult_fn fnc) { -} + LLTranslationAPIHandler& handler = getHandler(service); -LLTranslate::EService LLTranslate::KeyVerificationReceiver::getService() const -{ - return mService; + handler.verifyKey(key, fnc); } -// virtual -void LLTranslate::KeyVerificationReceiver::completedRaw( - const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer) -{ - bool ok = (getStatus() == HTTP_OK); - setVerificationStatus(ok); -} - -//static -void LLTranslate::translateMessage( - TranslationReceiverPtr &receiver, - const std::string &from_lang, - const std::string &to_lang, - const std::string &mesg) -{ - std::string url; - receiver->mHandler.getTranslateURL(url, from_lang, to_lang, mesg); - - LL_DEBUGS("Translate") << "Sending translation request: " << url << LL_ENDL; - sendRequest(url, receiver); -} - -// static -void LLTranslate::verifyKey( - KeyVerificationReceiverPtr& receiver, - const std::string& key) -{ - std::string url; - const LLTranslationAPIHandler& handler = getHandler(receiver->getService()); - handler.getKeyVerificationURL(url, key); - - LL_DEBUGS("Translate") << "Sending key verification request: " << url << LL_ENDL; - sendRequest(url, receiver); -} //static std::string LLTranslate::getTranslateLanguage() @@ -352,7 +551,7 @@ bool LLTranslate::isTranslationConfigured() } // static -const LLTranslationAPIHandler& LLTranslate::getPreferredHandler() +LLTranslationAPIHandler& LLTranslate::getPreferredHandler() { EService service = SERVICE_BING; @@ -366,7 +565,7 @@ const LLTranslationAPIHandler& LLTranslate::getPreferredHandler() } // static -const LLTranslationAPIHandler& LLTranslate::getHandler(EService service) +LLTranslationAPIHandler& LLTranslate::getHandler(EService service) { static LLGoogleTranslationHandler google; static LLBingTranslationHandler bing; @@ -378,25 +577,3 @@ const LLTranslationAPIHandler& LLTranslate::getHandler(EService service) return bing; } - -// static -void LLTranslate::sendRequest(const std::string& url, LLHTTPClient::ResponderPtr responder) -{ - static const float REQUEST_TIMEOUT = 5; - static LLSD sHeader; - - if (!sHeader.size()) - { - std::string user_agent = llformat("%s %d.%d.%d (%d)", - LLVersionInfo::getChannel().c_str(), - LLVersionInfo::getMajor(), - LLVersionInfo::getMinor(), - LLVersionInfo::getPatch(), - LLVersionInfo::getBuild()); - - sHeader.insert(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_TEXT_PLAIN); - sHeader.insert(HTTP_OUT_HEADER_USER_AGENT, user_agent); - } - - LLHTTPClient::get(url, responder, sHeader, REQUEST_TIMEOUT); -} diff --git a/indra/newview/lltranslate.h b/indra/newview/lltranslate.h index 972274714a..bf431cdfbb 100644 --- a/indra/newview/lltranslate.h +++ b/indra/newview/lltranslate.h @@ -27,136 +27,15 @@ #ifndef LL_LLTRANSLATE_H #define LL_LLTRANSLATE_H -#include "llhttpclient.h" #include "llbufferstream.h" +#include <boost/function.hpp> namespace Json { class Value; } -/** - * Handler of an HTTP machine translation service. - * - * Derived classes know the service URL - * and how to parse the translation result. - */ -class LLTranslationAPIHandler -{ -public: - /** - * Get URL for translation of the given string. - * - * Sending HTTP GET request to the URL will initiate translation. - * - * @param[out] url Place holder for the result. - * @param from_lang Source language. Leave empty for auto-detection. - * @param to_lang Target language. - * @param text Text to translate. - */ - virtual void getTranslateURL( - std::string &url, - const std::string &from_lang, - const std::string &to_lang, - const std::string &text) const = 0; - - /** - * Get URL to verify the given API key. - * - * Sending request to the URL verifies the key. - * Positive HTTP response (code 200) means that the key is valid. - * - * @param[out] url Place holder for the URL. - * @param[in] key Key to verify. - */ - virtual void getKeyVerificationURL( - std::string &url, - const std::string &key) const = 0; - - /** - * Parse translation response. - * - * @param[in,out] status HTTP status. May be modified while parsing. - * @param body Response text. - * @param[out] translation Translated text. - * @param[out] detected_lang Detected source language. May be empty. - * @param[out] err_msg Error message (in case of error). - */ - virtual bool parseResponse( - int& status, - const std::string& body, - std::string& translation, - std::string& detected_lang, - std::string& err_msg) const = 0; - - /** - * @return if the handler is configured to function properly - */ - virtual bool isConfigured() const = 0; - - virtual ~LLTranslationAPIHandler() {} -}; - -/// Google Translate v2 API handler. -class LLGoogleTranslationHandler : public LLTranslationAPIHandler -{ - LOG_CLASS(LLGoogleTranslationHandler); - -public: - /*virtual*/ void getTranslateURL( - std::string &url, - const std::string &from_lang, - const std::string &to_lang, - const std::string &text) const; - /*virtual*/ void getKeyVerificationURL( - std::string &url, - const std::string &key) const; - /*virtual*/ bool parseResponse( - int& status, - const std::string& body, - std::string& translation, - std::string& detected_lang, - std::string& err_msg) const; - /*virtual*/ bool isConfigured() const; - -private: - static void parseErrorResponse( - const Json::Value& root, - int& status, - std::string& err_msg); - static bool parseTranslation( - const Json::Value& root, - std::string& translation, - std::string& detected_lang); - static std::string getAPIKey(); -}; - -/// Microsoft Translator v2 API handler. -class LLBingTranslationHandler : public LLTranslationAPIHandler -{ - LOG_CLASS(LLBingTranslationHandler); - -public: - /*virtual*/ void getTranslateURL( - std::string &url, - const std::string &from_lang, - const std::string &to_lang, - const std::string &text) const; - /*virtual*/ void getKeyVerificationURL( - std::string &url, - const std::string &key) const; - /*virtual*/ bool parseResponse( - int& status, - const std::string& body, - std::string& translation, - std::string& detected_lang, - std::string& err_msg) const; - /*virtual*/ bool isConfigured() const; -private: - static std::string getAPIKey(); - static std::string getAPILanguageCode(const std::string& lang); -}; - +class LLTranslationAPIHandler; /** * Entry point for machine translation services. * @@ -180,84 +59,9 @@ public : SERVICE_GOOGLE, } EService; - /** - * Subclasses are supposed to handle translation results (e.g. show them in chat) - */ - class TranslationReceiver: public LLHTTPClient::Responder - { - public: - - /** - * Using mHandler, parse incoming response. - * - * Calls either handleResponse() or handleFailure() - * depending on the HTTP status code and parsing success. - * - * @see handleResponse() - * @see handleFailure() - * @see mHandler - */ - /*virtual*/ void completedRaw( - const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer); - - protected: - friend class LLTranslate; - - /// Remember source and target languages for subclasses to be able to filter inappropriate results. - TranslationReceiver(const std::string& from_lang, const std::string& to_lang); - - /// Override point to handle successful translation. - virtual void handleResponse(const std::string &translation, const std::string &recognized_lang) = 0; - - /// Override point to handle unsuccessful translation. - virtual void handleFailure(int status, const std::string& err_msg) = 0; - - std::string mFromLang; - std::string mToLang; - const LLTranslationAPIHandler& mHandler; - }; - - /** - * Subclasses are supposed to handle API key verification result. - */ - class KeyVerificationReceiver: public LLHTTPClient::Responder - { - public: - EService getService() const; - - protected: - /** - * Save the translation service the key belongs to. - * - * Subclasses need to know it. - * - * @see getService() - */ - KeyVerificationReceiver(EService service); - - /** - * Parse verification response. - * - * Calls setVerificationStatus() with the verification status, - * which is true if HTTP status code is 200. - * - * @see setVerificationStatus() - */ - /*virtual*/ void completedRaw( - const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer); - - /** - * Override point for subclasses to handle key verification status. - */ - virtual void setVerificationStatus(bool ok) = 0; - - EService mService; - }; - - typedef LLPointer<TranslationReceiver> TranslationReceiverPtr; - typedef LLPointer<KeyVerificationReceiver> KeyVerificationReceiverPtr; + typedef boost::function<void(EService, bool)> KeyVerificationResult_fn; + typedef boost::function<void(std::string , std::string )> TranslationSuccess_fn; + typedef boost::function<void(int, std::string)> TranslationFailure_fn; /** * Translate given text. @@ -267,15 +71,15 @@ public : * @param to_lang Target language. * @param mesg Text to translate. */ - static void translateMessage(TranslationReceiverPtr &receiver, const std::string &from_lang, const std::string &to_lang, const std::string &mesg); + static void translateMessage(const std::string &from_lang, const std::string &to_lang, const std::string &mesg, TranslationSuccess_fn success, TranslationFailure_fn failure); - /** + /** * Verify given API key of a translation service. * * @param receiver Object to pass verification result to. * @param key Key to verify. */ - static void verifyKey(KeyVerificationReceiverPtr& receiver, const std::string& key); + static void verifyKey(EService service, const std::string &key, KeyVerificationResult_fn fnc); /** * @return translation target language @@ -288,9 +92,8 @@ public : static bool isTranslationConfigured(); private: - static const LLTranslationAPIHandler& getPreferredHandler(); - static const LLTranslationAPIHandler& getHandler(EService service); - static void sendRequest(const std::string& url, LLHTTPClient::ResponderPtr responder); + static LLTranslationAPIHandler& getPreferredHandler(); + static LLTranslationAPIHandler& getHandler(EService service); }; #endif diff --git a/indra/newview/lltwitterconnect.cpp b/indra/newview/lltwitterconnect.cpp index e983bc883f..9b7c13b57d 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,305 @@ 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; + + LLSD result = httpAdapter->putAndSuspend(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); + + LLSD result = httpAdapter->postAndSuspend(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->postAndSuspend(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); - } + LLSD result = httpAdapter->deleteAndSuspend(httpRequest, getTwitterConnectURL("/connection"), httpOpts); - /* 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 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->getAndSuspend(httpRequest, getTwitterConnectURL("/connection", true), httpOpts); + + 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("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 { - LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_CONNECTION_IN_PROGRESS); + 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->getAndSuspend(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 +418,32 @@ 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()); + setConnectionState(LLTwitterConnect::TWITTER_CONNECTION_IN_PROGRESS); + + LLCoros::instance().launch("LLTwitterConnect::twitterConnectCoro", + boost::bind(&LLTwitterConnect::twitterConnectCoro, this, request_token, oauth_verifier)); } void LLTwitterConnect::disconnectFromTwitter() { - LLHTTPClient::del(getTwitterConnectURL("/connection"), new LLTwitterDisconnectResponder()); + setConnectionState(LLTwitterConnect::TWITTER_DISCONNECTING); + + 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 +452,19 @@ 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()); + + setConnectionState(LLTwitterConnect::TWITTER_POSTING); + + 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; + setConnectionState(LLTwitterConnect::TWITTER_POSTING); - 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 +472,10 @@ 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()); + setConnectionState(LLTwitterConnect::TWITTER_POSTING); + + 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 100644 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..15c3dad38e 100644 --- a/indra/newview/lluploadfloaterobservers.h +++ b/indra/newview/lluploadfloaterobservers.h @@ -28,7 +28,6 @@ #define LL_LLUPLOADFLOATEROBSERVERS_H #include "llfloater.h" -#include "llhttpclient.h" #include "llhandle.h" class LLUploadPermissionsObserver @@ -79,18 +78,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..f0dafec240 --- /dev/null +++ b/indra/newview/llviewerassetupload.cpp @@ -0,0 +1,841 @@ +/** +* @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(); +} + +LLResourceUploadInfo::LLResourceUploadInfo(LLAssetID assetId, LLAssetType::EType assetType, std::string name) : + mAssetId(assetId), + mAssetType(assetType), + mName(name), + mDescription(), + mCompressionInfo(0), + mDestinationFolderType(LLFolderType::FT_NONE), + mInventoryType(LLInventoryType::IT_NONE), + mNextOwnerPerms(0), + mGroupPerms(0), + mEveryonePerms(0), + mExpectedUploadCost(0), + mTransactionId(), + mFolderId(LLUUID::null), + mItemId(LLUUID::null) +{ +} + +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; +} + +S32 LLResourceUploadInfo::getEconomyUploadCost() +{ + // Update L$ and ownership credit information + // since it probably changed on the server + if (getAssetType() == LLAssetType::AT_TEXTURE || + getAssetType() == LLAssetType::AT_SOUND || + getAssetType() == LLAssetType::AT_ANIMATION || + getAssetType() == LLAssetType::AT_MESH) + { + return LLGlobalEconomy::Singleton::instance().getPriceUpload(); + } + + return 0; +} + + +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; +}; + +//========================================================================= +LLNewFileResourceUploadInfo::LLNewFileResourceUploadInfo( + 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 LLNewFileResourceUploadInfo::prepareUpload() +{ + if (getAssetId().isNull()) + generateNewAssetId(); + + LLSD result = exportTempFile(); + if (result.has("error")) + return result; + + return LLResourceUploadInfo::prepareUpload(); +} + +LLSD LLNewFileResourceUploadInfo::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 itemId, LLPointer<LLImageFormatted> image, 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(), + mInvnFinishFn(finish), + mTaskFinishFn(NULL), + mStoredToVFS(false) +{ + setItemId(itemId); + + EImageCodec codec = static_cast<EImageCodec>(image->getCodec()); + + switch (codec) + { + case IMG_CODEC_JPEG: + setAssetType(LLAssetType::AT_IMAGE_JPEG); + LL_INFOS() << "Upload Asset type set to JPEG." << LL_ENDL; + break; + case IMG_CODEC_TGA: + setAssetType(LLAssetType::AT_IMAGE_TGA); + LL_INFOS() << "Upload Asset type set to TGA." << LL_ENDL; + break; + default: + LL_WARNS() << "Unknown codec to asset type transition. Codec=" << (int)codec << "." << LL_ENDL; + break; + } + + size_t imageSize = image->getDataSize(); + mContents.reserve(imageSize); + mContents.assign((char *)image->getData(), imageSize); +} + +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 + { + LLUUID newItemId(LLUUID::null); + + if (itemId.notNull()) + { + 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); + + newItemId = newItem->getUUID(); + LL_INFOS() << "Inventory item " << item->getName() << " saved into " << newAssetId.asString() << LL_ENDL; + } + + if (mInvnFinishFn) + { + mInvnFinishFn(itemId, newAssetId, newItemId, result); + } + gInventory.notifyObservers(); + } + + return newAssetId; +} + +//========================================================================= + +LLScriptAssetUpload::LLScriptAssetUpload(LLUUID itemId, std::string buffer, invnUploadFinish_f finish): + LLBufferedAssetUploadInfo(itemId, LLAssetType::AT_LSL_TEXT, buffer, finish), + mExerienceId(), + mTargetType(LSL2), + mIsRunning(false) +{ +} + +LLScriptAssetUpload::LLScriptAssetUpload(LLUUID taskId, LLUUID itemId, TargetType_t targetType, + bool isRunning, LLUUID exerienceId, std::string buffer, taskUploadFinish_f finish): + LLBufferedAssetUploadInfo(taskId, itemId, LLAssetType::AT_LSL_TEXT, buffer, finish), + mExerienceId(exerienceId), + mTargetType(targetType), + mIsRunning(isRunning) +{ +} + +LLSD LLScriptAssetUpload::generatePostBody() +{ + LLSD body; + + if (getTaskId().isNull()) + { + body["item_id"] = getItemId(); + body["target"] = "lsl2"; + } + else + { + body["task_id"] = getTaskId(); + body["item_id"] = getItemId(); + body["is_script_running"] = getIsRunning(); + body["target"] = (getTargetType() == MONO) ? "mono" : "lsl2"; + body["experience"] = getExerienceId(); + } + + return body; +} + +//========================================================================= +/*static*/ +LLUUID LLViewerAssetUpload::EnqueueInventoryUpload(const std::string &url, const LLResourceUploadInfo::ptr_t &uploadInfo) +{ + std::string procName("LLViewerAssetUpload::AssetInventoryUploadCoproc("); + + LLUUID queueId = LLCoprocedureManager::instance().enqueueCoprocedure("Upload", + 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; + } + + llcoro::suspend(); + + if (uploadInfo->showUploadDialog()) + { + std::string uploadMessage = "Uploading...\n\n"; + uploadMessage.append(uploadInfo->getDisplayName()); + LLUploadDialog::modalUploadDialog(uploadMessage); + } + + LLSD body = uploadInfo->generatePostBody(); + + result = httpAdapter->postAndSuspend(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(); + + bool success = false; + if (!uploader.empty() && uploadInfo->getAssetId().notNull()) + { + result = httpAdapter->postFileAndSuspend(httpRequest, uploader, uploadInfo->getAssetId(), uploadInfo->getAssetType()); + httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + std::string ulstate = result["state"].asString(); + + if ((!status) || (ulstate != "complete")) + { + HandleUploadError(status, result, uploadInfo); + if (uploadInfo->showUploadDialog()) + LLUploadDialog::modalUploadFinished(); + return; + } + + S32 uploadPrice = result["upload_price"].asInteger();//uploadInfo->getEconomyUploadCost(); + + 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); + } + } + else + { + LL_WARNS() << "No upload url provided. Nothing uploaded, responding with previous result." << LL_ENDL; + } + LLUUID serverInventoryItem = uploadInfo->finishUpload(result); + + if (uploadInfo->showInventoryPanel()) + { + 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..43e23a0d42 --- /dev/null +++ b/indra/newview/llviewerassetupload.h @@ -0,0 +1,236 @@ +/** +* @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" +#include "llimage.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 S32 getEconomyUploadCost(); + 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 bool showInventoryPanel() 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); + + LLResourceUploadInfo( + LLAssetID assetId, + LLAssetType::EType assetType, + std::string name ); + + 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(); + + void setAssetId(LLUUID assetId) { mAssetId = assetId; } + +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 LLNewFileResourceUploadInfo : public LLResourceUploadInfo +{ +public: + LLNewFileResourceUploadInfo( + 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 itemId, LLPointer<LLImageFormatted> image, 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; } + virtual bool showInventoryPanel() 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: + enum TargetType_t + { + LSL2, + MONO + }; + + LLScriptAssetUpload(LLUUID itemId, std::string buffer, invnUploadFinish_f finish); + LLScriptAssetUpload(LLUUID taskId, LLUUID itemId, TargetType_t targetType, + bool isRunning, LLUUID exerienceId, std::string buffer, taskUploadFinish_f finish); + + virtual LLSD generatePostBody(); + + LLUUID getExerienceId() const { return mExerienceId; } + TargetType_t getTargetType() const { return mTargetType; } + bool getIsRunning() const { return mIsRunning; } + +private: + LLUUID mExerienceId; + TargetType_t mTargetType; + bool mIsRunning; + +}; + +//========================================================================= +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 100644 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 100644 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 65f427c3f1..91e4980e45 100644 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -57,7 +57,6 @@ #include "llfloaterdeleteenvpreset.h" #include "llfloaterdeleteprefpreset.h" #include "llfloaterdestinations.h" -#include "llfloaterdisplayname.h" #include "llfloatereditdaycycle.h" #include "llfloatereditsky.h" #include "llfloatereditwater.h" @@ -94,7 +93,6 @@ #include "llfloaternotificationstabbed.h" #include "llfloaterobjectweights.h" #include "llfloateropenobject.h" -#include "llfloateroutbox.h" #include "llfloaterpathfindingcharacters.h" #include "llfloaterpathfindingconsole.h" #include "llfloaterpathfindinglinksets.h" @@ -251,7 +249,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>); @@ -278,7 +275,6 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("object_weights", "floater_object_weights.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterObjectWeights>); LLFloaterReg::add("openobject", "floater_openobject.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterOpenObject>); - LLFloaterReg::add("outbox", "floater_merchant_outbox.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterOutbox>); LLFloaterReg::add("outgoing_call", "floater_outgoing_call.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLOutgoingCallDialog>); LLFloaterPayUtil::registerFloater(); diff --git a/indra/newview/llviewerfoldertype.cpp b/indra/newview/llviewerfoldertype.cpp index 158cacbc81..b8ff2cc9b4 100644 --- a/indra/newview/llviewerfoldertype.cpp +++ b/indra/newview/llviewerfoldertype.cpp @@ -137,7 +137,7 @@ LLViewerFolderDictionary::LLViewerFolderDictionary() bool boxes_invisible = !gSavedSettings.getBOOL("InventoryOutboxMakeVisible"); addEntry(LLFolderType::FT_INBOX, new ViewerFolderEntry("Received Items", "Inv_SysOpen", "Inv_SysClosed", FALSE, boxes_invisible)); - addEntry(LLFolderType::FT_OUTBOX, new ViewerFolderEntry("Merchant Outbox", "Inv_SysOpen", "Inv_SysClosed", FALSE, boxes_invisible)); + addEntry(LLFolderType::FT_OUTBOX, new ViewerFolderEntry("Merchant Outbox", "Inv_SysOpen", "Inv_SysClosed", FALSE, true)); addEntry(LLFolderType::FT_BASIC_ROOT, new ViewerFolderEntry("Basic Root", "Inv_SysOpen", "Inv_SysClosed", FALSE, true)); diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index 24096e3222..0ee873d7a1 100644 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -79,6 +79,16 @@ static const char * const LOG_INV("Inventory"); static const char * const LOG_LOCAL("InventoryLocalize"); static const char * const LOG_NOTECARD("copy_inventory_from_notecard"); +#if 1 +// *TODO$: LLInventoryCallback should be deprecated to conform to the new boost::bind/coroutine model. +// temp code in transition +void doInventoryCb(LLPointer<LLInventoryCallback> cb, LLUUID id) +{ + if (cb.notNull()) + cb->fire(id); +} +#endif + ///---------------------------------------------------------------------------- /// Helper class to store special inventory item names and their localized values. ///---------------------------------------------------------------------------- @@ -417,7 +427,7 @@ void LLViewerInventoryItem::fetchFromServer(void) const body["items"][0]["owner_id"] = mPermissions.getOwner(); body["items"][0]["item_id"] = mUUID; - LLInventoryModel::FetchItemHttpHandler * handler(new LLInventoryModel::FetchItemHttpHandler(body)); + LLCore::HttpHandler::ptr_t handler(new LLInventoryModel::FetchItemHttpHandler(body)); gInventory.requestPost(true, url, body, handler, "Inventory Item"); } else @@ -1280,16 +1290,14 @@ void link_inventory_array(const LLUUID& category, #endif } - bool ais_ran = false; - if (AISCommand::isAPIAvailable()) + if (AISAPI::isAvailable()) { LLSD new_inventory = LLSD::emptyMap(); new_inventory["links"] = links; - LLPointer<AISCommand> cmd_ptr = new CreateInventoryCommand(category, new_inventory, cb); - ais_ran = cmd_ptr->run_command(); + AISAPI::completion_t cr = (cb) ? boost::bind(&doInventoryCb, cb, _1) : AISAPI::completion_t(); + AISAPI::CreateInventory(category, new_inventory, cr); } - - if (!ais_ran) + else { LLMessageSystem* msg = gMessageSystem; for (LLSD::array_iterator iter = links.beginArray(); iter != links.endArray(); ++iter ) @@ -1346,8 +1354,7 @@ void update_inventory_item( LLPointer<LLInventoryCallback> cb) { const LLUUID& item_id = update_item->getUUID(); - bool ais_ran = false; - if (AISCommand::isAPIAvailable()) + if (AISAPI::isAvailable()) { LLSD updates = update_item->asLLSD(); // Replace asset_id and/or shadow_id with transaction_id (hash_id) @@ -1361,10 +1368,10 @@ void update_inventory_item( updates.erase("shadow_id"); updates["hash_id"] = update_item->getTransactionID(); } - LLPointer<AISCommand> cmd_ptr = new UpdateItemCommand(item_id, updates, cb); - ais_ran = cmd_ptr->run_command(); + AISAPI::completion_t cr = (cb) ? boost::bind(&doInventoryCb, cb, _1) : AISAPI::completion_t(); + AISAPI::UpdateItem(item_id, updates, cr); } - if (!ais_ran) + else { LLPointer<LLViewerInventoryItem> obj = gInventory.getItem(item_id); LL_DEBUGS(LOG_INV) << "item_id: [" << item_id << "] name " << (update_item ? update_item->getName() : "(NOT FOUND)") << LL_ENDL; @@ -1400,13 +1407,12 @@ void update_inventory_item( const LLSD& updates, LLPointer<LLInventoryCallback> cb) { - bool ais_ran = false; - if (AISCommand::isAPIAvailable()) + if (AISAPI::isAvailable()) { - LLPointer<AISCommand> cmd_ptr = new UpdateItemCommand(item_id, updates, cb); - ais_ran = cmd_ptr->run_command(); + AISAPI::completion_t cr = (cb) ? boost::bind(&doInventoryCb, cb, _1) : AISAPI::completion_t(); + AISAPI::UpdateItem(item_id, updates, cr); } - if (!ais_ran) + else { LLPointer<LLViewerInventoryItem> obj = gInventory.getItem(item_id); LL_DEBUGS(LOG_INV) << "item_id: [" << item_id << "] name " << (obj ? obj->getName() : "(NOT FOUND)") << LL_ENDL; @@ -1456,11 +1462,11 @@ void update_inventory_category( LLPointer<LLViewerInventoryCategory> new_cat = new LLViewerInventoryCategory(obj); new_cat->fromLLSD(updates); // FIXME - restore this once the back-end work has been done. - if (AISCommand::isAPIAvailable()) + if (AISAPI::isAvailable()) { LLSD new_llsd = new_cat->asLLSD(); - LLPointer<AISCommand> cmd_ptr = new UpdateCategoryCommand(cat_id, new_llsd, cb); - cmd_ptr->run_command(); + AISAPI::completion_t cr = (cb) ? boost::bind(&doInventoryCb, cb, _1) : AISAPI::completion_t(); + AISAPI::UpdateCategory(cat_id, new_llsd, cr); } else // no cap { @@ -1522,10 +1528,10 @@ void remove_inventory_item( { const LLUUID item_id(obj->getUUID()); LL_DEBUGS(LOG_INV) << "item_id: [" << item_id << "] name " << obj->getName() << LL_ENDL; - if (AISCommand::isAPIAvailable()) + if (AISAPI::isAvailable()) { - LLPointer<AISCommand> cmd_ptr = new RemoveItemCommand(item_id, cb); - cmd_ptr->run_command(); + AISAPI::completion_t cr = (cb) ? boost::bind(&doInventoryCb, cb, _1) : AISAPI::completion_t(); + AISAPI::RemoveItem(item_id, cr); if (immediate_delete) { @@ -1598,10 +1604,10 @@ void remove_inventory_category( LLNotificationsUtil::add("CannotRemoveProtectedCategories"); return; } - if (AISCommand::isAPIAvailable()) + if (AISAPI::isAvailable()) { - LLPointer<AISCommand> cmd_ptr = new RemoveCategoryCommand(cat_id, cb); - cmd_ptr->run_command(); + AISAPI::completion_t cr = (cb) ? boost::bind(&doInventoryCb, cb, _1) : AISAPI::completion_t(); + AISAPI::RemoveCategory(cat_id, cr); } else // no cap { @@ -1701,10 +1707,10 @@ void purge_descendents_of(const LLUUID& id, LLPointer<LLInventoryCallback> cb) } else { - if (AISCommand::isAPIAvailable()) + if (AISAPI::isAvailable()) { - LLPointer<AISCommand> cmd_ptr = new PurgeDescendentsCommand(id, cb); - cmd_ptr->run_command(); + AISAPI::completion_t cr = (cb) ? boost::bind(&doInventoryCb, cb, _1) : AISAPI::completion_t(); + AISAPI::PurgeDescendents(id, cr); } else // no cap { @@ -1780,27 +1786,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, @@ -1858,12 +1857,13 @@ void slam_inventory_folder(const LLUUID& folder_id, const LLSD& contents, LLPointer<LLInventoryCallback> cb) { - if (AISCommand::isAPIAvailable()) + if (AISAPI::isAvailable()) { LL_DEBUGS(LOG_INV) << "using AISv3 to slam folder, id " << folder_id << " new contents: " << ll_pretty_print_sd(contents) << LL_ENDL; - LLPointer<AISCommand> cmd_ptr = new SlamFolderCommand(folder_id, contents, cb); - cmd_ptr->run_command(); + + AISAPI::completion_t cr = (cb) ? boost::bind(&doInventoryCb, cb, _1) : AISAPI::completion_t(); + AISAPI::SlamFolder(folder_id, contents, cr); } else // no cap { diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index 0b70af44bc..3e027e3d05 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -70,6 +70,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. @@ -153,189 +154,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) - { - const std::string url = getURL(); - - const std::string& cookie = getResponseHeader(HTTP_IN_HEADER_SET_COOKIE); - - // *TODO: What about bad status codes? Does this destroy previous cookies? - LLViewerMedia::openIDCookieResponse(url, 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; @@ -1412,6 +1230,18 @@ bool LLViewerMedia::parseRawCookie(const std::string raw_cookie, std::string& na return false; } +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 @@ -1419,104 +1249,180 @@ void LLViewerMedia::setOpenIDCookie(const std::string& url) { 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(); - } + 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 @. + // 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(); + } - getCookieStore()->setCookiesFromHost(sOpenIDCookie, authority.substr(host_start, host_end - host_start)); + getCookieStore()->setCookiesFromHost(sOpenIDCookie, authority.substr(hostStart, hostEnd - hostStart)); - if (url.length()) + if (url.length()) + { + LLMediaCtrl* media_instance = LLFloaterReg::getInstance("destinations")->getChild<LLMediaCtrl>("destination_guide_contents"); + if (media_instance) { - LLMediaCtrl* media_instance = LLFloaterReg::getInstance("destinations")->getChild<LLMediaCtrl>("destination_guide_contents"); - if (media_instance) + std::string cookie_host = authority.substr(hostStart, hostEnd - hostStart); + std::string cookie_name = ""; + std::string cookie_value = ""; + std::string cookie_path = ""; + bool httponly = true; + bool secure = true; + if (parseRawCookie(sOpenIDCookie, cookie_name, cookie_value, cookie_path, httponly, secure) && + media_instance->getMediaPlugin()) { - std::string cookie_host = authority.substr(host_start, host_end - host_start); - std::string cookie_name = ""; - std::string cookie_value = ""; - std::string cookie_path = ""; - bool httponly = true; - bool secure = true; - if (parseRawCookie(sOpenIDCookie, cookie_name, cookie_value, cookie_path, httponly, secure) && - media_instance->getMediaPlugin()) - { - media_instance->getMediaPlugin()->setCookie(url, cookie_name, cookie_value, cookie_host, cookie_path, httponly, secure); - } + media_instance->getMediaPlugin()->setCookie(url, cookie_name, cookie_value, cookie_host, cookie_path, httponly, secure); } } + } - // NOTE: this is the original OpenID cookie code, so of which is no longer needed now that we - // are using CEF - it's very intertwined with other code so, for the moment, I'm going to - // leave it alone and make a task to come back to it once we're sure the CEF cookie code is robust. + // NOTE: this is the original OpenID cookie code, so of which is no longer needed now that we + // are using CEF - it's very intertwined with other code so, for the moment, I'm going to + // leave it alone and make a task to come back to it once we're sure the CEF cookie code is robust. - // 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(); + // 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()); - std::string profile_url = getProfileURL(""); - LLURL raw_profile_url( profile_url.c_str() ); - 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); - } + LL_DEBUGS("MediaAuth") << "Requesting " << url << LL_ENDL; + LL_DEBUGS("MediaAuth") << "sOpenIDCookie = [" << sOpenIDCookie << "]" << LL_ENDL; + + LLSD result = httpAdapter->getRawAndSuspend(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); + } ///////////////////////////////////////////////////////////////////////////////////////// // 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 = \"" << openidUrl << "\", token = \"" << openidToken << "\"" << LL_ENDL; + + LLCoros::instance().launch("LLViewerMedia::openIDSetupCoro", + boost::bind(&LLViewerMedia::openIDSetupCoro, openidUrl, openidToken)); +} + +/*static*/ +void LLViewerMedia::openIDSetupCoro(std::string openidUrl, std::string openidToken) { - LL_DEBUGS("MediaAuth") << "url = \"" << openid_url << "\", token = \"" << openid_token << "\"" << LL_ENDL; + 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); - // post the token to the url - // the responder will need to extract the cookie(s). + httpOpts->setWantHeaders(true); - // Save the OpenID URL for later -- we may need the host when adding the cookie. - sOpenIDURL.init(openid_url.c_str()); + // 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(); - // 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"); - 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"; + LLCore::BufferArray::ptr_t rawbody(new LLCore::BufferArray); + LLCore::BufferArrayStream bas(rawbody.get()); + + bas << std::noskipws << openidToken; + + LLSD result = httpAdapter->postRawAndSuspend(httpRequest, openidUrl, rawbody, httpOpts, httpHeaders); - // 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); + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); - LLHTTPClient::postRaw( - openid_url, - data, - size, - new LLViewerMediaOpenIDResponder(), - headers); + 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].asString(); + + // *TODO: What about bad status codes? Does this destroy previous cookies? + LLViewerMedia::openIDCookieResponse(openidUrl, cookie); + LL_DEBUGS("MediaAuth") << "OpenID cookie set." << LL_ENDL; + } ///////////////////////////////////////////////////////////////////////////////////////// @@ -1712,7 +1618,6 @@ LLViewerMediaImpl::LLViewerMediaImpl( const LLUUID& texture_id, mIsParcelMedia(false), mProximity(-1), mProximityDistance(0.0f), - mMimeTypeProbe(NULL), mMediaAutoPlay(false), mInNearbyMediaList(false), mClearCache(false), @@ -1722,7 +1627,9 @@ 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. @@ -2684,8 +2591,9 @@ void LLViewerMediaImpl::navigateInternal() mNavigateSuspendedDeferred = true; return; } - - if(mMimeTypeProbe != NULL) + + + if (!mMimeProbe.expired()) { LL_WARNS() << "MIME type probe already in progress -- bailing out." << LL_ENDL; return; @@ -2723,14 +2631,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) { @@ -2760,6 +2662,81 @@ 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); + + // Increment our refcount so that we do not go away while the coroutine is active. + this->ref(); + + mMimeProbe = httpAdapter; + + httpOpts->setFollowRedirects(true); + httpOpts->setHeadersOnly(true); + + httpHeaders->append(HTTP_OUT_HEADER_ACCEPT, "*/*"); + httpHeaders->append(HTTP_OUT_HEADER_COOKIE, ""); + + LLSD result = httpAdapter->getRawAndSuspend(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; + } + + if (this->getNumRefs() > 1) + { // if there is only a single ref count outstanding it will be the one we took out above... + // we can skip the rest of this routine + + 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(); + } + } + + } + else + { + LL_WARNS() << "LLViewerMediaImpl to be released." << LL_ENDL; + } + + this->unref(); +} + ////////////////////////////////////////////////////////////////////////////////////////// void LLViewerMediaImpl::navigateStop() { @@ -2878,7 +2855,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. } @@ -3775,18 +3752,11 @@ 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(); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t probeAdapter = mMimeProbe.lock(); - // 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; - } - } + if (probeAdapter) + probeAdapter->cancelSuspendedOperation(); + } void LLViewerMediaImpl::addObject(LLVOVolume* obj) diff --git a/indra/newview/llviewermedia.h b/indra/newview/llviewermedia.h index 268dcae20e..48f0d9dc4d 100644 --- 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,12 +164,16 @@ public: static void setOnlyAudibleMediaTextureID(const LLUUID& texture_id); static LLSD getHeaders(); + static LLCore::HttpHeaders::ptr_t getHttpHeaders(); private: static bool parseRawCookie(const std::string raw_cookie, std::string& name, std::string& value, std::string& path, bool& httponly, bool& secure); static void setOpenIDCookie(const std::string& url); 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; @@ -181,7 +188,6 @@ class LLViewerMediaImpl public: friend class LLViewerMedia; - friend class LLMimeDiscoveryResponder; LLViewerMediaImpl( const LLUUID& texture_id, @@ -457,7 +463,6 @@ private: S32 mProximity; F64 mProximityDistance; F64 mProximityCamera; - LLMimeDiscoveryResponder *mMimeTypeProbe; bool mMediaAutoPlay; std::string mMediaEntryURL; bool mInNearbyMediaList; // used by LLPanelNearbyMedia::refreshList() for performance reasons @@ -475,6 +480,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/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 20f3d25be3..99a9ed1d75 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -391,32 +391,10 @@ void set_underclothes_menu_options() void set_merchant_SLM_menu() { - // DD-170 : SLM Alpha and Beta program : for the moment, we always show the SLM menu and - // tools so that all merchants can try out the UI, even if not migrated. - // *TODO : Keep SLM UI hidden for non migrated merchant in released viewer - - //if (LLMarketplaceData::instance().getSLMStatus() == MarketplaceStatusCodes::MARKET_PLACE_NOT_MIGRATED_MERCHANT) - //{ - // Merchant not migrated: show only the old Merchant Outbox menu - // gMenuHolder->getChild<LLView>("MerchantOutbox")->setVisible(TRUE); - //} - //else - //{ - // All other cases (new merchant, not merchant, migrated merchant): show the new Marketplace Listings menu and enable the tool - gMenuHolder->getChild<LLView>("MarketplaceListings")->setVisible(TRUE); - LLCommand* command = LLCommandManager::instance().getCommand("marketplacelistings"); - gToolBarView->enableCommand(command->id(), true); - //} -} - -void set_merchant_outbox_menu(U32 status, const LLSD& content) -{ - // If the merchant is fully migrated, the API is disabled (503) and we won't show the old menu item. - // In all other cases, we show it. - if (status != MarketplaceErrorCodes::IMPORT_SERVER_API_DISABLED) - { - gMenuHolder->getChild<LLView>("MerchantOutbox")->setVisible(TRUE); - } + // All other cases (new merchant, not merchant, migrated merchant): show the new Marketplace Listings menu and enable the tool + gMenuHolder->getChild<LLView>("MarketplaceListings")->setVisible(TRUE); + LLCommand* command = LLCommandManager::instance().getCommand("marketplacelistings"); + gToolBarView->enableCommand(command->id(), true); } void check_merchant_status() @@ -435,17 +413,6 @@ void check_merchant_status() // Launch an SLM test connection to get the merchant status LLMarketplaceData::instance().initializeSLM(boost::bind(&set_merchant_SLM_menu)); - - // Do the Merchant Outbox init only once per session - if (LLMarketplaceInventoryImporter::instance().getMarketPlaceStatus() == MarketplaceStatusCodes::MARKET_PLACE_NOT_INITIALIZED) - { - // Hide merchant outbox related menu item - gMenuHolder->getChild<LLView>("MerchantOutbox")->setVisible(FALSE); - - // Launch a Merchant Outbox test connection to get the migration status - LLMarketplaceInventoryImporter::instance().setStatusReportCallback(boost::bind(&set_merchant_outbox_menu,_1, _2)); - LLMarketplaceInventoryImporter::instance().initialize(); - } } } diff --git a/indra/newview/llviewermenufile.cpp b/indra/newview/llviewermenufile.cpp index f8e50ba463..4f24dfafac 100644 --- a/indra/newview/llviewermenufile.cpp +++ b/indra/newview/llviewermenufile.cpp @@ -62,11 +62,10 @@ #include "lluploaddialog.h" #include "lltrans.h" #include "llfloaterbuycurrency.h" +#include "llviewerassetupload.h" // linden libraries -#include "llassetuploadresponders.h" #include "lleconomy.h" -#include "llhttpclient.h" #include "llnotificationsutil.h" #include "llsdserialize.h" #include "llsdutil.h" @@ -83,8 +82,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 +410,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 +421,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 LLNewFileResourceUploadInfo( + 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 +616,7 @@ void handle_compress_image(void*) } } + LLUUID upload_new_resource( const std::string& src_filename, std::string name, @@ -636,276 +632,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 LLNewFileResourceUploadInfo( + 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 +771,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 +842,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..6941b4dc0e 100644 --- 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,26 +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, - const std::string inventory_type_string, - const LLUUID& item_folder_id, - const std::string& item_name, - const std::string& item_description, - const LLSD& server_response, - S32 upload_price); - class LLFilePickerThread : public LLThread { //multi-threaded file picker (runs system specific file picker in background and calls "notify" from main thread) public: diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index e605cf1061..e6095acf27 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -122,6 +122,8 @@ #include "llnotificationmanager.h" // #include "llexperiencecache.h" +#include "llexperiencecache.h" + #if LL_MSVC // disable boost::lexical_cast warning #pragma warning (disable:4702) @@ -3454,53 +3456,29 @@ void process_decline_callingcard(LLMessageSystem* msg, void**) LLNotificationsUtil::add("CallingCardDeclined"); } -class ChatTranslationReceiver : public LLTranslate::TranslationReceiver +void translateSuccess(LLChat chat, LLSD toastArgs, std::string originalMsg, std::string expectLang, std::string translation, const std::string detected_language) { -public : - ChatTranslationReceiver(const std::string &from_lang, const std::string &to_lang, const std::string &mesg, - const LLChat &chat, const LLSD &toast_args) - : LLTranslate::TranslationReceiver(from_lang, to_lang), - m_chat(chat), - m_toastArgs(toast_args), - m_origMesg(mesg) - { - } - - static ChatTranslationReceiver* build(const std::string &from_lang, const std::string &to_lang, const std::string &mesg, const LLChat &chat, const LLSD &toast_args) - { - return new ChatTranslationReceiver(from_lang, to_lang, mesg, chat, toast_args); - } - -protected: - void handleResponse(const std::string &translation, const std::string &detected_language) - { - // filter out non-interesting responeses - if ( !translation.empty() - && (mToLang != detected_language) - && (LLStringUtil::compareInsensitive(translation, m_origMesg) != 0) ) - { - m_chat.mText += " (" + translation + ")"; - } + // filter out non-interesting responses + if (!translation.empty() + && ((detected_language.empty()) || (expectLang != detected_language)) + && (LLStringUtil::compareInsensitive(translation, originalMsg) != 0)) + { + chat.mText += " (" + translation + ")"; + } - LLNotificationsUI::LLNotificationManager::instance().onChat(m_chat, m_toastArgs); - } + LLNotificationsUI::LLNotificationManager::instance().onChat(chat, toastArgs); +} - void handleFailure(int status, const std::string& err_msg) - { - LL_WARNS() << "Translation failed for mesg " << m_origMesg << " toLang " << mToLang << " fromLang " << mFromLang << LL_ENDL; +void translateFailure(LLChat chat, LLSD toastArgs, int status, const std::string err_msg) +{ + std::string msg = LLTrans::getString("TranslationFailed", LLSD().with("[REASON]", err_msg)); + LLStringUtil::replaceString(msg, "\n", " "); // we want one-line error messages + chat.mText += " (" + msg + ")"; - std::string msg = LLTrans::getString("TranslationFailed", LLSD().with("[REASON]", err_msg)); - LLStringUtil::replaceString(msg, "\n", " "); // we want one-line error messages - m_chat.mText += " (" + msg + ")"; + LLNotificationsUI::LLNotificationManager::instance().onChat(chat, toastArgs); +} - LLNotificationsUI::LLNotificationManager::instance().onChat(m_chat, m_toastArgs); - } -private: - LLChat m_chat; - std::string m_origMesg; - LLSD m_toastArgs; -}; void process_chat_from_simulator(LLMessageSystem *msg, void **user_data) { LLChat chat; @@ -3736,8 +3714,10 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data) const std::string from_lang = ""; // leave empty to trigger autodetect const std::string to_lang = LLTranslate::getTranslateLanguage(); - LLTranslate::TranslationReceiverPtr result = ChatTranslationReceiver::build(from_lang, to_lang, mesg, chat, args); - LLTranslate::translateMessage(result, from_lang, to_lang, mesg); + LLTranslate::translateMessage(from_lang, to_lang, mesg, + boost::bind(&translateSuccess, chat, args, mesg, from_lang, _1, _2), + boost::bind(&translateFailure, chat, args, _1, _2)); + } else { @@ -6450,17 +6430,14 @@ bool script_question_cb(const LLSD& notification, const LLSD& response) if (!region) return false; - std::string lookup_url=region->getCapability("ExperiencePreferences"); - if(lookup_url.empty()) - return false; - LLSD permission; - LLSD data; - permission["permission"]="Block"; + LLExperienceCache::instance().setExperiencePermission(experience, std::string("Block"), LLExperienceCache::ExperienceGetFn_t()); - data[experience.asString()]=permission; - LLHTTPClient::put(lookup_url, data, NULL); - data["experience"]=experience; - LLEventPumps::instance().obtain("experience_permission").post(data); + LLSD permission; + LLSD data; + permission["permission"] = "Block"; + data[experience.asString()] = permission; + data["experience"] = experience; + LLEventPumps::instance().obtain("experience_permission").post(data); } } return false; @@ -6635,7 +6612,7 @@ void process_script_question(LLMessageSystem *msg, void **user_data) else if(experienceid.notNull()) { payload["experience"]=experienceid; - LLExperienceCache::get(experienceid, boost::bind(process_script_experience_details, _1, args, payload)); + LLExperienceCache::instance().get(experienceid, boost::bind(process_script_experience_details, _1, args, payload)); return; } diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index 05d0d56832..5edc3c9745 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -4459,21 +4459,21 @@ S32 LLViewerObject::setTETexture(const U8 te, const LLUUID& uuid) { // Invalid host == get from the agent's sim LLViewerFetchedTexture *image = LLViewerTextureManager::getFetchedTexture( - uuid, FTT_DEFAULT, TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE, 0, 0, LLHost::invalid); + uuid, FTT_DEFAULT, TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE, 0, 0, LLHost()); return setTETextureCore(te,image); } S32 LLViewerObject::setTENormalMap(const U8 te, const LLUUID& uuid) { LLViewerFetchedTexture *image = (uuid == LLUUID::null) ? NULL : LLViewerTextureManager::getFetchedTexture( - uuid, FTT_DEFAULT, TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE, 0, 0, LLHost::invalid); + uuid, FTT_DEFAULT, TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE, 0, 0, LLHost()); return setTENormalMapCore(te, image); } S32 LLViewerObject::setTESpecularMap(const U8 te, const LLUUID& uuid) { LLViewerFetchedTexture *image = (uuid == LLUUID::null) ? NULL : LLViewerTextureManager::getFetchedTexture( - uuid, FTT_DEFAULT, TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE, 0, 0, LLHost::invalid); + uuid, FTT_DEFAULT, TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE, 0, 0, LLHost()); return setTESpecularMapCore(te, image); } @@ -5048,7 +5048,7 @@ void LLViewerObject::updateText() { mText->setHidden(avatar->isInMuteList()); } - + LLVector3 up_offset(0,0,0); up_offset.mV[2] = getScale().mV[VZ]*0.6f; diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp index 61e918215c..8f98d66c0c 100644 --- a/indra/newview/llviewerobjectlist.cpp +++ b/indra/newview/llviewerobjectlist.cpp @@ -78,6 +78,10 @@ #include "llappviewer.h" #include "llfloaterperms.h" #include "llvocache.h" +#include "llcorehttputil.h" + +#include <algorithm> +#include <iterator> extern F32 gMinObjectDistance; extern BOOL gAnimateTextures; @@ -795,190 +799,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 +994,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 +1006,107 @@ 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); + + + + uuid_set_t diff; + + std::set_difference(mStaleObjectCost.begin(), mStaleObjectCost.end(), + mPendingObjectCost.begin(), mPendingObjectCost.end(), + std::inserter(diff, diff.begin())); + + if (diff.empty()) + { + LL_INFOS() << "No outstanding object IDs to request." << LL_ENDL; + return; + } + + LLSD idList(LLSD::emptyArray()); + + for (uuid_set_t::iterator it = diff.begin(); it != diff.end(); ++it) + { + idList.append(*it); + mStaleObjectCost.erase(*it); + } + + mPendingObjectCost.insert(diff.begin(), diff.end()); + + LLSD postData = LLSD::emptyMap(); + + postData["object_ids"] = idList; + + LLSD result = httpAdapter->postAndSuspend(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(); + + // If the object was added to the StaleObjectCost set after it had been + // added to mPendingObjectCost it would still be in the StaleObjectCost + // set when we got the response back. + mStaleObjectCost.erase(objectId); + mPendingObjectCost.erase(objectId); + + // Check to see if the request contains data for the object + if (result.has(it->asString())) + { + LLSD objectData = result[it->asString()]; + + F32 linkCost = objectData["linked_set_resource_cost"].asReal(); + F32 objectCost = objectData["resource_cost"].asReal(); + F32 physicsCost = objectData["physics_cost"].asReal(); + F32 linkPhysicsCost = objectData["linked_set_physics_cost"].asReal(); + + gObjectList.updateObjectCost(objectId, objectCost, linkCost, physicsCost, linkPhysicsCost); + } + else + { + // TODO*: Give user feedback about the missing data? + gObjectList.onObjectCostFetchFailure(objectId); + } + } + +} + void LLViewerObjectList::fetchPhysicsFlags() { // issue http request for stale object physics flags @@ -1232,41 +1120,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 +1132,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 (uuid_set_t::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->postAndSuspend(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() { @@ -1563,8 +1521,6 @@ void LLViewerObjectList::updateObjectCost(LLViewerObject* object) void LLViewerObjectList::updateObjectCost(const LLUUID& object_id, F32 object_cost, F32 link_cost, F32 physics_cost, F32 link_physics_cost) { - mPendingObjectCost.erase(object_id); - LLViewerObject* object = findObject(object_id); if (object) { diff --git a/indra/newview/llviewerobjectlist.h b/indra/newview/llviewerobjectlist.h index 594317cd9f..94c751acc6 100644 --- 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; @@ -203,17 +205,17 @@ protected: vobj_list_t mMapObjects; - std::set<LLUUID> mDeadObjects; + uuid_set_t mDeadObjects; std::map<LLUUID, LLPointer<LLViewerObject> > mUUIDObjectMap; //set of objects that need to update their cost - std::set<LLUUID> mStaleObjectCost; - std::set<LLUUID> mPendingObjectCost; + uuid_set_t mStaleObjectCost; + uuid_set_t mPendingObjectCost; //set of objects that need to update their physics flags - std::set<LLUUID> mStalePhysicsFlags; - std::set<LLUUID> mPendingPhysicsFlags; + uuid_set_t mStalePhysicsFlags; + uuid_set_t mPendingPhysicsFlags; std::vector<LLDebugBeacon> mDebugBeacons; @@ -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 fc275eb2f0..0ab3d2b4e7 100644 --- 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 aafb6b4d12..dddfb6745e 100644 --- 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 9771756266..cac2ed8585 100644 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -34,7 +34,6 @@ #include "llavatarnamecache.h" // name lookup cap url #include "llfloaterreg.h" #include "llmath.h" -#include "llhttpclient.h" #include "llregionflags.h" #include "llregionhandle.h" #include "llsurface.h" @@ -47,8 +46,6 @@ #include "llagentcamera.h" #include "llavatarrenderinfoaccountant.h" #include "llcallingcard.h" -#include "llcaphttpsender.h" -#include "llcapabilitylistener.h" #include "llcommandhandler.h" #include "lldir.h" #include "lleventpoll.h" @@ -78,6 +75,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 +92,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 +104,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 +211,292 @@ 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() - { } + 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); - static BaseCapabilitiesComplete* build( U64 region_handle, S32 id ) - { - return new BaseCapabilitiesComplete(region_handle, id); - } + LLSD result; + LLViewerRegion *regionp = NULL; -private: - /* virtual */void httpFailure() - { - LL_WARNS("AppInit", "Capabilities") << dumpResponse() << LL_ENDL; - LLViewerRegion *regionp = LLWorld::getInstance()->getRegionFromHandle(mRegionHandle); - if (regionp) - { - regionp->failedSeedCapability(); - } - } + // 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->postAndSuspend(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; + } - /* 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 ; - } +#if 0 + log_capabilities(mCapabilities); +#endif - 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); + regionp->setCapabilitiesReceived(true); - LL_DEBUGS("AppInit", "Capabilities") - << "Capability '" << iter->first << "' is '" << iter->second << "'" << LL_ENDL; + if (STATE_SEED_GRANTED_WAIT == LLStartUp::getStartupState()) + { + LLStartUp::setStartupState(STATE_SEED_CAP_GRANTED); + } - /* HACK we're waiting for the ServerReleaseNotes */ - if (iter->first == "ServerReleaseNotes" && regionp->getReleaseNotesRequested()) - { - regionp->showReleaseNotes(); - } - } + break; + } + while (true); - regionp->setCapabilitiesReceived(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() - { } + 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); - static BaseCapabilitiesCompleteTracker* build( U64 region_handle ) - { - return new BaseCapabilitiesCompleteTracker( region_handle ); - } + LLSD result; + LLViewerRegion *regionp = NULL; -private: - /* virtual */ void httpFailure() - { - LL_WARNS() << dumpResponse() << 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->postAndSuspend(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; + } - /* 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 ; - } +#if 0 + log_capabilities(mCapabilities); +#endif - 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; + 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->getAndSuspend(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, @@ -514,8 +616,9 @@ LLViewerRegion::~LLViewerRegion() delete mParcelOverlay; delete mImpl->mLandp; delete mImpl->mEventPoll; +#if 0 LLHTTPSender::clearSender(mImpl->mHost); - +#endif std::for_each(mImpl->mObjectPartition.begin(), mImpl->mObjectPartition.end(), DeletePointer()); saveObjectCache(); @@ -524,11 +627,6 @@ LLViewerRegion::~LLViewerRegion() mImpl = NULL; } -LLEventPump& LLViewerRegion::getCapAPI() const -{ - return mImpl->mCapabilityListener.getCapAPI(); -} - /*virtual*/ const LLHost& LLViewerRegion::getHost() const { @@ -2729,7 +2827,7 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames) capabilityNames.append("FetchInventory2"); capabilityNames.append("FetchInventoryDescendents2"); capabilityNames.append("IncrementCOFVersion"); - AISCommand::getCapabilityNames(capabilityNames); + AISAPI::getCapNames(capabilityNames); capabilityNames.append("GetDisplayNames"); capabilityNames.append("GetExperiences"); @@ -2809,15 +2907,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; } @@ -2827,15 +2924,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() @@ -2843,94 +2936,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") @@ -2941,12 +2946,16 @@ void LLViewerRegion::setCapability(const std::string& name, const std::string& u } else if(name == "UntrustedSimulatorMessage") { - LLHTTPSender::setSender(mImpl->mHost, new LLCapHTTPSender(url)); + mImpl->mHost.setUntrustedSimulatorCap(url); } 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 { @@ -2969,9 +2978,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"; @@ -3024,7 +3044,7 @@ void LLViewerRegion::setCapabilitiesReceived(bool received) { mCapabilitiesReceivedSignal(getRegionID()); - LLFloaterPermsDefault::sendInitialPerms(); + //LLFloaterPermsDefault::sendInitialPerms(); // This is a single-shot signal. Forget callbacks to save resources. mCapabilitiesReceivedSignal.disconnect_all_slots(); diff --git a/indra/newview/llviewerregion.h b/indra/newview/llviewerregion.h index 419034d626..a7bb546d2c 100644 --- 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 24a6758312..f52c82dab7 100644 --- a/indra/newview/llviewerstats.cpp +++ b/indra/newview/llviewerstats.cpp @@ -61,6 +61,7 @@ #include "llviewernetwork.h" #include "llmeshrepository.h" //for LLMeshRepository::sBytesReceived #include "llsdserialize.h" +#include "llcorehttputil.h" namespace LLStatViewer { @@ -411,24 +412,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. * @@ -621,8 +604,8 @@ void send_stats() LLViewerStats::getInstance()->addToMessage(body); LL_INFOS("LogViewerStatsPacket") << "Sending viewer statistics: " << body << LL_ENDL; - 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/llviewertexture.h b/indra/newview/llviewertexture.h index cedac44633..a3f8db6907 100644 --- a/indra/newview/llviewertexture.h +++ b/indra/newview/llviewertexture.h @@ -271,7 +271,7 @@ class LLViewerFetchedTexture : public LLViewerTexture protected: /*virtual*/ ~LLViewerFetchedTexture(); public: - LLViewerFetchedTexture(const LLUUID& id, FTType f_type, const LLHost& host = LLHost::invalid, BOOL usemipmaps = TRUE); + LLViewerFetchedTexture(const LLUUID& id, FTType f_type, const LLHost& host = LLHost(), BOOL usemipmaps = TRUE); LLViewerFetchedTexture(const LLImageRaw* raw, FTType f_type, BOOL usemipmaps); LLViewerFetchedTexture(const std::string& url, FTType f_type, const LLUUID& id, BOOL usemipmaps = TRUE); @@ -503,7 +503,7 @@ protected: S32 mCachedRawDiscardLevel; BOOL mCachedRawImageReady; //the rez of the mCachedRawImage reaches the upper limit. - LLHost mTargetHost; // if LLHost::invalid, just request from agent's simulator + LLHost mTargetHost; // if invalid, just request from agent's simulator // Timers LLFrameTimer mLastPacketTimer; // Time since last packet. @@ -533,7 +533,7 @@ protected: /*virtual*/ ~LLViewerLODTexture(){} public: - LLViewerLODTexture(const LLUUID& id, FTType f_type, const LLHost& host = LLHost::invalid, BOOL usemipmaps = TRUE); + LLViewerLODTexture(const LLUUID& id, FTType f_type, const LLHost& host = LLHost(), BOOL usemipmaps = TRUE); LLViewerLODTexture(const std::string& url, FTType f_type, const LLUUID& id, BOOL usemipmaps = TRUE); /*virtual*/ S8 getType() const; diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp index fc6eabd651..9ee5ed758f 100644 --- a/indra/newview/llviewertexturelist.cpp +++ b/indra/newview/llviewertexturelist.cpp @@ -259,7 +259,7 @@ void LLViewerTextureList::shutdown() if (!image->hasGLTexture() || !image->getUseDiscard() || image->needsAux() || - image->getTargetHost() != LLHost::invalid || + !image->getTargetHost().isInvalid() || !image->getUrl().empty() ) { diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 005dd65e01..1e9de3ba94 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -7313,6 +7313,7 @@ bool resolve_appearance_version(const LLAppearanceMessageContents& contents, S32 //----------------------------------------------------------------------------- void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys ) { + static S32 largestSelfCOFSeen(LLViewerInventoryCategory::VERSION_UNKNOWN); LL_DEBUGS("Avatar") << "starts" << LL_ENDL; bool enable_verbose_dumps = gSavedSettings.getBOOL("DebugAvatarAppearanceMessage"); @@ -7355,6 +7356,15 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys ) LL_DEBUGS("Avatar") << "this_update_cof_version " << this_update_cof_version << " last_update_request_cof_version " << last_update_request_cof_version << " my_cof_version " << LLAppearanceMgr::instance().getCOFVersion() << LL_ENDL; + + if (largestSelfCOFSeen > this_update_cof_version) + { + LL_WARNS("Avatar") << "Already processed appearance for COF version " << + largestSelfCOFSeen << ", discarding appearance with COF " << this_update_cof_version << LL_ENDL; + return; + } + largestSelfCOFSeen = this_update_cof_version; + } else { @@ -7389,6 +7399,7 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys ) } // No backsies zone - if we get here, the message should be valid and usable, will be processed. + LL_INFOS("Avatar") << "Processing appearance message version " << this_update_cof_version << LL_ENDL; // Note: // RequestAgentUpdateAppearanceResponder::onRequestRequested() @@ -8071,7 +8082,7 @@ LLHost LLVOAvatar::getObjectHost() const } else { - return LLHost::invalid; + return LLHost(); } } diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp index 6871c0b06f..5f48898cb1 100644 --- 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->postAndSuspend(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 60afd43781..a9c4ab26a9 100644 --- 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; @@ -394,6 +396,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..3abb716593 100644 --- 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->postAndSuspend(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 100644 --- 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/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp index e24884fe81..56c0910983 100644 --- a/indra/newview/llvoiceclient.cpp +++ b/indra/newview/llvoiceclient.cpp @@ -266,6 +266,18 @@ bool LLVoiceClient::deviceSettingsAvailable() } } +bool LLVoiceClient::deviceSettingsUpdated() +{ + if (mVoiceModule) + { + return mVoiceModule->deviceSettingsUpdated(); + } + else + { + return false; + } +} + void LLVoiceClient::refreshDeviceLists(bool clearCurrentList) { if (mVoiceModule) mVoiceModule->refreshDeviceLists(clearCurrentList); @@ -363,6 +375,7 @@ BOOL LLVoiceClient::isSessionCallBackPossible(const LLUUID& id) } } +/* obsolete BOOL LLVoiceClient::sendTextMessage(const LLUUID& participant_id, const std::string& message) { if (mVoiceModule) @@ -374,12 +387,13 @@ BOOL LLVoiceClient::sendTextMessage(const LLUUID& participant_id, const std::str return FALSE; } } +*/ void LLVoiceClient::endUserIMSession(const LLUUID& participant_id) { if (mVoiceModule) { - mVoiceModule->endUserIMSession(participant_id); + // mVoiceModule->endUserIMSession(participant_id); // A SLim leftover } } @@ -645,9 +659,8 @@ void LLVoiceClient::keyDown(KEY key, MASK mask) if(!mPTTIsMiddleMouse && LLAgent::isActionAllowed("speak")) { - bool down = (mPTTKey != KEY_NONE) - && gKeyboard->getKeyDown(mPTTKey); - inputUserControlState(down); + bool down = (mPTTKey != KEY_NONE) && gKeyboard->getKeyDown(mPTTKey); + if (down) { inputUserControlState(down); } } } @@ -655,9 +668,8 @@ void LLVoiceClient::keyUp(KEY key, MASK mask) { if(!mPTTIsMiddleMouse) { - bool down = (mPTTKey != KEY_NONE) - && gKeyboard->getKeyDown(mPTTKey); - inputUserControlState(down); + bool down = (mPTTKey != KEY_NONE) && gKeyboard->getKeyDown(mPTTKey); + if (down) { inputUserControlState(down); } } } void LLVoiceClient::middleMouseState(bool down) diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h index fb387301be..b05bcb23b7 100644 --- a/indra/newview/llvoiceclient.h +++ b/indra/newview/llvoiceclient.h @@ -128,6 +128,7 @@ public: // This returns true when it's safe to bring up the "device settings" dialog in the prefs. // i.e. when the daemon is running and connected, and the device lists are populated. virtual bool deviceSettingsAvailable()=0; + virtual bool deviceSettingsUpdated() = 0; // Requery the vivox daemon for the current list of input/output devices. // If you pass true for clearCurrentList, deviceSettingsAvailable() will be false until the query has completed @@ -155,7 +156,7 @@ public: virtual void setNonSpatialChannel(const std::string &uri, const std::string &credentials)=0; - virtual void setSpatialChannel(const std::string &uri, + virtual bool setSpatialChannel(const std::string &uri, const std::string &credentials)=0; virtual void leaveNonSpatialChannel()=0; @@ -214,7 +215,7 @@ public: //@{ virtual BOOL isSessionTextIMPossible(const LLUUID& id)=0; virtual BOOL isSessionCallBackPossible(const LLUUID& id)=0; - virtual BOOL sendTextMessage(const LLUUID& participant_id, const std::string& message)=0; + //virtual BOOL sendTextMessage(const LLUUID& participant_id, const std::string& message)=0; virtual void endUserIMSession(const LLUUID &uuid)=0; //@} @@ -335,6 +336,7 @@ public: // This returns true when it's safe to bring up the "device settings" dialog in the prefs. // i.e. when the daemon is running and connected, and the device lists are populated. bool deviceSettingsAvailable(); + bool deviceSettingsUpdated(); // returns true when the device list has been updated recently. // Requery the vivox daemon for the current list of input/output devices. // If you pass true for clearCurrentList, deviceSettingsAvailable() will be false until the query has completed @@ -431,7 +433,7 @@ public: //@{ BOOL isSessionTextIMPossible(const LLUUID& id); BOOL isSessionCallBackPossible(const LLUUID& id); - BOOL sendTextMessage(const LLUUID& participant_id, const std::string& message); + //BOOL sendTextMessage(const LLUUID& participant_id, const std::string& message) const {return true;} ; void endUserIMSession(const LLUUID &uuid); //@} diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index a6a7a35b03..c9661dfb11 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -25,6 +25,7 @@ */ #include "llviewerprecompiledheaders.h" +#include <algorithm> #include "llvoicevivox.h" #include "llsdutil.h" @@ -60,10 +61,14 @@ #include "lltrans.h" #include "llviewerwindow.h" #include "llviewercamera.h" +#include "llversioninfo.h" #include "llviewernetwork.h" #include "llnotificationsutil.h" +#include "llcorehttputil.h" +#include "lleventfilter.h" + #include "stringize.h" // for base64 decoding @@ -85,17 +90,19 @@ static const std::string VOICE_SERVER_TYPE = "Vivox"; const F32 CONNECT_THROTTLE_SECONDS = 1.0f; // Don't send positional updates more frequently than this: -const F32 UPDATE_THROTTLE_SECONDS = 0.1f; +const F32 UPDATE_THROTTLE_SECONDS = 0.5f; const F32 LOGIN_RETRY_SECONDS = 10.0f; const int MAX_LOGIN_RETRIES = 12; +// Cosine of a "trivially" small angle +const F32 MINUSCULE_ANGLE_COS = 0.999f; + // Defines the maximum number of times(in a row) "stateJoiningSession" case for spatial channel is reached in stateMachine() -// which is treated as normal. If this number is exceeded we suspect there is a problem with connection -// to voice server (EXT-4313). When voice works correctly, there is from 1 to 15 times. 50 was chosen -// to make sure we don't make mistake when slight connection problems happen- situation when connection to server is -// blocked is VERY rare and it's better to sacrifice response time in this situation for the sake of stability. -const int MAX_NORMAL_JOINING_SPATIAL_NUM = 50; +// which is treated as normal. The is the number of frames to wait for a channel join before giving up. This was changed +// from the original count of 50 for two reason. Modern PCs have higher frame rates and sometimes the SLVoice process +// backs up processing join requests. There is a log statement that records when channel joins take longer than 100 frames. +const int MAX_NORMAL_JOINING_SPATIAL_NUM = 1500; // How often to check for expired voice fonts in seconds const F32 VOICE_FONT_EXPIRY_INTERVAL = 10.f; @@ -122,65 +129,8 @@ 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; -}; - - +const int ERROR_VIVOX_OBJECT_NOT_FOUND = 1001; +const int ERROR_VIVOX_NOT_LOGGED_IN = 1007; /////////////////////////////////////////////////////////////////////////////////////////////// @@ -194,59 +144,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() @@ -265,7 +162,6 @@ static void killGateway() /////////////////////////////////////////////////////////////////////////////////////////////// LLVivoxVoiceClient::LLVivoxVoiceClient() : - mState(stateDisabled), mSessionTerminateRequested(false), mRelogRequested(false), mConnected(false), @@ -277,16 +173,18 @@ LLVivoxVoiceClient::LLVivoxVoiceClient() : mTuningEnergy(0.0f), mTuningMicVolume(0), mTuningMicVolumeDirty(true), - mTuningSpeakerVolume(0), + mTuningSpeakerVolume(50), // Set to 50 so the user can hear himself when he sets his mic volume mTuningSpeakerVolumeDirty(true), - mTuningExitState(stateDisabled), + mDevicesListUpdated(false), mAreaVoiceDisabled(false), - mAudioSession(NULL), + mAudioSession(), mAudioSessionChanged(false), - mNextAudioSession(NULL), + mNextAudioSession(), mCurrentParcelLocalID(0), + mConnectorEstablished(false), + mAccountLoggedIn(false), mNumberOfAliases(0), mCommandCookie(0), mLoginRetryCount(0), @@ -326,7 +224,16 @@ LLVivoxVoiceClient::LLVivoxVoiceClient() : mShutdownComplete(true), mPlayRequestCount(0), - mAvatarNameCacheConnection() + mAvatarNameCacheConnection(), + mIsInTuningMode(false), + mIsInChannel(false), + mIsJoiningSession(false), + mIsWaitingForFonts(false), + mIsLoggingIn(false), + mIsLoggedIn(false), + mIsProcessingChannels(false), + mIsCoroutineActive(false), + mVivoxPump("vivoxClientPump") { mSpeakerVolume = scale_speaker_volume(0); @@ -335,7 +242,7 @@ LLVivoxVoiceClient::LLVivoxVoiceClient() : // gMuteListp isn't set up at this point, so we defer this until later. // gMuteListp->addObserver(&mutelist_listener); - + #if LL_DARWIN || LL_LINUX || LL_SOLARIS // HACK: THIS DOES NOT BELONG HERE @@ -349,9 +256,7 @@ LLVivoxVoiceClient::LLVivoxVoiceClient() : signal(SIGCHLD, SIG_IGN); #endif - // set up state machine - setState(stateDisabled); - + gIdleCallbacks.addFunction(idle, this); } @@ -371,25 +276,22 @@ void LLVivoxVoiceClient::init(LLPumpIO *pump) { // constructor will set up LLVoiceClient::getInstance() LLVivoxVoiceClient::getInstance()->mPump = pump; + +// LLCoros::instance().launch("LLVivoxVoiceClient::voiceControlCoro();", +// boost::bind(&LLVivoxVoiceClient::voiceControlCoro, LLVivoxVoiceClient::getInstance())); + } void LLVivoxVoiceClient::terminate() { + // needs to be done manually here since we will not get another pass in + // coroutines... that mechanism is long since gone. + if (mIsLoggedIn) + logoutOfVivox(false); if(mConnected) { - logout(); - connectorShutdown(); -#ifdef LL_WINDOWS - int count=0; - while (!mShutdownComplete && 10 > count++) - { - stateMachine(); - _sleep(1000); - } - -#endif - closeSocket(); // Need to do this now -- bad things happen if the destructor does it later. - cleanUp(); + breakVoiceConnection(false); + mConnected = false; } else { @@ -435,6 +337,8 @@ void LLVivoxVoiceClient::updateSettings() bool LLVivoxVoiceClient::writeString(const std::string &str) { bool result = false; +// LL_WARNS("LOW Voice") << "sending:\n" << str << LL_ENDL; + if(mConnected) { apr_status_t err; @@ -450,17 +354,20 @@ bool LLVivoxVoiceClient::writeString(const std::string &str) (const char*)str.data(), &written); - if(err == 0) + if(err == 0 && written == size) { // Success. result = true; } - // TODO: handle partial writes (written is number of bytes written) - // Need to set socket to non-blocking before this will work. -// else if(APR_STATUS_IS_EAGAIN(err)) -// { -// // -// } + else if (err == 0 && written != size) { + // Did a short write, log it for now + LL_WARNS("Voice") << ") short write on socket sending data to vivox daemon." << "Sent " << written << "bytes instead of " << size <<LL_ENDL; + } + else if(APR_STATUS_IS_EAGAIN(err)) + { + char buf[MAX_STRING]; + LL_WARNS("Voice") << "EAGAIN error " << err << " (" << apr_strerror(err, buf, MAX_STRING) << ") sending data to vivox daemon." << LL_ENDL; + } else { // Assume any socket error means something bad. For now, just close the socket. @@ -483,8 +390,6 @@ void LLVivoxVoiceClient::connectorCreate() std::string loglevel = "0"; // Transition to stateConnectorStarted when the connector handle comes back. - setState(stateConnectorStarting); - std::string savedLogLevel = gSavedSettings.getString("VivoxDebugLevel"); if(savedLogLevel != "0") @@ -497,34 +402,34 @@ void LLVivoxVoiceClient::connectorCreate() << "<ClientName>V2 SDK</ClientName>" << "<AccountManagementServer>" << mVoiceAccountServerURI << "</AccountManagementServer>" << "<Mode>Normal</Mode>" + << "<ConnectorHandle>" << LLVivoxSecurity::getInstance()->connectorHandle() << "</ConnectorHandle>" << "<Logging>" - << "<Folder>" << logpath << "</Folder>" - << "<FileNamePrefix>Connector</FileNamePrefix>" - << "<FileNameSuffix>.log</FileNameSuffix>" - << "<LogLevel>" << loglevel << "</LogLevel>" + << "<Folder>" << logpath << "</Folder>" + << "<FileNamePrefix>Connector</FileNamePrefix>" + << "<FileNameSuffix>.log</FileNameSuffix>" + << "<LogLevel>" << loglevel << "</LogLevel>" << "</Logging>" - << "<Application></Application>" //Name can cause problems per vivox. - << "<MaxCalls>12</MaxCalls>" - << "</Request>\n\n\n"; + << "<Application>" << LLVersionInfo::getChannel().c_str() << " " << LLVersionInfo::getVersion().c_str() << "</Application>" + //<< "<Application></Application>" //Name can cause problems per vivox. + << "<MaxCalls>12</MaxCalls>" + << "</Request>\n\n\n"; writeString(stream.str()); } void LLVivoxVoiceClient::connectorShutdown() { - setState(stateConnectorStopping); - - if(!mConnectorHandle.empty()) + if(!mConnectorEstablished) { std::ostringstream stream; stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.InitiateShutdown.1\">" - << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>" + << "<ConnectorHandle>" << LLVivoxSecurity::getInstance()->connectorHandle() << "</ConnectorHandle>" << "</Request>" << "\n\n\n"; mShutdownComplete = false; - mConnectorHandle.clear(); + mConnectorEstablished = false; writeString(stream.str()); } @@ -540,33 +445,7 @@ void LLVivoxVoiceClient::userAuthorized(const std::string& user_id, const LLUUID mAccountName = nameFromID(agentID); } -void LLVivoxVoiceClient::requestVoiceAccountProvision(S32 retries) -{ - LLViewerRegion *region = gAgent.getRegion(); - - // If we've not received the capability yet, return. - // the password will remain empty, so we'll remain in - // stateIdle - if ( region && - region->capabilitiesReceived() && - (mVoiceEnabled || !mIsInitialized)) - { - std::string url = - region->getCapability("ProvisionVoiceAccountRequest"); - - if ( !url.empty() ) - { - LLHTTPClient::post( - url, - LLSD(), - new LLVivoxVoiceAccountProvisionResponder(retries)); - - setState(stateConnectorStart); - } - } -} - -void LLVivoxVoiceClient::login( +void LLVivoxVoiceClient::setLoginInfo( const std::string& account_name, const std::string& password, const std::string& voice_sip_uri_hostname, @@ -575,7 +454,7 @@ void LLVivoxVoiceClient::login( mVoiceSIPURIHostName = voice_sip_uri_hostname; mVoiceAccountServerURI = voice_account_server_uri; - if(!mAccountHandle.empty()) + if(mAccountLoggedIn) { // Already logged in. LL_WARNS("Voice") << "Called while already logged in." << LL_ENDL; @@ -634,1038 +513,1300 @@ void LLVivoxVoiceClient::login( void LLVivoxVoiceClient::idle(void* user_data) { - LLVivoxVoiceClient* self = (LLVivoxVoiceClient*)user_data; - self->stateMachine(); -} - -std::string LLVivoxVoiceClient::state2string(LLVivoxVoiceClient::state inState) -{ - std::string result = "UNKNOWN"; - - // Prevent copy-paste errors when updating this list... -#define CASE(x) case x: result = #x; break - - switch(inState) - { - CASE(stateDisableCleanup); - CASE(stateDisabled); - CASE(stateStart); - CASE(stateDaemonLaunched); - CASE(stateConnecting); - CASE(stateConnected); - CASE(stateIdle); - CASE(stateMicTuningStart); - CASE(stateMicTuningRunning); - CASE(stateMicTuningStop); - CASE(stateCaptureBufferPaused); - CASE(stateCaptureBufferRecStart); - CASE(stateCaptureBufferRecording); - CASE(stateCaptureBufferPlayStart); - CASE(stateCaptureBufferPlaying); - CASE(stateConnectorStart); - CASE(stateConnectorStarting); - CASE(stateConnectorStarted); - CASE(stateLoginRetry); - CASE(stateLoginRetryWait); - CASE(stateNeedsLogin); - CASE(stateLoggingIn); - CASE(stateLoggedIn); - CASE(stateVoiceFontsWait); - CASE(stateVoiceFontsReceived); - CASE(stateCreatingSessionGroup); - CASE(stateNoChannel); - CASE(stateRetrievingParcelVoiceInfo); - CASE(stateJoiningSession); - CASE(stateSessionJoined); - CASE(stateRunning); - CASE(stateLeavingSession); - CASE(stateSessionTerminated); - CASE(stateLoggingOut); - CASE(stateLoggedOut); - CASE(stateConnectorStopping); - CASE(stateConnectorStopped); - CASE(stateConnectorFailed); - CASE(stateConnectorFailedWaiting); - CASE(stateLoginFailed); - CASE(stateLoginFailedWaiting); - CASE(stateJoinSessionFailed); - CASE(stateJoinSessionFailedWaiting); - CASE(stateJail); - } - -#undef CASE - - return result; } +//========================================================================= +// the following are methods to support the coroutine implementation of the +// voice connection and processing. They should only be called in the context +// of a coroutine. +// +// +void LLVivoxVoiceClient::voiceControlCoro() +{ + mIsCoroutineActive = true; + LLCoros::set_consuming(true); + + do + { + + startAndConnectSession(); -void LLVivoxVoiceClient::setState(state inState) + if (mTuningMode) + { + performMicTuning(); + } + else if (mVoiceEnabled) + { + waitForChannel(); + } + + endAndDisconnectSession(); + + // if we hit this and mRelogRequested is true, that indicates + // that we attempted to relog into Vivox and were rejected. + // Rather than just quit out of voice, we will tear it down (above) + // and then reconstruct the voice connecino from scratch. + if (mRelogRequested) + { + while (isGatewayRunning()) + { + llcoro::suspendUntilTimeout(1.0); + } + } + } + while (mRelogRequested); + mIsCoroutineActive = false; +} + + +bool LLVivoxVoiceClient::startAndConnectSession() { - LL_DEBUGS("Voice") << "entering state " << state2string(inState) << LL_ENDL; - - mState = inState; + if (!startAndLaunchDaemon()) + { + return false; + } + + if (!provisionVoiceAccount()) + { + giveUp(); + return false; + } + + if (!establishVoiceConnection()) + { + giveUp(); + return false; + } + + return true; } -void LLVivoxVoiceClient::stateMachine() +bool LLVivoxVoiceClient::endAndDisconnectSession() { - if(gDisconnected) - { - // The viewer has been disconnected from the sim. Disable voice. - setVoiceEnabled(false); - } - - if(mVoiceEnabled || (!mIsInitialized &&!mTerminateDaemon) ) - { - updatePosition(); - } - else if(mTuningMode) - { - // Tuning mode is special -- it needs to launch SLVoice even if voice is disabled. - } - else - { - if((getState() != stateDisabled) && (getState() != stateDisableCleanup)) - { - // User turned off voice support. Send the cleanup messages, close the socket, and reset. - if(!mConnected || mTerminateDaemon) - { - // if voice was turned off after the daemon was launched but before we could connect to it, we may need to issue a kill. - LL_INFOS("Voice") << "Disabling voice before connection to daemon, terminating." << LL_ENDL; - killGateway(); - mTerminateDaemon = false; - } - - logout(); - connectorShutdown(); - - setState(stateDisableCleanup); - } - } - + breakVoiceConnection(true); - switch(getState()) - { - //MARK: stateDisableCleanup - case stateDisableCleanup: - // Clean up and reset everything. - closeSocket(); - cleanUp(); + killGateway(); - mAccountHandle.clear(); - mAccountPassword.clear(); - mVoiceAccountServerURI.clear(); - - setState(stateDisabled); - break; - - //MARK: stateDisabled - case stateDisabled: - if(mTuningMode || ((mVoiceEnabled || !mIsInitialized) && !mAccountName.empty())) - { - setState(stateStart); - } - break; - - //MARK: stateStart - case stateStart: - if(gSavedSettings.getBOOL("CmdLineDisableVoice")) - { - // Voice is locked out, we must not launch the vivox daemon. - setState(stateJail); - } - else if(!isGatewayRunning() && gSavedSettings.getBOOL("EnableVoiceChat")) - { - if (true) // production build, not test - { - // Launch the voice daemon - - // *FIX:Mani - Using the executable dir instead - // of mAppRODataDir, the working directory from which the app - // is launched. - //std::string exe_path = gDirUtilp->getAppRODataDir(); - std::string exe_path = gDirUtilp->getExecutableDir(); - exe_path += gDirUtilp->getDirDelimiter(); + return true; +} + + +bool LLVivoxVoiceClient::startAndLaunchDaemon() +{ + //--------------------------------------------------------------------- + if (gSavedSettings.getBOOL("CmdLineDisableVoice")) + { + // Voice is locked out, we must not launch the vivox daemon. + return false; + } + + if (!isGatewayRunning() && gSavedSettings.getBOOL("EnableVoiceChat")) + { +#ifndef VIVOXDAEMON_REMOTEHOST + // Launch the voice daemon + + // *FIX:Mani - Using the executable dir instead + // of mAppRODataDir, the working directory from which the app + // is launched. + //std::string exe_path = gDirUtilp->getAppRODataDir(); + std::string exe_path = gDirUtilp->getExecutableDir(); + exe_path += gDirUtilp->getDirDelimiter(); #if LL_WINDOWS - exe_path += "SLVoice.exe"; + exe_path += "SLVoice.exe"; #elif LL_DARWIN - exe_path += "../Resources/SLVoice"; + exe_path += "../Resources/SLVoice"; #else - exe_path += "SLVoice"; + exe_path += "SLVoice"; #endif - // See if the vivox executable exists - llstat s; - if (!LLFile::stat(exe_path, &s)) - { - // vivox executable exists. Build the command line and launch the daemon. - LLProcess::Params params; - params.executable = exe_path; + // See if the vivox executable exists + llstat s; + if (!LLFile::stat(exe_path, &s)) + { + // vivox executable exists. Build the command line and launch the daemon. + LLProcess::Params params; + params.executable = exe_path; - std::string loglevel = gSavedSettings.getString("VivoxDebugLevel"); - std::string shutdown_timeout = gSavedSettings.getString("VivoxShutdownTimeout"); - if(loglevel.empty()) - { - loglevel = "0"; // turn logging off completely - } - - params.args.add("-ll"); - params.args.add(loglevel); + std::string loglevel = gSavedSettings.getString("VivoxDebugLevel"); + std::string shutdown_timeout = gSavedSettings.getString("VivoxShutdownTimeout"); + if (loglevel.empty()) + { + loglevel = "-1"; // turn logging off completely, was 0 for error level logging. + } - std::string log_folder = gSavedSettings.getString("VivoxLogDirectory"); - - if (log_folder.empty()) - { - log_folder = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, ""); - } - - params.args.add("-lf"); - params.args.add(log_folder); + params.args.add("-ll"); + params.args.add(loglevel); - if(!shutdown_timeout.empty()) - { - params.args.add("-st"); - params.args.add(shutdown_timeout); - } - params.cwd = gDirUtilp->getAppRODataDir(); - sGatewayPtr = LLProcess::create(params); + std::string log_folder = gSavedSettings.getString("VivoxLogDirectory"); - mDaemonHost = LLHost(gSavedSettings.getString("VivoxVoiceHost").c_str(), gSavedSettings.getU32("VivoxVoicePort")); - } - else - { - LL_INFOS("Voice") << exe_path << " not found." << LL_ENDL; - } - } - else - { - // SLIM SDK: port changed from 44124 to 44125. - // We can connect to a client gateway running on another host. This is useful for testing. - // To do this, launch the gateway on a nearby host like this: - // vivox-gw.exe -p tcp -i 0.0.0.0:44125 - // and put that host's IP address here. - mDaemonHost = LLHost(gSavedSettings.getString("VivoxVoiceHost"), gSavedSettings.getU32("VivoxVoicePort")); - } + if (log_folder.empty()) + { + log_folder = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, ""); + } - mUpdateTimer.start(); - mUpdateTimer.setTimerExpirySec(CONNECT_THROTTLE_SECONDS); + params.args.add("-lf"); + params.args.add(log_folder); - setState(stateDaemonLaunched); - - // Dirty the states we'll need to sync with the daemon when it comes up. - mMuteMicDirty = true; - mMicVolumeDirty = true; - mSpeakerVolumeDirty = true; - mSpeakerMuteDirty = true; - // These only need to be set if they're not default (i.e. empty string). - mCaptureDeviceDirty = !mCaptureDevice.empty(); - mRenderDeviceDirty = !mRenderDevice.empty(); - - mMainSessionGroupHandle.clear(); - } - break; + if (!shutdown_timeout.empty()) + { + params.args.add("-st"); + params.args.add(shutdown_timeout); + } + params.cwd = gDirUtilp->getAppRODataDir(); - //MARK: stateDaemonLaunched - case stateDaemonLaunched: - if(mUpdateTimer.hasExpired()) - { - LL_DEBUGS("Voice") << "Connecting to vivox daemon:" << mDaemonHost << LL_ENDL; +# ifdef VIVOX_HANDLE_ARGS + params.args.add("-ah"); + params.args.add(LLVivoxSecurity::getInstance()->accountHandle()); - mUpdateTimer.setTimerExpirySec(CONNECT_THROTTLE_SECONDS); + params.args.add("-ch"); + params.args.add(LLVivoxSecurity::getInstance()->connectorHandle()); +# endif // VIVOX_HANDLE_ARGS - if(!mSocket) - { - mSocket = LLSocket::create(gAPRPoolp, LLSocket::STREAM_TCP); - } - - mConnected = mSocket->blockingConnect(mDaemonHost); - if(mConnected) - { - setState(stateConnecting); - } - else - { - // If the connect failed, the socket may have been put into a bad state. Delete it. - closeSocket(); - } - } - break; + sGatewayPtr = LLProcess::create(params); - //MARK: stateConnecting - case stateConnecting: - // Can't do this until we have the pump available. - if(mPump) - { - // MBW -- Note to self: pumps and pipes examples in - // indra/test/io.cpp - // indra/test/llpipeutil.{cpp|h} + mDaemonHost = LLHost(gSavedSettings.getString("VivoxVoiceHost").c_str(), gSavedSettings.getU32("VivoxVoicePort")); + } + else + { + LL_INFOS("Voice") << exe_path << " not found." << LL_ENDL; + return false; + } +#else + // SLIM SDK: port changed from 44124 to 44125. + // We can connect to a client gateway running on another host. This is useful for testing. + // To do this, launch the gateway on a nearby host like this: + // vivox-gw.exe -p tcp -i 0.0.0.0:44125 + // and put that host's IP address here. + mDaemonHost = LLHost(gSavedSettings.getString("VivoxVoiceHost"), gSavedSettings.getU32("VivoxVoicePort")); +#endif - // Attach the pumps and pipes - - LLPumpIO::chain_t readChain; + // Dirty the states we'll need to sync with the daemon when it comes up. + mMuteMicDirty = true; + mMicVolumeDirty = true; + mSpeakerVolumeDirty = true; + mSpeakerMuteDirty = true; + // These only need to be set if they're not default (i.e. empty string). + mCaptureDeviceDirty = !mCaptureDevice.empty(); + mRenderDeviceDirty = !mRenderDevice.empty(); - readChain.push_back(LLIOPipe::ptr_t(new LLIOSocketReader(mSocket))); - readChain.push_back(LLIOPipe::ptr_t(new LLVivoxProtocolParser())); + mMainSessionGroupHandle.clear(); + } - mPump->addChain(readChain, NEVER_CHAIN_EXPIRY_SECS); + //--------------------------------------------------------------------- + llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS); - setState(stateConnected); - } + LL_DEBUGS("Voice") << "Connecting to vivox daemon:" << mDaemonHost << LL_ENDL; - break; - - //MARK: stateConnected - case stateConnected: - // Initial devices query - getCaptureDevicesSendMessage(); - getRenderDevicesSendMessage(); + int connectAttempt = 0; - mLoginRetryCount = 0; + while (!mConnected) + { + ++connectAttempt; + LL_DEBUGS("Voice") << "Connecting to vivox daemon:" << mDaemonHost << " (#" << connectAttempt << ")" << LL_ENDL; + closeSocket(); + if (!mSocket) + { + mSocket = LLSocket::create(gAPRPoolp, LLSocket::STREAM_TCP); + } - setState(stateIdle); - break; + mConnected = mSocket->blockingConnect(mDaemonHost); + if (!mConnected) + llcoro::suspendUntilTimeout(CONNECT_THROTTLE_SECONDS); + } - //MARK: stateIdle - case stateIdle: - // This is the idle state where we're connected to the daemon but haven't set up a connector yet. - if(mTuningMode) - { - mTuningExitState = stateIdle; - setState(stateMicTuningStart); - } - else if(!mVoiceEnabled && mIsInitialized) - { - // We never started up the connector. This will shut down the daemon. - setState(stateConnectorStopped); - } - else if(!mAccountName.empty()) - { - if ( mAccountPassword.empty() ) - { - requestVoiceAccountProvision(); - } - } - break; + //--------------------------------------------------------------------- + llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS); - //MARK: stateMicTuningStart - case stateMicTuningStart: - if(mUpdateTimer.hasExpired()) - { - if(mCaptureDeviceDirty || mRenderDeviceDirty) - { - // These can't be changed while in tuning mode. Set them before starting. - std::ostringstream stream; - - buildSetCaptureDevice(stream); - buildSetRenderDevice(stream); + while (!mPump) + { // Can't do this until we have the pump available. + llcoro::suspend(); + } + + // MBW -- Note to self: pumps and pipes examples in + // indra/test/io.cpp + // indra/test/llpipeutil.{cpp|h} - if(!stream.str().empty()) - { - writeString(stream.str()); - } + // Attach the pumps and pipes - // This will come around again in the same state and start the capture, after the timer expires. - mUpdateTimer.start(); - mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS); - } - else - { - // duration parameter is currently unused, per Mike S. - tuningCaptureStartSendMessage(10000); + LLPumpIO::chain_t readChain; - setState(stateMicTuningRunning); - } - } - - break; - - //MARK: stateMicTuningRunning - case stateMicTuningRunning: - if(!mTuningMode || mCaptureDeviceDirty || mRenderDeviceDirty) - { - // All of these conditions make us leave tuning mode. - setState(stateMicTuningStop); - } - else - { - // process mic/speaker volume changes - if(mTuningMicVolumeDirty || mTuningSpeakerVolumeDirty) - { - std::ostringstream stream; - - if(mTuningMicVolumeDirty) - { - LL_INFOS("Voice") << "setting tuning mic level to " << mTuningMicVolume << LL_ENDL; - stream - << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetMicLevel.1\">" - << "<Level>" << mTuningMicVolume << "</Level>" - << "</Request>\n\n\n"; - } - - if(mTuningSpeakerVolumeDirty) - { - stream - << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetSpeakerLevel.1\">" - << "<Level>" << mTuningSpeakerVolume << "</Level>" - << "</Request>\n\n\n"; - } - - mTuningMicVolumeDirty = false; - mTuningSpeakerVolumeDirty = false; + readChain.push_back(LLIOPipe::ptr_t(new LLIOSocketReader(mSocket))); + readChain.push_back(LLIOPipe::ptr_t(new LLVivoxProtocolParser())); - if(!stream.str().empty()) - { - writeString(stream.str()); - } - } - } - break; - - //MARK: stateMicTuningStop - case stateMicTuningStop: - { - // transition out of mic tuning - tuningCaptureStopSendMessage(); - - setState(mTuningExitState); - - // if we exited just to change devices, this will keep us from re-entering too fast. - mUpdateTimer.start(); - mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS); - - } - break; + mPump->addChain(readChain, NEVER_CHAIN_EXPIRY_SECS); - //MARK: stateCaptureBufferPaused - case stateCaptureBufferPaused: - if (!mCaptureBufferMode) - { - // Leaving capture mode. + //--------------------------------------------------------------------- + llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS); - mCaptureBufferRecording = false; - mCaptureBufferRecorded = false; - mCaptureBufferPlaying = false; + // Initial devices query + getCaptureDevicesSendMessage(); + getRenderDevicesSendMessage(); - // Return to stateNoChannel to trigger reconnection to a channel. - setState(stateNoChannel); - } - else if (mCaptureBufferRecording) - { - setState(stateCaptureBufferRecStart); - } - else if (mCaptureBufferPlaying) - { - setState(stateCaptureBufferPlayStart); - } - break; + mLoginRetryCount = 0; + + return true; +} + +bool LLVivoxVoiceClient::provisionVoiceAccount() +{ + LL_INFOS("Voice") << "Provisioning voice account." << LL_ENDL; + while (!gAgent.getRegion()) + { + // *TODO* Set up a call back on agent that sends a message to a pump we can use to wake up. + llcoro::suspend(); + } - //MARK: stateCaptureBufferRecStart - case stateCaptureBufferRecStart: - captureBufferRecordStartSendMessage(); + LLViewerRegion *region = gAgent.getRegion(); - // Flag that something is recorded to allow playback. - mCaptureBufferRecorded = true; + while (!region->capabilitiesReceived()) + { + // *TODO* Pump a message for wake up. + llcoro::suspend(); + } - // Start the timer, recording will be stopped when it expires. - mCaptureTimer.start(); - mCaptureTimer.setTimerExpirySec(CAPTURE_BUFFER_MAX_TIME); + std::string url = region->getCapability("ProvisionVoiceAccountRequest"); - // Update UI, should really use a separate callback. - notifyVoiceFontObservers(); + 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); + int retryCount(0); - setState(stateCaptureBufferRecording); - break; + LLSD result; - //MARK: stateCaptureBufferRecording - case stateCaptureBufferRecording: - if (!mCaptureBufferMode || !mCaptureBufferRecording || - mCaptureBufferPlaying || mCaptureTimer.hasExpired()) - { - // Stop recording - captureBufferRecordStopSendMessage(); - mCaptureBufferRecording = false; + do + { + result = httpAdapter->postAndSuspend(httpRequest, url, LLSD(), httpOpts); - // Update UI, should really use a separate callback. - notifyVoiceFontObservers(); + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); - setState(stateCaptureBufferPaused); - } - break; + if (status == LLCore::HttpStatus(404)) + { + if (++retryCount > 5) + { + LL_WARNS("Voice") << "Could not access voice provision cap after 5 attempts." << LL_ENDL; + return false; + } + LL_WARNS("Voice") << "Provision CAP 404. Retrying in 1.0" << LL_ENDL; + llcoro::suspendUntilTimeout(1.0); - //MARK: stateCaptureBufferPlayStart - case stateCaptureBufferPlayStart: - captureBufferPlayStartSendMessage(mPreviewVoiceFont); + continue; + } + else if (!status) + { + LL_WARNS("Voice") << "Unable to provision voice account." << LL_ENDL; + return false; + } + break; + } while (true); - // Store the voice font being previewed, so that we know to restart if it changes. - mPreviewVoiceFontLast = mPreviewVoiceFont; + std::string voiceSipUriHostname; + std::string voiceAccountServerUri; + std::string voiceUserName = result["username"].asString(); + std::string voicePassword = result["password"].asString(); - // Update UI, should really use a separate callback. - notifyVoiceFontObservers(); + //LL_DEBUGS("Voice") << "ProvisionVoiceAccountRequest response:" << dumpResponse() << LL_ENDL; - setState(stateCaptureBufferPlaying); - break; + if (result.has("voice_sip_uri_hostname")) + voiceSipUriHostname = result["voice_sip_uri_hostname"].asString(); - //MARK: stateCaptureBufferPlaying - case stateCaptureBufferPlaying: - if (mCaptureBufferPlaying && mPreviewVoiceFont != mPreviewVoiceFontLast) - { - // If the preview voice font changes, restart playing with the new font. - setState(stateCaptureBufferPlayStart); - } - else if (!mCaptureBufferMode || !mCaptureBufferPlaying || mCaptureBufferRecording) - { - // Stop playing. - captureBufferPlayStopSendMessage(); - mCaptureBufferPlaying = false; + // this key is actually misnamed -- it will be an entire URI, not just a hostname. + if (result.has("voice_account_server_name")) + voiceAccountServerUri = result["voice_account_server_name"].asString(); - // Update UI, should really use a separate callback. - notifyVoiceFontObservers(); + setLoginInfo(voiceUserName, voicePassword, voiceSipUriHostname, voiceAccountServerUri); - setState(stateCaptureBufferPaused); - } - break; + return true; +} - //MARK: stateConnectorStart - case stateConnectorStart: - if(!mVoiceEnabled && mIsInitialized) - { - // We were never logged in. This will shut down the connector. - setState(stateLoggedOut); - } - else if(!mVoiceAccountServerURI.empty()) - { - connectorCreate(); - } - break; - - //MARK: stateConnectorStarting - case stateConnectorStarting: // waiting for connector handle - // connectorCreateResponse() will transition from here to stateConnectorStarted. - break; - - //MARK: stateConnectorStarted - case stateConnectorStarted: // connector handle received - if(!mVoiceEnabled && mIsInitialized) - { - // We were never logged in. This will shut down the connector. - setState(stateLoggedOut); - } - else - { - // The connector is started. Send a login message. - setState(stateNeedsLogin); - } - break; - - //MARK: stateLoginRetry - case stateLoginRetry: - if(mLoginRetryCount == 0) - { - // First retry -- display a message to the user - notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGIN_RETRY); - } - - mLoginRetryCount++; - - if(mLoginRetryCount > MAX_LOGIN_RETRIES) - { - LL_WARNS("Voice") << "too many login retries, giving up." << LL_ENDL; - setState(stateLoginFailed); - LLSD args; - std::stringstream errs; - errs << mVoiceAccountServerURI << "\n:UDP: 3478, 3479, 5060, 5062, 12000-17000"; - args["HOSTID"] = errs.str(); - mTerminateDaemon = true; - if (LLGridManager::getInstance()->isSystemGrid()) - { - LLNotificationsUtil::add("NoVoiceConnect", args); - } - else - { - LLNotificationsUtil::add("NoVoiceConnect-GIAB", args); - } - } - else - { - LL_INFOS("Voice") << "will retry login in " << LOGIN_RETRY_SECONDS << " seconds." << LL_ENDL; - mUpdateTimer.start(); - mUpdateTimer.setTimerExpirySec(LOGIN_RETRY_SECONDS); - setState(stateLoginRetryWait); - } - break; - - //MARK: stateLoginRetryWait - case stateLoginRetryWait: - if(mUpdateTimer.hasExpired()) - { - setState(stateNeedsLogin); - } - break; - - //MARK: stateNeedsLogin - case stateNeedsLogin: - if(!mAccountPassword.empty()) - { - setState(stateLoggingIn); - loginSendMessage(); - } - break; - - //MARK: stateLoggingIn - case stateLoggingIn: // waiting for account handle - // loginResponse() will transition from here to stateLoggedIn. - break; - - //MARK: stateLoggedIn - case stateLoggedIn: // account handle received +bool LLVivoxVoiceClient::establishVoiceConnection() +{ + LLEventPump &voiceConnectPump = LLEventPumps::instance().obtain("vivoxClientPump"); - notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); + if (!mVoiceEnabled && mIsInitialized) + return false; - if (LLVoiceClient::instance().getVoiceEffectEnabled()) - { - // Request the set of available voice fonts. - setState(stateVoiceFontsWait); - refreshVoiceEffectLists(true); - } - else - { - // If voice effects are disabled, pretend we've received them and carry on. - setState(stateVoiceFontsReceived); - } + connectorCreate(); - // Set up the mute list observer if it hasn't been set up already. - if((!sMuteListListener_listening)) - { - LLMuteList::getInstance()->addObserver(&mutelist_listener); - sMuteListListener_listening = true; - } - - // Set the initial state of mic mute, local speaker volume, etc. - { - std::ostringstream stream; - - buildLocalAudioUpdates(stream); - - if(!stream.str().empty()) - { - writeString(stream.str()); - } - } - break; - - //MARK: stateVoiceFontsWait - case stateVoiceFontsWait: // Await voice font list - // accountGetSessionFontsResponse() will transition from here to - // stateVoiceFontsReceived, to ensure we have the voice font list - // before attempting to create a session. - break; - - //MARK: stateVoiceFontsReceived - case stateVoiceFontsReceived: // Voice font list received - // Set up the timer to check for expiring voice fonts - mVoiceFontExpiryTimer.start(); - mVoiceFontExpiryTimer.setTimerExpirySec(VOICE_FONT_EXPIRY_INTERVAL); + LLSD result; + do + { + result = llcoro::suspendUntilEventOn(voiceConnectPump); + LL_DEBUGS("Voice") << "event=" << ll_pretty_print_sd(result) << LL_ENDL; + } + while (!result.has("connector")); + + if (!result["connector"]) + { + return false; + } + + if (!mVoiceEnabled && mIsInitialized) + return false; + + return true; +} + +bool LLVivoxVoiceClient::breakVoiceConnection(bool corowait) +{ + LLEventPump &voicePump = LLEventPumps::instance().obtain("vivoxClientPump"); + bool retval(true); + + mShutdownComplete = false; + connectorShutdown(); + + if (corowait) + { + LLSD result = llcoro::suspendUntilEventOn(voicePump); + LL_DEBUGS("Voice") << "event=" << ll_pretty_print_sd(result) << LL_ENDL; + + retval = result.has("connector"); + } + else + { // If we are not doing a corowait then we must sleep until the connector has responded + // otherwise we may very well close the socket too early. +#if LL_WINDOWS + int count = 0; + while (!mShutdownComplete && 10 > count++) + { // Rider: This comes out to a max wait time of 10 seconds. + // The situation that brings us here is a call from ::terminate() + // and so the viewer is attempting to go away. Don't slow it down + // longer than this. + _sleep(1000); + } +#endif + } + + closeSocket(); // Need to do this now -- bad things happen if the destructor does it later. + cleanUp(); + mConnected = false; + + return retval; +} + +bool LLVivoxVoiceClient::loginToVivox() +{ + int loginRetryCount(0); + LLEventPump &voicePump = LLEventPumps::instance().obtain("vivoxClientPump"); + + bool response_ok(false); + bool account_login(false); + bool send_login(true); + + do + { + mIsLoggingIn = true; + if (send_login) + loginSendMessage(); + + send_login = false; + + LLSD result = llcoro::suspendUntilEventOn(voicePump); + LL_DEBUGS("Voice") << "event=" << ll_pretty_print_sd(result) << LL_ENDL; + + if (result.has("login")) + { + std::string loginresp = result["login"]; + + if (loginresp == "retry") + { + if (!loginRetryCount) + { // on first retry notify user + notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGIN_RETRY); + } + + if ((++loginRetryCount > MAX_LOGIN_RETRIES) || (!result["login_retry"])) + { + LL_WARNS("Voice") << "too many login retries, giving up." << LL_ENDL; + LLSD args; + std::stringstream errs; + errs << mVoiceAccountServerURI << "\n:UDP: 3478, 3479, 5060, 5062, 12000-17000"; + args["HOSTID"] = errs.str(); + mTerminateDaemon = true; + if (LLGridManager::getInstance()->isSystemGrid()) + { + LLNotificationsUtil::add("NoVoiceConnect", args); + } + else + { + LLNotificationsUtil::add("NoVoiceConnect-GIAB", args); + } + + mIsLoggingIn = false; + return false; + } + + response_ok = false; + account_login = false; + send_login = true; + + LL_INFOS("Voice") << "will retry login in " << LOGIN_RETRY_SECONDS << " seconds." << LL_ENDL; + llcoro::suspendUntilTimeout(LOGIN_RETRY_SECONDS); + } + else if (loginresp == "failed") + { + mIsLoggingIn = false; + return false; + } + else if (loginresp == "response_ok") + { + response_ok = true; + } + else if (loginresp == "account_login") + { + account_login = true; + } + } + + } while (!response_ok || !account_login); + + mRelogRequested = false; + mIsLoggedIn = true; + notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); + + // Set up the mute list observer if it hasn't been set up already. + if ((!sMuteListListener_listening)) + { + LLMuteList::getInstance()->addObserver(&mutelist_listener); + sMuteListListener_listening = true; + } + + // Set the initial state of mic mute, local speaker volume, etc. + sendLocalAudioUpdates(); + mIsLoggingIn = false; + + return true; +} + +void LLVivoxVoiceClient::logoutOfVivox(bool wait) +{ + LLEventPump &voicePump = LLEventPumps::instance().obtain("vivoxClientPump"); + + if (!mIsLoggedIn) + return; + + // Ensure that we'll re-request provisioning before logging in again + mAccountPassword.clear(); + mVoiceAccountServerURI.clear(); + + logoutSendMessage(); + + if (wait) + { + LLSD result = llcoro::suspendUntilEventOn(voicePump); + + LL_DEBUGS("Voice") << "event=" << ll_pretty_print_sd(result) << LL_ENDL; + + if (result.has("logout")) + { + } + } + + mIsLoggedIn = false; +} + + +bool LLVivoxVoiceClient::retrieveVoiceFonts() +{ + LLEventPump &voicePump = LLEventPumps::instance().obtain("vivoxClientPump"); + + // Request the set of available voice fonts. + refreshVoiceEffectLists(true); + + mIsWaitingForFonts = true; + LLSD result; + do + { + result = llcoro::suspendUntilEventOn(voicePump); + + LL_DEBUGS("Voice") << "event=" << ll_pretty_print_sd(result) << LL_ENDL; + if (result.has("voice_fonts")) + break; + } while (true); + mIsWaitingForFonts = false; + + mVoiceFontExpiryTimer.start(); + mVoiceFontExpiryTimer.setTimerExpirySec(VOICE_FONT_EXPIRY_INTERVAL); + + return result["voice_fonts"].asBoolean(); +} + +bool LLVivoxVoiceClient::requestParcelVoiceInfo() +{ + //_INFOS("Voice") << "Requesting voice info for Parcel" << LL_ENDL; + + LLViewerRegion * region = gAgent.getRegion(); + if (region == NULL || !region->capabilitiesReceived()) + { + LL_DEBUGS("Voice") << "ParcelVoiceInfoRequest capability not yet available, deferring" << LL_ENDL; + return false; + } + + // grab the cap. + std::string url = gAgent.getRegion()->getCapability("ParcelVoiceInfoRequest"); + if (url.empty()) + { + // Region dosn't have the cap. Stop probing. + LL_DEBUGS("Voice") << "ParcelVoiceInfoRequest capability not available in this region" << LL_ENDL; + return false; + } + + // update the parcel + checkParcelChanged(true); + + LL_DEBUGS("Voice") << "sending ParcelVoiceInfoRequest (" << mCurrentRegionName << ", " << mCurrentParcelLocalID << ")" << LL_ENDL; + + 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); + + LLSD result = httpAdapter->postAndSuspend(httpRequest, url, LLSD()); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (mSessionTerminateRequested || (!mVoiceEnabled && mIsInitialized)) + { + // if a terminate request has been received, + // bail and go to the stateSessionTerminated + // state. If the cap request is still pending, + // the responder will check to see if we've moved + // to a new session and won't change any state. + terminateAudioSession(true); + return false; + } + + if ((!status) || (mSessionTerminateRequested || (!mVoiceEnabled && mIsInitialized))) + { + if (mSessionTerminateRequested || (!mVoiceEnabled && mIsInitialized)) + { + LL_WARNS("Voice") << "Session terminated." << LL_ENDL; + } + + LL_WARNS("Voice") << "No voice on parcel" << LL_ENDL; + sessionTerminate(); + return false; + } + + 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(); + } + } + + if (!uri.empty()) + 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. + return !setSpatialChannel(uri, credentials); +} + +bool LLVivoxVoiceClient::addAndJoinSession(const sessionStatePtr_t &nextSession) +{ + LLEventPump &voicePump = LLEventPumps::instance().obtain("vivoxClientPump"); + mIsJoiningSession = true; + + sessionStatePtr_t oldSession = mAudioSession; + + LL_INFOS("Voice") << "Adding or joining voice session " << nextSession->mHandle << LL_ENDL; + + mAudioSession = nextSession; + mAudioSessionChanged = true; + if (!mAudioSession->mReconnect) + { + mNextAudioSession.reset(); + } + + // The old session may now need to be deleted. + reapSession(oldSession); + + if (!mAudioSession->mHandle.empty()) + { + // Connect to a session by session handle + + sessionMediaConnectSendMessage(mAudioSession); + } + else + { + // Connect to a session by URI + sessionCreateSendMessage(mAudioSession, true, false); + } + + notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINING); + + llcoro::suspend(); + + LLSD result; + + if (mSpatialJoiningNum == MAX_NORMAL_JOINING_SPATIAL_NUM) + { + // Notify observers to let them know there is problem with voice + notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED); + LL_WARNS() << "There seems to be problem with connection to voice server. Disabling voice chat abilities." << LL_ENDL; + } + + // Increase mSpatialJoiningNum only for spatial sessions- it's normal to reach this case for + // example for p2p many times while waiting for response, so it can't be used to detect errors + if (mAudioSession && mAudioSession->mIsSpatial) + { + mSpatialJoiningNum++; + } + + if (!mVoiceEnabled && mIsInitialized) + { + mIsJoiningSession = false; + // User bailed out during connect -- jump straight to teardown. + terminateAudioSession(true); + notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED); + return false; + } + else if (mSessionTerminateRequested) + { + if (mAudioSession && !mAudioSession->mHandle.empty()) + { + // Only allow direct exits from this state in p2p calls (for cancelling an invite). + // Terminating a half-connected session on other types of calls seems to break something in the vivox gateway. + if (mAudioSession->mIsP2P) + { + terminateAudioSession(true); + mIsJoiningSession = false; + notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL); + return false; + } + } + } + + bool added(true); + bool joined(false); + + // It appears that I need to wait for BOTH the SessionGroup.AddSession response and the SessionStateChangeEvent with state 4 + // before continuing from this state. They can happen in either order, and if I don't wait for both, things can get stuck. + // For now, the SessionGroup.AddSession response handler sets mSessionHandle and the SessionStateChangeEvent handler transitions to stateSessionJoined. + // This is a cheap way to make sure both have happened before proceeding. + do + { + result = llcoro::suspendUntilEventOn(voicePump); + + LL_DEBUGS("Voice") << "event=" << ll_pretty_print_sd(result) << LL_ENDL; + if (result.has("session")) + { + if (result.has("handle")) + { + if (result["handle"] != mAudioSession->mHandle) + { + LL_WARNS("Voice") << "Message for session handle \"" << result["handle"] << "\" while waiting for \"" << mAudioSession->mHandle << "\"." << LL_ENDL; + continue; + } + } + + std::string message = result["session"].asString(); + if ((message == "added") || (message == "created")) + added = true; + else if (message == "joined") + joined = true; + else if ((message == "failed") || (message == "removed")) + { // we will get a removed message if a voice call is declined. + + if (message == "failed") + { + int reason = result["reason"].asInteger(); + LL_WARNS("Voice") << "Add and join failed for reason " << reason << LL_ENDL; + + if ((reason == ERROR_VIVOX_NOT_LOGGED_IN) || + (reason == ERROR_VIVOX_OBJECT_NOT_FOUND)) + { + LL_INFOS("Voice") << "Requesting reprovision and login." << LL_ENDL; + requestRelog(); + } + + } + + notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL); + mIsJoiningSession = false; + return false; + } + } + } while (!added || !joined); + + mIsJoiningSession = false; + + if (mSpatialJoiningNum > 100) + { + LL_WARNS("Voice") << "There seems to be problem with connecting to a voice channel. Frames to join were " << mSpatialJoiningNum << LL_ENDL; + } + + mSpatialJoiningNum = 0; + + // Events that need to happen when a session is joined could go here. + // send an initial positional information immediately upon joining. + // + // do an initial update for position and the camera position, then send a + // positional update. + updatePosition(); + enforceTether(); + + // Dirty state that may need to be sync'ed with the daemon. + mMuteMicDirty = true; + mSpeakerVolumeDirty = true; + mSpatialCoordsDirty = true; + + sendPositionAndVolumeUpdate(); + + notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED); + + return true; +} + +bool LLVivoxVoiceClient::terminateAudioSession(bool wait) +{ + + if (mAudioSession) + { + LL_INFOS("Voice") << "Terminating current voice session " << mAudioSession->mHandle << LL_ENDL; + + if (mIsLoggedIn) + { + if (!mAudioSession->mHandle.empty()) + { + +#if RECORD_EVERYTHING + // HACK: for testing only + // Save looped recording + std::string savepath("/tmp/vivoxrecording"); + { + time_t now = time(NULL); + const size_t BUF_SIZE = 64; + char time_str[BUF_SIZE]; /* Flawfinder: ignore */ + + strftime(time_str, BUF_SIZE, "%Y-%m-%dT%H:%M:%SZ", gmtime(&now)); + savepath += time_str; + } + recordingLoopSave(savepath); +#endif + + sessionMediaDisconnectSendMessage(mAudioSession); + + if (wait) + { + LLEventPump &voicePump = LLEventPumps::instance().obtain("vivoxClientPump"); + LLSD result; + do + { + result = llcoro::suspendUntilEventOn(voicePump); + + LL_DEBUGS("Voice") << "event=" << ll_pretty_print_sd(result) << LL_ENDL; + if (result.has("session")) + { + if (result.has("handle")) + { + if (result["handle"] != mAudioSession->mHandle) + { + LL_WARNS("Voice") << "Message for session handle \"" << result["handle"] << "\" while waiting for \"" << mAudioSession->mHandle << "\"." << LL_ENDL; + continue; + } + } + + std::string message = result["session"].asString(); + if (message == "removed") + break; + } + } while (true); + + } + } + else + { + LL_WARNS("Voice") << "called with no session handle" << LL_ENDL; + } + } + else + { + LL_WARNS("Voice") << "Session " << mAudioSession->mHandle << " already terminated by logout." << LL_ENDL; + } + + sessionStatePtr_t oldSession = mAudioSession; + + mAudioSession.reset(); + // We just notified status observers about this change. Don't do it again. + mAudioSessionChanged = false; + + // The old session may now need to be deleted. + reapSession(oldSession); + + } + else + { + LL_WARNS("Voice") << "stateSessionTerminated with NULL mAudioSession" << LL_ENDL; + } + + notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL); + + // Always reset the terminate request flag when we get here. + // Some slower PCs have a race condition where they can switch to an incoming P2P call faster than the state machine leaves + // the region chat. + mSessionTerminateRequested = false; + + if ((mVoiceEnabled || !mIsInitialized) && !mRelogRequested && !LLApp::isExiting()) + { + // Just leaving a channel, go back to stateNoChannel (the "logged in but have no channel" state). + return true; + } + + return false; +} + +bool LLVivoxVoiceClient::waitForChannel() +{ + LL_INFOS("Voice") << "Waiting for channel" << LL_ENDL; + + do + { + if (!loginToVivox()) + { + return false; + } + + if (LLVoiceClient::instance().getVoiceEffectEnabled()) + { + retrieveVoiceFonts(); + + // Request the set of available voice fonts. + refreshVoiceEffectLists(false); + } #if USE_SESSION_GROUPS - // create the main session group - setState(stateCreatingSessionGroup); - sessionGroupCreateSendMessage(); -#else - setState(stateNoChannel); + // Rider: This code is completely unchanged from the original state machine + // It does not seem to be in active use... but I'd rather not rip it out. + // create the main session group + setState(stateCreatingSessionGroup); + sessionGroupCreateSendMessage(); #endif - break; - - //MARK: stateCreatingSessionGroup - case stateCreatingSessionGroup: - if(mSessionTerminateRequested || (!mVoiceEnabled && mIsInitialized)) - { - // *TODO: Question: is this the right way out of this state - setState(stateSessionTerminated); - } - else if(!mMainSessionGroupHandle.empty()) - { - // Start looped recording (needed for "panic button" anti-griefing tool) - recordingLoopStart(); - setState(stateNoChannel); - } - break; - - //MARK: stateRetrievingParcelVoiceInfo - case stateRetrievingParcelVoiceInfo: - // wait until parcel voice info is received. - if(mSessionTerminateRequested || (!mVoiceEnabled && mIsInitialized)) - { - // if a terminate request has been received, - // bail and go to the stateSessionTerminated - // state. If the cap request is still pending, - // the responder will check to see if we've moved - // to a new session and won't change any state. - setState(stateSessionTerminated); - } - break; - - - //MARK: stateNoChannel - case stateNoChannel: - LL_DEBUGS("Voice") << "State No Channel" << LL_ENDL; - mSpatialJoiningNum = 0; - - if(mSessionTerminateRequested || (!mVoiceEnabled && mIsInitialized)) - { - // TODO: Question: Is this the right way out of this state? - setState(stateSessionTerminated); - } - else if(mTuningMode) - { - mTuningExitState = stateNoChannel; - setState(stateMicTuningStart); - } - else if(mCaptureBufferMode) - { - setState(stateCaptureBufferPaused); - } - else if(checkParcelChanged() || (mNextAudioSession == NULL)) - { - // the parcel is changed, or we have no pending audio sessions, - // so try to request the parcel voice info - // if we have the cap, we move to the appropriate state - if(requestParcelVoiceInfo()) - { - setState(stateRetrievingParcelVoiceInfo); - } - } - else if(sessionNeedsRelog(mNextAudioSession)) - { - requestRelog(); - setState(stateSessionTerminated); - } - else if(mNextAudioSession) - { - sessionState *oldSession = mAudioSession; + do + { + mIsProcessingChannels = true; + llcoro::suspend(); - mAudioSession = mNextAudioSession; - mAudioSessionChanged = true; - if(!mAudioSession->mReconnect) - { - mNextAudioSession = NULL; - } - - // The old session may now need to be deleted. - reapSession(oldSession); - - if(!mAudioSession->mHandle.empty()) - { - // Connect to a session by session handle + if (mTuningMode) + { + performMicTuning(); + } + else if (mCaptureBufferMode) + { + recordingAndPlaybackMode(); + } + else if (checkParcelChanged() || (mNextAudioSession == NULL)) + { + // the parcel is changed, or we have no pending audio sessions, + // so try to request the parcel voice info + // if we have the cap, we move to the appropriate state + requestParcelVoiceInfo(); + } + else if (sessionNeedsRelog(mNextAudioSession)) + { + requestRelog(); + break; + } + else if (mNextAudioSession) + { + sessionStatePtr_t joinSession = mNextAudioSession; + mNextAudioSession.reset(); + if (!runSession(joinSession)) + break; + } - sessionMediaConnectSendMessage(mAudioSession); - } - else - { - // Connect to a session by URI - sessionCreateSendMessage(mAudioSession, true, false); - } + if (!mNextAudioSession) + llcoro::suspendUntilTimeout(1.0); + } while (mVoiceEnabled && !mRelogRequested); - notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINING); - setState(stateJoiningSession); - } - break; - - //MARK: stateJoiningSession - case stateJoiningSession: // waiting for session handle - - // If this is true we have problem with connection to voice server (EXT-4313). - // See descriptions of mSpatialJoiningNum and MAX_NORMAL_JOINING_SPATIAL_NUM. - if(mSpatialJoiningNum == MAX_NORMAL_JOINING_SPATIAL_NUM) - { - // Notify observers to let them know there is problem with voice - notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED); - LL_WARNS() << "There seems to be problem with connection to voice server. Disabling voice chat abilities." << LL_ENDL; - } - - // Increase mSpatialJoiningNum only for spatial sessions- it's normal to reach this case for - // example for p2p many times while waiting for response, so it can't be used to detect errors - if(mAudioSession && mAudioSession->mIsSpatial) - { - - mSpatialJoiningNum++; - } - - // joinedAudioSession() will transition from here to stateSessionJoined. - if(!mVoiceEnabled && mIsInitialized) - { - // User bailed out during connect -- jump straight to teardown. - setState(stateSessionTerminated); - } - else if(mSessionTerminateRequested) - { - if(mAudioSession && !mAudioSession->mHandle.empty()) - { - // Only allow direct exits from this state in p2p calls (for cancelling an invite). - // Terminating a half-connected session on other types of calls seems to break something in the vivox gateway. - if(mAudioSession->mIsP2P) - { - sessionMediaDisconnectSendMessage(mAudioSession); - setState(stateSessionTerminated); - } - } - } - break; - - //MARK: stateSessionJoined - case stateSessionJoined: // session handle received + mIsProcessingChannels = false; + logoutOfVivox(true); - mSpatialJoiningNum = 0; - // It appears that I need to wait for BOTH the SessionGroup.AddSession response and the SessionStateChangeEvent with state 4 - // before continuing from this state. They can happen in either order, and if I don't wait for both, things can get stuck. - // For now, the SessionGroup.AddSession response handler sets mSessionHandle and the SessionStateChangeEvent handler transitions to stateSessionJoined. - // This is a cheap way to make sure both have happened before proceeding. - if(mAudioSession && mAudioSession->mVoiceEnabled) - { - // Dirty state that may need to be sync'ed with the daemon. - mMuteMicDirty = true; - mSpeakerVolumeDirty = true; - mSpatialCoordsDirty = true; - - setState(stateRunning); - - // Start the throttle timer - mUpdateTimer.start(); - mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS); + if (mRelogRequested) + { + if (!provisionVoiceAccount()) + { + giveUp(); + return false; + } + } + } while (mVoiceEnabled && mRelogRequested); - // Events that need to happen when a session is joined could go here. - // Maybe send initial spatial data? - notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED); + return true; +} - } - else if(!mVoiceEnabled && mIsInitialized) - { - // User bailed out during connect -- jump straight to teardown. - setState(stateSessionTerminated); - } - else if(mSessionTerminateRequested) - { - // Only allow direct exits from this state in p2p calls (for cancelling an invite). - // Terminating a half-connected session on other types of calls seems to break something in the vivox gateway. - if(mAudioSession && mAudioSession->mIsP2P) - { - sessionMediaDisconnectSendMessage(mAudioSession); - setState(stateSessionTerminated); - } - } - break; - - //MARK: stateRunning - case stateRunning: // steady state - // Disabling voice or disconnect requested. - if((!mVoiceEnabled && mIsInitialized) || mSessionTerminateRequested) - { - leaveAudioSession(); - } - else - { - - if(!inSpatialChannel()) - { - // When in a non-spatial channel, never send positional updates. - mSpatialCoordsDirty = false; - } - else - { - if(checkParcelChanged()) - { - // if the parcel has changed, attempted to request the - // cap for the parcel voice info. If we can't request it - // then we don't have the cap URL so we do nothing and will - // recheck next time around - if(requestParcelVoiceInfo()) - { - // we did get the cap, and we made the request, - // so go wait for the response. - setState(stateRetrievingParcelVoiceInfo); - } - } - // Do the calculation that enforces the listener<->speaker tether (and also updates the real camera position) - enforceTether(); - - } - - // Do notifications for expiring Voice Fonts. - if (mVoiceFontExpiryTimer.hasExpired()) - { - expireVoiceFonts(); - mVoiceFontExpiryTimer.setTimerExpirySec(VOICE_FONT_EXPIRY_INTERVAL); - } +bool LLVivoxVoiceClient::runSession(const sessionStatePtr_t &session) +{ + LL_INFOS("Voice") << "running new voice session " << session->mHandle << LL_ENDL; - // Send an update only if the ptt or mute state has changed (which shouldn't be able to happen that often - // -- the user can only click so fast) or every 10hz, whichever is sooner. - // Sending for every volume update causes an excessive flood of messages whenever a volume slider is dragged. - if((mAudioSession && mAudioSession->mMuteDirty) || mMuteMicDirty || mUpdateTimer.hasExpired()) - { - mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS); - sendPositionalUpdate(); - } - mIsInitialized = true; - } - break; - - //MARK: stateLeavingSession - case stateLeavingSession: // waiting for terminate session response - // The handler for the Session.Terminate response will transition from here to stateSessionTerminated. - break; + if (!addAndJoinSession(session)) + { + notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN); - //MARK: stateSessionTerminated - case stateSessionTerminated: - - // Must do this first, since it uses mAudioSession. - notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL); - - if(mAudioSession) - { - sessionState *oldSession = mAudioSession; + if (mSessionTerminateRequested) + terminateAudioSession(true); - mAudioSession = NULL; - // We just notified status observers about this change. Don't do it again. - mAudioSessionChanged = false; + // if a relog has been requested then addAndJoineSession + // failed in a spectacular way and we need to back out. + // If this is not the case then we were simply trying to + // make a call and the other party rejected it. + return !mRelogRequested; + } - // The old session may now need to be deleted. - reapSession(oldSession); - } - else - { - LL_WARNS("Voice") << "stateSessionTerminated with NULL mAudioSession" << LL_ENDL; - } - - // Always reset the terminate request flag when we get here. - mSessionTerminateRequested = false; + notifyParticipantObservers(); + notifyVoiceFontObservers(); - if((mVoiceEnabled || !mIsInitialized) && !mRelogRequested && !LLApp::isExiting()) - { - // Just leaving a channel, go back to stateNoChannel (the "logged in but have no channel" state). - setState(stateNoChannel); - } - else - { - // Shutting down voice, continue with disconnecting. - logout(); - - // The state machine will take it from here - mRelogRequested = false; - } - - break; - - //MARK: stateLoggingOut - case stateLoggingOut: // waiting for logout response - // The handler for the AccountLoginStateChangeEvent will transition from here to stateLoggedOut. - break; - - //MARK: stateLoggedOut - case stateLoggedOut: // logout response received - - // Once we're logged out, these things are invalid. - mAccountHandle.clear(); - cleanUp(); + LLSD timeoutEvent = LLSD::emptyMap(); + timeoutEvent["timeout"] = LLSD::Boolean(true); - if((mVoiceEnabled || !mIsInitialized) && !mRelogRequested) - { - // User was logged out, but wants to be logged in. Send a new login request. - setState(stateNeedsLogin); - } - else - { - // shut down the connector - connectorShutdown(); - } - break; - - //MARK: stateConnectorStopping - case stateConnectorStopping: // waiting for connector stop - // The handler for the Connector.InitiateShutdown response will transition from here to stateConnectorStopped. - mShutdownComplete = true; - break; - - //MARK: stateConnectorStopped - case stateConnectorStopped: // connector stop received - setState(stateDisableCleanup); - break; - - //MARK: stateConnectorFailed - case stateConnectorFailed: - setState(stateConnectorFailedWaiting); - break; - //MARK: stateConnectorFailedWaiting - case stateConnectorFailedWaiting: - if(!mVoiceEnabled) - { - setState(stateDisableCleanup); - } - break; - - //MARK: stateLoginFailed - case stateLoginFailed: - setState(stateLoginFailedWaiting); - break; - //MARK: stateLoginFailedWaiting - case stateLoginFailedWaiting: - if(!mVoiceEnabled) - { - setState(stateDisableCleanup); - } - break; + LLEventPump &voicePump = LLEventPumps::instance().obtain("vivoxClientPump"); + LLEventTimeout timeout(voicePump); + mIsInChannel = true; + mMuteMicDirty = true; - //MARK: stateJoinSessionFailed - case stateJoinSessionFailed: - // Transition to error state. Send out any notifications here. - if(mAudioSession) - { - LL_WARNS("Voice") << "stateJoinSessionFailed: (" << mAudioSession->mErrorStatusCode << "): " << mAudioSession->mErrorStatusString << LL_ENDL; - } - else - { - LL_WARNS("Voice") << "stateJoinSessionFailed with no current session" << LL_ENDL; - } - - notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN); - setState(stateJoinSessionFailedWaiting); - break; - - //MARK: stateJoinSessionFailedWaiting - case stateJoinSessionFailedWaiting: - // Joining a channel failed, either due to a failed channel name -> sip url lookup or an error from the join message. - // Region crossings may leave this state and try the join again. - if(mSessionTerminateRequested) - { - setState(stateSessionTerminated); - } - break; - - //MARK: stateJail - case stateJail: - // We have given up. Do nothing. - break; + while (mVoiceEnabled && !mSessionTerminateRequested && !mTuningMode) + { + sendCaptureAndRenderDevices(); + if (mAudioSession && mAudioSession->mParticipantsChanged) + { + mAudioSession->mParticipantsChanged = false; + notifyParticipantObservers(); + } + + if (!inSpatialChannel()) + { + // When in a non-spatial channel, never send positional updates. + mSpatialCoordsDirty = false; + } + else + { + updatePosition(); - } - - if (mAudioSessionChanged) - { - mAudioSessionChanged = false; - notifyParticipantObservers(); - notifyVoiceFontObservers(); - } - else if (mAudioSession && mAudioSession->mParticipantsChanged) - { - mAudioSession->mParticipantsChanged = false; - notifyParticipantObservers(); - } + if (checkParcelChanged()) + { + // *RIDER: I think I can just return here if the parcel has changed + // and grab the new voice channel from the outside loop. + // + // if the parcel has changed, attempted to request the + // cap for the parcel voice info. If we can't request it + // then we don't have the cap URL so we do nothing and will + // recheck next time around + if (requestParcelVoiceInfo()) + { // The parcel voice URI has changed.. break out and reconnect. + break; + } + } + // Do the calculation that enforces the listener<->speaker tether (and also updates the real camera position) + enforceTether(); + } + sendPositionAndVolumeUpdate(); + + // Do notifications for expiring Voice Fonts. + if (mVoiceFontExpiryTimer.hasExpired()) + { + expireVoiceFonts(); + mVoiceFontExpiryTimer.setTimerExpirySec(VOICE_FONT_EXPIRY_INTERVAL); + } + + // send any requests to adjust mic and speaker settings if they have changed + sendLocalAudioUpdates(); + + mIsInitialized = true; + timeout.eventAfter(UPDATE_THROTTLE_SECONDS, timeoutEvent); + LLSD result = llcoro::suspendUntilEventOn(timeout); + if (!result.has("timeout")) // logging the timeout event spams the log + LL_DEBUGS("Voice") << "event=" << ll_pretty_print_sd(result) << LL_ENDL; + if (result.has("session")) + { + if (result.has("handle")) + { + if (result["handle"] != mAudioSession->mHandle) + { + LL_WARNS("Voice") << "Message for session handle \"" << result["handle"] << "\" while waiting for \"" << mAudioSession->mHandle << "\"." << LL_ENDL; + continue; + } + } + + std::string message = result["session"]; + + if (message == "removed") + { + notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL); + break; + } + } + else if (result.has("login")) + { + std::string message = result["login"]; + if (message == "account_logout") + { + mIsLoggedIn = false; + mRelogRequested = true; + break; + } + } + + } + + mIsInChannel = false; + terminateAudioSession(true); + + return true; +} + +void LLVivoxVoiceClient::sendCaptureAndRenderDevices() +{ + if (mCaptureDeviceDirty || mRenderDeviceDirty) + { + std::ostringstream stream; + + buildSetCaptureDevice(stream); + buildSetRenderDevice(stream); + + if (!stream.str().empty()) + { + writeString(stream.str()); + } + + llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS); + } +} + +void LLVivoxVoiceClient::recordingAndPlaybackMode() +{ + LL_INFOS("Voice") << "In voice capture/playback mode." << LL_ENDL; + LLEventPump &voicePump = LLEventPumps::instance().obtain("vivoxClientPump"); + + while (true) + { + LLSD command; + do + { + command = llcoro::suspendUntilEventOn(voicePump); + LL_DEBUGS("Voice") << "event=" << ll_pretty_print_sd(command) << LL_ENDL; + } while (!command.has("recplay")); + + if (command["recplay"].asString() == "quit") + { + mCaptureBufferMode = false; + break; + } + else if (command["recplay"].asString() == "record") + { + voiceRecordBuffer(); + } + else if (command["recplay"].asString() == "playback") + { + voicePlaybackBuffer(); + } + } + + LL_INFOS("Voice") << "Leaving capture/playback mode." << LL_ENDL; + mCaptureBufferRecording = false; + mCaptureBufferRecorded = false; + mCaptureBufferPlaying = false; + + return; +} + +int LLVivoxVoiceClient::voiceRecordBuffer() +{ + LLSD timeoutResult; + timeoutResult["recplay"] = LLSD::String("stop"); + + LL_INFOS("Voice") << "Recording voice buffer" << LL_ENDL; + + LLEventPump &voicePump = LLEventPumps::instance().obtain("vivoxClientPump"); + LLEventTimeout timeout(voicePump); + timeout.eventAfter(CAPTURE_BUFFER_MAX_TIME, timeoutResult); + LLSD result; + + captureBufferRecordStartSendMessage(); + + notifyVoiceFontObservers(); + do + { + result = llcoro::suspendUntilEventOn(voicePump); + LL_DEBUGS("Voice") << "event=" << ll_pretty_print_sd(result) << LL_ENDL; + } while (!result.has("recplay")); + + mCaptureBufferRecorded = true; + + captureBufferRecordStopSendMessage(); + mCaptureBufferRecording = false; + + // Update UI, should really use a separate callback. + notifyVoiceFontObservers(); + + return true; + /*TODO expand return to move directly into play*/ +} + +int LLVivoxVoiceClient::voicePlaybackBuffer() +{ + LLSD timeoutResult; + timeoutResult["recplay"] = LLSD::String("stop"); + + LL_INFOS("Voice") << "Playing voice buffer" << LL_ENDL; + + LLEventPump &voicePump = LLEventPumps::instance().obtain("vivoxClientPump"); + LLEventTimeout timeout(voicePump); + timeout.eventAfter(CAPTURE_BUFFER_MAX_TIME, timeoutResult); + LLSD result; + + do + { + captureBufferPlayStartSendMessage(mPreviewVoiceFont); + + // Store the voice font being previewed, so that we know to restart if it changes. + mPreviewVoiceFontLast = mPreviewVoiceFont; + + do + { + // Update UI, should really use a separate callback. + notifyVoiceFontObservers(); + + result = llcoro::suspendUntilEventOn(voicePump); + LL_DEBUGS("Voice") << "event=" << ll_pretty_print_sd(result) << LL_ENDL; + } while (!result.has("recplay")); + + if (result["recplay"] == "playback") + continue; // restart playback... May be a font change. + + break; + } while (true); + + // Stop playing. + captureBufferPlayStopSendMessage(); + mCaptureBufferPlaying = false; + + // Update UI, should really use a separate callback. + notifyVoiceFontObservers(); + + return true; +} + + +bool LLVivoxVoiceClient::performMicTuning() +{ + LL_INFOS("Voice") << "Entering voice tuning mode." << LL_ENDL; + + mIsInTuningMode = true; + llcoro::suspend(); + + while (mTuningMode) + { + + if (mCaptureDeviceDirty || mRenderDeviceDirty) + { + // These can't be changed while in tuning mode. Set them before starting. + std::ostringstream stream; + + buildSetCaptureDevice(stream); + buildSetRenderDevice(stream); + + if (!stream.str().empty()) + { + writeString(stream.str()); + } + + llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS); + } + + // loop mic back to render device. + //setMuteMic(0); // make sure the mic is not muted + std::ostringstream stream; + + stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.MuteLocalMic.1\">" + << "<ConnectorHandle>" << LLVivoxSecurity::getInstance()->connectorHandle() << "</ConnectorHandle>" + << "<Value>false</Value>" + << "</Request>\n\n\n"; + + // Dirty the mute mic state so that it will get reset when we finishing previewing + mMuteMicDirty = true; + mTuningSpeakerVolumeDirty = true; + + writeString(stream.str()); + tuningCaptureStartSendMessage(1); // 1-loop, zero, don't loop + + //--------------------------------------------------------------------- + llcoro::suspend(); + + while (mTuningMode && !mCaptureDeviceDirty && !mRenderDeviceDirty) + { + // process mic/speaker volume changes + if (mTuningMicVolumeDirty || mTuningSpeakerVolumeDirty) + { + std::ostringstream stream; + + if (mTuningMicVolumeDirty) + { + LL_INFOS("Voice") << "setting tuning mic level to " << mTuningMicVolume << LL_ENDL; + stream + << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetMicLevel.1\">" + << "<Level>" << mTuningMicVolume << "</Level>" + << "</Request>\n\n\n"; + } + + if (mTuningSpeakerVolumeDirty) + { + stream + << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetSpeakerLevel.1\">" + << "<Level>" << mTuningSpeakerVolume << "</Level>" + << "</Request>\n\n\n"; + } + + mTuningMicVolumeDirty = false; + mTuningSpeakerVolumeDirty = false; + + if (!stream.str().empty()) + { + writeString(stream.str()); + } + } + llcoro::suspend(); + } + + //--------------------------------------------------------------------- + + // transition out of mic tuning + tuningCaptureStopSendMessage(); + if (mCaptureDeviceDirty || mRenderDeviceDirty) + { + llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS); + } + } + + mIsInTuningMode = false; + + //--------------------------------------------------------------------- + return true; } +//========================================================================= + void LLVivoxVoiceClient::closeSocket(void) { mSocket.reset(); mConnected = false; - mConnectorHandle.clear(); - mAccountHandle.clear(); + mConnectorEstablished = false; + mAccountLoggedIn = false; } void LLVivoxVoiceClient::loginSendMessage() @@ -1676,11 +1817,13 @@ void LLVivoxVoiceClient::loginSendMessage() stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.Login.1\">" - << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>" + << "<ConnectorHandle>" << LLVivoxSecurity::getInstance()->connectorHandle() << "</ConnectorHandle>" << "<AccountName>" << mAccountName << "</AccountName>" - << "<AccountPassword>" << mAccountPassword << "</AccountPassword>" + << "<AccountPassword>" << mAccountPassword << "</AccountPassword>" + << "<AccountHandle>" << LLVivoxSecurity::getInstance()->accountHandle() << "</AccountHandle>" << "<AudioSessionAnswerMode>VerifyAnswer</AudioSessionAnswerMode>" - << "<EnableBuddiesAndPresence>false</EnableBuddiesAndPresence>" + << "<EnableBuddiesAndPresence>false</EnableBuddiesAndPresence>" + << "<EnablePresencePersistence>0</EnablePresencePersistence>" << "<BuddyManagementMode>Application</BuddyManagementMode>" << "<ParticipantPropertyFrequency>5</ParticipantPropertyFrequency>" << (autoPostCrashDumps?"<AutopostCrashDumps>true</AutopostCrashDumps>":"") @@ -1695,22 +1838,21 @@ void LLVivoxVoiceClient::logout() mAccountPassword.clear(); mVoiceAccountServerURI.clear(); - setState(stateLoggingOut); logoutSendMessage(); } void LLVivoxVoiceClient::logoutSendMessage() { - if(!mAccountHandle.empty()) + if(mAccountLoggedIn) { std::ostringstream stream; stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.Logout.1\">" - << "<AccountHandle>" << mAccountHandle << "</AccountHandle>" + << "<AccountHandle>" << LLVivoxSecurity::getInstance()->accountHandle() << "</AccountHandle>" << "</Request>" << "\n\n\n"; - mAccountHandle.clear(); + mAccountLoggedIn = false; writeString(stream.str()); } @@ -1718,7 +1860,7 @@ void LLVivoxVoiceClient::logoutSendMessage() void LLVivoxVoiceClient::sessionGroupCreateSendMessage() { - if(!mAccountHandle.empty()) + if(mAccountLoggedIn) { std::ostringstream stream; @@ -1726,7 +1868,7 @@ void LLVivoxVoiceClient::sessionGroupCreateSendMessage() stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.Create.1\">" - << "<AccountHandle>" << mAccountHandle << "</AccountHandle>" + << "<AccountHandle>" << LLVivoxSecurity::getInstance()->accountHandle() << "</AccountHandle>" << "<Type>Normal</Type>" << "</Request>" << "\n\n\n"; @@ -1735,7 +1877,7 @@ void LLVivoxVoiceClient::sessionGroupCreateSendMessage() } } -void LLVivoxVoiceClient::sessionCreateSendMessage(sessionState *session, bool startAudio, bool startText) +void LLVivoxVoiceClient::sessionCreateSendMessage(const sessionStatePtr_t &session, bool startAudio, bool startText) { LL_DEBUGS("Voice") << "Requesting create: " << session->mSIPURI << LL_ENDL; @@ -1751,7 +1893,7 @@ void LLVivoxVoiceClient::sessionCreateSendMessage(sessionState *session, bool st std::ostringstream stream; stream << "<Request requestId=\"" << session->mSIPURI << "\" action=\"Session.Create.1\">" - << "<AccountHandle>" << mAccountHandle << "</AccountHandle>" + << "<AccountHandle>" << LLVivoxSecurity::getInstance()->accountHandle() << "</AccountHandle>" << "<URI>" << session->mSIPURI << "</URI>"; static const std::string allowed_chars = @@ -1775,7 +1917,7 @@ void LLVivoxVoiceClient::sessionCreateSendMessage(sessionState *session, bool st writeString(stream.str()); } -void LLVivoxVoiceClient::sessionGroupAddSessionSendMessage(sessionState *session, bool startAudio, bool startText) +void LLVivoxVoiceClient::sessionGroupAddSessionSendMessage(const sessionStatePtr_t &session, bool startAudio, bool startText) { LL_DEBUGS("Voice") << "Requesting create: " << session->mSIPURI << LL_ENDL; @@ -1816,7 +1958,7 @@ void LLVivoxVoiceClient::sessionGroupAddSessionSendMessage(sessionState *session writeString(stream.str()); } -void LLVivoxVoiceClient::sessionMediaConnectSendMessage(sessionState *session) +void LLVivoxVoiceClient::sessionMediaConnectSendMessage(const sessionStatePtr_t &session) { LL_DEBUGS("Voice") << "Connecting audio to session handle: " << session->mHandle << LL_ENDL; @@ -1838,7 +1980,7 @@ void LLVivoxVoiceClient::sessionMediaConnectSendMessage(sessionState *session) writeString(stream.str()); } -void LLVivoxVoiceClient::sessionTextConnectSendMessage(sessionState *session) +void LLVivoxVoiceClient::sessionTextConnectSendMessage(const sessionStatePtr_t &session) { LL_DEBUGS("Voice") << "connecting text to session handle: " << session->mHandle << LL_ENDL; @@ -1871,64 +2013,45 @@ void LLVivoxVoiceClient::leaveAudioSession() { LL_DEBUGS("Voice") << "leaving session: " << mAudioSession->mSIPURI << LL_ENDL; - switch(getState()) + if(!mAudioSession->mHandle.empty()) { - case stateNoChannel: - // In this case, we want to pretend the join failed so our state machine doesn't get stuck. - // Skip the join failed transition state so we don't send out error notifications. - setState(stateJoinSessionFailedWaiting); - break; - case stateJoiningSession: - case stateSessionJoined: - case stateRunning: - if(!mAudioSession->mHandle.empty()) - { #if RECORD_EVERYTHING - // HACK: for testing only - // Save looped recording - std::string savepath("/tmp/vivoxrecording"); - { - time_t now = time(NULL); - const size_t BUF_SIZE = 64; - char time_str[BUF_SIZE]; /* Flawfinder: ignore */ + // HACK: for testing only + // Save looped recording + std::string savepath("/tmp/vivoxrecording"); + { + time_t now = time(NULL); + const size_t BUF_SIZE = 64; + char time_str[BUF_SIZE]; /* Flawfinder: ignore */ - strftime(time_str, BUF_SIZE, "%Y-%m-%dT%H:%M:%SZ", gmtime(&now)); - savepath += time_str; - } - recordingLoopSave(savepath); + strftime(time_str, BUF_SIZE, "%Y-%m-%dT%H:%M:%SZ", gmtime(&now)); + savepath += time_str; + } + recordingLoopSave(savepath); #endif - sessionMediaDisconnectSendMessage(mAudioSession); - setState(stateLeavingSession); - } - else - { - LL_WARNS("Voice") << "called with no session handle" << LL_ENDL; - setState(stateSessionTerminated); - } - break; - case stateJoinSessionFailed: - case stateJoinSessionFailedWaiting: - setState(stateSessionTerminated); - break; - - default: - LL_WARNS("Voice") << "called from unknown state" << LL_ENDL; - break; + sessionMediaDisconnectSendMessage(mAudioSession); + } + else + { + LL_WARNS("Voice") << "called with no session handle" << LL_ENDL; } } else { LL_WARNS("Voice") << "called with no active session" << LL_ENDL; - setState(stateSessionTerminated); } + sessionTerminate(); } -void LLVivoxVoiceClient::sessionTerminateSendMessage(sessionState *session) +void LLVivoxVoiceClient::sessionTerminateSendMessage(const sessionStatePtr_t &session) { std::ostringstream stream; - + + sessionGroupTerminateSendMessage(session); + return; + /* LL_DEBUGS("Voice") << "Sending Session.Terminate with handle " << session->mHandle << LL_ENDL; stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.Terminate.1\">" @@ -1936,9 +2059,10 @@ void LLVivoxVoiceClient::sessionTerminateSendMessage(sessionState *session) << "</Request>\n\n\n"; writeString(stream.str()); + */ } -void LLVivoxVoiceClient::sessionGroupTerminateSendMessage(sessionState *session) +void LLVivoxVoiceClient::sessionGroupTerminateSendMessage(const sessionStatePtr_t &session) { std::ostringstream stream; @@ -1951,10 +2075,12 @@ void LLVivoxVoiceClient::sessionGroupTerminateSendMessage(sessionState *session) writeString(stream.str()); } -void LLVivoxVoiceClient::sessionMediaDisconnectSendMessage(sessionState *session) +void LLVivoxVoiceClient::sessionMediaDisconnectSendMessage(const sessionStatePtr_t &session) { std::ostringstream stream; - + sessionGroupTerminateSendMessage(session); + return; + /* LL_DEBUGS("Voice") << "Sending Session.MediaDisconnect with handle " << session->mHandle << LL_ENDL; stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.MediaDisconnect.1\">" @@ -1964,22 +2090,10 @@ void LLVivoxVoiceClient::sessionMediaDisconnectSendMessage(sessionState *session << "</Request>\n\n\n"; writeString(stream.str()); + */ } -void LLVivoxVoiceClient::sessionTextDisconnectSendMessage(sessionState *session) -{ - std::ostringstream stream; - - LL_DEBUGS("Voice") << "Sending Session.TextDisconnect with handle " << session->mHandle << LL_ENDL; - stream - << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.TextDisconnect.1\">" - << "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>" - << "<SessionHandle>" << session->mHandle << "</SessionHandle>" - << "</Request>\n\n\n"; - - writeString(stream.str()); -} void LLVivoxVoiceClient::getCaptureDevicesSendMessage() { @@ -2038,6 +2152,10 @@ void LLVivoxVoiceClient::setCaptureDevice(const std::string& name) } } } +void LLVivoxVoiceClient::setDevicesListUpdated(bool state) +{ + mDevicesListUpdated = state; +} void LLVivoxVoiceClient::clearRenderDevices() { @@ -2079,9 +2197,14 @@ void LLVivoxVoiceClient::setRenderDevice(const std::string& name) void LLVivoxVoiceClient::tuningStart() { - mTuningMode = true; - LL_DEBUGS("Voice") << "Starting tuning" << LL_ENDL; - if(getState() >= stateNoChannel) + LL_DEBUGS("Voice") << "Starting tuning" << LL_ENDL; + mTuningMode = true; + if (!mIsCoroutineActive) + { + LLCoros::instance().launch("LLVivoxVoiceClient::voiceControlCoro();", + boost::bind(&LLVivoxVoiceClient::voiceControlCoro, LLVivoxVoiceClient::getInstance())); + } + else if (mIsInChannel) { LL_DEBUGS("Voice") << "no channel" << LL_ENDL; sessionTerminate(); @@ -2095,16 +2218,7 @@ void LLVivoxVoiceClient::tuningStop() bool LLVivoxVoiceClient::inTuningMode() { - bool result = false; - switch(getState()) - { - case stateMicTuningRunning: - result = true; - break; - default: - break; - } - return result; + return mIsInTuningMode; } void LLVivoxVoiceClient::tuningRenderStartSendMessage(const std::string& name, bool loop) @@ -2131,14 +2245,15 @@ void LLVivoxVoiceClient::tuningRenderStopSendMessage() writeString(stream.str()); } -void LLVivoxVoiceClient::tuningCaptureStartSendMessage(int duration) +void LLVivoxVoiceClient::tuningCaptureStartSendMessage(int loop) { LL_DEBUGS("Voice") << "sending CaptureAudioStart" << LL_ENDL; std::ostringstream stream; stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.CaptureAudioStart.1\">" - << "<Duration>" << duration << "</Duration>" + << "<Duration>-1</Duration>" + << "<LoopToRenderDevice>" << loop << "</LoopToRenderDevice>" << "</Request>\n\n\n"; writeString(stream.str()); @@ -2197,6 +2312,16 @@ bool LLVivoxVoiceClient::deviceSettingsAvailable() return result; } +bool LLVivoxVoiceClient::deviceSettingsUpdated() +{ + if (mDevicesListUpdated) + { + // a hot swap event or a polling of the audio devices has been parsed since the last redraw of the input and output device panel. + mDevicesListUpdated = !mDevicesListUpdated; // toggle the setting + return true; + } + return false; +} void LLVivoxVoiceClient::refreshDeviceLists(bool clearCurrentList) { @@ -2215,7 +2340,7 @@ void LLVivoxVoiceClient::daemonDied() LL_WARNS("Voice") << "Connection to vivox daemon lost. Resetting state."<< LL_ENDL; // Try to relaunch the daemon - setState(stateDisableCleanup); + /*TODO:*/ } void LLVivoxVoiceClient::giveUp() @@ -2223,8 +2348,6 @@ void LLVivoxVoiceClient::giveUp() // All has failed. Clean up and stop trying. closeSocket(); cleanUp(); - - setState(stateJail); } static void oldSDKTransform (LLVector3 &left, LLVector3 &up, LLVector3 &at, LLVector3d &pos, LLVector3 &vel) @@ -2352,15 +2475,15 @@ void LLVivoxVoiceClient::setHidden(bool hidden) { mHidden = hidden; - sendPositionalUpdate(); + sendPositionAndVolumeUpdate(); return; } -void LLVivoxVoiceClient::sendPositionalUpdate(void) +void LLVivoxVoiceClient::sendPositionAndVolumeUpdate(void) { std::ostringstream stream; - if(mSpatialCoordsDirty) + if (mSpatialCoordsDirty && inSpatialChannel()) { LLVector3 l, u, a, vel; LLVector3d pos; @@ -2373,10 +2496,12 @@ void LLVivoxVoiceClient::sendPositionalUpdate(void) stream << "<SpeakerPosition>"; + LLMatrix3 avatarRot = mAvatarRot.getMatrix3(); + // LL_DEBUGS("Voice") << "Sending speaker position " << mAvatarPosition << LL_ENDL; - l = mAvatarRot.getLeftRow(); - u = mAvatarRot.getUpRow(); - a = mAvatarRot.getFwdRow(); + l = avatarRot.getLeftRow(); + u = avatarRot.getUpRow(); + a = avatarRot.getFwdRow(); pos = mAvatarPosition; vel = mAvatarVelocity; @@ -2440,7 +2565,7 @@ void LLVivoxVoiceClient::sendPositionalUpdate(void) case earLocAvatar: earPosition = mAvatarPosition; earVelocity = mAvatarVelocity; - earRot = mAvatarRot; + earRot = avatarRot; break; case earLocMixed: @@ -2499,6 +2624,7 @@ void LLVivoxVoiceClient::sendPositionalUpdate(void) stream << "</ListenerPosition>"; + stream << "<ReqDispositionType>1</ReqDispositionType>"; //do not generate responses for update requests stream << "</Request>\n\n\n"; } @@ -2511,7 +2637,7 @@ void LLVivoxVoiceClient::sendPositionalUpdate(void) for(; iter != mAudioSession->mParticipantsByURI.end(); iter++) { - participantState *p = iter->second; + participantStatePtr_t p(iter->second); if(p->mVolumeDirty) { @@ -2567,7 +2693,7 @@ void LLVivoxVoiceClient::sendPositionalUpdate(void) } } - buildLocalAudioUpdates(stream); + //sendLocalAudioUpdates(); obsolete, used to send volume setting on position updates if(!stream.str().empty()) { @@ -2607,68 +2733,73 @@ void LLVivoxVoiceClient::buildSetRenderDevice(std::ostringstream &stream) } } -void LLVivoxVoiceClient::buildLocalAudioUpdates(std::ostringstream &stream) +void LLVivoxVoiceClient::sendLocalAudioUpdates() { - buildSetCaptureDevice(stream); + // Check all of the dirty states and then send messages to those needing to be changed. + // Tuningmode hands its own mute settings. - buildSetRenderDevice(stream); + std::ostringstream stream; - if(mMuteMicDirty) + if (mMuteMicDirty && !mTuningMode) { mMuteMicDirty = false; // Send a local mute command. - - LL_DEBUGS("Voice") << "Sending MuteLocalMic command with parameter " << (mMuteMic?"true":"false") << LL_ENDL; + + LL_DEBUGS("Voice") << "Sending MuteLocalMic command with parameter " << (mMuteMic ? "true" : "false") << LL_ENDL; stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.MuteLocalMic.1\">" - << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>" - << "<Value>" << (mMuteMic?"true":"false") << "</Value>" + << "<ConnectorHandle>" << LLVivoxSecurity::getInstance()->connectorHandle() << "</ConnectorHandle>" + << "<Value>" << (mMuteMic ? "true" : "false") << "</Value>" << "</Request>\n\n\n"; - + } - if(mSpeakerMuteDirty) + if (mSpeakerMuteDirty && !mTuningMode) { - const char *muteval = ((mSpeakerVolume <= scale_speaker_volume(0))?"true":"false"); + const char *muteval = ((mSpeakerVolume <= scale_speaker_volume(0)) ? "true" : "false"); mSpeakerMuteDirty = false; - LL_INFOS("Voice") << "Setting speaker mute to " << muteval << LL_ENDL; - + LL_INFOS("Voice") << "Setting speaker mute to " << muteval << LL_ENDL; + stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.MuteLocalSpeaker.1\">" - << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>" + << "<ConnectorHandle>" << LLVivoxSecurity::getInstance()->connectorHandle() << "</ConnectorHandle>" << "<Value>" << muteval << "</Value>" - << "</Request>\n\n\n"; - + << "</Request>\n\n\n"; + } - - if(mSpeakerVolumeDirty) + + if (mSpeakerVolumeDirty) { mSpeakerVolumeDirty = false; - LL_INFOS("Voice") << "Setting speaker volume to " << mSpeakerVolume << LL_ENDL; + LL_INFOS("Voice") << "Setting speaker volume to " << mSpeakerVolume << LL_ENDL; stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.SetLocalSpeakerVolume.1\">" - << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>" + << "<ConnectorHandle>" << LLVivoxSecurity::getInstance()->connectorHandle() << "</ConnectorHandle>" << "<Value>" << mSpeakerVolume << "</Value>" << "</Request>\n\n\n"; - + } - - if(mMicVolumeDirty) + + if (mMicVolumeDirty) { mMicVolumeDirty = false; - LL_INFOS("Voice") << "Setting mic volume to " << mMicVolume << LL_ENDL; + LL_INFOS("Voice") << "Setting mic volume to " << mMicVolume << LL_ENDL; stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.SetLocalMicVolume.1\">" - << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>" + << "<ConnectorHandle>" << LLVivoxSecurity::getInstance()->connectorHandle() << "</ConnectorHandle>" << "<Value>" << mMicVolume << "</Value>" - << "</Request>\n\n\n"; + << "</Request>\n\n\n"; } - + + if (!stream.str().empty()) + { + writeString(stream.str()); + } } ///////////////////////////// @@ -2676,10 +2807,11 @@ void LLVivoxVoiceClient::buildLocalAudioUpdates(std::ostringstream &stream) void LLVivoxVoiceClient::connectorCreateResponse(int statusCode, std::string &statusString, std::string &connectorHandle, std::string &versionID) { + LLSD result = LLSD::emptyMap(); + if(statusCode != 0) { LL_WARNS("Voice") << "Connector.Create response failure: " << statusString << LL_ENDL; - setState(stateConnectorFailed); LLSD args; std::stringstream errs; errs << mVoiceAccountServerURI << "\n:UDP: 3478, 3479, 5060, 5062, 12000-17000"; @@ -2693,24 +2825,39 @@ void LLVivoxVoiceClient::connectorCreateResponse(int statusCode, std::string &st { LLNotificationsUtil::add("NoVoiceConnect-GIAB", args); } + + result["connector"] = LLSD::Boolean(false); } else { // Connector created, move forward. - LL_INFOS("Voice") << "Connector.Create succeeded, Vivox SDK version is " << versionID << LL_ENDL; - mVoiceVersion.serverVersion = versionID; - mConnectorHandle = connectorHandle; - mTerminateDaemon = false; - if(getState() == stateConnectorStarting) - { - setState(stateConnectorStarted); - } + if (connectorHandle == LLVivoxSecurity::getInstance()->connectorHandle()) + { + LL_INFOS("Voice") << "Connector.Create succeeded, Vivox SDK version is " << versionID << " connector handle " << connectorHandle << LL_ENDL; + mVoiceVersion.serverVersion = versionID; + mConnectorEstablished = true; + mTerminateDaemon = false; + + result["connector"] = LLSD::Boolean(true); + } + else + { + LL_WARNS("Voice") << "Connector.Create returned wrong handle " + << "(" << connectorHandle << ")" + << " expected (" << LLVivoxSecurity::getInstance()->connectorHandle() << ")" + << LL_ENDL; + result["connector"] = LLSD::Boolean(false); + } } + + LLEventPumps::instance().post("vivoxClientPump", result); } void LLVivoxVoiceClient::loginResponse(int statusCode, std::string &statusString, std::string &accountHandle, int numberOfAliases) { - LL_DEBUGS("Voice") << "Account.Login response (" << statusCode << "): " << statusString << LL_ENDL; + LLSD result = LLSD::emptyMap(); + + LL_DEBUGS("Voice") << "Account.Login response (" << statusCode << "): " << statusString << LL_ENDL; // Status code of 20200 means "bad password". We may want to special-case that at some point. @@ -2718,29 +2865,28 @@ void LLVivoxVoiceClient::loginResponse(int statusCode, std::string &statusString { // Login failure which is probably caused by the delay after a user's password being updated. LL_INFOS("Voice") << "Account.Login response failure (" << statusCode << "): " << statusString << LL_ENDL; - setState(stateLoginRetry); + result["login"] = LLSD::String("retry"); } else if(statusCode != 0) { LL_WARNS("Voice") << "Account.Login response failure (" << statusCode << "): " << statusString << LL_ENDL; - setState(stateLoginFailed); + result["login"] = LLSD::String("failed"); } else { // Login succeeded, move forward. - mAccountHandle = accountHandle; + mAccountLoggedIn = true; mNumberOfAliases = numberOfAliases; - // This needs to wait until the AccountLoginStateChangeEvent is received. -// if(getState() == stateLoggingIn) -// { -// setState(stateLoggedIn); -// } + result["login"] = LLSD::String("response_ok"); } + + LLEventPumps::instance().post("vivoxClientPump", result); + } void LLVivoxVoiceClient::sessionCreateResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle) { - sessionState *session = findSessionBeingCreatedByURI(requestId); + sessionStatePtr_t session(findSessionBeingCreatedByURI(requestId)); if(session) { @@ -2756,8 +2902,14 @@ void LLVivoxVoiceClient::sessionCreateResponse(std::string &requestId, int statu session->mErrorStatusString = statusString; if(session == mAudioSession) { - setState(stateJoinSessionFailed); - } + LLSD vivoxevent = LLSD::emptyMap(); + + vivoxevent["handle"] = LLSD::String(sessionHandle); + vivoxevent["session"] = LLSD::String("failed"); + vivoxevent["reason"] = LLSD::Integer(statusCode); + + LLEventPumps::instance().post("vivoxClientPump", vivoxevent); + } else { reapSession(session); @@ -2771,12 +2923,18 @@ void LLVivoxVoiceClient::sessionCreateResponse(std::string &requestId, int statu { setSessionHandle(session, sessionHandle); } + LLSD vivoxevent = LLSD::emptyMap(); + + vivoxevent["handle"] = LLSD::String(sessionHandle); + vivoxevent["session"] = LLSD::String("created"); + + LLEventPumps::instance().post("vivoxClientPump", vivoxevent); } } void LLVivoxVoiceClient::sessionGroupAddSessionResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle) { - sessionState *session = findSessionBeingCreatedByURI(requestId); + sessionStatePtr_t session(findSessionBeingCreatedByURI(requestId)); if(session) { @@ -2792,7 +2950,12 @@ void LLVivoxVoiceClient::sessionGroupAddSessionResponse(std::string &requestId, session->mErrorStatusString = statusString; if(session == mAudioSession) { - setState(stateJoinSessionFailed); + LLSD vivoxevent = LLSD::emptyMap(); + + vivoxevent["handle"] = LLSD::String(sessionHandle); + vivoxevent["session"] = LLSD::String("failed"); + + LLEventPumps::instance().post("vivoxClientPump", vivoxevent); } else { @@ -2807,28 +2970,46 @@ void LLVivoxVoiceClient::sessionGroupAddSessionResponse(std::string &requestId, { setSessionHandle(session, sessionHandle); } + + LLSD vivoxevent = LLSD::emptyMap(); + + vivoxevent["handle"] = LLSD::String(sessionHandle); + vivoxevent["session"] = LLSD::String("added"); + + LLEventPumps::instance().post("vivoxClientPump", vivoxevent); + } } void LLVivoxVoiceClient::sessionConnectResponse(std::string &requestId, int statusCode, std::string &statusString) { - sessionState *session = findSession(requestId); - if(statusCode != 0) + sessionStatePtr_t session(findSession(requestId)); + // 1026 is session already has media, somehow mediaconnect was called twice on the same session. + // set the session info to reflect that the user is already connected. + if (statusCode == 1026) + { + session->mVoiceEnabled = true; + session->mMediaConnectInProgress = false; + session->mMediaStreamState = streamStateConnected; + //session->mTextStreamState = streamStateConnected; + session->mErrorStatusCode = 0; + } + else if (statusCode != 0) { LL_WARNS("Voice") << "Session.Connect response failure (" << statusCode << "): " << statusString << LL_ENDL; - if(session) + if (session) { session->mMediaConnectInProgress = false; - session->mErrorStatusCode = statusCode; + session->mErrorStatusCode = statusCode; session->mErrorStatusString = statusString; - if(session == mAudioSession) - setState(stateJoinSessionFailed); } } else { LL_DEBUGS("Voice") << "Session.Connect response received (success)" << LL_ENDL; } + + /*TODO: Post response?*/ } void LLVivoxVoiceClient::logoutResponse(int statusCode, std::string &statusString) @@ -2838,6 +3019,11 @@ void LLVivoxVoiceClient::logoutResponse(int statusCode, std::string &statusStrin LL_WARNS("Voice") << "Account.Logout response failure: " << statusString << LL_ENDL; // Should this ever fail? do we care if it does? } + LLSD vivoxevent = LLSD::emptyMap(); + + vivoxevent["logout"] = LLSD::Boolean(true); + + LLEventPumps::instance().post("vivoxClientPump", vivoxevent); } void LLVivoxVoiceClient::connectorShutdownResponse(int statusCode, std::string &statusString) @@ -2850,10 +3036,11 @@ void LLVivoxVoiceClient::connectorShutdownResponse(int statusCode, std::string & mConnected = false; - if(getState() == stateConnectorStopping) - { - setState(stateConnectorStopped); - } + LLSD vivoxevent = LLSD::emptyMap(); + + vivoxevent["connector"] = LLSD::Boolean(false); + + LLEventPumps::instance().post("vivoxClientPump", vivoxevent); } void LLVivoxVoiceClient::sessionAddedEvent( @@ -2866,7 +3053,7 @@ void LLVivoxVoiceClient::sessionAddedEvent( std::string &nameString, std::string &applicationString) { - sessionState *session = NULL; + sessionStatePtr_t session; LL_INFOS("Voice") << "session " << uriString << ", alias " << alias << ", name " << nameString << " handle " << sessionHandle << LL_ENDL; @@ -2929,7 +3116,7 @@ void LLVivoxVoiceClient::sessionGroupAddedEvent(std::string &sessionGroupHandle) { LL_DEBUGS("Voice") << "handle " << sessionGroupHandle << LL_ENDL; -#if USE_SESSION_GROUPS +#if USE_SESSION_GROUPS if(mMainSessionGroupHandle.empty()) { // This is the first (i.e. "main") session group. Save its handle. @@ -2942,12 +3129,12 @@ void LLVivoxVoiceClient::sessionGroupAddedEvent(std::string &sessionGroupHandle) #endif } -void LLVivoxVoiceClient::joinedAudioSession(sessionState *session) +void LLVivoxVoiceClient::joinedAudioSession(const sessionStatePtr_t &session) { LL_DEBUGS("Voice") << "Joined Audio Session" << LL_ENDL; if(mAudioSession != session) { - sessionState *oldSession = mAudioSession; + sessionStatePtr_t oldSession = mAudioSession; mAudioSession = session; mAudioSessionChanged = true; @@ -2957,13 +3144,17 @@ void LLVivoxVoiceClient::joinedAudioSession(sessionState *session) } // This is the session we're joining. - if(getState() == stateJoiningSession) + if(mIsJoiningSession) { - setState(stateSessionJoined); - - // SLIM SDK: we don't always receive a participant state change for ourselves when joining a channel now. + LLSD vivoxevent = LLSD::emptyMap(); + + vivoxevent["handle"] = LLSD::String(session->mHandle); + vivoxevent["session"] = LLSD::String("joined"); + + LLEventPumps::instance().post("vivoxClientPump", vivoxevent); + // Add the current user as a participant here. - participantState *participant = session->addParticipant(sipURIFromName(mAccountName)); + participantStatePtr_t participant(session->addParticipant(sipURIFromName(mAccountName))); if(participant) { participant->mIsSelf = true; @@ -2976,7 +3167,7 @@ void LLVivoxVoiceClient::joinedAudioSession(sessionState *session) if(!session->mIsChannel) { // this is a p2p session. Make sure the other end is added as a participant. - participantState *participant = session->addParticipant(session->mSIPURI); + participantStatePtr_t participant(session->addParticipant(session->mSIPURI)); if(participant) { if(participant->mAvatarIDValid) @@ -3003,13 +3194,13 @@ void LLVivoxVoiceClient::sessionRemovedEvent( { LL_INFOS("Voice") << "handle " << sessionHandle << LL_ENDL; - sessionState *session = findSession(sessionHandle); + sessionStatePtr_t session(findSession(sessionHandle)); if(session) { leftAudioSession(session); // This message invalidates the session's handle. Set it to empty. - setSessionHandle(session); + clearSessionHandle(session); // This also means that the session's session group is now empty. // Terminate the session group so it doesn't leak. @@ -3017,26 +3208,25 @@ void LLVivoxVoiceClient::sessionRemovedEvent( // Reset the media state (we now have no info) session->mMediaStreamState = streamStateUnknown; - session->mTextStreamState = streamStateUnknown; + //session->mTextStreamState = streamStateUnknown; // Conditionally delete the session reapSession(session); } else { - LL_WARNS("Voice") << "unknown session " << sessionHandle << " removed" << LL_ENDL; + // Already reaped this session. + LL_DEBUGS("Voice") << "unknown session " << sessionHandle << " removed" << LL_ENDL; } + } -void LLVivoxVoiceClient::reapSession(sessionState *session) +void LLVivoxVoiceClient::reapSession(const sessionStatePtr_t &session) { if(session) { - if(!session->mHandle.empty()) - { - LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (non-null session handle)" << LL_ENDL; - } - else if(session->mCreateInProgress) + + if(session->mCreateInProgress) { LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (create in progress)" << LL_ENDL; } @@ -3054,11 +3244,9 @@ void LLVivoxVoiceClient::reapSession(sessionState *session) } else { - // TODO: Question: Should we check for queued text messages here? // We don't have a reason to keep tracking this session, so just delete it. LL_DEBUGS("Voice") << "deleting session " << session->mSIPURI << LL_ENDL; deleteSession(session); - session = NULL; } } else @@ -3068,11 +3256,11 @@ void LLVivoxVoiceClient::reapSession(sessionState *session) } // Returns true if the session seems to indicate we've moved to a region on a different voice server -bool LLVivoxVoiceClient::sessionNeedsRelog(sessionState *session) +bool LLVivoxVoiceClient::sessionNeedsRelog(const sessionStatePtr_t &session) { bool result = false; - if(session != NULL) + if(session) { // Only make this check for spatial channels (so it won't happen for group or p2p calls) if(session->mIsSpatial) @@ -3100,35 +3288,17 @@ bool LLVivoxVoiceClient::sessionNeedsRelog(sessionState *session) return result; } -void LLVivoxVoiceClient::leftAudioSession( - sessionState *session) +void LLVivoxVoiceClient::leftAudioSession(const sessionStatePtr_t &session) { - if(mAudioSession == session) - { - switch(getState()) - { - case stateJoiningSession: - case stateSessionJoined: - case stateRunning: - case stateLeavingSession: - case stateJoinSessionFailed: - case stateJoinSessionFailedWaiting: - // normal transition - LL_DEBUGS("Voice") << "left session " << session->mHandle << " in state " << state2string(getState()) << LL_ENDL; - setState(stateSessionTerminated); - break; - - case stateSessionTerminated: - // this will happen sometimes -- there are cases where we send the terminate and then go straight to this state. - LL_WARNS("Voice") << "left session " << session->mHandle << " in state " << state2string(getState()) << LL_ENDL; - break; - - default: - LL_WARNS("Voice") << "unexpected SessionStateChangeEvent (left session) in state " << state2string(getState()) << LL_ENDL; - setState(stateSessionTerminated); - break; - } - } + if (mAudioSession == session) + { + LLSD vivoxevent = LLSD::emptyMap(); + + vivoxevent["handle"] = LLSD::String(session->mHandle); + vivoxevent["session"] = LLSD::String("removed"); + + LLEventPumps::instance().post("vivoxClientPump", vivoxevent); + } } void LLVivoxVoiceClient::accountLoginStateChangeEvent( @@ -3137,6 +3307,8 @@ void LLVivoxVoiceClient::accountLoginStateChangeEvent( std::string &statusString, int state) { + LLSD levent = LLSD::emptyMap(); + /* According to Mike S., status codes for this event are: login_state_logged_out=0, @@ -3151,34 +3323,37 @@ void LLVivoxVoiceClient::accountLoginStateChangeEvent( switch(state) { case 1: - if(getState() == stateLoggingIn) - { - setState(stateLoggedIn); - } - break; + levent["login"] = LLSD::String("account_login"); + LLEventPumps::instance().post("vivoxClientPump", levent); + break; case 3: - // The user is in the process of logging out. - setState(stateLoggingOut); - break; + levent["login"] = LLSD::String("account_loggingOut"); + + LLEventPumps::instance().post("vivoxClientPump", levent); + break; case 0: - // The user has been logged out. - setState(stateLoggedOut); - break; + levent["login"] = LLSD::String("account_logout"); + + LLEventPumps::instance().post("vivoxClientPump", levent); + break; default: //Used to be a commented out warning LL_DEBUGS("Voice") << "unknown state: " << state << LL_ENDL; - break; + break; } } void LLVivoxVoiceClient::mediaCompletionEvent(std::string &sessionGroupHandle, std::string &mediaCompletionType) { + LLSD result; + if (mediaCompletionType == "AuxBufferAudioCapture") { mCaptureBufferRecording = false; + result["recplay"] = "end"; } else if (mediaCompletionType == "AuxBufferAudioRender") { @@ -3186,12 +3361,17 @@ void LLVivoxVoiceClient::mediaCompletionEvent(std::string &sessionGroupHandle, s if (--mPlayRequestCount <= 0) { mCaptureBufferPlaying = false; - } + result["recplay"] = "end"; +// result["recplay"] = "done"; + } } else { LL_DEBUGS("Voice") << "Unknown MediaCompletionType: " << mediaCompletionType << LL_ENDL; } + + if (!result.isUndefined()) + LLEventPumps::instance().post("vivoxClientPump", result); } void LLVivoxVoiceClient::mediaStreamUpdatedEvent( @@ -3202,7 +3382,7 @@ void LLVivoxVoiceClient::mediaStreamUpdatedEvent( int state, bool incoming) { - sessionState *session = findSession(sessionHandle); + sessionStatePtr_t session(findSession(sessionHandle)); LL_DEBUGS("Voice") << "session " << sessionHandle << ", status code " << statusCode << ", string \"" << statusString << "\"" << LL_ENDL; @@ -3228,8 +3408,9 @@ void LLVivoxVoiceClient::mediaStreamUpdatedEvent( switch(state) { - case streamStateIdle: - // Standard "left audio session" + case streamStateDisconnecting: + case streamStateIdle: + // Standard "left audio session", Vivox state 'disconnected' session->mVoiceEnabled = false; session->mMediaConnectInProgress = false; leftAudioSession(session); @@ -3239,6 +3420,7 @@ void LLVivoxVoiceClient::mediaStreamUpdatedEvent( session->mVoiceEnabled = true; session->mMediaConnectInProgress = false; joinedAudioSession(session); + case streamStateConnecting: // do nothing, but prevents a warning getting into the logs. break; case streamStateRinging: @@ -3269,54 +3451,8 @@ void LLVivoxVoiceClient::mediaStreamUpdatedEvent( } else { - LL_WARNS("Voice") << "session " << sessionHandle << "not found"<< LL_ENDL; - } -} - -void LLVivoxVoiceClient::textStreamUpdatedEvent( - std::string &sessionHandle, - std::string &sessionGroupHandle, - bool enabled, - int state, - bool incoming) -{ - sessionState *session = findSession(sessionHandle); - - if(session) - { - // Save the state for later use - session->mTextStreamState = state; - - // We know about this session - switch(state) - { - case 0: // We see this when the text stream closes - LL_DEBUGS("Voice") << "stream closed" << LL_ENDL; - break; - - case 1: // We see this on an incoming call from the Connector - // Try to send any text messages queued for this session. - sendQueuedTextMessages(session); - - // Send the text chat invite to the GUI layer - // TODO: Question: Should we correlate with the mute list here? - session->mTextInvitePending = true; - if(session->mName.empty()) - { - lookupName(session->mCallerID); - } - else - { - // Act like we just finished resolving the name - avatarNameResolved(session->mCallerID, session->mName); - } - break; - - default: - LL_WARNS("Voice") << "unknown state " << state << LL_ENDL; - break; - - } + // session disconnectintg and disconnected events arriving after we have already left the session. + LL_DEBUGS("Voice") << "session " << sessionHandle << " not found"<< LL_ENDL; } } @@ -3329,10 +3465,10 @@ void LLVivoxVoiceClient::participantAddedEvent( std::string &displayNameString, int participantType) { - sessionState *session = findSession(sessionHandle); + sessionStatePtr_t session(findSession(sessionHandle)); if(session) { - participantState *participant = session->addParticipant(uriString); + participantStatePtr_t participant(session->addParticipant(uriString)); if(participant) { participant->mAccountName = nameString; @@ -3375,10 +3511,10 @@ void LLVivoxVoiceClient::participantRemovedEvent( std::string &alias, std::string &nameString) { - sessionState *session = findSession(sessionHandle); + sessionStatePtr_t session(findSession(sessionHandle)); if(session) { - participantState *participant = session->findParticipant(uriString); + participantStatePtr_t participant(session->findParticipant(uriString)); if(participant) { session->removeParticipant(participant); @@ -3390,6 +3526,7 @@ void LLVivoxVoiceClient::participantRemovedEvent( } else { + // a late arriving event on a session we have already left. LL_DEBUGS("Voice") << "unknown session " << sessionHandle << LL_ENDL; } } @@ -3405,13 +3542,15 @@ void LLVivoxVoiceClient::participantUpdatedEvent( int volume, F32 energy) { - sessionState *session = findSession(sessionHandle); + sessionStatePtr_t session(findSession(sessionHandle)); if(session) { - participantState *participant = session->findParticipant(uriString); + participantStatePtr_t participant(session->findParticipant(uriString)); if(participant) { + //LL_INFOS("Voice") << "Participant Update for " << participant->mDisplayName << LL_ENDL; + participant->mIsSpeaking = isSpeaking; participant->mIsModeratorMuted = isModeratorMuted; @@ -3474,7 +3613,7 @@ void LLVivoxVoiceClient::participantUpdatedEvent( } else { - LL_INFOS("Voice") << "unknown session " << sessionHandle << LL_ENDL; + LL_DEBUGS("Voice") << "unknown session " << sessionHandle << LL_ENDL; } } @@ -3488,7 +3627,9 @@ void LLVivoxVoiceClient::messageEvent( { LL_DEBUGS("Voice") << "Message event, session " << sessionHandle << " from " << uriString << LL_ENDL; // LL_DEBUGS("Voice") << " header " << messageHeader << ", body: \n" << messageBody << LL_ENDL; - + + LL_INFOS("Voice") << "Vivox raw message:" << std::endl << messageBody << LL_ENDL; + if(messageHeader.find(HTTP_CONTENT_TEXT_HTML) != std::string::npos) { std::string message; @@ -3592,7 +3733,7 @@ void LLVivoxVoiceClient::messageEvent( // LL_DEBUGS("Voice") << " stripped message = \n" << message << LL_ENDL; - sessionState *session = findSession(sessionHandle); + sessionStatePtr_t session(findSession(sessionHandle)); if(session) { bool is_do_not_disturb = gAgent.isDoNotDisturb(); @@ -3633,11 +3774,11 @@ void LLVivoxVoiceClient::messageEvent( void LLVivoxVoiceClient::sessionNotificationEvent(std::string &sessionHandle, std::string &uriString, std::string ¬ificationType) { - sessionState *session = findSession(sessionHandle); + sessionStatePtr_t session(findSession(sessionHandle)); if(session) { - participantState *participant = session->findParticipant(uriString); + participantStatePtr_t participant(session->findParticipant(uriString)); if(participant) { if (!stricmp(notificationType.c_str(), "Typing")) @@ -3683,7 +3824,7 @@ void LLVivoxVoiceClient::muteListChanged() for(; iter != mAudioSession->mParticipantsByURI.end(); iter++) { - participantState *p = iter->second; + participantStatePtr_t p(iter->second); // Check to see if this participant is on the mute list already if(p->updateMuteState()) @@ -3711,9 +3852,9 @@ LLVivoxVoiceClient::participantState::participantState(const std::string &uri) : { } -LLVivoxVoiceClient::participantState *LLVivoxVoiceClient::sessionState::addParticipant(const std::string &uri) +LLVivoxVoiceClient::participantStatePtr_t LLVivoxVoiceClient::sessionState::addParticipant(const std::string &uri) { - participantState *result = NULL; + participantStatePtr_t result; bool useAlternateURI = false; // Note: this is mostly the body of LLVivoxVoiceClient::sessionState::findParticipant(), but since we need to know if it @@ -3741,7 +3882,7 @@ LLVivoxVoiceClient::participantState *LLVivoxVoiceClient::sessionState::addParti if(!result) { // participant isn't already in one list or the other. - result = new participantState(useAlternateURI?mSIPURI:uri); + result.reset(new participantState(useAlternateURI?mSIPURI:uri)); mParticipantsByURI.insert(participantMap::value_type(result->mURI, result)); mParticipantsChanged = true; @@ -3760,12 +3901,11 @@ LLVivoxVoiceClient::participantState *LLVivoxVoiceClient::sessionState::addParti result->mAvatarID.generate(uri); } } - - if(result->updateMuteState()) - { - mMuteDirty = true; - } + if(result->updateMuteState()) + { + mMuteDirty = true; + } mParticipantsByUUID.insert(participantUUIDMap::value_type(result->mAvatarID, result)); @@ -3784,8 +3924,6 @@ LLVivoxVoiceClient::participantState *LLVivoxVoiceClient::sessionState::addParti bool LLVivoxVoiceClient::participantState::updateMuteState() { bool result = false; - - bool isMuted = LLMuteList::getInstance()->isMuted(mAvatarID, LLMute::flagVoiceChat); if(mOnMuteList != isMuted) @@ -3802,7 +3940,7 @@ bool LLVivoxVoiceClient::participantState::isAvatar() return mAvatarIDValid; } -void LLVivoxVoiceClient::sessionState::removeParticipant(LLVivoxVoiceClient::participantState *participant) +void LLVivoxVoiceClient::sessionState::removeParticipant(const LLVivoxVoiceClient::participantStatePtr_t &participant) { if(participant) { @@ -3813,23 +3951,22 @@ void LLVivoxVoiceClient::sessionState::removeParticipant(LLVivoxVoiceClient::par if(iter == mParticipantsByURI.end()) { - LL_ERRS("Voice") << "Internal error: participant " << participant->mURI << " not in URI map" << LL_ENDL; + LL_WARNS("Voice") << "Internal error: participant " << participant->mURI << " not in URI map" << LL_ENDL; } else if(iter2 == mParticipantsByUUID.end()) { - LL_ERRS("Voice") << "Internal error: participant ID " << participant->mAvatarID << " not in UUID map" << LL_ENDL; + LL_WARNS("Voice") << "Internal error: participant ID " << participant->mAvatarID << " not in UUID map" << LL_ENDL; } else if(iter->second != iter2->second) { - LL_ERRS("Voice") << "Internal error: participant mismatch!" << LL_ENDL; + LL_WARNS("Voice") << "Internal error: participant mismatch!" << LL_ENDL; } else { mParticipantsByURI.erase(iter); mParticipantsByUUID.erase(iter2); - - delete participant; - mParticipantsChanged = true; + + mParticipantsChanged = true; } } } @@ -3845,10 +3982,27 @@ void LLVivoxVoiceClient::sessionState::removeAllParticipants() if(!mParticipantsByUUID.empty()) { - LL_ERRS("Voice") << "Internal error: empty URI map, non-empty UUID map" << LL_ENDL; + LL_WARNS("Voice") << "Internal error: empty URI map, non-empty UUID map" << LL_ENDL; } } +/*static*/ +void LLVivoxVoiceClient::sessionState::VerifySessions() +{ + std::set<wptr_t>::iterator it = mSession.begin(); + while (it != mSession.end()) + { + if ((*it).expired()) + { + LL_WARNS("Voice") << "Expired session found! removing" << LL_ENDL; + mSession.erase(it++); + } + else + ++it; + } +} + + void LLVivoxVoiceClient::getParticipantList(std::set<LLUUID> &participants) { if(mAudioSession) @@ -3872,9 +4026,9 @@ bool LLVivoxVoiceClient::isParticipant(const LLUUID &speaker_id) } -LLVivoxVoiceClient::participantState *LLVivoxVoiceClient::sessionState::findParticipant(const std::string &uri) +LLVivoxVoiceClient::participantStatePtr_t LLVivoxVoiceClient::sessionState::findParticipant(const std::string &uri) { - participantState *result = NULL; + participantStatePtr_t result; participantMap::iterator iter = mParticipantsByURI.find(uri); @@ -3896,9 +4050,9 @@ LLVivoxVoiceClient::participantState *LLVivoxVoiceClient::sessionState::findPart return result; } -LLVivoxVoiceClient::participantState* LLVivoxVoiceClient::sessionState::findParticipantByID(const LLUUID& id) +LLVivoxVoiceClient::participantStatePtr_t LLVivoxVoiceClient::sessionState::findParticipantByID(const LLUUID& id) { - participantState * result = NULL; + participantStatePtr_t result; participantUUIDMap::iterator iter = mParticipantsByUUID.find(id); if(iter != mParticipantsByUUID.end()) @@ -3909,9 +4063,9 @@ LLVivoxVoiceClient::participantState* LLVivoxVoiceClient::sessionState::findPart return result; } -LLVivoxVoiceClient::participantState* LLVivoxVoiceClient::findParticipantByID(const LLUUID& id) +LLVivoxVoiceClient::participantStatePtr_t LLVivoxVoiceClient::findParticipantByID(const LLUUID& id) { - participantState * result = NULL; + participantStatePtr_t result; if(mAudioSession) { @@ -3956,137 +4110,67 @@ bool LLVivoxVoiceClient::checkParcelChanged(bool update) return false; } -bool LLVivoxVoiceClient::parcelVoiceInfoReceived(state requesting_state) -{ - // pop back to the state we were in when the parcel changed and we managed to - // do the request. - if(getState() == stateRetrievingParcelVoiceInfo) - { - setState(requesting_state); - return true; - } - else - { - // we've dropped out of stateRetrievingParcelVoiceInfo - // before we received the cap result, due to a terminate - // or transition to a non-voice channel. Don't switch channels. - return false; - } -} - - -bool LLVivoxVoiceClient::requestParcelVoiceInfo() -{ - LLViewerRegion * region = gAgent.getRegion(); - if (region == NULL || !region->capabilitiesReceived()) - { - // we don't have the cap yet, so return false so the caller can try again later. - - LL_DEBUGS("Voice") << "ParcelVoiceInfoRequest capability not yet available, deferring" << LL_ENDL; - return false; - } - - // grab the cap. - std::string url = gAgent.getRegion()->getCapability("ParcelVoiceInfoRequest"); - if (url.empty()) - { - // Region dosn't have the cap. Stop probing. - LL_DEBUGS("Voice") << "ParcelVoiceInfoRequest capability not available in this region" << LL_ENDL; - setState(stateDisableCleanup); - return false; - } - else - { - // if we've already retrieved the cap from the region, go ahead and make the request, - // and return true so we can go into the state that waits for the response. - checkParcelChanged(true); - LLSD data; - LL_DEBUGS("Voice") << "sending ParcelVoiceInfoRequest (" << mCurrentRegionName << ", " << mCurrentParcelLocalID << ")" << LL_ENDL; - - LLHTTPClient::post( - url, - data, - new LLVivoxVoiceClientCapResponder(getState())); - return true; - } -} - -void LLVivoxVoiceClient::switchChannel( +bool LLVivoxVoiceClient::switchChannel( std::string uri, bool spatial, bool no_reconnect, bool is_p2p, std::string hash) { - bool needsSwitch = false; - - LL_DEBUGS("Voice") - << "called in state " << state2string(getState()) - << " with uri \"" << uri << "\"" - << (spatial?", spatial is true":", spatial is false") - << LL_ENDL; + bool needsSwitch = !mIsInChannel; - switch(getState()) - { - case stateJoinSessionFailed: - case stateJoinSessionFailedWaiting: - case stateNoChannel: - case stateRetrievingParcelVoiceInfo: - // Always switch to the new URI from these states. - needsSwitch = true; - break; + if (mIsInChannel) + { + if (mSessionTerminateRequested) + { + // If a terminate has been requested, we need to compare against where the URI we're already headed to. + if(mNextAudioSession) + { + if(mNextAudioSession->mSIPURI != uri) + needsSwitch = true; + } + else + { + // mNextAudioSession is null -- this probably means we're on our way back to spatial. + if(!uri.empty()) + { + // We do want to process a switch in this case. + needsSwitch = true; + } + } + } + else + { + // Otherwise, compare against the URI we're in now. + if(mAudioSession) + { + if(mAudioSession->mSIPURI != uri) + { + needsSwitch = true; + } + } + else + { + if(!uri.empty()) + { + // mAudioSession is null -- it's not clear what case would cause this. + // For now, log it as a warning and see if it ever crops up. + LL_WARNS("Voice") << "No current audio session... Forcing switch" << LL_ENDL; + needsSwitch = true; + } + } + } + } - default: - if(mSessionTerminateRequested) - { - // If a terminate has been requested, we need to compare against where the URI we're already headed to. - if(mNextAudioSession) - { - if(mNextAudioSession->mSIPURI != uri) - needsSwitch = true; - } - else - { - // mNextAudioSession is null -- this probably means we're on our way back to spatial. - if(!uri.empty()) - { - // We do want to process a switch in this case. - needsSwitch = true; - } - } - } - else - { - // Otherwise, compare against the URI we're in now. - if(mAudioSession) - { - if(mAudioSession->mSIPURI != uri) - { - needsSwitch = true; - } - } - else - { - if(!uri.empty()) - { - // mAudioSession is null -- it's not clear what case would cause this. - // For now, log it as a warning and see if it ever crops up. - LL_WARNS("Voice") << "No current audio session." << LL_ENDL; - } - } - } - break; - } - - if(needsSwitch) + if(needsSwitch) { if(uri.empty()) { // Leave any channel we may be in LL_DEBUGS("Voice") << "leaving channel" << LL_ENDL; - sessionState *oldSession = mNextAudioSession; - mNextAudioSession = NULL; + sessionStatePtr_t oldSession = mNextAudioSession; + mNextAudioSession.reset(); // The old session may now need to be deleted. reapSession(oldSession); @@ -4104,28 +4188,27 @@ void LLVivoxVoiceClient::switchChannel( mNextAudioSession->mIsP2P = is_p2p; } - if(getState() >= stateRetrievingParcelVoiceInfo) + if (mIsInChannel) { // If we're already in a channel, or if we're joining one, terminate // so we can rejoin with the new session data. sessionTerminate(); } } + + return needsSwitch; } -void LLVivoxVoiceClient::joinSession(sessionState *session) +void LLVivoxVoiceClient::joinSession(const sessionStatePtr_t &session) { mNextAudioSession = session; - - if(getState() <= stateNoChannel) - { - // We're already set up to join a channel, just needed to fill in the session handle - } - else - { - // State machine will come around and rejoin if uri/handle is not empty. - sessionTerminate(); - } + + if (mIsInChannel) + { + // If we're already in a channel, or if we're joining one, terminate + // so we can rejoin with the new session data. + sessionTerminate(); + } } void LLVivoxVoiceClient::setNonSpatialChannel( @@ -4135,7 +4218,7 @@ void LLVivoxVoiceClient::setNonSpatialChannel( switchChannel(uri, false, false, false, credentials); } -void LLVivoxVoiceClient::setSpatialChannel( +bool LLVivoxVoiceClient::setSpatialChannel( const std::string &uri, const std::string &credentials) { @@ -4145,14 +4228,15 @@ void LLVivoxVoiceClient::setSpatialChannel( LL_DEBUGS("Voice") << "got spatial channel uri: \"" << uri << "\"" << LL_ENDL; - if((mAudioSession && !(mAudioSession->mIsSpatial)) || (mNextAudioSession && !(mNextAudioSession->mIsSpatial))) + if((mIsInChannel && mAudioSession && !(mAudioSession->mIsSpatial)) || (mNextAudioSession && !(mNextAudioSession->mIsSpatial))) { // User is in a non-spatial chat or joining a non-spatial chat. Don't switch channels. LL_INFOS("Voice") << "in non-spatial chat, not switching channels" << LL_ENDL; + return false; } else { - switchChannel(mSpatialSessionURI, true, false, false, mSpatialSessionCredentials); + return switchChannel(mSpatialSessionURI, true, false, false, mSpatialSessionCredentials); } } @@ -4163,10 +4247,12 @@ void LLVivoxVoiceClient::callUser(const LLUUID &uuid) switchChannel(userURI, false, true, true); } -LLVivoxVoiceClient::sessionState* LLVivoxVoiceClient::startUserIMSession(const LLUUID &uuid) +#if 0 +// Vivox text IMs are not in use. +LLVivoxVoiceClient::sessionStatePtr_t LLVivoxVoiceClient::startUserIMSession(const LLUUID &uuid) { // Figure out if a session with the user already exists - sessionState *session = findSession(uuid); + sessionStatePtr_t session(findSession(uuid)); if(!session) { // No session with user, need to start one. @@ -4174,7 +4260,8 @@ LLVivoxVoiceClient::sessionState* LLVivoxVoiceClient::startUserIMSession(const L session = addSession(uri); llassert(session); - if (!session) return NULL; + if (!session) + return session; session->mIsSpatial = false; session->mReconnect = false; @@ -4185,7 +4272,7 @@ LLVivoxVoiceClient::sessionState* LLVivoxVoiceClient::startUserIMSession(const L if(session->mHandle.empty()) { // Session isn't active -- start it up. - sessionCreateSendMessage(session, false, true); + sessionCreateSendMessage(session, false, false); } else { @@ -4195,77 +4282,28 @@ LLVivoxVoiceClient::sessionState* LLVivoxVoiceClient::startUserIMSession(const L return session; } - -BOOL LLVivoxVoiceClient::sendTextMessage(const LLUUID& participant_id, const std::string& message) -{ - bool result = false; - - // Attempt to locate the indicated session - sessionState *session = startUserIMSession(participant_id); - if(session) - { - // found the session, attempt to send the message - session->mTextMsgQueue.push(message); - - // Try to send queued messages (will do nothing if the session is not open yet) - sendQueuedTextMessages(session); - - // The message is queued, so we succeed. - result = true; - } - else - { - LL_DEBUGS("Voice") << "Session not found for participant ID " << participant_id << LL_ENDL; - } - - return result; -} - -void LLVivoxVoiceClient::sendQueuedTextMessages(sessionState *session) -{ - if(session->mTextStreamState == 1) - { - if(!session->mTextMsgQueue.empty()) - { - std::ostringstream stream; - - while(!session->mTextMsgQueue.empty()) - { - std::string message = session->mTextMsgQueue.front(); - session->mTextMsgQueue.pop(); - stream - << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.SendMessage.1\">" - << "<SessionHandle>" << session->mHandle << "</SessionHandle>" - << "<MessageHeader>text/HTML</MessageHeader>" - << "<MessageBody>" << message << "</MessageBody>" - << "</Request>" - << "\n\n\n"; - } - writeString(stream.str()); - } - } - else - { - // Session isn't connected yet, defer until later. - } -} +#endif void LLVivoxVoiceClient::endUserIMSession(const LLUUID &uuid) { - // Figure out if a session with the user exists - sessionState *session = findSession(uuid); +#if 0 + // Vivox text IMs are not in use. + + // Figure out if a session with the user exists + sessionStatePtr_t session(findSession(uuid)); if(session) { // found the session if(!session->mHandle.empty()) { - sessionTextDisconnectSendMessage(session); + // sessionTextDisconnectSendMessage(session); // a SLim leftover, not used any more. } } else { LL_DEBUGS("Voice") << "Session not found for participant ID " << uuid << LL_ENDL; } +#endif } bool LLVivoxVoiceClient::isValidChannel(std::string &sessionHandle) { @@ -4276,7 +4314,7 @@ bool LLVivoxVoiceClient::answerInvite(std::string &sessionHandle) { // this is only ever used to answer incoming p2p call invites. - sessionState *session = findSession(sessionHandle); + sessionStatePtr_t session(findSession(sessionHandle)); if(session) { session->mIsSpatial = false; @@ -4292,10 +4330,12 @@ bool LLVivoxVoiceClient::answerInvite(std::string &sessionHandle) bool LLVivoxVoiceClient::isVoiceWorking() const { - //Added stateSessionTerminated state to avoid problems with call in parcels with disabled voice (EXT-4758) - // Condition with joining spatial num was added to take into account possible problems with connection to voice - // server(EXT-4313). See bug descriptions and comments for MAX_NORMAL_JOINING_SPATIAL_NUM for more info. - return (mSpatialJoiningNum < MAX_NORMAL_JOINING_SPATIAL_NUM) && (stateLoggedIn <= mState) && (mState <= stateSessionTerminated); + + //Added stateSessionTerminated state to avoid problems with call in parcels with disabled voice (EXT-4758) + // Condition with joining spatial num was added to take into account possible problems with connection to voice + // server(EXT-4313). See bug descriptions and comments for MAX_NORMAL_JOINING_SPATIAL_NUM for more info. + return (mSpatialJoiningNum < MAX_NORMAL_JOINING_SPATIAL_NUM) && mIsProcessingChannels; +// return (mSpatialJoiningNum < MAX_NORMAL_JOINING_SPATIAL_NUM) && (stateLoggedIn <= mState) && (mState <= stateSessionTerminated); } // Returns true if the indicated participant in the current audio session is really an SL avatar. @@ -4303,9 +4343,9 @@ bool LLVivoxVoiceClient::isVoiceWorking() const BOOL LLVivoxVoiceClient::isParticipantAvatar(const LLUUID &id) { BOOL result = TRUE; - sessionState *session = findSession(id); + sessionStatePtr_t session(findSession(id)); - if(session != NULL) + if(session) { // this is a p2p session with the indicated caller, or the session with the specified UUID. if(session->mSynthesizedCallerID) @@ -4314,10 +4354,10 @@ BOOL LLVivoxVoiceClient::isParticipantAvatar(const LLUUID &id) else { // Didn't find a matching session -- check the current audio session for a matching participant - if(mAudioSession != NULL) + if(mAudioSession) { - participantState *participant = findParticipantByID(id); - if(participant != NULL) + participantStatePtr_t participant(findParticipantByID(id)); + if(participant) { result = participant->isAvatar(); } @@ -4332,7 +4372,7 @@ BOOL LLVivoxVoiceClient::isParticipantAvatar(const LLUUID &id) BOOL LLVivoxVoiceClient::isSessionCallBackPossible(const LLUUID &session_id) { BOOL result = TRUE; - sessionState *session = findSession(session_id); + sessionStatePtr_t session(findSession(session_id)); if(session != NULL) { @@ -4342,12 +4382,12 @@ BOOL LLVivoxVoiceClient::isSessionCallBackPossible(const LLUUID &session_id) return result; } -// Returns true if the session can accepte text IM's. +// Returns true if the session can accept text IM's. // Currently this will be false only for PSTN P2P calls. BOOL LLVivoxVoiceClient::isSessionTextIMPossible(const LLUUID &session_id) { bool result = TRUE; - sessionState *session = findSession(session_id); + sessionStatePtr_t session(findSession(session_id)); if(session != NULL) { @@ -4360,7 +4400,7 @@ BOOL LLVivoxVoiceClient::isSessionTextIMPossible(const LLUUID &session_id) void LLVivoxVoiceClient::declineInvite(std::string &sessionHandle) { - sessionState *session = findSession(sessionHandle); + sessionStatePtr_t session(findSession(sessionHandle)); if(session) { sessionMediaDisconnectSendMessage(session); @@ -4369,13 +4409,11 @@ void LLVivoxVoiceClient::declineInvite(std::string &sessionHandle) void LLVivoxVoiceClient::leaveNonSpatialChannel() { - LL_DEBUGS("Voice") - << "called in state " << state2string(getState()) - << LL_ENDL; + LL_DEBUGS("Voice") << "Request to leave spacial channel." << LL_ENDL; // Make sure we don't rejoin the current session. - sessionState *oldNextSession = mNextAudioSession; - mNextAudioSession = NULL; + sessionStatePtr_t oldNextSession(mNextAudioSession); + mNextAudioSession.reset(); // Most likely this will still be the current session at this point, but check it anyway. reapSession(oldNextSession); @@ -4389,7 +4427,7 @@ std::string LLVivoxVoiceClient::getCurrentChannel() { std::string result; - if((getState() == stateRunning) && !mSessionTerminateRequested) + if (mIsInChannel && !mSessionTerminateRequested) { result = getAudioSessionURI(); } @@ -4401,7 +4439,7 @@ bool LLVivoxVoiceClient::inProximalChannel() { bool result = false; - if((getState() == stateRunning) && !mSessionTerminateRequested) + if (mIsInChannel && !mSessionTerminateRequested) { result = inSpatialChannel(); } @@ -4610,12 +4648,13 @@ void LLVivoxVoiceClient::enforceTether(void) void LLVivoxVoiceClient::updatePosition(void) { - + LLViewerRegion *region = gAgent.getRegion(); if(region && isAgentAvatarValid()) { LLMatrix3 rot; LLVector3d pos; + LLQuaternion qrot; // TODO: If camera and avatar velocity are actually used by the voice system, we could compute them here... // They're currently always set to zero. @@ -4630,7 +4669,7 @@ void LLVivoxVoiceClient::updatePosition(void) rot); // rotation matrix // Send the current avatar position to the voice code - rot = gAgentAvatarp->getRootJoint()->getWorldRotation().getMatrix3(); + qrot = gAgentAvatarp->getRootJoint()->getWorldRotation(); pos = gAgentAvatarp->getPositionGlobal(); // TODO: Can we get the head offset from outside the LLVOAvatar? @@ -4640,7 +4679,7 @@ void LLVivoxVoiceClient::updatePosition(void) LLVivoxVoiceClient::getInstance()->setAvatarPosition( pos, // position LLVector3::zero, // velocity - rot); // rotation matrix + qrot); // rotation matrix } } @@ -4661,7 +4700,7 @@ void LLVivoxVoiceClient::setCameraPosition(const LLVector3d &position, const LLV } } -void LLVivoxVoiceClient::setAvatarPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot) +void LLVivoxVoiceClient::setAvatarPosition(const LLVector3d &position, const LLVector3 &velocity, const LLQuaternion &rot) { if(dist_vec_squared(mAvatarPosition, position) > 0.01) { @@ -4675,8 +4714,9 @@ void LLVivoxVoiceClient::setAvatarPosition(const LLVector3d &position, const LLV mSpatialCoordsDirty = true; } - if(mAvatarRot != rot) - { + if ((mAvatarRot != rot) && (llabs(dot(mAvatarRot, rot)) > MINUSCULE_ANGLE_COS)) + { // if the two rotations are not exactly equal test their dot product + // to get the cos of the angle between them. If it is minuscule don't update. mAvatarRot = rot; mSpatialCoordsDirty = true; } @@ -4699,7 +4739,7 @@ bool LLVivoxVoiceClient::channelFromRegion(LLViewerRegion *region, std::string & void LLVivoxVoiceClient::leaveChannel(void) { - if(getState() == stateRunning) + if (mIsInChannel) { LL_DEBUGS("Voice") << "leaving channel for teleport/logout" << LL_ENDL; mChannelName.clear(); @@ -4730,6 +4770,12 @@ void LLVivoxVoiceClient::setVoiceEnabled(bool enabled) { LLVoiceChannel::getCurrentVoiceChannel()->activate(); status = LLVoiceClientStatusObserver::STATUS_VOICE_ENABLED; + + if (!mIsCoroutineActive) + { + LLCoros::instance().launch("LLVivoxVoiceClient::voiceControlCoro();", + boost::bind(&LLVivoxVoiceClient::voiceControlCoro, LLVivoxVoiceClient::getInstance())); + } } else { @@ -4754,7 +4800,7 @@ void LLVivoxVoiceClient::setLipSyncEnabled(BOOL enabled) BOOL LLVivoxVoiceClient::lipSyncEnabled() { - if ( mVoiceEnabled && stateDisabled != getState() ) + if ( mVoiceEnabled ) { return mLipSyncEnabled; } @@ -4809,7 +4855,7 @@ void LLVivoxVoiceClient::setMicGain(F32 volume) BOOL LLVivoxVoiceClient::getVoiceEnabled(const LLUUID& id) { BOOL result = FALSE; - participantState *participant = findParticipantByID(id); + participantStatePtr_t participant(findParticipantByID(id)); if(participant) { // I'm not sure what the semantics of this should be. @@ -4823,7 +4869,7 @@ BOOL LLVivoxVoiceClient::getVoiceEnabled(const LLUUID& id) std::string LLVivoxVoiceClient::getDisplayName(const LLUUID& id) { std::string result; - participantState *participant = findParticipantByID(id); + participantStatePtr_t participant(findParticipantByID(id)); if(participant) { result = participant->mDisplayName; @@ -4838,7 +4884,7 @@ BOOL LLVivoxVoiceClient::getIsSpeaking(const LLUUID& id) { BOOL result = FALSE; - participantState *participant = findParticipantByID(id); + participantStatePtr_t participant(findParticipantByID(id)); if(participant) { if (participant->mSpeakingTimeout.getElapsedTimeF32() > SPEAKING_TIMEOUT) @@ -4855,7 +4901,7 @@ BOOL LLVivoxVoiceClient::getIsModeratorMuted(const LLUUID& id) { BOOL result = FALSE; - participantState *participant = findParticipantByID(id); + participantStatePtr_t participant(findParticipantByID(id)); if(participant) { result = participant->mIsModeratorMuted; @@ -4867,7 +4913,7 @@ BOOL LLVivoxVoiceClient::getIsModeratorMuted(const LLUUID& id) F32 LLVivoxVoiceClient::getCurrentPower(const LLUUID& id) { F32 result = 0; - participantState *participant = findParticipantByID(id); + participantStatePtr_t participant(findParticipantByID(id)); if(participant) { result = participant->mPower; @@ -4882,7 +4928,7 @@ BOOL LLVivoxVoiceClient::getUsingPTT(const LLUUID& id) { BOOL result = FALSE; - participantState *participant = findParticipantByID(id); + participantStatePtr_t participant(findParticipantByID(id)); if(participant) { // I'm not sure what the semantics of this should be. @@ -4897,7 +4943,7 @@ BOOL LLVivoxVoiceClient::getOnMuteList(const LLUUID& id) { BOOL result = FALSE; - participantState *participant = findParticipantByID(id); + participantStatePtr_t participant(findParticipantByID(id)); if(participant) { result = participant->mOnMuteList; @@ -4909,11 +4955,11 @@ BOOL LLVivoxVoiceClient::getOnMuteList(const LLUUID& id) // External accessors. F32 LLVivoxVoiceClient::getUserVolume(const LLUUID& id) { - // Minimum volume will be returned for users with voice disabled - F32 result = LLVoiceClient::VOLUME_MIN; + // Minimum volume will be returned for users with voice disabled + F32 result = LLVoiceClient::VOLUME_MIN; - participantState *participant = findParticipantByID(id); - if(participant) + participantStatePtr_t participant(findParticipantByID(id)); + if(participant) { result = participant->mVolume; @@ -4928,7 +4974,7 @@ void LLVivoxVoiceClient::setUserVolume(const LLUUID& id, F32 volume) { if(mAudioSession) { - participantState *participant = findParticipantByID(id); + participantStatePtr_t participant(findParticipantByID(id)); if (participant && !participant->mIsSelf) { if (!is_approx_equal(volume, LLVoiceClient::VOLUME_DEFAULT)) @@ -4954,7 +5000,7 @@ std::string LLVivoxVoiceClient::getGroupID(const LLUUID& id) { std::string result; - participantState *participant = findParticipantByID(id); + participantStatePtr_t participant(findParticipantByID(id)); if(participant) { result = participant->mGroupID; @@ -5070,29 +5116,49 @@ void LLVivoxVoiceClient::filePlaybackSetMode(bool vox, float speed) // TODO: Implement once Vivox gives me a sample } +//------------------------------------------------------------------------ +std::set<LLVivoxVoiceClient::sessionState::wptr_t> LLVivoxVoiceClient::sessionState::mSession; + + LLVivoxVoiceClient::sessionState::sessionState() : - mErrorStatusCode(0), - mMediaStreamState(streamStateUnknown), - mTextStreamState(streamStateUnknown), - mCreateInProgress(false), - mMediaConnectInProgress(false), - mVoiceInvitePending(false), - mTextInvitePending(false), - mSynthesizedCallerID(false), - mIsChannel(false), - mIsSpatial(false), - mIsP2P(false), - mIncoming(false), - mVoiceEnabled(false), - mReconnect(false), - mVolumeDirty(false), - mMuteDirty(false), - mParticipantsChanged(false) + mErrorStatusCode(0), + mMediaStreamState(streamStateUnknown), + mCreateInProgress(false), + mMediaConnectInProgress(false), + mVoiceInvitePending(false), + mTextInvitePending(false), + mSynthesizedCallerID(false), + mIsChannel(false), + mIsSpatial(false), + mIsP2P(false), + mIncoming(false), + mVoiceEnabled(false), + mReconnect(false), + mVolumeDirty(false), + mMuteDirty(false), + mParticipantsChanged(false) { } +/*static*/ +LLVivoxVoiceClient::sessionState::ptr_t LLVivoxVoiceClient::sessionState::createSession() +{ + sessionState::ptr_t ptr(new sessionState()); + + std::pair<std::set<wptr_t>::iterator, bool> result = mSession.insert(ptr); + + if (result.second) + ptr->mMyIterator = result.first; + + return ptr; +} + LLVivoxVoiceClient::sessionState::~sessionState() { + LL_INFOS("Voice") << "Destroying session handle=" << mHandle << " SIP=" << mSIPURI << LL_ENDL; + if (mMyIterator != mSession.end()) + mSession.erase(mMyIterator); + removeAllParticipants(); } @@ -5111,20 +5177,116 @@ bool LLVivoxVoiceClient::sessionState::isTextIMPossible() } -LLVivoxVoiceClient::sessionIterator LLVivoxVoiceClient::sessionsBegin(void) +/*static*/ +LLVivoxVoiceClient::sessionState::ptr_t LLVivoxVoiceClient::sessionState::matchSessionByHandle(const std::string &handle) +{ + sessionStatePtr_t result; + + // *TODO: My kingdom for a lambda! + std::set<wptr_t>::iterator it = std::find_if(mSession.begin(), mSession.end(), boost::bind(testByHandle, _1, handle)); + + if (it != mSession.end()) + result = (*it).lock(); + + return result; +} + +/*static*/ +LLVivoxVoiceClient::sessionState::ptr_t LLVivoxVoiceClient::sessionState::matchCreatingSessionByURI(const std::string &uri) +{ + sessionStatePtr_t result; + + // *TODO: My kingdom for a lambda! + std::set<wptr_t>::iterator it = std::find_if(mSession.begin(), mSession.end(), boost::bind(testByCreatingURI, _1, uri)); + + if (it != mSession.end()) + result = (*it).lock(); + + return result; +} + +/*static*/ +LLVivoxVoiceClient::sessionState::ptr_t LLVivoxVoiceClient::sessionState::matchSessionByURI(const std::string &uri) +{ + sessionStatePtr_t result; + + // *TODO: My kingdom for a lambda! + std::set<wptr_t>::iterator it = std::find_if(mSession.begin(), mSession.end(), boost::bind(testBySIPOrAlterateURI, _1, uri)); + + if (it != mSession.end()) + result = (*it).lock(); + + return result; +} + +/*static*/ +LLVivoxVoiceClient::sessionState::ptr_t LLVivoxVoiceClient::sessionState::matchSessionByParticipant(const LLUUID &participant_id) +{ + sessionStatePtr_t result; + + // *TODO: My kingdom for a lambda! + std::set<wptr_t>::iterator it = std::find_if(mSession.begin(), mSession.end(), boost::bind(testByCallerId, _1, participant_id)); + + if (it != mSession.end()) + result = (*it).lock(); + + return result; +} + +void LLVivoxVoiceClient::sessionState::for_each(sessionFunc_t func) +{ + std::for_each(mSession.begin(), mSession.end(), boost::bind(for_eachPredicate, _1, func)); +} + +// simple test predicates. +// *TODO: These should be made into lambdas when we can pull the trigger on newer C++ features. +bool LLVivoxVoiceClient::sessionState::testByHandle(const LLVivoxVoiceClient::sessionState::wptr_t &a, std::string handle) { - return mSessions.begin(); + ptr_t aLock(a.lock()); + + return aLock ? aLock->mHandle == handle : false; } -LLVivoxVoiceClient::sessionIterator LLVivoxVoiceClient::sessionsEnd(void) +bool LLVivoxVoiceClient::sessionState::testByCreatingURI(const LLVivoxVoiceClient::sessionState::wptr_t &a, std::string uri) { - return mSessions.end(); + ptr_t aLock(a.lock()); + + return aLock ? (aLock->mCreateInProgress && (aLock->mSIPURI == uri)) : false; } +bool LLVivoxVoiceClient::sessionState::testBySIPOrAlterateURI(const LLVivoxVoiceClient::sessionState::wptr_t &a, std::string uri) +{ + ptr_t aLock(a.lock()); -LLVivoxVoiceClient::sessionState *LLVivoxVoiceClient::findSession(const std::string &handle) + return aLock ? ((aLock->mSIPURI == uri) || (aLock->mAlternateSIPURI == uri)) : false; +} + + +bool LLVivoxVoiceClient::sessionState::testByCallerId(const LLVivoxVoiceClient::sessionState::wptr_t &a, LLUUID participantId) { - sessionState *result = NULL; + ptr_t aLock(a.lock()); + + return aLock ? ((aLock->mCallerID == participantId) || (aLock->mIMSessionID == participantId)) : false; +} + +/*static*/ +void LLVivoxVoiceClient::sessionState::for_eachPredicate(const LLVivoxVoiceClient::sessionState::wptr_t &a, sessionFunc_t func) +{ + ptr_t aLock(a.lock()); + + if (aLock) + func(aLock); + else + { + LL_WARNS("Voice") << "Stale handle in session map!" << LL_ENDL; + } +} + + + +LLVivoxVoiceClient::sessionStatePtr_t LLVivoxVoiceClient::findSession(const std::string &handle) +{ + sessionStatePtr_t result; sessionMap::iterator iter = mSessionsByHandle.find(handle); if(iter != mSessionsByHandle.end()) { @@ -5134,57 +5296,29 @@ LLVivoxVoiceClient::sessionState *LLVivoxVoiceClient::findSession(const std::str return result; } -LLVivoxVoiceClient::sessionState *LLVivoxVoiceClient::findSessionBeingCreatedByURI(const std::string &uri) +LLVivoxVoiceClient::sessionStatePtr_t LLVivoxVoiceClient::findSessionBeingCreatedByURI(const std::string &uri) { - sessionState *result = NULL; - for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++) - { - sessionState *session = *iter; - if(session->mCreateInProgress && (session->mSIPURI == uri)) - { - result = session; - break; - } - } + sessionStatePtr_t result = sessionState::matchCreatingSessionByURI(uri); return result; } -LLVivoxVoiceClient::sessionState *LLVivoxVoiceClient::findSession(const LLUUID &participant_id) +LLVivoxVoiceClient::sessionStatePtr_t LLVivoxVoiceClient::findSession(const LLUUID &participant_id) { - sessionState *result = NULL; - - for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++) - { - sessionState *session = *iter; - if((session->mCallerID == participant_id) || (session->mIMSessionID == participant_id)) - { - result = session; - break; - } - } + sessionStatePtr_t result = sessionState::matchSessionByParticipant(participant_id); return result; } -LLVivoxVoiceClient::sessionState *LLVivoxVoiceClient::addSession(const std::string &uri, const std::string &handle) +LLVivoxVoiceClient::sessionStatePtr_t LLVivoxVoiceClient::addSession(const std::string &uri, const std::string &handle) { - sessionState *result = NULL; + sessionStatePtr_t result; if(handle.empty()) { - // No handle supplied. - // Check whether there's already a session with this URI - for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++) - { - sessionState *s = *iter; - if((s->mSIPURI == uri) || (s->mAlternateSIPURI == uri)) - { - // TODO: I need to think about this logic... it's possible that this case should raise an internal error. - result = s; - break; - } - } + // No handle supplied. + // Check whether there's already a session with this URI + result = sessionState::matchSessionByURI(uri); } else // (!handle.empty()) { @@ -5201,8 +5335,8 @@ LLVivoxVoiceClient::sessionState *LLVivoxVoiceClient::addSession(const std::stri { // No existing session found. - LL_DEBUGS("Voice") << "adding new session: handle " << handle << " URI " << uri << LL_ENDL; - result = new sessionState(); + LL_DEBUGS("Voice") << "adding new session: handle \"" << handle << "\" URI " << uri << LL_ENDL; + result = sessionState::createSession(); result->mSIPURI = uri; result->mHandle = handle; @@ -5211,10 +5345,11 @@ LLVivoxVoiceClient::sessionState *LLVivoxVoiceClient::addSession(const std::stri result->mVoiceFontID = LLVoiceClient::instance().getVoiceEffectDefault(); } - mSessions.insert(result); - if(!result->mHandle.empty()) { + // *TODO: Rider: This concerns me. There is a path (via switchChannel) where + // we do not track the session. In theory this means that we could end up with + // a mAuidoSession that does not match the session tracked in mSessionsByHandle mSessionsByHandle.insert(sessionMap::value_type(result->mHandle, result)); } } @@ -5252,7 +5387,31 @@ LLVivoxVoiceClient::sessionState *LLVivoxVoiceClient::addSession(const std::stri return result; } -void LLVivoxVoiceClient::setSessionHandle(sessionState *session, const std::string &handle) +void LLVivoxVoiceClient::clearSessionHandle(const sessionStatePtr_t &session) +{ + if (session) + { + if (session->mHandle.empty()) + { + sessionMap::iterator iter = mSessionsByHandle.find(session->mHandle); + if (iter != mSessionsByHandle.end()) + { + mSessionsByHandle.erase(iter); + } + } + else + { + LL_WARNS("Voice") << "Session has empty handle!" << LL_ENDL; + } + } + else + { + LL_WARNS("Voice") << "Attempt to clear NULL session!" << LL_ENDL; + } + +} + +void LLVivoxVoiceClient::setSessionHandle(const sessionStatePtr_t &session, const std::string &handle) { // Have to remove the session from the handle-indexed map before changing the handle, or things will break badly. @@ -5264,14 +5423,14 @@ void LLVivoxVoiceClient::setSessionHandle(sessionState *session, const std::stri { if(iter->second != session) { - LL_ERRS("Voice") << "Internal error: session mismatch!" << LL_ENDL; + LL_WARNS("Voice") << "Internal error: session mismatch! Session may have been duplicated. Removing version in map." << LL_ENDL; } mSessionsByHandle.erase(iter); } else { - LL_ERRS("Voice") << "Internal error: session handle not found in map!" << LL_ENDL; + LL_WARNS("Voice") << "Attempt to remove session with handle " << session->mHandle << " not found in map!" << LL_ENDL; } } @@ -5285,7 +5444,7 @@ void LLVivoxVoiceClient::setSessionHandle(sessionState *session, const std::stri verifySessionState(); } -void LLVivoxVoiceClient::setSessionURI(sessionState *session, const std::string &uri) +void LLVivoxVoiceClient::setSessionURI(const sessionStatePtr_t &session, const std::string &uri) { // There used to be a map of session URIs to sessions, which made this complex.... session->mSIPURI = uri; @@ -5293,7 +5452,7 @@ void LLVivoxVoiceClient::setSessionURI(sessionState *session, const std::string verifySessionState(); } -void LLVivoxVoiceClient::deleteSession(sessionState *session) +void LLVivoxVoiceClient::deleteSession(const sessionStatePtr_t &session) { // Remove the session from the handle map if(!session->mHandle.empty()) @@ -5303,96 +5462,45 @@ void LLVivoxVoiceClient::deleteSession(sessionState *session) { if(iter->second != session) { - LL_ERRS("Voice") << "Internal error: session mismatch" << LL_ENDL; + LL_WARNS("Voice") << "Internal error: session mismatch, removing session in map." << LL_ENDL; } mSessionsByHandle.erase(iter); } } - // Remove the session from the URI map - mSessions.erase(session); - // At this point, the session should be unhooked from all lists and all state should be consistent. verifySessionState(); // If this is the current audio session, clean up the pointer which will soon be dangling. if(mAudioSession == session) { - mAudioSession = NULL; + mAudioSession.reset(); mAudioSessionChanged = true; } // ditto for the next audio session if(mNextAudioSession == session) { - mNextAudioSession = NULL; + mNextAudioSession.reset(); } - // delete the session - delete session; } void LLVivoxVoiceClient::deleteAllSessions() { LL_DEBUGS("Voice") << "called" << LL_ENDL; - while(!mSessions.empty()) + while (!mSessionsByHandle.empty()) { - deleteSession(*(sessionsBegin())); + deleteSession(mSessionsByHandle.begin()->second); } - if(!mSessionsByHandle.empty()) - { - LL_ERRS("Voice") << "Internal error: empty session map, non-empty handle map" << LL_ENDL; - } } void LLVivoxVoiceClient::verifySessionState(void) { - // This is mostly intended for debugging problems with session state management. - LL_DEBUGS("Voice") << "Total session count: " << mSessions.size() << " , session handle map size: " << mSessionsByHandle.size() << LL_ENDL; - - for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++) - { - sessionState *session = *iter; - - LL_DEBUGS("Voice") << "session " << session << ": handle " << session->mHandle << ", URI " << session->mSIPURI << LL_ENDL; - - if(!session->mHandle.empty()) - { - // every session with a non-empty handle needs to be in the handle map - sessionMap::iterator i2 = mSessionsByHandle.find(session->mHandle); - if(i2 == mSessionsByHandle.end()) - { - LL_ERRS("Voice") << "internal error (handle " << session->mHandle << " not found in session map)" << LL_ENDL; - } - else - { - if(i2->second != session) - { - LL_ERRS("Voice") << "internal error (handle " << session->mHandle << " in session map points to another session)" << LL_ENDL; - } - } - } - } - - // check that every entry in the handle map points to a valid session in the session set - for(sessionMap::iterator iter = mSessionsByHandle.begin(); iter != mSessionsByHandle.end(); iter++) - { - sessionState *session = iter->second; - sessionIterator i2 = mSessions.find(session); - if(i2 == mSessions.end()) - { - LL_ERRS("Voice") << "internal error (session for handle " << session->mHandle << " not found in session map)" << LL_ENDL; - } - else - { - if(session->mHandle != (*i2)->mHandle) - { - LL_ERRS("Voice") << "internal error (session for handle " << session->mHandle << " points to session with different handle " << (*i2)->mHandle << ")" << LL_ENDL; - } - } - } + LL_DEBUGS("Voice") << "Sessions in handle map=" << mSessionsByHandle.size() << LL_ENDL; + sessionState::VerifySessions(); } void LLVivoxVoiceClient::addObserver(LLVoiceClientParticipantObserver* observer) @@ -5543,50 +5651,49 @@ void LLVivoxVoiceClient::onAvatarNameCache(const LLUUID& agent_id, avatarNameResolved(agent_id, display_name); } -void LLVivoxVoiceClient::avatarNameResolved(const LLUUID &id, const std::string &name) +void LLVivoxVoiceClient::predAvatarNameResolution(const LLVivoxVoiceClient::sessionStatePtr_t &session, LLUUID id, std::string name) { - // Iterate over all sessions. - for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++) - { - sessionState *session = *iter; - // Check for this user as a participant in this session - participantState *participant = session->findParticipantByID(id); - if(participant) - { - // Found -- fill in the name - participant->mAccountName = name; - // and post a "participants updated" message to listeners later. - session->mParticipantsChanged = true; - } - - // Check whether this is a p2p session whose caller name just resolved - if(session->mCallerID == id) - { - // this session's "caller ID" just resolved. Fill in the name. - session->mName = name; - if(session->mTextInvitePending) - { - session->mTextInvitePending = false; + participantStatePtr_t participant(session->findParticipantByID(id)); + if (participant) + { + // Found -- fill in the name + participant->mAccountName = name; + // and post a "participants updated" message to listeners later. + session->mParticipantsChanged = true; + } - // We don't need to call LLIMMgr::getInstance()->addP2PSession() here. The first incoming message will create the panel. - } - if(session->mVoiceInvitePending) - { - session->mVoiceInvitePending = false; - - LLIMMgr::getInstance()->inviteToSession( - session->mIMSessionID, - session->mName, - session->mCallerID, - session->mName, - IM_SESSION_P2P_INVITE, - LLIMMgr::INVITATION_TYPE_VOICE, - session->mHandle, - session->mSIPURI); - } - - } - } + // Check whether this is a p2p session whose caller name just resolved + if (session->mCallerID == id) + { + // this session's "caller ID" just resolved. Fill in the name. + session->mName = name; + if (session->mTextInvitePending) + { + session->mTextInvitePending = false; + + // We don't need to call LLIMMgr::getInstance()->addP2PSession() here. The first incoming message will create the panel. + } + if (session->mVoiceInvitePending) + { + session->mVoiceInvitePending = false; + + LLIMMgr::getInstance()->inviteToSession( + session->mIMSessionID, + session->mName, + session->mCallerID, + session->mName, + IM_SESSION_P2P_INVITE, + LLIMMgr::INVITATION_TYPE_VOICE, + session->mHandle, + session->mSIPURI); + } + + } +} + +void LLVivoxVoiceClient::avatarNameResolved(const LLUUID &id, const std::string &name) +{ + sessionState::for_each(boost::bind(predAvatarNameResolution, _1, id, name)); } bool LLVivoxVoiceClient::setVoiceEffect(const LLUUID& id) @@ -5987,7 +6094,7 @@ S32 LLVivoxVoiceClient::getVoiceFontTemplateIndex(const LLUUID& id) const void LLVivoxVoiceClient::accountGetSessionFontsSendMessage() { - if(!mAccountHandle.empty()) + if(mAccountLoggedIn) { std::ostringstream stream; @@ -5995,7 +6102,7 @@ void LLVivoxVoiceClient::accountGetSessionFontsSendMessage() stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.GetSessionFonts.1\">" - << "<AccountHandle>" << mAccountHandle << "</AccountHandle>" + << "<AccountHandle>" << LLVivoxSecurity::getInstance()->accountHandle() << "</AccountHandle>" << "</Request>" << "\n\n\n"; @@ -6005,7 +6112,7 @@ void LLVivoxVoiceClient::accountGetSessionFontsSendMessage() void LLVivoxVoiceClient::accountGetTemplateFontsSendMessage() { - if(!mAccountHandle.empty()) + if(mAccountLoggedIn) { std::ostringstream stream; @@ -6013,7 +6120,7 @@ void LLVivoxVoiceClient::accountGetTemplateFontsSendMessage() stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.GetTemplateFonts.1\">" - << "<AccountHandle>" << mAccountHandle << "</AccountHandle>" + << "<AccountHandle>" << LLVivoxSecurity::getInstance()->accountHandle() << "</AccountHandle>" << "</Request>" << "\n\n\n"; @@ -6021,7 +6128,7 @@ void LLVivoxVoiceClient::accountGetTemplateFontsSendMessage() } } -void LLVivoxVoiceClient::sessionSetVoiceFontSendMessage(sessionState *session) +void LLVivoxVoiceClient::sessionSetVoiceFontSendMessage(const sessionStatePtr_t &session) { S32 font_index = getVoiceFontIndex(session->mVoiceFontID); LL_DEBUGS("Voice") << "Requesting voice font: " << session->mVoiceFontID << " (" << font_index << "), session handle: " << session->mHandle << LL_ENDL; @@ -6039,12 +6146,16 @@ void LLVivoxVoiceClient::sessionSetVoiceFontSendMessage(sessionState *session) void LLVivoxVoiceClient::accountGetSessionFontsResponse(int statusCode, const std::string &statusString) { - // Voice font list entries were updated via addVoiceFont() during parsing. - if(getState() == stateVoiceFontsWait) - { - setState(stateVoiceFontsReceived); - } + if (mIsWaitingForFonts) + { + // *TODO: We seem to get multiple events of this type. Should figure a way to advance only after + // receiving the last one. + LLSD result = LLSD::emptyMap(); + result["voice_fonts"] = LLSD::Boolean(true); + + LLEventPumps::instance().post("vivoxClientPump", result); + } notifyVoiceFontObservers(); mVoiceFontsReceived = true; } @@ -6159,36 +6270,44 @@ void LLVivoxVoiceClient::updateVoiceMorphingMenu() } void LLVivoxVoiceClient::notifyVoiceFontObservers() { - LL_DEBUGS("Voice") << "Notifying voice effect observers. Lists changed: " << mVoiceFontListDirty << LL_ENDL; + LL_DEBUGS("Voice") << "Notifying voice effect observers. Lists changed: " << mVoiceFontListDirty << LL_ENDL; - updateVoiceMorphingMenu(); + updateVoiceMorphingMenu(); - for (voice_font_observer_set_t::iterator it = mVoiceFontObservers.begin(); - it != mVoiceFontObservers.end(); - ) - { - LLVoiceEffectObserver* observer = *it; - observer->onVoiceEffectChanged(mVoiceFontListDirty); - // In case onVoiceEffectChanged() deleted an entry. - it = mVoiceFontObservers.upper_bound(observer); - } - mVoiceFontListDirty = false; + for (voice_font_observer_set_t::iterator it = mVoiceFontObservers.begin(); + it != mVoiceFontObservers.end();) + { + LLVoiceEffectObserver* observer = *it; + observer->onVoiceEffectChanged(mVoiceFontListDirty); + // In case onVoiceEffectChanged() deleted an entry. + it = mVoiceFontObservers.upper_bound(observer); + } + mVoiceFontListDirty = false; // If new Voice Fonts have been added notify the user. - if (mVoiceFontsNew) - { - if(mVoiceFontsReceived) - { - LLNotificationsUtil::add("VoiceEffectsNew"); - } - mVoiceFontsNew = false; - } + if (mVoiceFontsNew) + { + if (mVoiceFontsReceived) + { + LLNotificationsUtil::add("VoiceEffectsNew"); + } + mVoiceFontsNew = false; + } } void LLVivoxVoiceClient::enablePreviewBuffer(bool enable) { - mCaptureBufferMode = enable; - if(mCaptureBufferMode && getState() >= stateNoChannel) + LLSD result; + mCaptureBufferMode = enable; + + if (enable) + result["recplay"] = "start"; + else + result["recplay"] = "quit"; + + LLEventPumps::instance().post("vivoxClientPump", result); + + if(mCaptureBufferMode && mIsInChannel) { LL_DEBUGS("Voice") << "no channel" << LL_ENDL; sessionTerminate(); @@ -6205,6 +6324,10 @@ void LLVivoxVoiceClient::recordPreviewBuffer() } mCaptureBufferRecording = true; + + LLSD result; + result["recplay"] = "record"; + LLEventPumps::instance().post("vivoxClientPump", result); } void LLVivoxVoiceClient::playPreviewBuffer(const LLUUID& effect_id) @@ -6225,12 +6348,20 @@ void LLVivoxVoiceClient::playPreviewBuffer(const LLUUID& effect_id) mPreviewVoiceFont = effect_id; mCaptureBufferPlaying = true; + + LLSD result; + result["recplay"] = "playback"; + LLEventPumps::instance().post("vivoxClientPump", result); } void LLVivoxVoiceClient::stopPreviewBuffer() { mCaptureBufferRecording = false; mCaptureBufferPlaying = false; + + LLSD result; + result["recplay"] = "quit"; + LLEventPumps::instance().post("vivoxClientPump", result); } bool LLVivoxVoiceClient::isPreviewRecording() @@ -6244,7 +6375,7 @@ bool LLVivoxVoiceClient::isPreviewPlaying() } void LLVivoxVoiceClient::captureBufferRecordStartSendMessage() -{ if(!mAccountHandle.empty()) +{ if(mAccountLoggedIn) { std::ostringstream stream; @@ -6258,7 +6389,7 @@ void LLVivoxVoiceClient::captureBufferRecordStartSendMessage() // Unmute the mic stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.MuteLocalMic.1\">" - << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>" + << "<ConnectorHandle>" << LLVivoxSecurity::getInstance()->connectorHandle() << "</ConnectorHandle>" << "<Value>false</Value>" << "</Request>\n\n\n"; @@ -6271,7 +6402,7 @@ void LLVivoxVoiceClient::captureBufferRecordStartSendMessage() void LLVivoxVoiceClient::captureBufferRecordStopSendMessage() { - if(!mAccountHandle.empty()) + if(mAccountLoggedIn) { std::ostringstream stream; @@ -6279,14 +6410,14 @@ void LLVivoxVoiceClient::captureBufferRecordStopSendMessage() // Mute the mic. Mic mute state was dirtied at recording start, so will be reset when finished previewing. stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.MuteLocalMic.1\">" - << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>" + << "<ConnectorHandle>" << LLVivoxSecurity::getInstance()->connectorHandle() << "</ConnectorHandle>" << "<Value>true</Value>" << "</Request>\n\n\n"; // Stop capture stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.CaptureAudioStop.1\">" - << "<AccountHandle>" << mAccountHandle << "</AccountHandle>" + << "<AccountHandle>" << LLVivoxSecurity::getInstance()->accountHandle() << "</AccountHandle>" << "</Request>" << "\n\n\n"; @@ -6296,7 +6427,7 @@ void LLVivoxVoiceClient::captureBufferRecordStopSendMessage() void LLVivoxVoiceClient::captureBufferPlayStartSendMessage(const LLUUID& voice_font_id) { - if(!mAccountHandle.empty()) + if(mAccountLoggedIn) { // Track how may play requests are sent, so we know how many stop events to // expect before play actually stops. @@ -6311,7 +6442,7 @@ void LLVivoxVoiceClient::captureBufferPlayStartSendMessage(const LLUUID& voice_f stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.PlayAudioBuffer.1\">" - << "<AccountHandle>" << mAccountHandle << "</AccountHandle>" + << "<AccountHandle>" << LLVivoxSecurity::getInstance()->accountHandle() << "</AccountHandle>" << "<TemplateFontID>" << font_index << "</TemplateFontID>" << "<FontDelta />" << "</Request>" @@ -6323,7 +6454,7 @@ void LLVivoxVoiceClient::captureBufferPlayStartSendMessage(const LLUUID& voice_f void LLVivoxVoiceClient::captureBufferPlayStopSendMessage() { - if(!mAccountHandle.empty()) + if(mAccountLoggedIn) { std::ostringstream stream; @@ -6331,7 +6462,7 @@ void LLVivoxVoiceClient::captureBufferPlayStopSendMessage() stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.RenderAudioStop.1\">" - << "<AccountHandle>" << mAccountHandle << "</AccountHandle>" + << "<AccountHandle>" << LLVivoxSecurity::getInstance()->accountHandle() << "</AccountHandle>" << "</Request>" << "\n\n\n"; @@ -6669,6 +6800,10 @@ void LLVivoxProtocolParser::EndTag(const char *tag) uriString = string; else if (!stricmp("Presence", tag)) statusString = string; + else if (!stricmp("CaptureDevices", tag)) + LLVivoxVoiceClient::getInstance()->setDevicesListUpdated(true); + else if (!stricmp("RenderDevices", tag)) + LLVivoxVoiceClient::getInstance()->setDevicesListUpdated(true); else if (!stricmp("CaptureDevice", tag)) { LLVivoxVoiceClient::getInstance()->addCaptureDevice(deviceString); @@ -6797,7 +6932,16 @@ void LLVivoxProtocolParser::processResponse(std::string tag) if (isEvent) { const char *eventTypeCstr = eventTypeString.c_str(); - if (!stricmp(eventTypeCstr, "AccountLoginStateChangeEvent")) +// LL_WARNS("LOW Voice") << eventTypeCstr << LL_ENDL; + + if (!stricmp(eventTypeCstr, "ParticipantUpdatedEvent")) + { + // These happen so often that logging them is pretty useless. + squelchDebugOutput = true; +// LL_WARNS("LOW Voice") << "Updated Params: " << sessionHandle << ", " << sessionGroupHandle << ", " << uriString << ", " << alias << ", " << isModeratorMuted << ", " << isSpeaking << ", " << volume << ", " << energy << LL_ENDL; + LLVivoxVoiceClient::getInstance()->participantUpdatedEvent(sessionHandle, sessionGroupHandle, uriString, alias, isModeratorMuted, isSpeaking, volume, energy); + } + else if (!stricmp(eventTypeCstr, "AccountLoginStateChangeEvent")) { LLVivoxVoiceClient::getInstance()->accountLoginStateChangeEvent(accountHandle, statusCode, statusString, state); } @@ -6819,6 +6963,10 @@ void LLVivoxProtocolParser::processResponse(std::string tag) { LLVivoxVoiceClient::getInstance()->sessionRemovedEvent(sessionHandle, sessionGroupHandle); } + else if (!stricmp(eventTypeCstr, "SessionGroupUpdatedEvent")) + { + //nothng useful to process for this event, but we should not WARN that we have received it. + } else if (!stricmp(eventTypeCstr, "SessionGroupAddedEvent")) { LLVivoxVoiceClient::getInstance()->sessionGroupAddedEvent(sessionGroupHandle); @@ -6847,19 +6995,6 @@ void LLVivoxProtocolParser::processResponse(std::string tag) */ LLVivoxVoiceClient::getInstance()->mediaCompletionEvent(sessionGroupHandle, mediaCompletionType); } - else if (!stricmp(eventTypeCstr, "TextStreamUpdatedEvent")) - { - /* - <Event type="TextStreamUpdatedEvent"> - <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg1</SessionGroupHandle> - <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==1</SessionHandle> - <Enabled>true</Enabled> - <State>1</State> - <Incoming>true</Incoming> - </Event> - */ - LLVivoxVoiceClient::getInstance()->textStreamUpdatedEvent(sessionHandle, sessionGroupHandle, enabled, state, incoming); - } else if (!stricmp(eventTypeCstr, "ParticipantAddedEvent")) { /* @@ -6872,6 +7007,7 @@ void LLVivoxProtocolParser::processResponse(std::string tag) <ParticipantType>0</ParticipantType> </Event> */ +// LL_WARNS("LOW Voice") << "Added Params: " << sessionHandle << ", " << sessionGroupHandle << ", " << uriString << ", " << alias << ", " << nameString << ", " << displayNameString << ", " << participantType << LL_ENDL; LLVivoxVoiceClient::getInstance()->participantAddedEvent(sessionHandle, sessionGroupHandle, uriString, alias, nameString, displayNameString, participantType); } else if (!stricmp(eventTypeCstr, "ParticipantRemovedEvent")) @@ -6884,54 +7020,24 @@ void LLVivoxProtocolParser::processResponse(std::string tag) <AccountName>xtx7YNV-3SGiG7rA1fo5Ndw==</AccountName> </Event> */ +// LL_WARNS("LOW Voice") << "Removed params:" << sessionHandle << ", " << sessionGroupHandle << ", " << uriString << ", " << alias << ", " << nameString << LL_ENDL; + LLVivoxVoiceClient::getInstance()->participantRemovedEvent(sessionHandle, sessionGroupHandle, uriString, alias, nameString); } - else if (!stricmp(eventTypeCstr, "ParticipantUpdatedEvent")) - { - /* - <Event type="ParticipantUpdatedEvent"> - <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle> - <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==0</SessionHandle> - <ParticipantUri>sip:xFnPP04IpREWNkuw1cOXlhw==@bhr.vivox.com</ParticipantUri> - <IsModeratorMuted>false</IsModeratorMuted> - <IsSpeaking>true</IsSpeaking> - <Volume>44</Volume> - <Energy>0.0879437</Energy> - </Event> - */ - - // These happen so often that logging them is pretty useless. - squelchDebugOutput = true; - - LLVivoxVoiceClient::getInstance()->participantUpdatedEvent(sessionHandle, sessionGroupHandle, uriString, alias, isModeratorMuted, isSpeaking, volume, energy); - } else if (!stricmp(eventTypeCstr, "AuxAudioPropertiesEvent")) { // These are really spammy in tuning mode squelchDebugOutput = true; - LLVivoxVoiceClient::getInstance()->auxAudioPropertiesEvent(energy); } - else if (!stricmp(eventTypeCstr, "BuddyChangedEvent")) - { - /* - <Event type="BuddyChangedEvent"> - <AccountHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==</AccountHandle> - <BuddyURI>sip:x9fFHFZjOTN6OESF1DUPrZQ==@bhr.vivox.com</BuddyURI> - <DisplayName>Monroe Tester</DisplayName> - <BuddyData /> - <GroupID>0</GroupID> - <ChangeType>Set</ChangeType> - </Event> - */ - // TODO: Question: Do we need to process this at all? - } else if (!stricmp(eventTypeCstr, "MessageEvent")) { + //TODO: This probably is not received any more, it was used to support SLim clients LLVivoxVoiceClient::getInstance()->messageEvent(sessionHandle, uriString, alias, messageHeader, messageBody, applicationString); } else if (!stricmp(eventTypeCstr, "SessionNotificationEvent")) { + //TODO: This probably is not received any more, it was used to support SLim clients LLVivoxVoiceClient::getInstance()->sessionNotificationEvent(sessionHandle, uriString, notificationType); } else if (!stricmp(eventTypeCstr, "SessionUpdatedEvent")) @@ -6951,19 +7057,29 @@ void LLVivoxProtocolParser::processResponse(std::string tag) */ // We don't need to process this, but we also shouldn't warn on it, since that confuses people. } - - else if (!stricmp(eventTypeCstr, "SessionGroupRemovedEvent")) + else if (!stricmp(eventTypeCstr, "SessionGroupRemovedEvent")) { - /* - <Event type="SessionGroupRemovedEvent"> - <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle> - </Event> - */ // We don't need to process this, but we also shouldn't warn on it, since that confuses people. } - else if (!stricmp(eventTypeCstr, "VoiceServiceConnectionStateChangedEvent")) + else if (!stricmp(eventTypeCstr, "VoiceServiceConnectionStateChangedEvent")) { // Yet another ignored event } + else if (!stricmp(eventTypeCstr, "AudioDeviceHotSwapEvent")) + { + /* + <Event type = "AudioDeviceHotSwapEvent"> + <EventType>RenderDeviceChanged< / EventType> + <RelevantDevice> + <Device>Speakers(Turtle Beach P11 Headset)< / Device> + <DisplayName>Speakers(Turtle Beach P11 Headset)< / DisplayName> + <Type>SpecificDevice< / Type> + < / RelevantDevice> + < / Event> + */ + // an audio device was removed or added, fetch and update the local list of audio devices. + LLVivoxVoiceClient::getInstance()->getCaptureDevicesSendMessage(); + LLVivoxVoiceClient::getInstance()->getRenderDevicesSendMessage(); + } else { LL_WARNS("VivoxProtocolParser") << "Unknown event type " << eventTypeString << LL_ENDL; @@ -6972,7 +7088,14 @@ void LLVivoxProtocolParser::processResponse(std::string tag) else { const char *actionCstr = actionString.c_str(); - if (!stricmp(actionCstr, "Connector.Create.1")) +// LL_WARNS("LOW Voice") << actionCstr << LL_ENDL; + + if (!stricmp(actionCstr, "Session.Set3DPosition.1")) + { + // We don't need to process these, but they're so spammy we don't want to log them. + squelchDebugOutput = true; + } + else if (!stricmp(actionCstr, "Connector.Create.1")) { LLVivoxVoiceClient::getInstance()->connectorCreateResponse(statusCode, statusString, connectorHandle, versionID); } @@ -7000,11 +7123,6 @@ void LLVivoxProtocolParser::processResponse(std::string tag) { LLVivoxVoiceClient::getInstance()->connectorShutdownResponse(statusCode, statusString); } - else if (!stricmp(actionCstr, "Session.Set3DPosition.1")) - { - // We don't need to process these, but they're so spammy we don't want to log them. - squelchDebugOutput = true; - } else if (!stricmp(actionCstr, "Account.GetSessionFonts.1")) { LLVivoxVoiceClient::getInstance()->accountGetSessionFontsResponse(statusCode, statusString); @@ -7094,3 +7212,26 @@ void LLVivoxProtocolParser::processResponse(std::string tag) } } +LLVivoxSecurity::LLVivoxSecurity() +{ + // This size is an arbitrary choice; Vivox does not care + // Use a multiple of three so that there is no '=' padding in the base64 (purely an esthetic choice) + #define VIVOX_TOKEN_BYTES 9 + U8 random_value[VIVOX_TOKEN_BYTES]; + + for (int b = 0; b < VIVOX_TOKEN_BYTES; b++) + { + random_value[b] = ll_rand() & 0xff; + } + mConnectorHandle = LLBase64::encode(random_value, VIVOX_TOKEN_BYTES); + + for (int b = 0; b < VIVOX_TOKEN_BYTES; b++) + { + random_value[b] = ll_rand() & 0xff; + } + mAccountHandle = LLBase64::encode(random_value, VIVOX_TOKEN_BYTES); +} + +LLVivoxSecurity::~LLVivoxSecurity() +{ +} diff --git a/indra/newview/llvoicevivox.h b/indra/newview/llvoicevivox.h index a4ec9f2a69..f32c7c975f 100644 --- 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; @@ -91,6 +93,7 @@ public: // This returns true when it's safe to bring up the "device settings" dialog in the prefs. // i.e. when the daemon is running and connected, and the device lists are populated. virtual bool deviceSettingsAvailable(); + virtual bool deviceSettingsUpdated(); //return if the list has been updated and never fetched, only to be called from the voicepanel. // Requery the vivox daemon for the current list of input/output devices. // If you pass true for clearCurrentList, deviceSettingsAvailable() will be false until the query has completed @@ -109,11 +112,11 @@ public: virtual bool isParticipant(const LLUUID& speaker_id); // Send a text message to the specified user, initiating the session if necessary. - virtual BOOL sendTextMessage(const LLUUID& participant_id, const std::string& message); + // virtual BOOL sendTextMessage(const LLUUID& participant_id, const std::string& message) const {return false;}; // close any existing text IM session with the specified user virtual void endUserIMSession(const LLUUID &uuid); - + // Returns true if calling back the session URI after the session has closed is possible. // Currently this will be false only for PSTN P2P calls. // NOTE: this will return true if the session can't be found. @@ -135,7 +138,7 @@ public: virtual void setNonSpatialChannel(const std::string &uri, const std::string &credentials); - virtual void setSpatialChannel(const std::string &uri, + virtual bool setSpatialChannel(const std::string &uri, const std::string &credentials); virtual void leaveNonSpatialChannel(); @@ -251,9 +254,9 @@ protected: ////////////////////// // Vivox Specific definitions - friend class LLVivoxVoiceAccountProvisionResponder; friend class LLVivoxVoiceClientMuteListObserver; friend class LLVivoxVoiceClientFriendsObserver; + enum streamState { @@ -261,7 +264,10 @@ protected: streamStateIdle = 1, streamStateConnected = 2, streamStateRinging = 3, + streamStateConnecting = 6, // same as Vivox session_media_connecting enum + streamStateDisconnecting = 7, //Same as Vivox session_media_disconnecting enum }; + struct participantState { public: @@ -289,28 +295,40 @@ protected: bool mAvatarIDValid; bool mIsSelf; }; - - typedef std::map<const std::string, participantState*> participantMap; - typedef std::map<const LLUUID, participantState*> participantUUIDMap; + typedef boost::shared_ptr<participantState> participantStatePtr_t; + typedef boost::weak_ptr<participantState> participantStateWptr_t; + + typedef std::map<const std::string, participantStatePtr_t> participantMap; + typedef std::map<const LLUUID, participantStatePtr_t> participantUUIDMap; struct sessionState { - public: - sessionState(); + public: + typedef boost::shared_ptr<sessionState> ptr_t; + typedef boost::weak_ptr<sessionState> wptr_t; + + typedef boost::function<void(const ptr_t &)> sessionFunc_t; + + static ptr_t createSession(); ~sessionState(); - participantState *addParticipant(const std::string &uri); - // Note: after removeParticipant returns, the participant* that was passed to it will have been deleted. - // Take care not to use the pointer again after that. - void removeParticipant(participantState *participant); + participantStatePtr_t addParticipant(const std::string &uri); + void removeParticipant(const participantStatePtr_t &participant); void removeAllParticipants(); - - participantState *findParticipant(const std::string &uri); - participantState *findParticipantByID(const LLUUID& id); - + + participantStatePtr_t findParticipant(const std::string &uri); + participantStatePtr_t findParticipantByID(const LLUUID& id); + + static ptr_t matchSessionByHandle(const std::string &handle); + static ptr_t matchCreatingSessionByURI(const std::string &uri); + static ptr_t matchSessionByURI(const std::string &uri); + static ptr_t matchSessionByParticipant(const LLUUID &participant_id); + bool isCallBackPossible(); bool isTextIMPossible(); + static void for_each(sessionFunc_t func); + std::string mHandle; std::string mGroupHandle; std::string mSIPURI; @@ -325,7 +343,6 @@ protected: LLUUID mCallerID; int mErrorStatusCode; int mMediaStreamState; - int mTextStreamState; bool mCreateInProgress; // True if a Session.Create has been sent for this session and no response has been received yet. bool mMediaConnectInProgress; // True if a Session.MediaConnect has been sent for this session and no response has been received yet. bool mVoiceInvitePending; // True if a voice invite is pending for this session (usually waiting on a name lookup) @@ -348,72 +365,33 @@ protected: participantUUIDMap mParticipantsByUUID; LLUUID mVoiceFontID; - }; - // internal state for a simple state machine. This is used to deal with the asynchronous nature of some of the messages. - // Note: if you change this list, please make corresponding changes to LLVivoxVoiceClient::state2string(). - enum state - { - stateDisableCleanup, - stateDisabled, // Voice is turned off. - stateStart, // Class is initialized, socket is created - stateDaemonLaunched, // Daemon has been launched - stateConnecting, // connect() call has been issued - stateConnected, // connection to the daemon has been made, send some initial setup commands. - stateIdle, // socket is connected, ready for messaging - stateMicTuningStart, - stateMicTuningRunning, - stateMicTuningStop, - stateCaptureBufferPaused, - stateCaptureBufferRecStart, - stateCaptureBufferRecording, - stateCaptureBufferPlayStart, - stateCaptureBufferPlaying, - stateConnectorStart, // connector needs to be started - stateConnectorStarting, // waiting for connector handle - stateConnectorStarted, // connector handle received - stateLoginRetry, // need to retry login (failed due to changing password) - stateLoginRetryWait, // waiting for retry timer - stateNeedsLogin, // send login request - stateLoggingIn, // waiting for account handle - stateLoggedIn, // account handle received - stateVoiceFontsWait, // Awaiting the list of voice fonts - stateVoiceFontsReceived, // List of voice fonts received - stateCreatingSessionGroup, // Creating the main session group - stateNoChannel, // Need to join a channel - stateRetrievingParcelVoiceInfo, // waiting for parcel voice info request to return with spatial credentials - stateJoiningSession, // waiting for session handle - stateSessionJoined, // session handle received - stateRunning, // in session, steady state - stateLeavingSession, // waiting for terminate session response - stateSessionTerminated, // waiting for terminate session response - - stateLoggingOut, // waiting for logout response - stateLoggedOut, // logout response received - stateConnectorStopping, // waiting for connector stop - stateConnectorStopped, // connector stop received - - // We go to this state if the login fails because the account needs to be provisioned. - - // error states. No way to recover from these yet. - stateConnectorFailed, - stateConnectorFailedWaiting, - stateLoginFailed, - stateLoginFailedWaiting, - stateJoinSessionFailed, - stateJoinSessionFailedWaiting, - - stateJail // Go here when all else has failed. Nothing will be retried, we're done. + static void VerifySessions(); + + private: + sessionState(); + + static std::set<wptr_t> mSession; // canonical list of outstanding sessions. + std::set<wptr_t>::iterator mMyIterator; // used for delete + + static void for_eachPredicate(const wptr_t &a, sessionFunc_t func); + + static bool testByHandle(const LLVivoxVoiceClient::sessionState::wptr_t &a, std::string handle); + static bool testByCreatingURI(const LLVivoxVoiceClient::sessionState::wptr_t &a, std::string uri); + static bool testBySIPOrAlterateURI(const LLVivoxVoiceClient::sessionState::wptr_t &a, std::string uri); + static bool testByCallerId(const LLVivoxVoiceClient::sessionState::wptr_t &a, LLUUID participantId); + }; - - typedef std::map<std::string, sessionState*> sessionMap; - - + typedef boost::shared_ptr<sessionState> sessionStatePtr_t; + + typedef std::map<std::string, sessionStatePtr_t> sessionMap; /////////////////////////////////////////////////////// // Private Member Functions ////////////////////////////////////////////////////// - + + + ////////////////////////////// /// @name TVC/Server management and communication //@{ @@ -430,8 +408,8 @@ protected: void connectorShutdown(); void closeSocket(void); - void requestVoiceAccountProvision(S32 retries = 3); - void login( +// void requestVoiceAccountProvision(S32 retries = 3); + void setLoginInfo( const std::string& account_name, const std::string& password, const std::string& voice_sip_uri_hostname, @@ -457,14 +435,15 @@ protected: void clearCaptureDevices(); void addCaptureDevice(const std::string& name); void clearRenderDevices(); + void setDevicesListUpdated(bool state); void addRenderDevice(const std::string& name); void buildSetAudioDevices(std::ostringstream &stream); void getCaptureDevicesSendMessage(); void getRenderDevicesSendMessage(); - // local audio updates - void buildLocalAudioUpdates(std::ostringstream &stream); + // local audio updates, mic mute, speaker mute, mic volume and speaker volumes + void sendLocalAudioUpdates(); ///////////////////////////// @@ -480,7 +459,6 @@ protected: void accountLoginStateChangeEvent(std::string &accountHandle, int statusCode, std::string &statusString, int state); void mediaCompletionEvent(std::string &sessionGroupHandle, std::string &mediaCompletionType); void mediaStreamUpdatedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, int statusCode, std::string &statusString, int state, bool incoming); - void textStreamUpdatedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, bool enabled, int state, bool incoming); void sessionAddedEvent(std::string &uriString, std::string &alias, std::string &sessionHandle, std::string &sessionGroupHandle, bool isChannel, bool incoming, std::string &nameString, std::string &applicationString); void sessionGroupAddedEvent(std::string &sessionGroupHandle); void sessionRemovedEvent(std::string &sessionHandle, std::string &sessionGroupHandle); @@ -497,7 +475,7 @@ protected: // Sending updates of current state void updatePosition(void); void setCameraPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot); - void setAvatarPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot); + void setAvatarPosition(const LLVector3d &position, const LLVector3 &velocity, const LLQuaternion &rot); bool channelFromRegion(LLViewerRegion *region, std::string &name); void setEarLocation(S32 loc); @@ -527,39 +505,41 @@ protected: void filePlaybackSetPaused(bool paused); void filePlaybackSetMode(bool vox = false, float speed = 1.0f); - participantState *findParticipantByID(const LLUUID& id); + participantStatePtr_t findParticipantByID(const LLUUID& id); +#if 0 //////////////////////////////////////// // voice sessions. - typedef std::set<sessionState*> sessionSet; + typedef std::set<sessionStatePtr_t> sessionSet; typedef sessionSet::iterator sessionIterator; sessionIterator sessionsBegin(void); sessionIterator sessionsEnd(void); +#endif - sessionState *findSession(const std::string &handle); - sessionState *findSessionBeingCreatedByURI(const std::string &uri); - sessionState *findSession(const LLUUID &participant_id); - sessionState *findSessionByCreateID(const std::string &create_id); + sessionStatePtr_t findSession(const std::string &handle); + sessionStatePtr_t findSessionBeingCreatedByURI(const std::string &uri); + sessionStatePtr_t findSession(const LLUUID &participant_id); - sessionState *addSession(const std::string &uri, const std::string &handle = LLStringUtil::null); - void setSessionHandle(sessionState *session, const std::string &handle = LLStringUtil::null); - void setSessionURI(sessionState *session, const std::string &uri); - void deleteSession(sessionState *session); + sessionStatePtr_t addSession(const std::string &uri, const std::string &handle = std::string()); + void clearSessionHandle(const sessionStatePtr_t &session); + void setSessionHandle(const sessionStatePtr_t &session, const std::string &handle); + void setSessionURI(const sessionStatePtr_t &session, const std::string &uri); + void deleteSession(const sessionStatePtr_t &session); void deleteAllSessions(void); void verifySessionState(void); - void joinedAudioSession(sessionState *session); - void leftAudioSession(sessionState *session); + void joinedAudioSession(const sessionStatePtr_t &session); + void leftAudioSession(const sessionStatePtr_t &session); // This is called in several places where the session _may_ need to be deleted. // It contains logic for whether to delete the session or keep it around. - void reapSession(sessionState *session); + void reapSession(const sessionStatePtr_t &session); // Returns true if the session seems to indicate we've moved to a region on a different voice server - bool sessionNeedsRelog(sessionState *session); + bool sessionNeedsRelog(const sessionStatePtr_t &session); ////////////////////////////////////// @@ -589,14 +569,14 @@ protected: void accountListAutoAcceptRulesSendMessage(); void sessionGroupCreateSendMessage(); - void sessionCreateSendMessage(sessionState *session, bool startAudio = true, bool startText = false); - void sessionGroupAddSessionSendMessage(sessionState *session, bool startAudio = true, bool startText = false); - void sessionMediaConnectSendMessage(sessionState *session); // just joins the audio session - void sessionTextConnectSendMessage(sessionState *session); // just joins the text session - void sessionTerminateSendMessage(sessionState *session); - void sessionGroupTerminateSendMessage(sessionState *session); - void sessionMediaDisconnectSendMessage(sessionState *session); - void sessionTextDisconnectSendMessage(sessionState *session); + void sessionCreateSendMessage(const sessionStatePtr_t &session, bool startAudio = true, bool startText = false); + void sessionGroupAddSessionSendMessage(const sessionStatePtr_t &session, bool startAudio = true, bool startText = false); + void sessionMediaConnectSendMessage(const sessionStatePtr_t &session); // just joins the audio session + void sessionTextConnectSendMessage(const sessionStatePtr_t &session); // just joins the text session + void sessionTerminateSendMessage(const sessionStatePtr_t &session); + void sessionGroupTerminateSendMessage(const sessionStatePtr_t &session); + void sessionMediaDisconnectSendMessage(const sessionStatePtr_t &session); + // void sessionTextDisconnectSendMessage(sessionState *session); @@ -609,15 +589,14 @@ protected: // Does the actual work to get out of the audio session void leaveAudioSession(); - // notifies the voice client that we've received parcel voice info - bool parcelVoiceInfoReceived(state requesting_state); - friend class LLVivoxVoiceClientCapResponder; void lookupName(const LLUUID &id); void onAvatarNameCache(const LLUUID& id, const LLAvatarName& av_name); void avatarNameResolved(const LLUUID &id, const std::string &name); + static void predAvatarNameResolution(const LLVivoxVoiceClient::sessionStatePtr_t &session, LLUUID id, std::string name); + boost::signals2::connection mAvatarNameCacheConnection; ///////////////////////////// @@ -635,12 +614,41 @@ protected: void accountGetTemplateFontsResponse(int statusCode, const std::string &statusString); private: + LLVoiceVersionInfo mVoiceVersion; - /// Clean up objects created during a voice session. + // Coroutine support methods + //--- + void voiceControlCoro(); + + bool startAndConnectSession(); + bool endAndDisconnectSession(); + + bool startAndLaunchDaemon(); + bool provisionVoiceAccount(); + bool establishVoiceConnection(); + bool breakVoiceConnection(bool wait); + bool loginToVivox(); + void logoutOfVivox(bool wait); + bool retrieveVoiceFonts(); + + bool requestParcelVoiceInfo(); + + bool addAndJoinSession(const sessionStatePtr_t &nextSession); + bool terminateAudioSession(bool wait); + + bool waitForChannel(); + bool runSession(const sessionStatePtr_t &session); + + void recordingAndPlaybackMode(); + int voiceRecordBuffer(); + int voicePlaybackBuffer(); + + bool performMicTuning(); + //--- + /// Clean up objects created during a voice session. void cleanUp(); - state mState; bool mSessionTerminateRequested; bool mRelogRequested; // Number of times (in a row) "stateJoiningSession" case for spatial channel is reached in stateMachine(). @@ -648,11 +656,6 @@ private: // Introduced while fixing EXT-4313. int mSpatialJoiningNum; - void setState(state inState); - state getState(void) { return mState; }; - std::string state2string(state inState); - - void stateMachine(); static void idle(void *user_data); LLHost mDaemonHost; @@ -676,7 +679,8 @@ private: bool mTuningMicVolumeDirty; int mTuningSpeakerVolume; bool mTuningSpeakerVolumeDirty; - state mTuningExitState; // state to return to when we leave tuning mode. + bool mDevicesListUpdated; // set to true when the device list has been updated + // and false when the panelvoicedevicesettings has queried for an update status. std::string mSpatialSessionURI; std::string mSpatialSessionCredentials; @@ -685,21 +689,18 @@ private: std::string mChannelName; // Name of the channel to be looked up bool mAreaVoiceDisabled; - sessionState *mAudioSession; // Session state for the current audio session + sessionStatePtr_t mAudioSession; // Session state for the current audio session bool mAudioSessionChanged; // set to true when the above pointer gets changed, so observers can be notified. - sessionState *mNextAudioSession; // Session state for the audio session we're trying to join + sessionStatePtr_t mNextAudioSession; // Session state for the audio session we're trying to join -// std::string mSessionURI; // URI of the session we're in. -// std::string mSessionHandle; // returned by ? - S32 mCurrentParcelLocalID; // Used to detect parcel boundary crossings std::string mCurrentRegionName; // Used to detect parcel boundary crossings - std::string mConnectorHandle; // returned by "Create Connector" message - std::string mAccountHandle; // returned by login message - int mNumberOfAliases; - U32 mCommandCookie; + bool mConnectorEstablished; // set by "Create Connector" response + bool mAccountLoggedIn; // set by login message + int mNumberOfAliases; + U32 mCommandCookie; std::string mVoiceAccountServerURI; std::string mVoiceSIPURIHostName; @@ -707,7 +708,9 @@ private: int mLoginRetryCount; sessionMap mSessionsByHandle; // Active sessions, indexed by session handle. Sessions which are being initiated may not be in this map. +#if 0 sessionSet mSessions; // All sessions, not indexed. This is the canonical session list. +#endif bool mBuddyListMapPopulated; bool mBlockRulesListReceived; @@ -725,14 +728,9 @@ private: bool mIsInitialized; bool mShutdownComplete; - bool checkParcelChanged(bool update = false); - // This should be called when the code detects we have changed parcels. - // It initiates the call to the server that gets the parcel channel. - bool requestParcelVoiceInfo(); - - void switchChannel(std::string uri = std::string(), bool spatial = true, bool no_reconnect = false, bool is_p2p = false, std::string hash = ""); - void joinSession(sessionState *session); + bool switchChannel(std::string uri = std::string(), bool spatial = true, bool no_reconnect = false, bool is_p2p = false, std::string hash = ""); + void joinSession(const sessionStatePtr_t &session); std::string nameFromAvatar(LLVOAvatar *avatar); std::string nameFromID(const LLUUID &id); @@ -749,19 +747,21 @@ private: std::string getAudioSessionHandle(); void setHidden(bool hidden); //virtual - void sendPositionalUpdate(void); + void sendPositionAndVolumeUpdate(void); - void buildSetCaptureDevice(std::ostringstream &stream); + void sendCaptureAndRenderDevices(); + void buildSetCaptureDevice(std::ostringstream &stream); void buildSetRenderDevice(std::ostringstream &stream); void sendFriendsListUpdates(); +#if 0 // start a text IM session with the specified user // This will be asynchronous, the session may be established at a future time. - sessionState* startUserIMSession(const LLUUID& uuid); - void sendQueuedTextMessages(sessionState *session); - + sessionStatePtr_t startUserIMSession(const LLUUID& uuid); +#endif + void enforceTether(void); bool mSpatialCoordsDirty; @@ -773,7 +773,7 @@ private: LLVector3d mAvatarPosition; LLVector3 mAvatarVelocity; - LLMatrix3 mAvatarRot; + LLQuaternion mAvatarRot; bool mMuteMic; bool mMuteMicDirty; @@ -803,8 +803,6 @@ private: std::string mWriteString; size_t mWriteOffset; - LLTimer mUpdateTimer; - BOOL mLipSyncEnabled; typedef std::set<LLVoiceClientParticipantObserver*> observer_set_t; @@ -833,7 +831,7 @@ private: void accountGetSessionFontsSendMessage(); void accountGetTemplateFontsSendMessage(); - void sessionSetVoiceFontSendMessage(sessionState *session); + void sessionSetVoiceFontSendMessage(const sessionStatePtr_t &session); void updateVoiceMorphingMenu(); void notifyVoiceFontObservers(); @@ -899,12 +897,23 @@ private: bool mCaptureBufferRecorded; // A voice sample is captured in the buffer ready to play. bool mCaptureBufferPlaying; // A voice sample is being played. - LLTimer mCaptureTimer; - LLUUID mPreviewVoiceFont; - LLUUID mPreviewVoiceFontLast; - S32 mPlayRequestCount; + LLTimer mCaptureTimer; + LLUUID mPreviewVoiceFont; + LLUUID mPreviewVoiceFontLast; + S32 mPlayRequestCount; + bool mIsInTuningMode; + bool mIsInChannel; + bool mIsJoiningSession; + bool mIsWaitingForFonts; + bool mIsLoggingIn; + bool mIsLoggedIn; + bool mIsProcessingChannels; + bool mIsCoroutineActive; + + LLEventMailDrop mVivoxPump; }; + /** * @class LLVivoxProtocolParser * @brief This class helps construct new LLIOPipe specializations @@ -1013,8 +1022,22 @@ protected: void EndTag(const char *tag); void CharData(const char *buffer, int length); LLDate expiryTimeStampToLLDate(const std::string& vivox_ts); + }; +class LLVivoxSecurity : public LLSingleton<LLVivoxSecurity> +{ + public: + LLVivoxSecurity(); + virtual ~LLVivoxSecurity(); + + std::string connectorHandle() { return mConnectorHandle; }; + std::string accountHandle() { return mAccountHandle; }; + + private: + std::string mConnectorHandle; + std::string mAccountHandle; +}; #endif //LL_VIVOX_VOICE_CLIENT_H diff --git a/indra/newview/llwebprofile.cpp b/indra/newview/llwebprofile.cpp index ddb7f7bfce..06ce497510 100644 --- 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->getJsonAndSuspend(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->postAndSuspend(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->getRawAndSuspend(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 100644 --- 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..87e8c3008e 100644 --- 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->getAndSuspend(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) && regionId.notNull()) + { + 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->postAndSuspend(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 100644 --- 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..f8b38669b6 100644 --- a/indra/newview/llxmlrpctransaction.cpp +++ b/indra/newview/llxmlrpctransaction.cpp @@ -34,7 +34,12 @@ #include "llxmlrpctransaction.h" #include "llxmlrpclistener.h" -#include "llcurl.h" +#include "httpcommon.h" +#include "llhttpconstants.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::shared_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); - 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 100644 --- 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/pipeline.cpp b/indra/newview/pipeline.cpp index af29a1751a..c6bbfb1c8f 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -105,7 +105,6 @@ #include "llspatialpartition.h" #include "llmutelist.h" #include "lltoolpie.h" -#include "llcurl.h" #include "llnotifications.h" #include "llpathinglib.h" #include "llfloaterpathfindingconsole.h" diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index 4cb0e67521..dd39be344a 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -173,13 +173,13 @@ <menu_item_call.on_click function="BuyCurrency" /> </menu_item_call> - <menu_item_call - label="Merchant Outbox..." - name="MerchantOutbox"> - <menu_item_call.on_click - function="Floater.ToggleOrBringToFront" - parameter="outbox" /> - </menu_item_call> + <menu_item_call + label="Marketplace listings..." + name="MarketplaceListings"> + <menu_item_call.on_click + function="Floater.ToggleOrBringToFront" + parameter="marketplace_listings" /> + </menu_item_call> <menu_item_call label="Marketplace listings..." name="MarketplaceListings"> diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index 5153c62885..cf6eac66b7 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -244,7 +244,7 @@ You don't have permission to copy one or more of these items to the Merchant Out <notification icon="OutboxStatus_Success" name="OutboxFolderCreated" - type="outbox"> + type="alertmodal"> <unique/> A new folder has been created for each item you have transferred into the top level of your Merchant Outbox. @@ -257,7 +257,7 @@ A new folder has been created for each item you have transferred into the top le <notification icon="OutboxStatus_Success" name="OutboxImportComplete" - type="outbox"> + type="alertmodal"> Success All folders were successfully sent to the Marketplace. @@ -271,7 +271,7 @@ All folders were successfully sent to the Marketplace. <notification icon="OutboxStatus_Warning" name="OutboxImportHadErrors" - type="outbox"> + type="alertmodal"> Some folders did not transfer Errors occurred when some folders were sent to the Marketplace. Those folders are still in your Merchant Outbox. @@ -286,7 +286,7 @@ See the [[MARKETPLACE_IMPORTS_URL] error log] for more information. <notification icon="OutboxStatus_Error" name="OutboxImportFailed" - type="outbox"> + type="alertmodal"> Transfer failed with error '[ERROR_CODE]' No folders were sent to the Marketplace because of a system or network error. Try again later. @@ -299,7 +299,7 @@ No folders were sent to the Marketplace because of a system or network error. T <notification icon="OutboxStatus_Error" name="OutboxInitFailed" - type="outbox"> + type="alertmodal"> Marketplace initialization failed with error '[ERROR_CODE]' Initialization with the Marketplace failed because of a system or network error. Try again later. @@ -312,7 +312,7 @@ Initialization with the Marketplace failed because of a system or network error. <notification icon="OutboxStatus_Error" name="StockPasteFailed" - type="outbox"> + type="alertmodal"> Copy or move to Stock Folder failed with error : '[ERROR_CODE]' @@ -325,7 +325,7 @@ Initialization with the Marketplace failed because of a system or network error. <notification icon="OutboxStatus_Error" name="MerchantPasteFailed" - type="outbox"> + type="alertmodal"> Copy or move to Marketplace Listings failed with error : '[ERROR_CODE]' @@ -338,7 +338,7 @@ Initialization with the Marketplace failed because of a system or network error. <notification icon="OutboxStatus_Error" name="MerchantTransactionFailed" - type="outbox"> + type="alertmodal"> The transaction with the Marketplace failed with the following error : Reason : '[ERROR_REASON]' @@ -352,7 +352,7 @@ Initialization with the Marketplace failed because of a system or network error. <notification icon="OutboxStatus_Error" name="MerchantUnprocessableEntity" - type="outbox"> + type="alertmodal"> We are unable to list this product or activate the version folder. Usually this is caused by missing information in the listing description form, but it may be due to errors in the folder structure. Either edit the listing or check the listing folder for errors. <usetemplate @@ -363,7 +363,7 @@ Initialization with the Marketplace failed because of a system or network error. <notification icon="OutboxStatus_Error" name="MerchantListingFailed" - type="outbox"> + type="alertmodal"> Listing to Marketplace failed with error : '[ERROR_CODE]' @@ -376,7 +376,7 @@ Initialization with the Marketplace failed because of a system or network error. <notification icon="OutboxStatus_Error" name="MerchantFolderActivationFailed" - type="outbox"> + type="alertmodal"> Activating this version folder failed with error : '[ERROR_CODE]' @@ -4172,27 +4172,6 @@ Leave Group? </notification> <notification - name="GroupDepartError" - type="alert"> -Unable to leave group: [reason]. - <tag>reason</tag> - <usetemplate - name="okbutton" - yestext="OK"/> - </notification> - - <notification - icon="alert.tga" - name="GroupDepart" - type="alert"> -You have left the group [group_name]. - <tag>group_name</tag> - <usetemplate - name="okbutton" - yestext="OK"/> - </notification> - - <notification icon="alert.tga" name="ConfirmKick" type="alert"> diff --git a/indra/newview/skins/default/xui/en/panel_sound_devices.xml b/indra/newview/skins/default/xui/en/panel_sound_devices.xml index 46cbc1e87f..3dbb7fb7fc 100644 --- a/indra/newview/skins/default/xui/en/panel_sound_devices.xml +++ b/indra/newview/skins/default/xui/en/panel_sound_devices.xml @@ -98,7 +98,7 @@ name="My volume label" top_pad="14" width="200"> - My volume: + Mic volume: </text> <slider_bar control_name="AudioLevelMic" @@ -110,7 +110,7 @@ left_delta="95" max_val="2" name="mic_volume_slider" - tool_tip="Change the volume using this slider" + tool_tip="Change the mic level using this slider" top_pad="-18" width="110" /> <text diff --git a/indra/newview/tests/llcapabilitylistener_test.cpp b/indra/newview/tests/llcapabilitylistener_test.cpp deleted file mode 100644 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..21c83184dc 100644 --- a/indra/newview/tests/llhttpretrypolicy_test.cpp +++ b/indra/newview/tests/llhttpretrypolicy_test.cpp @@ -234,13 +234,13 @@ void RetryPolicyTestObject::test<6>() std::string str1("0"); seconds_to_wait = F32_MAX; - success = getSecondsUntilRetryAfter(str1, seconds_to_wait); + success = LLAdaptiveRetryPolicy::getSecondsUntilRetryAfter(str1, seconds_to_wait); ensure("parse 1", success); ensure_equals("parse 1", seconds_to_wait, 0.0); std::string str2("999.9"); seconds_to_wait = F32_MAX; - success = getSecondsUntilRetryAfter(str2, seconds_to_wait); + success = LLAdaptiveRetryPolicy::getSecondsUntilRetryAfter(str2, seconds_to_wait); ensure("parse 2", success); ensure_approximately_equals("parse 2", seconds_to_wait, 999.9F, 8); @@ -248,7 +248,7 @@ void RetryPolicyTestObject::test<6>() time(&nowseconds); std::string str3 = LLDate((F64)(nowseconds+44)).asRFC1123(); seconds_to_wait = F32_MAX; - success = getSecondsUntilRetryAfter(str3, seconds_to_wait); + success = LLAdaptiveRetryPolicy::getSecondsUntilRetryAfter(str3, seconds_to_wait); std::cerr << " str3 [" << str3 << "]" << std::endl; ensure("parse 3", success); ensure_approximately_equals_range("parse 3", seconds_to_wait, 44.0F, 2.0F); @@ -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 7cb4aeb121..4c0acded9e 100644 --- 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 100644 --- 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/newview/tests/lltranslate_test.cpp b/indra/newview/tests/lltranslate_test.cpp deleted file mode 100644 index 5e73dbb981..0000000000 --- a/indra/newview/tests/lltranslate_test.cpp +++ /dev/null @@ -1,340 +0,0 @@ -/** - * @file lltranslate_test.cpp - * - * $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 "linden_common.h" - -#include "../test/lltut.h" -#include "../lltranslate.h" -#include "../llversioninfo.h" -#include "../llviewercontrol.h" - -#include "llbufferstream.h" -#include "lltrans.h" -#include "llui.h" - -#include "../../llmessage/llhttpconstants.cpp" - -static const std::string GOOGLE_VALID_RESPONSE1 = -"{\ - \"data\": {\ - \"translations\": [\ - {\ - \"translatedText\": \"привет\",\ - \"detectedSourceLanguage\": \"es\"\ - }\ - ]\ - }\ -}"; - -static const std::string GOOGLE_VALID_RESPONSE2 = -"{\ - \"data\": {\ - \"translations\": [\ - {\ - \"translatedText\": \"привет\"\ - }\ - ]\ - }\ -}\ -"; - -static const std::string GOOGLE_VALID_RESPONSE3 = -"{\ - \"error\": {\ - \"errors\": [\ - {\ - \"domain\": \"global\",\ - \"reason\": \"invalid\",\ - \"message\": \"Invalid Value\"\ - }\ - ],\ - \"code\": 400,\ - \"message\": \"Invalid Value\"\ - }\ -}"; - -static const std::string BING_VALID_RESPONSE1 = -"<string xmlns=\"http://schemas.microsoft.com/2003/10/Serialization/\">Привет</string>"; - -static const std::string BING_VALID_RESPONSE2 = -"<html><body><h1>Argument Exception</h1><p>Method: Translate()</p><p>Parameter: </p>\ -<p>Message: 'from' must be a valid language</p><code></code>\ -<p>message id=3743.V2_Rest.Translate.58E8454F</p></body></html>"; - -static const std::string BING_VALID_RESPONSE3 = -"<html><body><h1>Argument Exception</h1><p>Method: Translate()</p>\ -<p>Parameter: appId</p><p>Message: Invalid appId
\nParameter name: appId</p>\ -<code></code><p>message id=3737.V2_Rest.Translate.56016759</p></body></html>"; - -namespace tut -{ - class translate_test - { - protected: - void test_translation( - LLTranslationAPIHandler& handler, - int status, const std::string& resp, - const std::string& exp_trans, const std::string& exp_lang, const std::string& exp_err) - { - std::string translation, detected_lang, err_msg; - bool rc = handler.parseResponse(status, resp, translation, detected_lang, err_msg); - ensure_equals("rc", rc, (status == 200)); - ensure_equals("err_msg", err_msg, exp_err); - ensure_equals("translation", translation, exp_trans); - ensure_equals("detected_lang", detected_lang, exp_lang); - } - - LLGoogleTranslationHandler mGoogle; - LLBingTranslationHandler mBing; - }; - - typedef test_group<translate_test> translate_test_group_t; - typedef translate_test_group_t::object translate_test_object_t; - tut::translate_test_group_t tut_translate("LLTranslate"); - - template<> template<> - void translate_test_object_t::test<1>() - { - test_translation(mGoogle, 200, GOOGLE_VALID_RESPONSE1, "привет", "es", ""); - } - - template<> template<> - void translate_test_object_t::test<2>() - { - test_translation(mGoogle, 200, GOOGLE_VALID_RESPONSE2, "привет", "", ""); - } - - template<> template<> - void translate_test_object_t::test<3>() - { - test_translation(mGoogle, 400, GOOGLE_VALID_RESPONSE3, "", "", "Invalid Value"); - } - - template<> template<> - void translate_test_object_t::test<4>() - { - test_translation(mGoogle, 400, - "", - "", "", "* Line 1, Column 1\n Syntax error: value, object or array expected.\n"); - } - - template<> template<> - void translate_test_object_t::test<5>() - { - test_translation(mGoogle, 400, - "[]", - "", "", ""); - } - - template<> template<> - void translate_test_object_t::test<6>() - { - test_translation(mGoogle, 400, - "{\"oops\": \"invalid\"}", - "", "", ""); - } - - template<> template<> - void translate_test_object_t::test<7>() - { - test_translation(mGoogle, 400, - "{\"data\": {}}", - "", "", ""); - } - - template<> template<> - void translate_test_object_t::test<8>() - { - test_translation(mGoogle, 400, - "{\"data\": { \"translations\": [ {} ] }}", - "", "", ""); - } - - template<> template<> - void translate_test_object_t::test<9>() - { - test_translation(mGoogle, 400, - "{\"data\": { \"translations\": [ { \"translatedTextZZZ\": \"привет\", \"detectedSourceLanguageZZZ\": \"es\" } ] }}", - "", "", ""); - } - - template<> template<> - void translate_test_object_t::test<10>() - { - test_translation(mBing, 200, BING_VALID_RESPONSE1, "Привет", "", ""); - } - - template<> template<> - void translate_test_object_t::test<11>() - { - test_translation(mBing, 400, BING_VALID_RESPONSE2, "", "", "'from' must be a valid language"); - } - - template<> template<> - void translate_test_object_t::test<12>() - { - test_translation(mBing, 400, BING_VALID_RESPONSE3, "", "", "Invalid appId\nParameter name: appId"); - } - - template<> template<> - void translate_test_object_t::test<13>() - { - test_translation(mBing, 200, - "Привет</string>", - "Привет", "", ""); - } - - template<> template<> - void translate_test_object_t::test<14>() - { - test_translation(mBing, 200, - "<string xmlns=\"http://schemas.microsoft.com/2003/10/Serialization/\">Привет", - "Привет", "", ""); - } - - template<> template<> - void translate_test_object_t::test<15>() - { - test_translation(mBing, 200, - "Привет", - "Привет", "", ""); - } - - template<> template<> - void translate_test_object_t::test<16>() - { - test_translation(mBing, 400, - "Message: some error</p>", - "", "", "some error"); - } - - template<> template<> - void translate_test_object_t::test<17>() - { - test_translation(mBing, 400, - "Message: some error", - "", "", "some error"); - } - - template<> template<> - void translate_test_object_t::test<18>() - { - test_translation(mBing, 400, - "some error</p>", - "", "", "some error"); - } - - template<> template<> - void translate_test_object_t::test<19>() - { - test_translation(mBing, 400, - "some error", - "", "", "some error"); - } - - template<> template<> - void translate_test_object_t::test<20>() - { - std::string url; - mBing.getTranslateURL(url, "en", "es", "hi"); - ensure_equals("bing URL", url, - "http://api.microsofttranslator.com/v2/Http.svc/Translate?appId=dummy&text=hi&to=es&from=en"); - } - - template<> template<> - void translate_test_object_t::test<21>() - { - std::string url; - mBing.getTranslateURL(url, "", "es", "hi"); - ensure_equals("bing URL", url, - "http://api.microsofttranslator.com/v2/Http.svc/Translate?appId=dummy&text=hi&to=es"); - } - - template<> template<> - void translate_test_object_t::test<22>() - { - std::string url; - mGoogle.getTranslateURL(url, "en", "es", "hi"); - ensure_equals("google URL", url, - "https://www.googleapis.com/language/translate/v2?key=dummy&q=hi&target=es&source=en"); - } - - template<> template<> - void translate_test_object_t::test<23>() - { - std::string url; - mGoogle.getTranslateURL(url, "", "es", "hi"); - ensure_equals("google URL", url, - "https://www.googleapis.com/language/translate/v2?key=dummy&q=hi&target=es"); - } -} - -//== Misc stubs =============================================================== -LLControlGroup gSavedSettings("test"); - -std::string LLUI::getLanguage() { return "en"; } -std::string LLTrans::getString(const std::string &xml_desc, const LLStringUtil::format_map_t& args) { return "dummy"; } - -LLControlGroup::LLControlGroup(const std::string& name) : LLInstanceTracker<LLControlGroup, std::string>(name) {} -std::string LLControlGroup::getString(const std::string& name) { return "dummy"; } -LLControlGroup::~LLControlGroup() {} - -LLCurl::Responder::Responder() {} -void LLCurl::Responder::httpFailure() { } -void LLCurl::Responder::httpSuccess() { } -void LLCurl::Responder::httpCompleted() { } -void LLCurl::Responder::completedRaw(LLChannelDescriptors const &,boost::shared_ptr<LLBufferArray> const &) { } -LLCurl::Responder::~Responder() {} - -void LLHTTPClient::get(const std::string&, const LLSD&, ResponderPtr, const LLSD&, const F32, bool) {} -void LLHTTPClient::get(const std::string&, LLPointer<LLCurl::Responder>, const LLSD&, const F32, bool) {} - -LLBufferStream::LLBufferStream(const LLChannelDescriptors& channels, LLBufferArray* buffer) -: std::iostream(&mStreamBuf), mStreamBuf(channels, buffer) {} -LLBufferStream::~LLBufferStream() {} - -LLBufferStreamBuf::LLBufferStreamBuf(const LLChannelDescriptors&, LLBufferArray*) {} -#if( LL_WINDOWS || __GNUC__ > 2) -LLBufferStreamBuf::pos_type LLBufferStreamBuf::seekoff( - off_type off, - std::ios::seekdir way, - std::ios::openmode which) -#else -streampos LLBufferStreamBuf::seekoff( - streamoff off, - std::ios::seekdir way, - std::ios::openmode which) -#endif -{ return 0; } -int LLBufferStreamBuf::sync() {return 0;} -int LLBufferStreamBuf::underflow() {return 0;} -int LLBufferStreamBuf::overflow(int) {return 0;} -LLBufferStreamBuf::~LLBufferStreamBuf() {} - -S32 LLVersionInfo::getBuild() { return 0; } -const std::string& LLVersionInfo::getChannel() {static std::string dummy; return dummy;} -S32 LLVersionInfo::getMajor() { return 0; } -S32 LLVersionInfo::getMinor() { return 0; } -S32 LLVersionInfo::getPatch() { return 0; } diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index ab87f0da35..1c77cf805e 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -401,8 +401,6 @@ class Windows_i686_Manifest(ViewerManifest): self.path("vivoxsdk.dll") self.path("ortp.dll") self.path("libsndfile-1.dll") - self.path("zlib1.dll") - self.path("vivoxplatform.dll") self.path("vivoxoal.dll") self.path("ca-bundle.crt") diff --git a/indra/test/CMakeLists.txt b/indra/test/CMakeLists.txt index 9404dcf88e..55442b2521 100644 --- a/indra/test/CMakeLists.txt +++ b/indra/test/CMakeLists.txt @@ -4,6 +4,7 @@ project (lltest) include(00-Common) include(LLCommon) +include(LLCoreHttp) include(LLInventory) include(LLMath) include(LLMessage) @@ -17,6 +18,7 @@ include(GoogleMock) include_directories( ${LLCOMMON_INCLUDE_DIRS} + ${LLCOREHTTP_INCLUDE_DIRS} ${LLDATABASE_INCLUDE_DIRS} ${LLMATH_INCLUDE_DIRS} ${LLMESSAGE_INCLUDE_DIRS} @@ -89,12 +91,16 @@ target_link_libraries(lltest ${LLXML_LIBRARIES} ${LSCRIPT_LIBRARIES} ${LLCOMMON_LIBRARIES} + ${LLCOREHTTP_LIBRARIES} ${EXPAT_LIBRARIES} ${GOOGLEMOCK_LIBRARIES} ${PTHREAD_LIBRARY} ${WINDOWS_LIBRARIES} ${BOOST_PROGRAM_OPTIONS_LIBRARY} ${BOOST_REGEX_LIBRARY} + ${BOOST_COROUTINE_LIBRARY} + ${BOOST_CONTEXT_LIBRARY} + ${BOOST_SYSTEM_LIBRARY} ${DL_LIBRARY} ${GOOGLE_PERFTOOLS_LIBRARIES} ) diff --git a/indra/test/io.cpp b/indra/test/io.cpp index 97726c2b92..ff900ab96b 100644 --- a/indra/test/io.cpp +++ b/indra/test/io.cpp @@ -41,8 +41,6 @@ #include "llpipeutil.h" #include "llpumpio.h" #include "llsd.h" -#include "llsdrpcclient.h" -#include "llsdrpcserver.h" #include "llsdserialize.h" #include "llcommon.h" #include "lluuid.h" @@ -1177,425 +1175,6 @@ namespace tut } } -namespace tut -{ - struct rpc_server_data - { - class LLSimpleRPCResponse : public LLSDRPCResponse - { - public: - LLSimpleRPCResponse(LLSD* response) : - mResponsePtr(response) - { - } - ~LLSimpleRPCResponse() - { - } - virtual bool response(LLPumpIO* pump) - { - *mResponsePtr = mReturnValue; - return true; - } - virtual bool fault(LLPumpIO* pump) - { - *mResponsePtr = mReturnValue; - return false; - } - virtual bool error(LLPumpIO* pump) - { - ensure("LLSimpleRPCResponse::error()", false); - return false; - } - public: - LLSD* mResponsePtr; - }; - - class LLSimpleRPCClient : public LLSDRPCClient - { - public: - LLSimpleRPCClient(LLSD* response) : - mResponsePtr(response) - { - } - ~LLSimpleRPCClient() {} - void echo(const LLSD& parameter) - { - LLSimpleRPCResponse* resp; - resp = new LLSimpleRPCResponse(mResponsePtr); - static const std::string URI_NONE; - static const std::string METHOD_ECHO("echo"); - call(URI_NONE, METHOD_ECHO, parameter, resp, EPBQ_CALLBACK); - } - public: - LLSD* mResponsePtr; - }; - - class LLSimpleRPCServer : public LLSDRPCServer - { - public: - LLSimpleRPCServer() - { - mMethods["echo"] = new mem_fn_t( - this, - &LLSimpleRPCServer::rpc_Echo); - } - ~LLSimpleRPCServer() {} - protected: - typedef LLSDRPCMethodCall<LLSimpleRPCServer> mem_fn_t; - ESDRPCSStatus rpc_Echo( - const LLSD& parameter, - const LLChannelDescriptors& channels, - LLBufferArray* data) - { - buildResponse(channels, data, parameter); - return ESDRPCS_DONE; - } - }; - - apr_pool_t* mPool; - LLPumpIO* mPump; - LLPumpIO::chain_t mChain; - LLSimpleRPCClient* mClient; - LLSD mResponse; - - rpc_server_data() : - mPool(NULL), - mPump(NULL), - mClient(NULL) - { - apr_pool_create(&mPool, NULL); - mPump = new LLPumpIO(mPool); - mClient = new LLSimpleRPCClient(&mResponse); - mChain.push_back(LLIOPipe::ptr_t(mClient)); - mChain.push_back(LLIOPipe::ptr_t(new LLFilterSD2XMLRPCRequest)); - mChain.push_back(LLIOPipe::ptr_t(new LLFilterXMLRPCRequest2LLSD)); - mChain.push_back(LLIOPipe::ptr_t(new LLSimpleRPCServer)); - mChain.push_back(LLIOPipe::ptr_t(new LLFilterSD2XMLRPCResponse)); - mChain.push_back(LLIOPipe::ptr_t(new LLFilterXMLRPCResponse2LLSD)); - mChain.push_back(LLIOPipe::ptr_t(mClient)); - } - ~rpc_server_data() - { - mChain.clear(); - delete mPump; - mPump = NULL; - apr_pool_destroy(mPool); - mPool = NULL; - } - void pump_loop(const LLSD& request) - { - LLTimer timer; - timer.setTimerExpirySec(1.0f); - mClient->echo(request); - mPump->addChain(mChain, DEFAULT_CHAIN_EXPIRY_SECS); - while(mResponse.isUndefined() && !timer.hasExpired()) - { - mPump->pump(); - mPump->callback(); - } - } - }; - typedef test_group<rpc_server_data> rpc_server_test; - typedef rpc_server_test::object rpc_server_object; - tut::rpc_server_test rpc("rpc_server"); - - template<> template<> - void rpc_server_object::test<1>() - { - LLSD request; - request = 1; - pump_loop(request); - //LL_INFOS() << "request: " << *request << LL_ENDL; - //LL_INFOS() << "response: " << *mResponse << LL_ENDL; - ensure_equals("integer request response", mResponse.asInteger(), 1); - } - - template<> template<> - void rpc_server_object::test<2>() - { -//#if LL_WINDOWS && _MSC_VER >= 1400 -// skip_fail("Fails on VS2005 due to broken LLSDSerialize::fromNotation() parser."); -//#endif - std::string uri("sl-am:66.150.244.180:12035/location?start=region&px=70.9247&py=254.378&pz=38.7304&lx=-0.043753&ly=-0.999042&lz=0"); - std::stringstream stream; - stream << "{'task_id':ucc706f2d-0b68-68f8-11a4-f1043ff35ca0}\n{\n\tname\tObject|\n\tpermissions 0\n}"; - std::vector<U8> expected_binary; - expected_binary.resize(stream.str().size()); - memcpy(&expected_binary[0], stream.str().c_str(), stream.str().size()); /* Flawfinder: ignore */ - stream.str(""); - stream << "[{'uri':'" << uri << "'}, {'version':i1}, " - << "{'agent_id':'3c115e51-04f4-523c-9fa6-98aff1034730', 'session_id':'2c585cec-038c-40b0-b42e-a25ebab4d132', 'circuit_code':i1075, 'start':'region', 'limited_to_estate':i1 'first_name':'Phoenix', 'last_name':'Linden', 'group_title':'', 'group_id':u00000000-0000-0000-0000-000000000000, 'position':[r70.9247,r254.378,r38.7304], 'look_at':[r-0.043753,r-0.999042,r0], 'granters':[ua2e76fcd-9360-4f6d-a924-000000000003], 'texture_data':['5e481e8a-58a6-fc34-6e61-c7a36095c07f', 'c39675f5-ca90-a304-bb31-42cdb803a132', '5c989edf-88d1-b2ac-b00b-5ed4bab8e368', '6522e74d-1660-4e7f-b601-6f48c1659a77', '7ca39b4c-bd19-4699-aff7-f93fd03d3e7b', '41c58177-5eb6-5aeb-029d-bc4093f3c130', '97b75473-8b93-9b25-2a11-035b9ae93195', '1c2d8d9b-90eb-89d4-dea8-c1ed83990614', '69ec543f-e27b-c07c-9094-a8be6300f274', 'c9f8b80f-c629-4633-04ee-c566ce9fea4b', '989cddba-7ab6-01ed-67aa-74accd2a2a65', '45e319b2-6a8c-fa5c-895b-1a7149b88aef', '5748decc-f629-461c-9a36-a35a221fe21f', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97', '685fbe10-ab40-f065-0aec-726cc6dfd7a1', '406f98fd-9c89-1d52-5f39-e67d508c5ee5', '685fbe10-ab40-f065-0aec-726cc6dfd7a1', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97'], " - << "'attachment_data':[" - << "{'attachment_point':i2, 'item_id':'d6852c11-a74e-309a-0462-50533f1ef9b3', 'asset_id':'c69b29b1-8944-58ae-a7c5-2ca7b23e22fb'}," - << "{'attachment_point':i10, 'item_id':'ff852c22-a74e-309a-0462-50533f1ef900', 'asset_data':b(" << expected_binary.size() << ")\""; - stream.write((const char*)&expected_binary[0], expected_binary.size()); - stream << "\"}" - << "]" - << "}]"; - - LLSD request; - S32 count = LLSDSerialize::fromNotation( - request, - stream, - stream.str().size()); - ensure("parsed something", (count > 0)); - - pump_loop(request); - ensure_equals("return type", mResponse.type(), LLSD::TypeArray); - ensure_equals("return size", mResponse.size(), 3); - - ensure_equals( - "uri parameter type", - mResponse[0].type(), - LLSD::TypeMap); - ensure_equals( - "uri type", - mResponse[0]["uri"].type(), - LLSD::TypeString); - ensure_equals("uri value", mResponse[0]["uri"].asString(), uri); - - ensure_equals( - "version parameter type", - mResponse[1].type(), - LLSD::TypeMap); - ensure_equals( - "version type", - mResponse[1]["version"].type(), - LLSD::TypeInteger); - ensure_equals( - "version value", - mResponse[1]["version"].asInteger(), - 1); - - ensure_equals("agent params type", mResponse[2].type(), LLSD::TypeMap); - LLSD attachment_data = mResponse[2]["attachment_data"]; - ensure("attachment data exists", attachment_data.isDefined()); - ensure_equals( - "attachment type", - attachment_data.type(), - LLSD::TypeArray); - ensure_equals( - "attachment type 0", - attachment_data[0].type(), - LLSD::TypeMap); - ensure_equals( - "attachment type 1", - attachment_data[1].type(), - LLSD::TypeMap); - ensure_equals("attachment size 1", attachment_data[1].size(), 3); - ensure_equals( - "asset data type", - attachment_data[1]["asset_data"].type(), - LLSD::TypeBinary); - std::vector<U8> actual_binary; - actual_binary = attachment_data[1]["asset_data"].asBinary(); - ensure_equals( - "binary data size", - actual_binary.size(), - expected_binary.size()); - ensure( - "binary data", - (0 == memcmp( - &actual_binary[0], - &expected_binary[0], - expected_binary.size()))); - } - - template<> template<> - void rpc_server_object::test<3>() - { -//#if LL_WINDOWS && _MSC_VER >= 1400 -// skip_fail("Fails on VS2005 due to broken LLSDSerialize::fromNotation() parser."); -//#endif - std::string uri("sl-am:66.150.244.180:12035/location?start=region&px=70.9247&py=254.378&pz=38.7304&lx=-0.043753&ly=-0.999042&lz=0"); - - LLBufferArray buffer; - LLChannelDescriptors buffer_channels = buffer.nextChannel(); - LLBufferStream stream(buffer_channels, &buffer); - stream << "[{'uri':'" << uri << "'}, {'version':i1}, " - << "{'agent_id':'3c115e51-04f4-523c-9fa6-98aff1034730', 'session_id':'2c585cec-038c-40b0-b42e-a25ebab4d132', 'circuit_code':i1075, 'start':'region', 'limited_to_estate':i1 'first_name':'Phoenix', 'last_name':'Linden', 'group_title':'', 'group_id':u00000000-0000-0000-0000-000000000000, 'position':[r70.9247,r254.378,r38.7304], 'look_at':[r-0.043753,r-0.999042,r0], 'granters':[ua2e76fcd-9360-4f6d-a924-000000000003], 'texture_data':['5e481e8a-58a6-fc34-6e61-c7a36095c07f', 'c39675f5-ca90-a304-bb31-42cdb803a132', '5c989edf-88d1-b2ac-b00b-5ed4bab8e368', '6522e74d-1660-4e7f-b601-6f48c1659a77', '7ca39b4c-bd19-4699-aff7-f93fd03d3e7b', '41c58177-5eb6-5aeb-029d-bc4093f3c130', '97b75473-8b93-9b25-2a11-035b9ae93195', '1c2d8d9b-90eb-89d4-dea8-c1ed83990614', '69ec543f-e27b-c07c-9094-a8be6300f274', 'c9f8b80f-c629-4633-04ee-c566ce9fea4b', '989cddba-7ab6-01ed-67aa-74accd2a2a65', '45e319b2-6a8c-fa5c-895b-1a7149b88aef', '5748decc-f629-461c-9a36-a35a221fe21f', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97', '685fbe10-ab40-f065-0aec-726cc6dfd7a1', '406f98fd-9c89-1d52-5f39-e67d508c5ee5', '685fbe10-ab40-f065-0aec-726cc6dfd7a1', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97'], " - << "'attachment_data':[" - << "{'attachment_point':i2, 'item_id':'d6852c11-a74e-309a-0462-50533f1ef9b3', 'asset_id':'c69b29b1-8944-58ae-a7c5-2ca7b23e22fb'},"; - - std::stringstream tmp_str; - tmp_str << "{'task_id':ucc706f2d-0b68-68f8-11a4-f1043ff35ca0}\n{\n\tname\tObject|\n\tpermissions 0\n}"; - std::vector<U8> expected_binary; - expected_binary.resize(tmp_str.str().size()); - memcpy( /* Flawfinder: ignore */ - &expected_binary[0], - tmp_str.str().c_str(), - tmp_str.str().size()); - - LLBufferArray attachment_buffer; - LLChannelDescriptors attach_channels = attachment_buffer.nextChannel(); - LLBufferStream attach_stream(attach_channels, &attachment_buffer); - attach_stream.write((const char*)&expected_binary[0], expected_binary.size()); - attach_stream.flush(); - S32 len = attachment_buffer.countAfter(attach_channels.out(), NULL); - stream << "{'attachment_point':i10, 'item_id':'ff852c22-a74e-309a-0462-50533f1ef900', 'asset_data':b(" << len << ")\""; - stream.flush(); - buffer.takeContents(attachment_buffer); - stream << "\"}]}]"; - stream.flush(); - - LLChannelDescriptors read_channel = buffer.nextChannel(); - LLBufferStream read_stream(read_channel, &buffer); - LLSD request; - S32 count = LLSDSerialize::fromNotation( - request, - read_stream, - buffer.count(read_channel.in())); - ensure("parsed something", (count > 0)); - ensure("deserialized", request.isDefined()); - - // do the rpc round trip - pump_loop(request); - - ensure_equals("return type", mResponse.type(), LLSD::TypeArray); - ensure_equals("return size", mResponse.size(), 3); - - LLSD child = mResponse[0]; - ensure("uri map exists", child.isDefined()); - ensure_equals("uri parameter type", child.type(), LLSD::TypeMap); - ensure("uri string exists", child.has("uri")); - ensure_equals("uri type", child["uri"].type(), LLSD::TypeString); - ensure_equals("uri value", child["uri"].asString(), uri); - - child = mResponse[1]; - ensure("version map exists", child.isDefined()); - ensure_equals("version param type", child.type(), LLSD::TypeMap); - ensure_equals( - "version type", - child["version"].type(), - LLSD::TypeInteger); - ensure_equals("version value", child["version"].asInteger(), 1); - - child = mResponse[2]; - ensure("agent params map exists", child.isDefined()); - ensure_equals("agent params type", child.type(), LLSD::TypeMap); - child = child["attachment_data"]; - ensure("attachment data exists", child.isDefined()); - ensure_equals("attachment type", child.type(), LLSD::TypeArray); - LLSD attachment = child[0]; - ensure_equals("attachment type 0", attachment.type(), LLSD::TypeMap); - attachment = child[1]; - ensure_equals("attachment type 1", attachment.type(), LLSD::TypeMap); - ensure_equals("attachment size 1", attachment.size(), 3); - ensure_equals( - "asset data type", - attachment["asset_data"].type(), - LLSD::TypeBinary); - std::vector<U8> actual_binary = attachment["asset_data"].asBinary(); - ensure_equals( - "binary data size", - actual_binary.size(), - expected_binary.size()); - ensure( - "binary data", - (0 == memcmp( - &actual_binary[0], - &expected_binary[0], - expected_binary.size()))); - } - - template<> template<> - void rpc_server_object::test<4>() - { - std::string message("parcel '' is naughty."); - std::stringstream str; - str << "{'message':'" << LLSDNotationFormatter::escapeString(message) - << "'}"; - LLSD request; - S32 count = LLSDSerialize::fromNotation( - request, - str, - str.str().size()); - ensure_equals("parse count", count, 2); - ensure_equals("request type", request.type(), LLSD::TypeMap); - pump_loop(request); - ensure("valid response", mResponse.isDefined()); - ensure_equals("response type", mResponse.type(), LLSD::TypeMap); - std::string actual = mResponse["message"].asString(); - ensure_equals("message contents", actual, message); - } - - template<> template<> - void rpc_server_object::test<5>() - { - // test some of the problem cases with llsdrpc over xmlrpc - - // for example: - // * arrays are auto-converted to parameter lists, thus, this - // becomes one parameter. - // * undef goes over the wire as false (this might not be a good idea) - // * uuids are converted to string. - std::string val = "[{'failures':!,'successfuls':[u3c115e51-04f4-523c-9fa6-98aff1034730]}]"; - std::istringstream istr; - istr.str(val); - LLSD sd; - LLSDSerialize::fromNotation(sd, istr, val.size()); - pump_loop(sd); - ensure("valid response", mResponse.isDefined()); - ensure_equals("parsed type", mResponse.type(), LLSD::TypeMap); - ensure_equals("parsed size", mResponse.size(), 2); - LLSD failures = mResponse["failures"]; - ensure_equals("no failures.", failures.asBoolean(), false); - LLSD success = mResponse["successfuls"]; - ensure_equals("success type", success.type(), LLSD::TypeArray); - ensure_equals("success size", success.size(), 1); - ensure_equals( - "success instance type", - success[0].type(), - LLSD::TypeString); - } - -/* - template<> template<> - void rpc_server_object::test<5>() - { - std::string expected("\xf3");//\xffsomething"); - LLSD* request = LLSD::createString(expected); - pump_loop(request); - std::string actual; - mResponse->getString(actual); - if(actual != expected) - { - //LL_WARNS() << "iteration " << i << LL_ENDL; - std::ostringstream e_str; - std::string::iterator iter = expected.begin(); - std::string::iterator end = expected.end(); - for(; iter != end; ++iter) - { - e_str << (S32)((U8)(*iter)) << " "; - } - e_str << std::endl; - llsd_serialize_string(e_str, expected); - LL_WARNS() << "expected size: " << expected.size() << LL_ENDL; - LL_WARNS() << "expected: " << e_str.str() << LL_ENDL; - - std::ostringstream a_str; - iter = actual.begin(); - end = actual.end(); - for(; iter != end; ++iter) - { - a_str << (S32)((U8)(*iter)) << " "; - } - a_str << std::endl; - llsd_serialize_string(a_str, actual); - LL_WARNS() << "actual size: " << actual.size() << LL_ENDL; - LL_WARNS() << "actual: " << a_str.str() << LL_ENDL; - } - ensure_equals("binary string request response", actual, expected); - delete request; - } - - template<> template<> - void rpc_server_object::test<5>() - { - } -*/ -} - - /* 'asset_data':b(12100)"{'task_id':ucc706f2d-0b68-68f8-11a4-f1043ff35ca0}\n{\n\tname\tObject|\n\tpermissions 0\n\t{\n\t\tbase_mask\t7fffffff\n\t\towner_mask\t7fffffff\n\t\tgroup_mask\t00000000\n\t\teveryone_mask\t00000000\n\t\tnext_owner_mask\t7fffffff\n\t\tcreator_id\t13fd9595-a47b-4d64-a5fb-6da645f038e0\n\t\towner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tlast_owner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tgroup_id\t00000000-0000-0000-0000-000000000000\n\t}\n\tlocal_id\t217444921\n\ttotal_crc\t323\n\ttype\t2\n\ttask_valid\t2\n\ttravel_access\t13\n\tdisplayopts\t2\n\tdisplaytype\tv\n\tpos\t-0.368634403\t0.00781063363\t-0.569040775\n\toldpos\t150.117996\t25.8658009\t8.19664001\n\trotation\t-0.06293071806430816650390625\t-0.6995697021484375\t-0.7002241611480712890625\t0.1277817934751510620117188\n\tchildpos\t-0.00499999989\t-0.0359999985\t0.307999998\n\tchildrot\t-0.515492737293243408203125\t-0.46601200103759765625\t0.529055416584014892578125\t0.4870323240756988525390625\n\tscale\t0.074629\t0.289956\t0.01\n\tsit_offset\t0\t0\t0\n\tcamera_eye_offset\t0\t0\t0\n\tcamera_at_offset\t0\t0\t0\n\tsit_quat\t0\t0\t0\t1\n\tsit_hint\t0\n\tstate\t160\n\tmaterial\t3\n\tsoundid\t00000000-0000-0000-0000-000000000000\n\tsoundgain\t0\n\tsoundradius\t0\n\tsoundflags\t0\n\ttextcolor\t0 0 0 1\n\tselected\t0\n\tselector\t00000000-0000-0000-0000-000000000000\n\tusephysics\t0\n\trotate_x\t1\n\trotate_y\t1\n\trotate_z\t1\n\tphantom\t0\n\tremote_script_access_pin\t0\n\tvolume_detect\t0\n\tblock_grabs\t0\n\tdie_at_edge\t0\n\treturn_at_edge\t0\n\ttemporary\t0\n\tsandbox\t0\n\tsandboxhome\t0\t0\t0\n\tshape 0\n\t{\n\t\tpath 0\n\t\t{\n\t\t\tcurve\t16\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\tscale_x\t1\n\t\t\tscale_y\t1\n\t\t\tshear_x\t0\n\t\t\tshear_y\t0\n\t\t\ttwist\t0\n\t\t\ttwist_begin\t0\n\t\t\tradius_offset\t0\n\t\t\ttaper_x\t0\n\t\t\ttaper_y\t0\n\t\t\trevolutions\t1\n\t\t\tskew\t0\n\t\t}\n\t\tprofile 0\n\t\t{\n\t\t\tcurve\t1\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\thollow\t0\n\t\t}\n\t}\n\tfaces\t6\n\t{\n\t\timageid\tddde1ffc-678b-3cda-1748-513086bdf01b\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tddde1ffc-678b-3cda-1748-513086bdf01b\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t-1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\tps_next_crc\t1\n\tgpw_bias\t1\n\tip\t0\n\tcomplete\tTRUE\n\tdelay\t50000\n\tnextstart\t0\n\tbirthtime\t1061088050622956\n\treztime\t1094866329019785\n\tparceltime\t1133568981980596\n\ttax_rate\t1.00084\n\tscratchpad\t0\n\t{\n\t\n\t}\n\tsale_info\t0\n\t{\n\t\tsale_type\tnot\n\t\tsale_price\t10\n\t}\n\tcorrect_family_id\t00000000-0000-0000-0000-000000000000\n\thas_rezzed\t0\n\tpre_link_base_mask\t7fffffff\n\tlinked \tchild\n\tdefault_pay_price\t-2\t1\t5\t10\t20\n}\n{'task_id':u61fa7364-e151-0597-774c-523312dae31b}\n{\n\tname\tObject|\n\tpermissions 0\n\t{\n\t\tbase_mask\t7fffffff\n\t\towner_mask\t7fffffff\n\t\tgroup_mask\t00000000\n\t\teveryone_mask\t00000000\n\t\tnext_owner_mask\t7fffffff\n\t\tcreator_id\t13fd9595-a47b-4d64-a5fb-6da645f038e0\n\t\towner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tlast_owner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tgroup_id\t00000000-0000-0000-0000-000000000000\n\t}\n\tlocal_id\t217444922\n\ttotal_crc\t324\n\ttype\t2\n\ttask_valid\t2\n\ttravel_access\t13\n\tdisplayopts\t2\n\tdisplaytype\tv\n\tpos\t-0.367110789\t0.00780026987\t-0.566269755\n\toldpos\t150.115005\t25.8479004\t8.18669987\n\trotation\t0.47332942485809326171875\t-0.380102097988128662109375\t-0.5734078884124755859375\t0.550168216228485107421875\n\tchildpos\t-0.00499999989\t-0.0370000005\t0.305000007\n\tchildrot\t-0.736649334430694580078125\t-0.03042060509324073791503906\t-0.02784589119255542755126953\t0.67501628398895263671875\n\tscale\t0.074629\t0.289956\t0.01\n\tsit_offset\t0\t0\t0\n\tcamera_eye_offset\t0\t0\t0\n\tcamera_at_offset\t0\t0\t0\n\tsit_quat\t0\t0\t0\t1\n\tsit_hint\t0\n\tstate\t160\n\tmaterial\t3\n\tsoundid\t00000000-0000-0000-0000-000000000000\n\tsoundgain\t0\n\tsoundradius\t0\n\tsoundflags\t0\n\ttextcolor\t0 0 0 1\n\tselected\t0\n\tselector\t00000000-0000-0000-0000-000000000000\n\tusephysics\t0\n\trotate_x\t1\n\trotate_y\t1\n\trotate_z\t1\n\tphantom\t0\n\tremote_script_access_pin\t0\n\tvolume_detect\t0\n\tblock_grabs\t0\n\tdie_at_edge\t0\n\treturn_at_edge\t0\n\ttemporary\t0\n\tsandbox\t0\n\tsandboxhome\t0\t0\t0\n\tshape 0\n\t{\n\t\tpath 0\n\t\t{\n\t\t\tcurve\t16\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\tscale_x\t1\n\t\t\tscale_y\t1\n\t\t\tshear_x\t0\n\t\t\tshear_y\t0\n\t\t\ttwist\t0\n\t\t\ttwist_begin\t0\n\t\t\tradius_offset\t0\n\t\t\ttaper_x\t0\n\t\t\ttaper_y\t0\n\t\t\trevolutions\t1\n\t\t\tskew\t0\n\t\t}\n\t\tprofile 0\n\t\t{\n\t\t\tcurve\t1\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\thollow\t0\n\t\t}\n\t}\n\tfaces\t6\n\t{\n\t\timageid\tddde1ffc-678b-3cda-1748-513086bdf01b\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tddde1ffc-678b-3cda-1748-513086bdf01b\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t-1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\tps_next_crc\t1\n\tgpw_bias\t1\n\tip\t0\n\tcomplete\tTRUE\n\tdelay\t50000\n\tnextstart\t0\n\tbirthtime\t1061087839248891\n\treztime\t1094866329020800\n\tparceltime\t1133568981981983\n\ttax_rate\t1.00084\n\tscratchpad\t0\n\t{\n\t\n\t}\n\tsale_info\t0\n\t{\n\t\tsale_type\tnot\n\t\tsale_price\t10\n\t}\n\tcorrect_family_id\t00000000-0000-0000-0000-000000000000\n\thas_rezzed\t0\n\tpre_link_base_mask\t7fffffff\n\tlinked \tchild\n\tdefault_pay_price\t-2\t1\t5\t10\t20\n}\n{'task_id':ub8d68643-7dd8-57af-0d24-8790032aed0c}\n{\n\tname\tObject|\n\tpermissions 0\n\t{\n\t\tbase_mask\t7fffffff\n\t\towner_mask\t7fffffff\n\t\tgroup_mask\t00000000\n\t\teveryone_mask\t00000000\n\t\tnext_owner_mask\t7fffffff\n\t\tcreator_id\t13fd9595-a47b-4d64-a5fb-6da645f038e0\n\t\towner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tlast_owner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tgroup_id\t00000000-0000-0000-0000-000000000000\n\t}\n\tlocal_id\t217444923\n\ttotal_crc\t235\n\ttype\t2\n\ttask_valid\t2\n\ttravel_access\t13\n\tdisplayopts\t2\n\tdisplaytype\tv\n\tpos\t-0.120029509\t-0.00284469454\t-0.0302077383\n\toldpos\t150.710999\t25.8584995\t8.19172001\n\trotation\t0.145459949970245361328125\t-0.1646589934825897216796875\t0.659558117389678955078125\t-0.718826770782470703125\n\tchildpos\t0\t-0.182999998\t-0.26699999\n\tchildrot\t0.991444766521453857421875\t3.271923924330621957778931e-05\t-0.0002416197530692443251609802\t0.1305266767740249633789062\n\tscale\t0.0382982\t0.205957\t0.368276\n\tsit_offset\t0\t0\t0\n\tcamera_eye_offset\t0\t0\t0\n\tcamera_at_offset\t0\t0\t0\n\tsit_quat\t0\t0\t0\t1\n\tsit_hint\t0\n\tstate\t160\n\tmaterial\t3\n\tsoundid\t00000000-0000-0000-0000-000000000000\n\tsoundgain\t0\n\tsoundradius\t0\n\tsoundflags\t0\n\ttextcolor\t0 0 0 1\n\tselected\t0\n\tselector\t00000000-0000-0000-0000-000000000000\n\tusephysics\t0\n\trotate_x\t1\n\trotate_y\t1\n\trotate_z\t1\n\tphantom\t0\n\tremote_script_access_pin\t0\n\tvolume_detect\t0\n\tblock_grabs\t0\n\tdie_at_edge\t0\n\treturn_at_edge\t0\n\ttemporary\t0\n\tsandbox\t0\n\tsandboxhome\t0\t0\t0\n\tshape 0\n\t{\n\t\tpath 0\n\t\t{\n\t\t\tcurve\t32\n\t\t\tbegin\t0.3\n\t\t\tend\t0.65\n\t\t\tscale_x\t1\n\t\t\tscale_y\t0.05\n\t\t\tshear_x\t0\n\t\t\tshear_y\t0\n\t\t\ttwist\t0\n\t\t\ttwist_begin\t0\n\t\t\tradius_offset\t0\n\t\t\ttaper_x\t0\n\t\t\ttaper_y\t0\n\t\t\trevolutions\t1\n\t\t\tskew\t0\n\t\t}\n\t\tprofile 0\n\t\t{\n\t\t\tcurve\t0\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\thollow\t0\n\t\t}\n\t}\n\tfaces\t3\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1.57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1.57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1.57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\tps_next_crc\t1\n\tgpw_bias\t1\n\tip\t0\n\tcomplete\tTRUE\n\tdelay\t50000\n\tnextstart\t0\n\tbirthtime\t1061087534454174\n\treztime\t1094866329021741\n\tparceltime\t1133568981982889\n\ttax_rate\t1.00326\n\tscratchpad\t0\n\t{\n\t\n\t}\n\tsale_info\t0\n\t{\n\t\tsale_type\tnot\n\t\tsale_price\t10\n\t}\n\tcorrect_family_id\t00000000-0000-0000-0000-000000000000\n\thas_rezzed\t0\n\tpre_link_base_mask\t7fffffff\n\tlinked \tchild\n\tdefault_pay_price\t-2\t1\t5\t10\t20\n}\n{'task_id':ue4b19200-9d33-962f-c8c5-6f25be3a3fd0}\n{\n\tname\tApotheosis_Immolaine_tail|\n\tpermissions 0\n\t{\n\t\tbase_mask\t7fffffff\n\t\towner_mask\t7fffffff\n\t\tgroup_mask\t00000000\n\t\teveryone_mask\t00000000\n\t\tnext_owner_mask\t7fffffff\n\t\tcreator_id\t13fd9595-a47b-4d64-a5fb-6da645f038e0\n\t\towner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tlast_owner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tgroup_id\t00000000-0000-0000-0000-000000000000\n\t}\n\tlocal_id\t217444924\n\ttotal_crc\t675\n\ttype\t1\n\ttask_valid\t2\n\ttravel_access\t13\n\tdisplayopts\t2\n\tdisplaytype\tv\n\tpos\t-0.34780401\t-0.00968400016\t-0.260098994\n\toldpos\t0\t0\t0\n\trotation\t0.73164522647857666015625\t-0.67541944980621337890625\t-0.07733880728483200073242188\t0.05022468417882919311523438\n\tvelocity\t0\t0\t0\n\tangvel\t0\t0\t0\n\tscale\t0.0382982\t0.32228\t0.383834\n\tsit_offset\t0\t0\t0\n\tcamera_eye_offset\t0\t0\t0\n\tcamera_at_offset\t0\t0\t0\n\tsit_quat\t0\t0\t0\t1\n\tsit_hint\t0\n\tstate\t160\n\tmaterial\t3\n\tsoundid\t00000000-0000-0000-0000-000000000000\n\tsoundgain\t0\n\tsoundradius\t0\n\tsoundflags\t0\n\ttextcolor\t0 0 0 1\n\tselected\t0\n\tselector\t00000000-0000-0000-0000-000000000000\n\tusephysics\t0\n\trotate_x\t1\n\trotate_y\t1\n\trotate_z\t1\n\tphantom\t0\n\tremote_script_access_pin\t0\n\tvolume_detect\t0\n\tblock_grabs\t0\n\tdie_at_edge\t0\n\treturn_at_edge\t0\n\ttemporary\t0\n\tsandbox\t0\n\tsandboxhome\t0\t0\t0\n\tshape 0\n\t{\n\t\tpath 0\n\t\t{\n\t\t\tcurve\t32\n\t\t\tbegin\t0.3\n\t\t\tend\t0.65\n\t\t\tscale_x\t1\n\t\t\tscale_y\t0.05\n\t\t\tshear_x\t0\n\t\t\tshear_y\t0\n\t\t\ttwist\t0\n\t\t\ttwist_begin\t0\n\t\t\tradius_offset\t0\n\t\t\ttaper_x\t0\n\t\t\ttaper_y\t0\n\t\t\trevolutions\t1\n\t\t\tskew\t0\n\t\t}\n\t\tprofile 0\n\t\t{\n\t\t\tcurve\t0\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\thollow\t0\n\t\t}\n\t}\n\tfaces\t3\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1.57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1.57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1.57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\tps_next_crc\t1\n\tgpw_bias\t1\n\tip\t0\n\tcomplete\tTRUE\n\tdelay\t50000\n\tnextstart\t0\n\tbirthtime\t1061087463950186\n\treztime\t1094866329022555\n\tparceltime\t1133568981984359\n\tdescription\t(No Description)|\n\ttax_rate\t1.01736\n\tnamevalue\tAttachPt U32 RW S 10\n\tnamevalue\tAttachmentOrientation VEC3 RW DS -3.110088, -0.182018, 1.493795\n\tnamevalue\tAttachmentOffset VEC3 RW DS -0.347804, -0.009684, -0.260099\n\tnamevalue\tAttachItemID STRING RW SV 20f36c3a-b44b-9bc7-87f3-018bfdfc8cda\n\tscratchpad\t0\n\t{\n\t\n\t}\n\tsale_info\t0\n\t{\n\t\tsale_type\tnot\n\t\tsale_price\t10\n\t}\n\torig_asset_id\t8747acbc-d391-1e59-69f1-41d06830e6c0\n\torig_item_id\t20f36c3a-b44b-9bc7-87f3-018bfdfc8cda\n\tfrom_task_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\tcorrect_family_id\t00000000-0000-0000-0000-000000000000\n\thas_rezzed\t0\n\tpre_link_base_mask\t7fffffff\n\tlinked \tlinked\n\tdefault_pay_price\t-2\t1\t5\t10\t20\n}\n" */ diff --git a/indra/test/message_tut.cpp b/indra/test/message_tut.cpp index aa23699de0..8718360f0c 100644 --- a/indra/test/message_tut.cpp +++ b/indra/test/message_tut.cpp @@ -28,7 +28,7 @@ #include <tut/tut.hpp> #include "linden_common.h" #include "lltut.h" - +#include "llhttpconstants.h" #include "llapr.h" #include "llmessageconfig.h" #include "llsdserialize.h" diff --git a/indra/viewer_components/login/CMakeLists.txt b/indra/viewer_components/login/CMakeLists.txt index c152f7c0a6..3bedeb7292 100644 --- a/indra/viewer_components/login/CMakeLists.txt +++ b/indra/viewer_components/login/CMakeLists.txt @@ -10,6 +10,8 @@ include(LLCommon) include(LLMath) include(LLXML) include(Boost) +include(LLCoreHttp) +include(LLMessage) include_directories( ${LLCOMMON_INCLUDE_DIRS} @@ -42,12 +44,14 @@ add_library(lllogin ) target_link_libraries(lllogin + ${LLMESSAGE_LIBRARIES} + ${LLCOREHTTP_LIBRARIES} ${LLCOMMON_LIBRARIES} ${LLMATH_LIBRARIES} ${LLXML_LIBRARIES} - ${BOOST_CONTEXT_LIBRARY} ${BOOST_THREAD_LIBRARY} ${BOOST_COROUTINE_LIBRARY} + ${BOOST_CONTEXT_LIBRARY} ${BOOST_SYSTEM_LIBRARY} ) @@ -58,7 +62,7 @@ if(LL_TESTS) set_source_files_properties( lllogin.cpp PROPERTIES - LL_TEST_ADDITIONAL_LIBRARIES "${BOOST_COROUTINE_LIBRARY};${BOOST_CONTEXT_LIBRARY};${BOOST_THREAD_LIBRARY};${BOOST_SYSTEM_LIBRARY}" + LL_TEST_ADDITIONAL_LIBRARIES "${LLMESSAGE_LIBRARIES};${LLCOREHTTP_LIBRARIES};${LLCOMMON_LIBRARIES};${BOOST_COROUTINE_LIBRARY};${BOOST_CONTEXT_LIBRARY};${BOOST_THREAD_LIBRARY};${BOOST_SYSTEM_LIBRARY}" ) LL_ADD_PROJECT_UNIT_TESTS(lllogin "${lllogin_TEST_SOURCE_FILES}") diff --git a/indra/viewer_components/login/lllogin.cpp b/indra/viewer_components/login/lllogin.cpp index b8408a6fb4..53d4acc9e0 100644 --- 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 loginCoro(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::loginCoro, 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::loginCoro(std::string uri, LLSD login_params) { try { @@ -137,50 +136,13 @@ 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 LLEventStream replyPump("SRVreply", true); // Should be an array of one or more uri strings. - LLSD rewrittenURIs; - { - LLEventTimeout filter(replyPump); - sendProgressEvent("offline", "srvrequest"); - - // Request SRV record. - LL_DEBUGS("LLLogin") << "Requesting SRV record from " << uri << LL_ENDL; - - // *NOTE:Mani - Completely arbitrary default timeout value for SRV request. - F32 seconds_to_timeout = 5.0f; - if(login_params.has("cfg_srv_timeout")) - { - seconds_to_timeout = login_params["cfg_srv_timeout"].asReal(); - } - - // If the SRV request times out (e.g. EXT-3934), simulate response: an - // array containing our original URI. - LLSD fakeResponse(LLSD::emptyArray()); - fakeResponse.append(uri); - filter.eventAfter(seconds_to_timeout, fakeResponse); - - std::string srv_pump_name = "LLAres"; - if(login_params.has("cfg_srv_pump")) - { - srv_pump_name = login_params["cfg_srv_pump"].asString(); - } - - // Make request - LLSD request; - request["op"] = "rewriteURI"; - request["uri"] = uri; - request["reply"] = replyPump.getName(); - rewrittenURIs = postAndWait(self, 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 - LLEventPump& xmlrpcPump(LLEventPumps::instance().obtain("LLXMLRPCTransaction")); // EXT-4193: use a DIFFERENT reply pump than for the SRV request. We used // to share them -- but the EXT-3934 fix made it possible for an abandoned @@ -191,107 +153,103 @@ void LLLogin::Impl::login_(LLCoros::self& self, std::string uri, LLSD login_para // Because of possible redirect responses, we may make more than one // attempt per rewrittenURIs entry. LLSD::Integer attempts = 0; - for (LLSD::array_const_iterator urit(rewrittenURIs.beginArray()), - urend(rewrittenURIs.endArray()); - urit != urend; ++urit) + + LLSD request(login_params); + request["reply"] = loginReplyPump.getName(); + request["uri"] = uri; + std::string status; + + // Loop back to here if login attempt redirects to a different + // request["uri"] + for (;;) { - LLSD request(login_params); - request["reply"] = loginReplyPump.getName(); - request["uri"] = *urit; - std::string status; - - // Loop back to here if login attempt redirects to a different - // request["uri"] - for (;;) + ++attempts; + LLSD progress_data; + progress_data["attempt"] = attempts; + progress_data["request"] = request; + if(progress_data["request"].has("params") + && progress_data["request"]["params"].has("passwd")) + { + progress_data["request"]["params"]["passwd"] = "*******"; + } + sendProgressEvent("offline", "authenticating", progress_data); + + // We expect zero or more "Downloading" status events, followed by + // exactly one event with some other status. Use postAndSuspend() the + // first time, because -- at least in unit-test land -- it's + // possible for the reply to arrive before the post() call + // returns. Subsequent responses, of course, must be awaited + // without posting again. + for (mAuthResponse = validateResponse(loginReplyPump.getName(), + llcoro::postAndSuspend(request, xmlrpcPump, loginReplyPump, "reply")); + mAuthResponse["status"].asString() == "Downloading"; + mAuthResponse = validateResponse(loginReplyPump.getName(), + llcoro::suspendUntilEventOn(loginReplyPump))) { - ++attempts; - LLSD progress_data; - progress_data["attempt"] = attempts; - progress_data["request"] = request; - if(progress_data["request"].has("params") - && progress_data["request"]["params"].has("passwd")) - { - progress_data["request"]["params"]["passwd"] = "*******"; - } - sendProgressEvent("offline", "authenticating", progress_data); - - // We expect zero or more "Downloading" status events, followed by - // exactly one event with some other status. Use postAndWait() the - // first time, because -- at least in unit-test land -- it's - // possible for the reply to arrive before the post() call - // returns. Subsequent responses, of course, must be awaited - // without posting again. - for (mAuthResponse = validateResponse(loginReplyPump.getName(), - postAndWait(self, request, xmlrpcPump, loginReplyPump, "reply")); - mAuthResponse["status"].asString() == "Downloading"; - mAuthResponse = validateResponse(loginReplyPump.getName(), - waitForEventOn(self, loginReplyPump))) - { - // Still Downloading -- send progress update. - sendProgressEvent("offline", "downloading"); - } + // Still Downloading -- send progress update. + sendProgressEvent("offline", "downloading"); + } - LL_DEBUGS("LLLogin") << "Auth Response: " << mAuthResponse << LL_ENDL; - status = mAuthResponse["status"].asString(); - - // Okay, we've received our final status event for this - // request. Unless we got a redirect response, break the retry - // loop for the current rewrittenURIs entry. - if (!(status == "Complete" && - mAuthResponse["responses"]["login"].asString() == "indeterminate")) - { - break; - } - - sendProgressEvent("offline", "indeterminate", mAuthResponse["responses"]); - - // Here the login service at the current URI is redirecting us - // to some other URI ("indeterminate" -- why not "redirect"?). - // The response should contain another uri to try, with its - // own auth method. - request["uri"] = mAuthResponse["responses"]["next_url"].asString(); - request["method"] = mAuthResponse["responses"]["next_method"].asString(); - } // loop back to try the redirected URI - - // Here we're done with redirects for the current rewrittenURIs - // entry. - if (status == "Complete") + LL_DEBUGS("LLLogin") << "Auth Response: " << mAuthResponse << LL_ENDL; + status = mAuthResponse["status"].asString(); + + // Okay, we've received our final status event for this + // request. Unless we got a redirect response, break the retry + // loop for the current rewrittenURIs entry. + if (!(status == "Complete" && + mAuthResponse["responses"]["login"].asString() == "indeterminate")) { - // StatusComplete does not imply auth success. Check the - // actual outcome of the request. We've already handled the - // "indeterminate" case in the loop above. - if (mAuthResponse["responses"]["login"].asString() == "true") - { - sendProgressEvent("online", "connect", mAuthResponse["responses"]); - } - else - { - sendProgressEvent("offline", "fail.login", mAuthResponse["responses"]); - } - return; // Done! + break; } - /* Sometimes we end with "Started" here. Slightly slow server? - * Seems to be ok to just skip it. Otherwise we'd error out and crash in the if below. - */ - if( status == "Started") - { - LL_DEBUGS("LLLogin") << mAuthResponse << LL_ENDL; - continue; - } + sendProgressEvent("offline", "indeterminate", mAuthResponse["responses"]); - // If we don't recognize status at all, trouble - if (! (status == "CURLError" - || status == "XMLRPCError" - || status == "OtherError")) + // Here the login service at the current URI is redirecting us + // to some other URI ("indeterminate" -- why not "redirect"?). + // The response should contain another uri to try, with its + // own auth method. + request["uri"] = mAuthResponse["responses"]["next_url"].asString(); + request["method"] = mAuthResponse["responses"]["next_method"].asString(); + } // loop back to try the redirected URI + + // Here we're done with redirects for the current rewrittenURIs + // entry. + if (status == "Complete") + { + // StatusComplete does not imply auth success. Check the + // actual outcome of the request. We've already handled the + // "indeterminate" case in the loop above. + if (mAuthResponse["responses"]["login"].asString() == "true") + { + sendProgressEvent("online", "connect", mAuthResponse["responses"]); + } + else { - LL_ERRS("LLLogin") << "Unexpected status from " << xmlrpcPump.getName() << " pump: " - << mAuthResponse << LL_ENDL; - return; + sendProgressEvent("offline", "fail.login", mAuthResponse["responses"]); } + return; // Done! + } - // Here status IS one of the errors tested above. - } // Retry if there are any more rewrittenURIs. +// /* Sometimes we end with "Started" here. Slightly slow server? +// * Seems to be ok to just skip it. Otherwise we'd error out and crash in the if below. +// */ +// if( status == "Started") +// { +// LL_DEBUGS("LLLogin") << mAuthResponse << LL_ENDL; +// continue; +// } + + // If we don't recognize status at all, trouble + if (! (status == "CURLError" + || status == "XMLRPCError" + || status == "OtherError")) + { + LL_ERRS("LLLogin") << "Unexpected status from " << xmlrpcPump.getName() << " pump: " + << mAuthResponse << LL_ENDL; + return; + } + + // Here status IS one of the errors tested above. // Here we got through all the rewrittenURIs without succeeding. Tell // caller this didn't work out so well. Of course, the only failure data @@ -352,29 +310,8 @@ LLEventPump& LLLogin::getEventPump() // The list associates to event with the original idle_startup() 'STATE'. -// Rewrite URIs - // State_LOGIN_AUTH_INIT -// Given a vector of login uris (usually just one), perform a dns lookup for the -// SRV record from each URI. I think this is used to distribute login requests to -// a single URI to multiple hosts. -// This is currently a synchronous action. (See LLSRV::rewriteURI() implementation) -// On dns lookup error the output uris == the input uris. -// -// Input: A vector of login uris -// Output: A vector of login uris -// -// Code: -// std::vector<std::string> uris; -// LLViewerLogin::getInstance()->getLoginURIs(uris); -// std::vector<std::string>::const_iterator iter, end; -// for (iter = uris.begin(), end = uris.end(); iter != end; ++iter) -// { -// std::vector<std::string> rewritten; -// rewritten = LLSRV::rewriteURI(*iter); -// sAuthUris.insert(sAuthUris.end(), -// rewritten.begin(), rewritten.end()); -// } -// sAuthUriNum = 0; +// Setup login +// State_LOGIN_AUTH_INIT // Authenticate // STATE_LOGIN_AUTHENTICATE diff --git a/indra/viewer_components/login/tests/lllogin_test.cpp b/indra/viewer_components/login/tests/lllogin_test.cpp index 58bf371a04..e96c495446 100644 --- a/indra/viewer_components/login/tests/lllogin_test.cpp +++ b/indra/viewer_components/login/tests/lllogin_test.cpp @@ -96,61 +96,6 @@ public: } }; -class LLAresListener: public LLEventTrackable -{ - std::string mName; - LLSD mEvent; - bool mImmediateResponse; - bool mMultipleURIResponse; - Debug mDebug; - -public: - LLAresListener(const std::string& name, - bool i = false, - bool m = false - ) : - mName(name), - mImmediateResponse(i), - mMultipleURIResponse(m), - mDebug(stringize(*this)) - {} - - bool handle_event(const LLSD& event) - { - mDebug(STRINGIZE("LLAresListener called!: " << event)); - mEvent = event; - if(mImmediateResponse) - { - sendReply(); - } - return false; - } - - void sendReply() - { - if(mEvent["op"].asString() == "rewriteURI") - { - LLSD result; - if(mMultipleURIResponse) - { - result.append(LLSD("login.foo.com")); - } - result.append(mEvent["uri"]); - LLEventPumps::instance().obtain(mEvent["reply"]).post(result); - } - } - - LLBoundListener listenTo(LLEventPump& pump) - { - return pump.listen(mName, boost::bind(&LLAresListener::handle_event, this, _1)); - } - - friend std::ostream& operator<<(std::ostream& out, const LLAresListener& listener) - { - return out << "LLAresListener(" << listener.mName << ')'; - } -}; - class LLXMLRPCListener: public LLEventTrackable { std::string mName; @@ -232,16 +177,12 @@ namespace tut void llviewerlogin_object::test<1>() { DEBUG; - // Testing login with immediate responses from Ares and XMLPRC - // The response from both requests will come before the post request exits. + // Testing login with an immediate response from XMLPRC + // The response will come before the post request exits. // This tests an edge case of the login state handling. - LLEventStream llaresPump("LLAres"); // Dummy LLAres pump. LLEventStream xmlrpcPump("LLXMLRPCTransaction"); // Dummy XMLRPC pump bool respond_immediately = true; - // Have 'dummy ares' respond immediately. - LLAresListener dummyLLAres("dummy_llares", respond_immediately); - dummyLLAres.listenTo(llaresPump); // Have dummy XMLRPC respond immediately. LLXMLRPCListener dummyXMLRPC("dummy_xmlrpc", respond_immediately); @@ -266,109 +207,12 @@ namespace tut void llviewerlogin_object::test<2>() { DEBUG; - // Tests a successful login in with delayed responses. - // Also includes 'failure' that cause the login module - // to re-attempt connection, once from a basic failure - // and once from the 'indeterminate' response. - - set_test_name("LLLogin multiple srv uris w/ success"); - - // Testing normal login procedure. - LLEventStream llaresPump("LLAres"); // Dummy LLAres pump. - LLEventStream xmlrpcPump("LLXMLRPCTransaction"); // Dummy XMLRPC pump - - bool respond_immediately = false; - bool multiple_addresses = true; - LLAresListener dummyLLAres("dummy_llares", respond_immediately, multiple_addresses); - dummyLLAres.listenTo(llaresPump); - - LLXMLRPCListener dummyXMLRPC("dummy_xmlrpc"); - dummyXMLRPC.listenTo(xmlrpcPump); - - LLLogin login; - - LoginListener listener("test_ear"); - listener.listenTo(login.getEventPump()); - - LLSD credentials; - credentials["first"] = "foo"; - credentials["last"] = "bar"; - credentials["passwd"] = "secret"; - - login.connect("login.bar.com", credentials); - - ensure_equals("SRV state", listener.lastEvent()["change"].asString(), "srvrequest"); - - dummyLLAres.sendReply(); - - // Test Authenticating State prior to first response. - ensure_equals("Auth state 1", listener.lastEvent()["change"].asString(), "authenticating"); - ensure_equals("Attempt 1", listener.lastEvent()["data"]["attempt"].asInteger(), 1); - ensure_equals("URI 1", listener.lastEvent()["data"]["request"]["uri"].asString(), "login.foo.com"); - - // First send emulated LLXMLRPCListener failure, - // this should return login to the authenticating step and increase the attempt - // count. - LLSD data; - data["status"] = "OtherError"; - data["errorcode"] = 0; - data["error"] = "dummy response"; - data["transfer_rate"] = 0; - dummyXMLRPC.setResponse(data); - dummyXMLRPC.sendReply(); - - ensure_equals("Fail back to authenticate 1", listener.lastEvent()["change"].asString(), "authenticating"); - ensure_equals("Attempt 2", listener.lastEvent()["data"]["attempt"].asInteger(), 2); - ensure_equals("URI 2", listener.lastEvent()["data"]["request"]["uri"].asString(), "login.bar.com"); - - // Now send the 'indeterminate' response. - data.clear(); - data["status"] = "Complete"; // StatusComplete - data["errorcode"] = 0; - data["error"] = "dummy response"; - data["transfer_rate"] = 0; - data["responses"]["login"] = "indeterminate"; - data["responses"]["next_url"] = "login.indeterminate.com"; - data["responses"]["next_method"] = "test_login_method"; - dummyXMLRPC.setResponse(data); - dummyXMLRPC.sendReply(); - - ensure_equals("Fail back to authenticate 2", listener.lastEvent()["change"].asString(), "authenticating"); - ensure_equals("Attempt 3", listener.lastEvent()["data"]["attempt"].asInteger(), 3); - ensure_equals("URI 3", listener.lastEvent()["data"]["request"]["uri"].asString(), "login.indeterminate.com"); - ensure_equals("Method 3", listener.lastEvent()["data"]["request"]["method"].asString(), "test_login_method"); - - // Finally let the auth succeed. - data.clear(); - data["status"] = "Complete"; // StatusComplete - data["errorcode"] = 0; - data["error"] = "dummy response"; - data["transfer_rate"] = 0; - data["responses"]["login"] = "true"; - dummyXMLRPC.setResponse(data); - dummyXMLRPC.sendReply(); - - ensure_equals("Success state", listener.lastEvent()["state"].asString(), "online"); - - login.disconnect(); - - ensure_equals("Disconnected state", listener.lastEvent()["state"].asString(), "offline"); - } - - template<> template<> - void llviewerlogin_object::test<3>() - { - DEBUG; // Test completed response, that fails to login. set_test_name("LLLogin valid response, failure (eg. bad credentials)"); // Testing normal login procedure. - LLEventStream llaresPump("LLAres"); // Dummy LLAres pump. LLEventStream xmlrpcPump("LLXMLRPCTransaction"); // Dummy XMLRPC pump - LLAresListener dummyLLAres("dummy_llares"); - dummyLLAres.listenTo(llaresPump); - LLXMLRPCListener dummyXMLRPC("dummy_xmlrpc"); dummyXMLRPC.listenTo(xmlrpcPump); @@ -383,10 +227,6 @@ namespace tut login.connect("login.bar.com", credentials); - ensure_equals("SRV state", listener.lastEvent()["change"].asString(), "srvrequest"); - - dummyLLAres.sendReply(); - ensure_equals("Auth state", listener.lastEvent()["change"].asString(), "authenticating"); // Send the failed auth request reponse @@ -403,19 +243,15 @@ namespace tut } template<> template<> - void llviewerlogin_object::test<4>() + void llviewerlogin_object::test<3>() { DEBUG; // Test incomplete response, that end the attempt. set_test_name("LLLogin valid response, failure (eg. bad credentials)"); // Testing normal login procedure. - LLEventStream llaresPump("LLAres"); // Dummy LLAres pump. LLEventStream xmlrpcPump("LLXMLRPCTransaction"); // Dummy XMLRPC pump - LLAresListener dummyLLAres("dummy_llares"); - dummyLLAres.listenTo(llaresPump); - LLXMLRPCListener dummyXMLRPC("dummy_xmlrpc"); dummyXMLRPC.listenTo(xmlrpcPump); @@ -430,10 +266,6 @@ namespace tut login.connect("login.bar.com", credentials); - ensure_equals("SRV state", listener.lastEvent()["change"].asString(), "srvrequest"); - - dummyLLAres.sendReply(); - ensure_equals("Auth state", listener.lastEvent()["change"].asString(), "authenticating"); // Send the failed auth request reponse @@ -449,17 +281,13 @@ namespace tut } template<> template<> - void llviewerlogin_object::test<5>() + void llviewerlogin_object::test<4>() { DEBUG; // Test SRV request timeout. set_test_name("LLLogin SRV timeout testing"); // Testing normal login procedure. - LLEventStream llaresPump("LLAres"); // Dummy LLAres pump. - - LLAresListener dummyLLAres("dummy_llares"); - dummyLLAres.listenTo(llaresPump); LLLogin login; LoginListener listener("test_ear"); @@ -473,26 +301,15 @@ namespace tut login.connect("login.bar.com", credentials); - ensure_equals("SRV State", listener.lastEvent()["change"].asString(), "srvrequest"); - // Get the mainloop eventpump, which needs a pinging in order to drive the // SRV timeout. LLEventPump& mainloop(LLEventPumps::instance().obtain("mainloop")); LLSD frame_event; mainloop.post(frame_event); - // In this state we have NOT sent a reply from LLAresListener -- in - // fact there's no such object. Nonetheless, we expect the timeout to - // have stepped the login module forward to try to authenticate with - // the original URI. ensure_equals("Auth state", listener.lastEvent()["change"].asString(), "authenticating"); ensure_equals("Attempt", listener.lastEvent()["data"]["attempt"].asInteger(), 1); ensure_equals("URI", listener.lastEvent()["data"]["request"]["uri"].asString(), "login.bar.com"); - // EXT-4193: if the SRV reply isn't lost but merely late, and if it - // arrives just at the moment we're expecting the XMLRPC reply, the - // original code got confused and crashed. Drive that case here. We - // observe that without the fix, this call DOES repro. - dummyLLAres.sendReply(); } } diff --git a/indra/viewer_components/updater/CMakeLists.txt b/indra/viewer_components/updater/CMakeLists.txt index 61fd4220e0..73e18aacb3 100644 --- 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} @@ -60,25 +63,36 @@ add_library(llupdaterservice target_link_libraries(llupdaterservice ${LLCOMMON_LIBRARIES} ${LLMESSAGE_LIBRARIES} + ${LLCOREHTTP_LIBRARIES} ${LLPLUGIN_LIBRARIES} ${LLVFS_LIBRARIES} ) if(LL_TESTS) +if (NOT LINUX) SET(llupdater_service_TEST_SOURCE_FILES llupdaterservice.cpp ) +set(test_libs + ${LLCOMMON_LIBRARIES} + ${BOOST_COROUTINE_LIBRARY} + ${BOOST_CONTEXT_LIBRARY} + ${BOOST_THREAD_LIBRARY} + ${BOOST_SYSTEM_LIBRARY}) + +set_source_files_properties( + llupdaterservice.cpp + PROPERTIES + LL_TEST_ADDITIONAL_LIBRARIES ${test_libs} # *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}") + LL_ADD_PROJECT_UNIT_TESTS(llupdaterservice "${llupdater_service_TEST_SOURCE_FILES}" ${test_libs}) +endif (NOT LINUX) endif(LL_TESTS) set(UPDATER_INCLUDE_DIRS diff --git a/indra/viewer_components/updater/llupdatechecker.cpp b/indra/viewer_components/updater/llupdatechecker.cpp index 8da4f88905..1bb5e95740 100644 --- 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->getAndSuspend(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 100644 --- 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/viewer_components/updater/llupdatedownloader.cpp b/indra/viewer_components/updater/llupdatedownloader.cpp index f868e5cc2c..382689afa0 100644 --- a/indra/viewer_components/updater/llupdatedownloader.cpp +++ b/indra/viewer_components/updater/llupdatedownloader.cpp @@ -26,7 +26,7 @@ #include "linden_common.h" #include "llupdatedownloader.h" - +#include "httpcommon.h" #include <stdexcept> #include <boost/format.hpp> #include <boost/lexical_cast.hpp> @@ -39,7 +39,6 @@ #include "llsdserialize.h" #include "llthread.h" #include "llupdaterservice.h" -#include "llcurl.h" class LLUpdateDownloader::Implementation: public LLThread @@ -57,7 +56,7 @@ public: bool isDownloading(void); size_t onHeader(void * header, size_t size); size_t onBody(void * header, size_t size); - int onProgress(double downloadSize, double bytesDownloaded); + int onProgress(curl_off_t downloadSize, curl_off_t bytesDownloaded); void resume(void); void setBandwidthLimit(U64 bytesPerSecond); @@ -65,7 +64,7 @@ private: curl_off_t mBandwidthLimit; bool mCancelled; LLUpdateDownloader::Client & mClient; - CURL * mCurl; + LLCore::LLHttp::CURL_ptr mCurl; LLSD mDownloadData; llofstream mDownloadStream; unsigned char mDownloadPercent; @@ -175,11 +174,11 @@ namespace { } - int progress_callback(void * downloader, - double dowloadTotal, - double downloadNow, - double uploadTotal, - double uploadNow) + int xferinfo_callback(void * downloader, + curl_off_t dowloadTotal, + curl_off_t downloadNow, + curl_off_t uploadTotal, + curl_off_t uploadNow) { return reinterpret_cast<LLUpdateDownloader::Implementation *>(downloader)-> onProgress(dowloadTotal, downloadNow); @@ -192,7 +191,7 @@ LLUpdateDownloader::Implementation::Implementation(LLUpdateDownloader::Client & mBandwidthLimit(0), mCancelled(false), mClient(client), - mCurl(0), + mCurl(), mDownloadPercent(0), mHeaderList(0) { @@ -212,10 +211,7 @@ LLUpdateDownloader::Implementation::~Implementation() { ; // No op. } - if(mCurl) - { - LLCurl::deleteEasyHandle(mCurl); - } + mCurl.reset(); } @@ -331,9 +327,9 @@ void LLUpdateDownloader::Implementation::setBandwidthLimit(U64 bytesPerSecond) { if((mBandwidthLimit != bytesPerSecond) && isDownloading() && !mDownloadData["required"].asBoolean()) { - llassert(mCurl != 0); + llassert(static_cast<bool>(mCurl)); mBandwidthLimit = bytesPerSecond; - CURLcode code = curl_easy_setopt(mCurl, CURLOPT_MAX_RECV_SPEED_LARGE, &mBandwidthLimit); + CURLcode code = curl_easy_setopt(mCurl.get(), CURLOPT_MAX_RECV_SPEED_LARGE, &mBandwidthLimit); if(code != CURLE_OK) { LL_WARNS("UpdaterService") << "unable to change dowload bandwidth" << LL_ENDL; @@ -390,9 +386,9 @@ size_t LLUpdateDownloader::Implementation::onBody(void * buffer, size_t size) } -int LLUpdateDownloader::Implementation::onProgress(double downloadSize, double bytesDownloaded) +int LLUpdateDownloader::Implementation::onProgress(curl_off_t downloadSize, curl_off_t bytesDownloaded) { - int downloadPercent = static_cast<int>(100. * (bytesDownloaded / downloadSize)); + int downloadPercent = static_cast<int>(100.0 * ((double) bytesDownloaded / (double) downloadSize)); if(downloadPercent > mDownloadPercent) { mDownloadPercent = downloadPercent; @@ -400,8 +396,8 @@ int LLUpdateDownloader::Implementation::onProgress(double downloadSize, double b event["pump"] = LLUpdaterService::pumpName(); LLSD payload; payload["type"] = LLSD(LLUpdaterService::PROGRESS); - payload["download_size"] = downloadSize; - payload["bytes_downloaded"] = bytesDownloaded; + payload["download_size"] = (LLSD::Integer) downloadSize; + payload["bytes_downloaded"] = (LLSD::Integer) bytesDownloaded; event["payload"] = payload; LLEventPumps::instance().obtain("mainlooprepeater").post(event); @@ -416,7 +412,7 @@ int LLUpdateDownloader::Implementation::onProgress(double downloadSize, double b void LLUpdateDownloader::Implementation::run(void) { - CURLcode code = curl_easy_perform(mCurl); + CURLcode code = curl_easy_perform(mCurl.get()); mDownloadStream.close(); if(code == CURLE_OK) { @@ -460,36 +456,39 @@ void LLUpdateDownloader::Implementation::run(void) void LLUpdateDownloader::Implementation::initializeCurlGet(std::string const & url, bool processHeader) { - if(mCurl == 0) + if(!mCurl) { - mCurl = LLCurl::newEasyHandle(); + mCurl = LLCore::LLHttp::createEasyHandle(); } else { - curl_easy_reset(mCurl); + curl_easy_reset(mCurl.get()); } - if(mCurl == 0) + if(!mCurl) { throw DownloadError("failed to initialize curl"); } - throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_NOSIGNAL, true)); - throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_FOLLOWLOCATION, true)); - throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_WRITEFUNCTION, &write_function)); - throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_WRITEDATA, this)); + throwOnCurlError(curl_easy_setopt(mCurl.get(), CURLOPT_NOSIGNAL, true)); + throwOnCurlError(curl_easy_setopt(mCurl.get(), CURLOPT_FOLLOWLOCATION, true)); + throwOnCurlError(curl_easy_setopt(mCurl.get(), CURLOPT_WRITEFUNCTION, &write_function)); + throwOnCurlError(curl_easy_setopt(mCurl.get(), CURLOPT_WRITEDATA, this)); if(processHeader) { - throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_HEADERFUNCTION, &header_function)); - throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_HEADERDATA, this)); - } - throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_HTTPGET, true)); - throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_URL, url.c_str())); - throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_PROGRESSFUNCTION, &progress_callback)); - throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_PROGRESSDATA, this)); - throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_NOPROGRESS, false)); + throwOnCurlError(curl_easy_setopt(mCurl.get(), CURLOPT_HEADERFUNCTION, &header_function)); + throwOnCurlError(curl_easy_setopt(mCurl.get(), CURLOPT_HEADERDATA, this)); + } + throwOnCurlError(curl_easy_setopt(mCurl.get(), CURLOPT_HTTPGET, true)); + throwOnCurlError(curl_easy_setopt(mCurl.get(), CURLOPT_URL, url.c_str())); + throwOnCurlError(curl_easy_setopt(mCurl.get(), CURLOPT_XFERINFOFUNCTION, &xferinfo_callback)); + throwOnCurlError(curl_easy_setopt(mCurl.get(), CURLOPT_XFERINFODATA, this)); + throwOnCurlError(curl_easy_setopt(mCurl.get(), CURLOPT_NOPROGRESS, 0)); // if it's a required update set the bandwidth limit to 0 (unlimited) curl_off_t limit = mDownloadData["required"].asBoolean() ? 0 : mBandwidthLimit; - throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_MAX_RECV_SPEED_LARGE, limit)); + throwOnCurlError(curl_easy_setopt(mCurl.get(), CURLOPT_MAX_RECV_SPEED_LARGE, limit)); + throwOnCurlError(curl_easy_setopt(mCurl.get(), CURLOPT_CAINFO, gDirUtilp->getCAFile().c_str())); + throwOnCurlError(curl_easy_setopt(mCurl.get(), CURLOPT_SSL_VERIFYHOST, 2)); + throwOnCurlError(curl_easy_setopt(mCurl.get(), CURLOPT_SSL_VERIFYPEER, 1)); mDownloadPercent = 0; } @@ -511,7 +510,7 @@ void LLUpdateDownloader::Implementation::resumeDownloading(size_t startByte) { throw DownloadError("cannot add Range header"); } - throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_HTTPHEADER, mHeaderList)); + throwOnCurlError(curl_easy_setopt(mCurl.get(), CURLOPT_HTTPHEADER, mHeaderList)); mDownloadStream.open(mDownloadData["path"].asString().c_str(), std::ios_base::out | std::ios_base::binary | std::ios_base::app); diff --git a/indra/win_crash_logger/CMakeLists.txt b/indra/win_crash_logger/CMakeLists.txt index c6070020db..144d037a31 100644 --- 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} |