diff --git a/.gitignore b/.gitignore index c56bf2a54..84a272544 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ /LICENSES/ /edited-files.txt qtcreator-build/ +/.pc diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt index 08daa9681..5f86c3509 100644 --- a/indra/CMakeLists.txt +++ b/indra/CMakeLists.txt @@ -46,7 +46,6 @@ endif(NOT STANDALONE) add_custom_target(prepare DEPENDS ${prepare_depends}) add_subdirectory(cmake) -add_subdirectory(${LIBS_OPEN_PREFIX}cwdebug) add_subdirectory(${LIBS_OPEN_PREFIX}llaudio) add_subdirectory(${LIBS_OPEN_PREFIX}llcharacter) add_subdirectory(${LIBS_OPEN_PREFIX}llcommon) diff --git a/indra/cmake/Boost.cmake b/indra/cmake/Boost.cmake index 8a3620b47..434dbff44 100644 --- a/indra/cmake/Boost.cmake +++ b/indra/cmake/Boost.cmake @@ -7,10 +7,8 @@ set(Boost_FIND_REQUIRED ON) if (STANDALONE) include(FindBoost) - set(BOOST_FILESYSTEM_LIBRARY boost_filesystem-mt) - set(BOOST_PROGRAM_OPTIONS_LIBRARY boost_program_options-mt) - set(BOOST_REGEX_LIBRARY boost_regex-mt) - set(BOOST_SYSTEM_LIBRARY boost_system-mt) + set(Boost_USE_MULTITHREADED ON) + find_package(Boost 1.40.0 COMPONENTS date_time filesystem program_options regex system thread wave) else (STANDALONE) use_prebuilt_binary(boost) set(Boost_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include) @@ -18,36 +16,36 @@ else (STANDALONE) if (WINDOWS) set(BOOST_VERSION 1_45) - # SNOW-788 - # 00-Common.cmake alreay sets MSVC_SUFFIX to be correct for the VS we are using eg VC71, VC80, VC90 etc - # The precompiled boost libs for VC71 use a different suffix to VS80 and VS90 - # This code should ensure the cmake rules are valid for any VS being used in future as long as the approprate - # boost libs are avaiable - RC. - - if (MSVC71) - set(BOOST_OPTIM_SUFFIX mt-s) - set(BOOST_DEBUG_SUFFIX mt-sgd) - else (MSVC71) - set(BOOST_OPTIM_SUFFIX mt) - set(BOOST_DEBUG_SUFFIX mt-gd) - endif (MSVC71) - - set(BOOST_PROGRAM_OPTIONS_LIBRARY + # SNOW-788 + # 00-Common.cmake alreay sets MSVC_SUFFIX to be correct for the VS we are using eg VC71, VC80, VC90 etc + # The precompiled boost libs for VC71 use a different suffix to VS80 and VS90 + # This code should ensure the cmake rules are valid for any VS being used in future as long as the approprate + # boost libs are avaiable - RC. + + if (MSVC71) + set(BOOST_OPTIM_SUFFIX mt-s) + set(BOOST_DEBUG_SUFFIX mt-sgd) + else (MSVC71) + set(BOOST_OPTIM_SUFFIX mt) + set(BOOST_DEBUG_SUFFIX mt-gd) + endif (MSVC71) + + set(Boost_PROGRAM_OPTIONS_LIBRARY optimized libboost_program_options-vc${MSVC_SUFFIX}-${BOOST_OPTIM_SUFFIX}-${BOOST_VERSION} debug libboost_program_options-vc${MSVC_SUFFIX}-${BOOST_DEBUG_SUFFIX}-${BOOST_VERSION}) - set(BOOST_REGEX_LIBRARY + set(Boost_REGEX_LIBRARY optimized libboost_regex-vc${MSVC_SUFFIX}-${BOOST_OPTIM_SUFFIX}-${BOOST_VERSION} debug libboost_regex-vc${MSVC_SUFFIX}-${BOOST_DEBUG_SUFFIX}-${BOOST_VERSION}) - + elseif (DARWIN) - set(BOOST_FILESYSTEM_LIBRARY boost_filesystem) - set(BOOST_PROGRAM_OPTIONS_LIBRARY boost_program_options) - set(BOOST_REGEX_LIBRARY boost_regex) - set(BOOST_SYSTEM_LIBRARY boost_system) + set(Boost_FILESYSTEM_LIBRARY boost_filesystem) + set(Boost_PROGRAM_OPTIONS_LIBRARY boost_program_options) + set(Boost_REGEX_LIBRARY boost_regex) + set(Boost_SYSTEM_LIBRARY boost_system) elseif (LINUX) - set(BOOST_FILESYSTEM_LIBRARY boost_filesystem-mt) - set(BOOST_PROGRAM_OPTIONS_LIBRARY boost_program_options-mt) - set(BOOST_REGEX_LIBRARY boost_regex-mt) - set(BOOST_SYSTEM_LIBRARY boost_system-mt) + set(Boost_FILESYSTEM_LIBRARY boost_filesystem-mt) + set(Boost_PROGRAM_OPTIONS_LIBRARY boost_program_options-mt) + set(Boost_REGEX_LIBRARY boost_regex-mt) + set(Boost_SYSTEM_LIBRARY boost_system-mt) endif (WINDOWS) endif (STANDALONE) diff --git a/indra/cmake/CURL.cmake b/indra/cmake/CURL.cmake index aa6ff57f9..10ef1b0b6 100644 --- a/indra/cmake/CURL.cmake +++ b/indra/cmake/CURL.cmake @@ -1,7 +1,7 @@ # -*- cmake -*- include(Prebuilt) -set(CURL_FIND_QUIETLY ON) +set(CURL_FIND_QUIETLY OFF) set(CURL_FIND_REQUIRED ON) if (STANDALONE) diff --git a/indra/cmake/Cwdebug.cmake b/indra/cmake/Cwdebug.cmake index 9568a625f..d469386e1 100644 --- a/indra/cmake/Cwdebug.cmake +++ b/indra/cmake/Cwdebug.cmake @@ -1 +1,18 @@ -set(CWDEBUG_LIBRARIES cwdebug) +include_directories (${CMAKE_SOURCE_DIR}/cwdebug) + +set(cwdebug_SOURCE_FILES + ${CMAKE_SOURCE_DIR}/cwdebug/debug.cc + ) + +set(cwdebug_HEADER_FILES + ${CMAKE_SOURCE_DIR}/cwdebug/cwdebug.h + ${CMAKE_SOURCE_DIR}/cwdebug/sys.h + ${CMAKE_SOURCE_DIR}/cwdebug/debug.h + ${CMAKE_SOURCE_DIR}/cwdebug/debug_ostream_operators.h + ) + +set_source_files_properties(${cwdebug_HEADER_FILES} + PROPERTIES HEADER_FILE_ONLY TRUE) + +list(APPEND cwdebug_SOURCE_FILES ${cwdebug_HEADER_FILES}) + diff --git a/indra/cmake/FMODEX.cmake b/indra/cmake/FMODEX.cmake index 9a39b1264..0a4bc0647 100644 --- a/indra/cmake/FMODEX.cmake +++ b/indra/cmake/FMODEX.cmake @@ -8,7 +8,7 @@ if(INSTALL_PROPRIETARY) endif(INSTALL_PROPRIETARY) find_library(FMODEX_LIBRARY - NAMES fmodex fmodex_vc fmodexL_vc + NAMES fmodex fmodexL fmodex_vc fmodexL_vc PATHS optimized ${ARCH_PREBUILT_DIRS_RELEASE} debug ${ARCH_PREBUILT_DIRS_DEBUG} @@ -26,26 +26,25 @@ if (NOT FMODEX_LIBRARY) ${FMODEX_SDK_DIR} ) - endif(FMODEX_SDK_DIR) if(WINDOWS AND NOT FMODEX_LIBRARY) - set(FMODEX_PROG_DIR "$ENV{PROGRAMFILES}/FMOD SoundSystem/FMOD Programmers API Windows") - find_library(FMODEX_LIBRARY - fmodex_vc fmodexL_vc + set(FMODEX_PROG_DIR "$ENV{PROGRAMFILES}/FMOD SoundSystem/FMOD Programmers API Windows") + find_library(FMODEX_LIBRARY + fmodex_vc fmodexL_vc PATHS ${FMODEX_PROG_DIR}/api/lib - ${FMODEX_PROG_DIR}/api - ${FMODEX_PROG_DIR} + ${FMODEX_PROG_DIR}/api + ${FMODEX_PROG_DIR} ) - if(FMODEX_LIBRARY) - message(STATUS "Found fmodex in ${FMODEX_PROG_DIR}") - set(FMODEX_SDK_DIR ${FMODEX_PROG_DIR}) - set(FMODEX_SDK_DIR ${FMODEX_PROG_DIR} CACHE PATH "Path to the FMOD Ex SDK." FORCE) - endif(FMODEX_LIBRARY) + if(FMODEX_LIBRARY) + message(STATUS "Found fmodex in ${FMODEX_PROG_DIR}") + set(FMODEX_SDK_DIR ${FMODEX_PROG_DIR}) + set(FMODEX_SDK_DIR ${FMODEX_PROG_DIR} CACHE PATH "Path to the FMOD Ex SDK." FORCE) + endif(FMODEX_LIBRARY) endif(WINDOWS AND NOT FMODEX_LIBRARY) endif (NOT FMODEX_LIBRARY) -find_path(FMODEX_INCLUDE_DIR fmod.h +find_path(FMODEX_INCLUDE_DIR fmod.hpp ${LIBS_PREBUILT_DIR}/include/fmodex ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/fmodex ${FMODEX_SDK_DIR}/api/inc diff --git a/indra/cmake/FindHunSpell.cmake b/indra/cmake/FindHunSpell.cmake index d41f9cf76..31576eb56 100644 --- a/indra/cmake/FindHunSpell.cmake +++ b/indra/cmake/FindHunSpell.cmake @@ -5,7 +5,7 @@ FIND_PATH(HUNSPELL_INCLUDE_DIR hunspell.hxx /usr/include ) -SET(HUNSPELL_NAMES ${HUNSPELL_NAMES} hunspell hunspell-1.2) +SET(HUNSPELL_NAMES ${HUNSPELL_NAMES} hunspell hunspell-1.2 hunspell-1.3) FIND_LIBRARY(HUNSPELL_LIBRARY NAMES ${HUNSPELL_NAMES} PATHS /usr/lib /usr/local/lib diff --git a/indra/cmake/LLVFS.cmake b/indra/cmake/LLVFS.cmake index 0ff227c12..a71976465 100644 --- a/indra/cmake/LLVFS.cmake +++ b/indra/cmake/LLVFS.cmake @@ -8,5 +8,5 @@ set(LLVFS_INCLUDE_DIRS set(LLVFS_LIBRARIES llvfs - ${BOOST_REGEX_LIBRARY} + ${Boost_REGEX_LIBRARY} ) diff --git a/indra/cmake/WebKitLibPlugin.cmake b/indra/cmake/WebKitLibPlugin.cmake index 7ed824aea..6c8e60103 100644 --- a/indra/cmake/WebKitLibPlugin.cmake +++ b/indra/cmake/WebKitLibPlugin.cmake @@ -14,18 +14,6 @@ if (STANDALONE) "or unset QTDIR if the found Qt is correct.") endif (QTDIR AND NOT "${QT_BINARY_DIR}" STREQUAL "${QTDIR}/bin") find_package(LLQtWebkit REQUIRED QUIET) - # Add the plugins. - set(QT_PLUGIN_LIBRARIES) - foreach(qlibname qgif qjpeg) - find_library(QT_PLUGIN_${qlibname} ${qlibname} PATHS ${QT_PLUGINS_DIR}/imageformats NO_DEFAULT_PATH) - if (QT_PLUGIN_${qlibname}) - list(APPEND QT_PLUGIN_LIBRARIES ${QT_PLUGIN_${qlibname}}) - else (QT_PLUGIN_${qtlibname}) - message(FATAL_ERROR "Could not find the Qt plugin ${qlibname} in \"${QT_PLUGINS_DIR}/imageformats\"!") - endif (QT_PLUGIN_${qlibname}) - endforeach(qlibname) - # qjpeg depends on libjpeg - list(APPEND QT_PLUGIN_LIBRARIES jpeg) set(WEBKITLIBPLUGIN OFF CACHE BOOL "WEBKITLIBPLUGIN support for the llplugin/llmedia test apps.") else (STANDALONE) @@ -58,7 +46,7 @@ elseif (DARWIN) ) elseif (LINUX) if (STANDALONE) - set(WEBKIT_PLUGIN_LIBRARIES ${LLQTWEBKIT_LIBRARY} ${QT_LIBRARIES} ${QT_PLUGIN_LIBRARIES}) + set(WEBKIT_PLUGIN_LIBRARIES ${LLQTWEBKIT_LIBRARY} ${QT_LIBRARIES}) else (STANDALONE) set(WEBKIT_PLUGIN_LIBRARIES llqtwebkit diff --git a/indra/cwdebug/CMakeLists.txt b/indra/cwdebug/CMakeLists.txt deleted file mode 100644 index 9f423c736..000000000 --- a/indra/cwdebug/CMakeLists.txt +++ /dev/null @@ -1,39 +0,0 @@ -# -*- cmake -*- - -project(cwdebug) - -include(00-Common) -include(LLCommon) -include(LLMath) -include(LLMessage) -include(LLVFS) - -include_directories (${CMAKE_CURRENT_SOURCE_DIR}) - -set(cwdebug_SOURCE_FILES - debug.cc - ) - -set(cwdebug_HEADER_FILES - CMakeLists.txt - - cwdebug.h - sys.h - debug.h - debug_ostream_operators.h - ) - -set_source_files_properties(${cwdebug_HEADER_FILES} - PROPERTIES HEADER_FILE_ONLY TRUE) - -if(NOT WORD_SIZE EQUAL 32) - if(WINDOWS) - add_definitions(/FIXED:NO) - else(WINDOWS) # not windows therefore gcc LINUX and DARWIN - add_definitions(-fPIC) - endif(WINDOWS) -endif (NOT WORD_SIZE EQUAL 32) - -list(APPEND cwdebug_SOURCE_FILES ${cwdebug_HEADER_FILES}) - -add_library (cwdebug ${cwdebug_SOURCE_FILES}) diff --git a/indra/cwdebug/debug.cc b/indra/cwdebug/debug.cc index 2a4f1c4fa..06723f8ae 100644 --- a/indra/cwdebug/debug.cc +++ b/indra/cwdebug/debug.cc @@ -173,6 +173,8 @@ void stop_recording_backtraces(void) channel_ct backtrace DDCN("BACKTRACE"); //!< This debug channel is used for backtraces. channel_ct statemachine DDCN("STATEMACHINE"); //!< This debug channel is used for output related to class AIStateMachine. channel_ct caps DDCN("CAPS"); //!< This debug channel is used for output related to Capabilities. + channel_ct curl DDCN("CURL"); //!< This debug channel is used for output related to Curl. + channel_ct curlio DDCN("CURLIO"); //!< This debug channel is used to print debug output of libcurl. } // namespace dc } // namespace DEBUGCHANNELS @@ -411,4 +413,94 @@ void cwdebug_backtrace(int n) } #endif -#endif // CWDEBUG +#elif defined(DEBUG_CURLIO) + +#include "debug.h" +#include "aithreadid.h" + +namespace debug +{ + +namespace libcwd { libcwd_do_type const libcw_do; } + +ll_thread_local int Indent::S_indentation; + +Indent::Indent(int indent) : M_indent(indent) +{ + S_indentation += M_indent; +} + +Indent::~Indent() +{ + S_indentation -= M_indent; +} + +std::ostream& operator<<(std::ostream& os, Indent::print_nt) +{ + if (Indent::S_indentation) + os << std::string(Indent::S_indentation, ' '); + return os; +} + +#ifdef DEBUG_CURLIO +std::ostream& operator<<(std::ostream& os, print_thread_id_t) +{ + if (!AIThreadID::in_main_thread_inline()) + { + os << std::hex << (size_t)AIThreadID::getCurrentThread_inline() << std::dec << ' '; + } + return os; +} +#endif + +std::ostream& operator<<(std::ostream& os, libcwd::buf2str const& b2s) +{ + static char const c2s_tab[7] = { 'a', 'b', 't', 'n', 'v', 'f', 'r' }; + size_t size = b2s.mSize; + for (char const* p1 = b2s.mBuf; size > 0; --size, ++p1) + { + char c =*p1; + if ((c > 31 && c != 92 && c != 127) || (unsigned char)c > 159) + os.put(c); + else + { + os.put('\\'); + if (c > 6 && c < 14) + { + os.put(c2s_tab[c - 7]); + return os; + } + else if (c == 27) + { + os.put('e'); + return os; + } + else if (c == '\\') + { + os.put('\\'); + return os; + } + std::ostream::char_type old_fill = os.fill('0'); + std::ios_base::fmtflags old_flgs = os.flags(); + os.width(3); + os << std::oct << (int)((unsigned char)c); + os.setf(old_flgs); + os.fill(old_fill); + } + } + return os; +} + +namespace dc +{ + +fake_channel const warning(1, "WARNING "); +fake_channel const curl(1, "CURL "); +fake_channel const curlio(1, "CURLIO "); +fake_channel const statemachine(1, "STATEMACHINE"); +fake_channel const notice(1, "NOTICE "); + +} // namespace dc +} // namespace debug + +#endif diff --git a/indra/cwdebug/debug.h b/indra/cwdebug/debug.h index 786b0c52a..a52837411 100644 --- a/indra/cwdebug/debug.h +++ b/indra/cwdebug/debug.h @@ -27,6 +27,94 @@ #ifndef CWDEBUG +#ifdef DEBUG_CURLIO + +// If CWDEBUG is not defined, but DEBUG_CURLIO is, then replace +// some of the cwd macro's with something that generates viewer +// specific debug output. Note that this generates a LOT of +// output and should not normally be defined. + +#include +#include "llpreprocessor.h" + +namespace debug { +namespace libcwd { + +struct buf2str { + buf2str(char const* buf, int size) : mBuf(buf), mSize(size) { } + char const* mBuf; + int mSize; +}; + +struct libcwd_do_type { + void on() const { } +}; +extern LL_COMMON_API libcwd_do_type const libcw_do; + +} // namespace libcwd + +enum print_thread_id_t { print_thread_id }; +inline void init() { } +struct Indent { + int M_indent; + static ll_thread_local int S_indentation; + enum LL_COMMON_API print_nt { print }; + LL_COMMON_API Indent(int indent); + LL_COMMON_API ~Indent(); +}; + +extern LL_COMMON_API std::ostream& operator<<(std::ostream& os, libcwd::buf2str const& b2s); +extern LL_COMMON_API std::ostream& operator<<(std::ostream& os, Indent::print_nt); +extern LL_COMMON_API std::ostream& operator<<(std::ostream& os, print_thread_id_t); + +namespace dc { + +struct fake_channel { + int mOn; + char const* mLabel; + fake_channel(int on, char const* label) : mOn(on), mLabel(label) { } + fake_channel(void) : mOn(0) { } + bool is_on() const { return !!mOn; } + bool is_off() const { return !mOn; } + void on() const { } + void off() const { } +}; +extern LL_COMMON_API fake_channel const warning; +extern LL_COMMON_API fake_channel const curl; +extern LL_COMMON_API fake_channel const curlio; +extern LL_COMMON_API fake_channel const statemachine; +extern LL_COMMON_API fake_channel const notice; + +} // namespace dc +} // namespace debug + +#define LIBCWD_DEBUG_CHANNELS debug +#define LibcwDoutScopeBegin(a, b, c) do { using namespace debug; using namespace debug::libcwd; llinfos_nf << print_thread_id << (c).mLabel << ": " << Indent::print; +#define LibcwDoutStream llcont +#define LibcwDoutScopeEnd llcont << llendl; } while(0) + +#define Debug(x) do { using namespace debug; using namespace debug::libcwd; x; } while(0) +#define Dout(a, b) do { using namespace debug; using namespace debug::libcwd; if ((a).mOn) { llinfos_nf << print_thread_id << (a).mLabel << ": " << Indent::print << b << llendl; } } while(0) +#define DoutEntering(a, b) \ + int __slviewer_debug_indentation = 2; \ + { \ + using namespace debug; \ + using namespace debug::libcwd; \ + if ((a).mOn) \ + llinfos_nf << print_thread_id << (a).mLabel << ": " << Indent::print << "Entering " << b << llendl; \ + else \ + __slviewer_debug_indentation = 0; \ + } \ + debug::Indent __slviewer_debug_indent(__slviewer_debug_indentation); + +#else // !DEBUG_CURLIO + +#define Debug(x) +#define Dout(a, b) +#define DoutEntering(a, b) + +#endif // !DEBUG_CURLIO + #ifndef DOXYGEN // No need to document this. See http://libcwd.sourceforge.net/ for more info. #include @@ -36,9 +124,6 @@ #define AllocTag2(p, desc) #define AllocTag_dynamic_description(p, x) #define AllocTag(p, x) -#define Debug(x) -#define Dout(a, b) -#define DoutEntering(a, b) #define DoutFatal(a, b) LibcwDoutFatal(::std, , a, b) #define ForAllDebugChannels(STATEMENT) #define ForAllDebugObjects(STATEMENT) @@ -118,6 +203,8 @@ extern CWD_API channel_ct sdl; extern CWD_API channel_ct backtrace; extern CWD_API channel_ct statemachine; extern CWD_API channel_ct caps; +extern CWD_API channel_ct curl; +extern CWD_API channel_ct curlio; #endif diff --git a/indra/llaudio/llaudiodecodemgr.cpp b/indra/llaudio/llaudiodecodemgr.cpp index 5063cbe7a..596a6dcff 100644 --- a/indra/llaudio/llaudiodecodemgr.cpp +++ b/indra/llaudio/llaudiodecodemgr.cpp @@ -41,6 +41,7 @@ #include "lldir.h" #include "llendianswizzle.h" #include "llassetstorage.h" +#include "llrefcount.h" #include "vorbis/codec.h" #include "vorbis/vorbisfile.h" diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 64e0140e1..5d86a6415 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -16,6 +16,7 @@ include_directories( set(llcommon_SOURCE_FILES aiframetimer.cpp + aithreadid.cpp imageids.cpp indra_constants.cpp llallocator.cpp @@ -104,6 +105,7 @@ set(llcommon_HEADER_FILES CMakeLists.txt aiframetimer.h + aithreadid.h aithreadsafe.h bitpack.h ctype_workaround.h @@ -254,6 +256,7 @@ set(llcommon_HEADER_FILES set_source_files_properties(${llcommon_HEADER_FILES} PROPERTIES HEADER_FILE_ONLY TRUE) +list(APPEND llcommon_SOURCE_FILES ${cwdebug_SOURCE_FILES}) list(APPEND llcommon_SOURCE_FILES ${llcommon_HEADER_FILES}) add_library (llcommon SHARED ${llcommon_SOURCE_FILES}) @@ -265,16 +268,10 @@ target_link_libraries( ${EXPAT_LIBRARIES} ${ZLIB_LIBRARIES} ${WINDOWS_LIBRARIES} - ${BOOST_REGEX_LIBRARY} - ${CWDEBUG_LIBRARIES} + ${Boost_REGEX_LIBRARY} ${CORESERVICES_LIBRARY} ) -if (LINUX) - # When linking with llcommon later, we do not want to link with cwdebug.a again. - set_property(TARGET llcommon PROPERTY LINK_INTERFACE_LIBRARIES "-lapr-1 -laprutil-1 -lz") -endif (LINUX) - if (DARWIN) # Don't embed a full path in the library's install name set_target_properties( diff --git a/indra/llcommon/aithreadid.cpp b/indra/llcommon/aithreadid.cpp new file mode 100644 index 000000000..e8dd17097 --- /dev/null +++ b/indra/llcommon/aithreadid.cpp @@ -0,0 +1,78 @@ +/** + * @file aithreadid.cpp + * + * Copyright (c) 2012, Aleric Inglewood. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution. + * + * CHANGELOG + * and additional copyright holders. + * + * 08/08/2012 + * - Initial version, written by Aleric Inglewood @ SL + */ + +#include +#include +#include "aithreadid.h" + +AIThreadID const AIThreadID::sNone(AIThreadID::none); +apr_os_thread_t AIThreadID::sMainThreadID; +apr_os_thread_t const AIThreadID::undefinedID = (apr_os_thread_t)-1; +#ifndef LL_DARWIN +apr_os_thread_t ll_thread_local AIThreadID::lCurrentThread; +#endif + +void AIThreadID::set_main_thread_id(void) +{ + sMainThreadID = apr_os_thread_current(); +} + +void AIThreadID::set_current_thread_id(void) +{ +#ifndef LL_DARWIN + lCurrentThread = apr_os_thread_current(); +#endif +} + +#ifndef LL_DARWIN +void AIThreadID::reset(void) +{ + mID = lCurrentThread; +} + +bool AIThreadID::equals_current_thread(void) const +{ + return apr_os_thread_equal(mID, lCurrentThread); +} + +bool AIThreadID::in_main_thread(void) +{ + return apr_os_thread_equal(lCurrentThread, sMainThreadID); +} + +apr_os_thread_t AIThreadID::getCurrentThread(void) +{ + return lCurrentThread; +} +#endif + +std::ostream& operator<<(std::ostream& os, AIThreadID const& id) +{ + return os << id.mID; +} diff --git a/indra/llcommon/aithreadid.h b/indra/llcommon/aithreadid.h new file mode 100644 index 000000000..3b3b1500c --- /dev/null +++ b/indra/llcommon/aithreadid.h @@ -0,0 +1,94 @@ +/** + * @file aithreadid.h + * @brief Declaration of AIThreadID. + * + * Copyright (c) 2012, Aleric Inglewood. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution. + * + * CHANGELOG + * and additional copyright holders. + * + * 08/08/2012 + * Initial version, written by Aleric Inglewood @ SL + */ + +#ifndef AI_THREAD_ID +#define AI_THREAD_ID + +#include // apr_os_thread_t, apr_os_thread_current(), apr_os_thread_equal(). +#include // std::ostream. +#include "llpreprocessor.h" // LL_COMMON_API, LL_COMMON_API_TLS + +// Lightweight wrapper around apr_os_thread_t. +// This class introduces no extra assembly code after optimization; it's only intend is to provide type-safety. +class AIThreadID +{ +private: + apr_os_thread_t mID; + static LL_COMMON_API apr_os_thread_t sMainThreadID; + static LL_COMMON_API apr_os_thread_t const undefinedID; +#ifndef LL_DARWIN + static ll_thread_local apr_os_thread_t lCurrentThread; +#endif +public: + static LL_COMMON_API AIThreadID const sNone; + enum undefined_thread_t { none }; + +public: + AIThreadID(void) : mID(apr_os_thread_current()) { } + explicit AIThreadID(undefined_thread_t) : mID(undefinedID) { } // Used for sNone. + AIThreadID(AIThreadID const& id) : mID(id.mID) { } + AIThreadID& operator=(AIThreadID const& id) { mID = id.mID; return *this; } + bool is_main_thread(void) const { return apr_os_thread_equal(mID, sMainThreadID); } + bool is_no_thread(void) const { return apr_os_thread_equal(mID, sNone.mID); } + friend LL_COMMON_API bool operator==(AIThreadID const& id1, AIThreadID const& id2) { return apr_os_thread_equal(id1.mID, id2.mID); } + friend LL_COMMON_API bool operator!=(AIThreadID const& id1, AIThreadID const& id2) { return !apr_os_thread_equal(id1.mID, id2.mID); } + friend LL_COMMON_API std::ostream& operator<<(std::ostream& os, AIThreadID const& id); + static void set_main_thread_id(void); // Called once to set sMainThreadID. + static void set_current_thread_id(void); // Called once for every thread to set lCurrentThread. +#ifndef LL_DARWIN + LL_COMMON_API void reset(void); + LL_COMMON_API bool equals_current_thread(void) const; + LL_COMMON_API static bool in_main_thread(void); + LL_COMMON_API static apr_os_thread_t getCurrentThread(void); + // The *_inline variants cannot be exported because they access a thread-local member. + void reset_inline(void) { mID = lCurrentThread; } + bool equals_current_thread_inline(void) const { return apr_os_thread_equal(mID, lCurrentThread); } + static bool in_main_thread_inline(void) { return apr_os_thread_equal(lCurrentThread, sMainThreadID); } + static apr_os_thread_t getCurrentThread_inline(void) { return lCurrentThread; } +#else + // Both variants are inline on OS X. + void reset(void) { mID = apr_os_thread_current(); } + void reset_inline(void) { mID = apr_os_thread_current(); } + bool equals_current_thread(void) const { return apr_os_thread_equal(mID, apr_os_thread_current()); } + bool equals_current_thread_inline(void) const { return apr_os_thread_equal(mID, apr_os_thread_current()); } + static bool in_main_thread(void) { return apr_os_thread_equal(apr_os_thread_current(), sMainThreadID); } + static bool in_main_thread_inline(void) { return apr_os_thread_equal(apr_os_thread_current(), sMainThreadID); } + static apr_os_thread_t getCurrentThread(void) { return apr_os_thread_current(); } + static apr_os_thread_t getCurrentThread_inline(void) { return apr_os_thread_current(); } +#endif +}; + +// Legacy function. +inline bool is_main_thread(void) +{ + return AIThreadID::in_main_thread(); +} + +#endif // AI_THREAD_ID diff --git a/indra/llcommon/aithreadsafe.h b/indra/llcommon/aithreadsafe.h index b7d0045cb..02bfa8cd7 100644 --- a/indra/llcommon/aithreadsafe.h +++ b/indra/llcommon/aithreadsafe.h @@ -673,18 +673,18 @@ protected: #ifdef LL_DEBUG mutable bool mAccessed; - mutable apr_os_thread_t mTheadID; + mutable AIThreadID mTheadID; void accessed(void) const { if (!mAccessed) { mAccessed = true; - mTheadID = apr_os_thread_current(); + mTheadID.reset(); } else { - llassert_always(apr_os_thread_equal(mTheadID, apr_os_thread_current())); + llassert_always(mTheadID.equals_current_thread()); } } #endif diff --git a/indra/llcommon/llapp.cpp b/indra/llcommon/llapp.cpp index cb2b3c0ca..316b07e83 100644 --- a/indra/llcommon/llapp.cpp +++ b/indra/llcommon/llapp.cpp @@ -297,6 +297,20 @@ void LLApp::startErrorThread() } } +void LLApp::stopErrorThread() +{ + LLApp::setStopped(); // Signal error thread that we stopped. + int count = 0; + while (mThreadErrorp && !mThreadErrorp->isStopped() && ++count < 100) + { + ms_sleep(10); + } + if (mThreadErrorp && !mThreadErrorp->isStopped()) + { + llwarns << "Failed to stop Error Thread." << llendl; + } +} + void LLApp::setErrorHandler(LLAppErrorHandler handler) { LLApp::sErrorHandler = handler; diff --git a/indra/llcommon/llapp.h b/indra/llcommon/llapp.h index 32f1e84c3..788177dd9 100644 --- a/indra/llcommon/llapp.h +++ b/indra/llcommon/llapp.h @@ -264,6 +264,10 @@ protected: * @ brief This method is called once as soon as logging is initialized. */ void startErrorThread(); + /** + * @brief This method is called at the end, just prior to deinitializing curl. + */ + void stopErrorThread(); private: void setupErrorHandling(); // Do platform-specific error-handling setup (signals, structured exceptions) diff --git a/indra/llcommon/llaprpool.cpp b/indra/llcommon/llaprpool.cpp index 3559ff430..3dffa8300 100644 --- a/indra/llcommon/llaprpool.cpp +++ b/indra/llcommon/llaprpool.cpp @@ -60,10 +60,10 @@ void LLAPRPool::create(LLAPRPool& parent) // // In other words, it's safe for any thread to create a (sub)pool, independent of who // owns the parent pool. - mOwner = apr_os_thread_current(); + mOwner.reset_inline(); #else mOwner = mParent->mOwner; - llassert(apr_os_thread_equal(mOwner, apr_os_thread_current())); + llassert(mOwner.equals_current_thread_inline()); #endif apr_status_t const apr_pool_create_status = apr_pool_create(&mPool, mParent->mPool); llassert_always(apr_pool_create_status == APR_SUCCESS); @@ -83,7 +83,7 @@ void LLAPRPool::destroy(void) // of course. Otherwise, if we are a subpool, only the thread that owns // the parent may destruct us, since that is the pool that is still alive, // possibly being used by others and being altered here. - llassert(!mParent || apr_os_thread_equal(mParent->mOwner, apr_os_thread_current())); + llassert(!mParent || mParent->mOwner.equals_current_thread_inline()); #endif apr_pool_t* pool = mPool; mPool = NULL; // Mark that we are BEING destructed. diff --git a/indra/llcommon/llaprpool.h b/indra/llcommon/llaprpool.h index dc123e942..74af351e4 100644 --- a/indra/llcommon/llaprpool.h +++ b/indra/llcommon/llaprpool.h @@ -48,6 +48,7 @@ #include "apr_portable.h" #include "apr_pools.h" #include "llerror.h" +#include "aithreadid.h" extern void ll_init_apr(); @@ -62,22 +63,22 @@ class LL_COMMON_API LLAPRPool protected: apr_pool_t* mPool; //!< Pointer to the underlaying pool. NULL if not initialized. LLAPRPool* mParent; //!< Pointer to the parent pool, if any. Only valid when mPool is non-zero. - apr_os_thread_t mOwner; //!< The thread that owns this memory pool. Only valid when mPool is non-zero. + AIThreadID mOwner; //!< The thread that owns this memory pool. Only valid when mPool is non-zero. public: //! Construct an uninitialized (destructed) pool. - LLAPRPool(void) : mPool(NULL) { } + LLAPRPool(void) : mPool(NULL), mOwner(AIThreadID::none) { } //! Construct a subpool from an existing pool. // This is not a copy-constructor, this class doesn't have one! - LLAPRPool(LLAPRPool& parent) : mPool(NULL) { create(parent); } + LLAPRPool(LLAPRPool& parent) : mPool(NULL), mOwner(AIThreadID::none) { create(parent); } //! Destruct the memory pool (free all of it's subpools and allocated memory). ~LLAPRPool() { destroy(); } protected: // Create a pool that is allocated from the Operating System. Only used by LLAPRRootPool. - LLAPRPool(int) : mPool(NULL), mParent(NULL), mOwner(apr_os_thread_current()) + LLAPRPool(int) : mPool(NULL), mParent(NULL) { apr_status_t const apr_pool_create_status = apr_pool_create(&mPool, NULL); llassert_always(apr_pool_create_status == APR_SUCCESS); @@ -104,7 +105,7 @@ public: apr_pool_t* operator()(void) const { llassert(mPool); - llassert(apr_os_thread_equal(mOwner, apr_os_thread_current())); + llassert(mOwner.equals_current_thread()); return mPool; } @@ -112,7 +113,7 @@ public: void clear(void) { llassert(mPool); - llassert(apr_os_thread_equal(mOwner, apr_os_thread_current())); + llassert(mOwner.equals_current_thread()); apr_pool_clear(mPool); } @@ -124,13 +125,13 @@ public: void* palloc(size_t size) { llassert(mPool); - llassert(apr_os_thread_equal(mOwner, apr_os_thread_current())); + llassert(mOwner.equals_current_thread()); return apr_palloc(mPool, size); } void* pcalloc(size_t size) { llassert(mPool); - llassert(apr_os_thread_equal(mOwner, apr_os_thread_current())); + llassert(mOwner.equals_current_thread()); return apr_pcalloc(mPool, size); } #endif diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp index c537a76a8..7b362b230 100644 --- a/indra/llcommon/llerror.cpp +++ b/indra/llcommon/llerror.cpp @@ -963,10 +963,14 @@ namespace LLError settings_w->shouldLogCallCounter += 1; std::string class_name = className(site.mClassInfo); - std::string function_name = functionName(site.mFunction); - if (site.mClassInfo != typeid(NoClassInfo)) + std::string function_name; + if (site.mFunction) { - function_name = class_name + "::" + function_name; + function_name = functionName(site.mFunction); + if (site.mClassInfo != typeid(NoClassInfo)) + { + function_name = class_name + "::" + function_name; + } } ELevel compareLevel = settings_w->defaultLevel; @@ -976,7 +980,7 @@ namespace LLError // So, in increasing order of importance: // Default < Broad Tag < File < Class < Function < Narrow Tag ((site.mNarrowTag != NULL) ? checkLevelMap(settings_w->tagLevelMap, site.mNarrowTag, compareLevel) : false) - || checkLevelMap(settings_w->functionLevelMap, function_name, compareLevel) + || (site.mFunction && checkLevelMap(settings_w->functionLevelMap, function_name, compareLevel)) || checkLevelMap(settings_w->classLevelMap, class_name, compareLevel) || checkLevelMap(settings_w->fileLevelMap, abbreviateFile(site.mFile), compareLevel) || ((site.mBroadTag != NULL) ? checkLevelMap(settings_w->tagLevelMap, site.mBroadTag, compareLevel) : false); @@ -1083,8 +1087,8 @@ namespace LLError default: prefix << "XXX"; break; }; - bool need_function = true; - if (site.mBroadTag && *site.mBroadTag != '\0') + bool need_function = site.mFunction; + if (need_function && site.mBroadTag && *site.mBroadTag != '\0') { prefix << "(\"" << site.mBroadTag << "\")"; #if LL_DEBUG @@ -1112,7 +1116,7 @@ namespace LLError #if LL_WINDOWS // DevStudio: __FUNCTION__ already includes the full class name #else - if (need_function && site.mClassInfo != typeid(NoClassInfo)) + if (site.mClassInfo != typeid(NoClassInfo)) { prefix << className(site.mClassInfo) << "::"; } diff --git a/indra/llcommon/llerror.h b/indra/llcommon/llerror.h index 34934c5c2..c4865e947 100644 --- a/indra/llcommon/llerror.h +++ b/indra/llcommon/llerror.h @@ -238,10 +238,10 @@ typedef LLError::NoClassInfo _LL_CLASS_TO_LOG; See top of file for common usage. */ -#define lllog(level, broadTag, narrowTag, once) \ +#define lllog(level, broadTag, narrowTag, once, nofunction) \ do { \ static LLError::CallSite _site( \ - level, __FILE__, __LINE__, typeid(_LL_CLASS_TO_LOG), __FUNCTION__, broadTag, narrowTag, once);\ + level, __FILE__, __LINE__, typeid(_LL_CLASS_TO_LOG), nofunction ? NULL : __FUNCTION__, broadTag, narrowTag, once);\ if (LL_UNLIKELY(_site.shouldLog())) \ { \ std::ostringstream* _out = LLError::Log::out(); \ @@ -255,33 +255,39 @@ typedef LLError::NoClassInfo _LL_CLASS_TO_LOG; } while(0) // DEPRECATED: Use the new macros that allow tags and *look* like macros. -#define lldebugs lllog(LLError::LEVEL_DEBUG, NULL, NULL, false) -#define llinfos lllog(LLError::LEVEL_INFO, NULL, NULL, false) -#define llwarns lllog(LLError::LEVEL_WARN, NULL, NULL, false) -#define llerrs lllog(LLError::LEVEL_ERROR, NULL, NULL, false) +#define lldebugs lllog(LLError::LEVEL_DEBUG, NULL, NULL, false, false) +#define llinfos lllog(LLError::LEVEL_INFO, NULL, NULL, false, false) +#define llwarns lllog(LLError::LEVEL_WARN, NULL, NULL, false, false) +#define llerrs lllog(LLError::LEVEL_ERROR, NULL, NULL, false, false) #define llcont (*_out) +// No function name +#define lldebugs_nf lllog(LLError::LEVEL_DEBUG, NULL, NULL, false, true) +#define llinfos_nf lllog(LLError::LEVEL_INFO, NULL, NULL, false, true) +#define llwarns_nf lllog(LLError::LEVEL_WARN, NULL, NULL, false, true) +#define llerrs_nf lllog(LLError::LEVEL_ERROR, NULL, NULL, false, true) + // NEW Macros for debugging, allow the passing of a string tag // One Tag -#define LL_DEBUGS(broadTag) lllog(LLError::LEVEL_DEBUG, broadTag, NULL, false) -#define LL_INFOS(broadTag) lllog(LLError::LEVEL_INFO, broadTag, NULL, false) -#define LL_WARNS(broadTag) lllog(LLError::LEVEL_WARN, broadTag, NULL, false) -#define LL_ERRS(broadTag) lllog(LLError::LEVEL_ERROR, broadTag, NULL, false) +#define LL_DEBUGS(broadTag) lllog(LLError::LEVEL_DEBUG, broadTag, NULL, false, false) +#define LL_INFOS(broadTag) lllog(LLError::LEVEL_INFO, broadTag, NULL, false, false) +#define LL_WARNS(broadTag) lllog(LLError::LEVEL_WARN, broadTag, NULL, false, false) +#define LL_ERRS(broadTag) lllog(LLError::LEVEL_ERROR, broadTag, NULL, false, false) // Two Tags -#define LL_DEBUGS2(broadTag, narrowTag) lllog(LLError::LEVEL_DEBUG, broadTag, narrowTag, false) -#define LL_INFOS2(broadTag, narrowTag) lllog(LLError::LEVEL_INFO, broadTag, narrowTag, false) -#define LL_WARNS2(broadTag, narrowTag) lllog(LLError::LEVEL_WARN, broadTag, narrowTag, false) -#define LL_ERRS2(broadTag, narrowTag) lllog(LLError::LEVEL_ERROR, broadTag, narrowTag, false) +#define LL_DEBUGS2(broadTag, narrowTag) lllog(LLError::LEVEL_DEBUG, broadTag, narrowTag, false, false) +#define LL_INFOS2(broadTag, narrowTag) lllog(LLError::LEVEL_INFO, broadTag, narrowTag, false, false) +#define LL_WARNS2(broadTag, narrowTag) lllog(LLError::LEVEL_WARN, broadTag, narrowTag, false, false) +#define LL_ERRS2(broadTag, narrowTag) lllog(LLError::LEVEL_ERROR, broadTag, narrowTag, false, false) // Only print the log message once (good for warnings or infos that would otherwise // spam the log file over and over, such as tighter loops). -#define LL_DEBUGS_ONCE(broadTag) lllog(LLError::LEVEL_DEBUG, broadTag, NULL, true) -#define LL_INFOS_ONCE(broadTag) lllog(LLError::LEVEL_INFO, broadTag, NULL, true) -#define LL_WARNS_ONCE(broadTag) lllog(LLError::LEVEL_WARN, broadTag, NULL, true) -#define LL_DEBUGS2_ONCE(broadTag, narrowTag) lllog(LLError::LEVEL_DEBUG, broadTag, narrowTag, true) -#define LL_INFOS2_ONCE(broadTag, narrowTag) lllog(LLError::LEVEL_INFO, broadTag, narrowTag, true) -#define LL_WARNS2_ONCE(broadTag, narrowTag) lllog(LLError::LEVEL_WARN, broadTag, narrowTag, true) +#define LL_DEBUGS_ONCE(broadTag) lllog(LLError::LEVEL_DEBUG, broadTag, NULL, true, false) +#define LL_INFOS_ONCE(broadTag) lllog(LLError::LEVEL_INFO, broadTag, NULL, true, false) +#define LL_WARNS_ONCE(broadTag) lllog(LLError::LEVEL_WARN, broadTag, NULL, true, false) +#define LL_DEBUGS2_ONCE(broadTag, narrowTag) lllog(LLError::LEVEL_DEBUG, broadTag, narrowTag, true, false) +#define LL_INFOS2_ONCE(broadTag, narrowTag) lllog(LLError::LEVEL_INFO, broadTag, narrowTag, true, false) +#define LL_WARNS2_ONCE(broadTag, narrowTag) lllog(LLError::LEVEL_WARN, broadTag, narrowTag, true, false) #define LL_ENDL llendl #define LL_CONT (*_out) @@ -302,8 +308,4 @@ typedef LLError::NoClassInfo _LL_CLASS_TO_LOG; Such computation is done iff the message will be logged. */ -#ifdef SHOW_ASSERT -extern LL_COMMON_API bool is_main_thread(); -#endif - #endif // LL_LLERROR_H diff --git a/indra/llcommon/llerrorlegacy.h b/indra/llcommon/llerrorlegacy.h index fa41c5b3d..278f781ae 100644 --- a/indra/llcommon/llerrorlegacy.h +++ b/indra/llcommon/llerrorlegacy.h @@ -114,7 +114,7 @@ const int LL_ERR_PRICE_MISMATCH = -23018; : liru_slashpos2 == std::string::npos ? std::string(__FILE__)/*Apparently, we're in / or perhaps the top of the drive, print as is*/\ : std::string(__FILE__).substr(1+liru_slashpos2))/*print foo/bar.cpp or perhaps foo\bar.cpp*/ -#define llassert_always(func) if (LL_UNLIKELY(!(func))) llerrs <<"\nASSERT(" #func ")\nfile:"< LLFastTimer::sLogQueue; +const int LLFastTimer::NamedTimer::HISTORY_NUM = 300; #if LL_WINDOWS #define USE_RDTSC 1 @@ -435,16 +436,12 @@ LLFastTimer::NamedTimer::NamedTimer(const std::string& name) mFrameStateIndex = frame_state_list.size(); getFrameStateList().push_back(FrameState(this)); - mCountHistory = new U32[HISTORY_NUM]; - memset(mCountHistory, 0, sizeof(U32) * HISTORY_NUM); - mCallHistory = new U32[HISTORY_NUM]; - memset(mCallHistory, 0, sizeof(U32) * HISTORY_NUM); + mCountHistory.resize(HISTORY_NUM); + mCallHistory.resize(HISTORY_NUM); } LLFastTimer::NamedTimer::~NamedTimer() { - delete[] mCountHistory; - delete[] mCallHistory; } std::string LLFastTimer::NamedTimer::getToolTip(S32 history_idx) @@ -637,10 +634,12 @@ void LLFastTimer::NamedTimer::accumulateTimings() // update timer history int hidx = cur_frame % HISTORY_NUM; + int weight = llmin(100, cur_frame); + timerp->mCountHistory[hidx] = timerp->mTotalTimeCounter; - timerp->mCountAverage = ((U64)timerp->mCountAverage * cur_frame + timerp->mTotalTimeCounter) / (cur_frame+1); + timerp->mCountAverage = ((F64)timerp->mCountAverage * weight + (F64)timerp->mTotalTimeCounter) / (weight+1); timerp->mCallHistory[hidx] = timerp->getFrameState().mCalls; - timerp->mCallAverage = ((U64)timerp->mCallAverage * cur_frame + timerp->getFrameState().mCalls) / (cur_frame+1); + timerp->mCallAverage = ((F64)timerp->mCallAverage * weight + (F64)timerp->getFrameState().mCalls) / (weight+1); } } } @@ -776,8 +775,10 @@ void LLFastTimer::NamedTimer::reset() timer.mCountAverage = 0; timer.mCallAverage = 0; - memset(timer.mCountHistory, 0, sizeof(U32) * HISTORY_NUM); - memset(timer.mCallHistory, 0, sizeof(U32) * HISTORY_NUM); + timer.mCountHistory.clear(); + timer.mCountHistory.resize(HISTORY_NUM); + timer.mCallHistory.clear(); + timer.mCallHistory.resize(HISTORY_NUM); } } @@ -856,7 +857,8 @@ void LLFastTimer::nextFrame() if (!sPauseHistory) { NamedTimer::processTimes(); - sLastFrameIndex = sCurFrameIndex++; + sLastFrameIndex = sCurFrameIndex; + ++sCurFrameIndex; } // get ready for next frame diff --git a/indra/llcommon/llfasttimer_class.h b/indra/llcommon/llfasttimer_class.h index c6a6f59c1..aa1f8ffac 100644 --- a/indra/llcommon/llfasttimer_class.h +++ b/indra/llcommon/llfasttimer_class.h @@ -66,7 +66,7 @@ public: public: ~NamedTimer(); - enum { HISTORY_NUM = 300 }; + static const int HISTORY_NUM; const std::string& getName() const { return mName; } NamedTimer* getParent() const { return mParent; } @@ -82,8 +82,8 @@ public: void setCollapsed(bool collapsed) { mCollapsed = collapsed; } bool getCollapsed() const { return mCollapsed; } - U32 getCountAverage() const; //{ return mCountAverage } - U32 getCallAverage() const; //{ return mCallAverage } + U32 getCountAverage() const; + U32 getCallAverage() const; U32 getHistoricalCount(S32 history_index = 0) const; U32 getHistoricalCalls(S32 history_index = 0) const; @@ -122,11 +122,11 @@ public: U32 mTotalTimeCounter; - U32 mCountAverage; - U32 mCallAverage; + F64 mCountAverage; + F64 mCallAverage; - U32* mCountHistory; - U32* mCallHistory; + std::vector mCountHistory; + std::vector mCallHistory; // tree structure NamedTimer* mParent; // NamedTimer of caller(parent) @@ -258,11 +258,10 @@ public: static CurTimerData sCurTimerData; static std::string sClockType; -public: +private: static U32 getCPUClockCount32(); static U64 getCPUClockCount64(); -private: static S32 sCurFrameIndex; static S32 sLastFrameIndex; static U64 sLastFrameTime; diff --git a/indra/llcommon/llframetimer.h b/indra/llcommon/llframetimer.h index bae3e5615..2bb1943c8 100644 --- a/indra/llcommon/llframetimer.h +++ b/indra/llcommon/llframetimer.h @@ -44,6 +44,9 @@ #include "lltimer.h" #include "timing.h" #include +#ifdef SHOW_ASSERT +#include "aithreadid.h" // is_main_thread() +#endif class LL_COMMON_API LLFrameTimer { diff --git a/indra/llcommon/llmemory.h b/indra/llcommon/llmemory.h index d01b43917..19cc720b0 100644 --- a/indra/llcommon/llmemory.h +++ b/indra/llcommon/llmemory.h @@ -523,7 +523,6 @@ void LLPrivateMemoryPoolTester::operator delete[](void* addr) //EVENTUALLY REMOVE THESE: #include "llpointer.h" -#include "llrefcount.h" #include "llsingleton.h" #include "llsafehandle.h" diff --git a/indra/llcommon/llpreprocessor.h b/indra/llcommon/llpreprocessor.h index 009871908..a1f598d4a 100644 --- a/indra/llcommon/llpreprocessor.h +++ b/indra/llcommon/llpreprocessor.h @@ -195,4 +195,13 @@ # define LL_COMMON_API #endif // LL_COMMON_LINK_SHARED +// Darwin does not support thread-local data. +#ifndef LL_DARWIN +#if LL_WINDOWS +#define ll_thread_local __declspec(thread) +#else // Linux +#define ll_thread_local __thread +#endif +#endif + #endif // not LL_LINDEN_PREPROCESSOR_H diff --git a/indra/llcommon/llqueuedthread.cpp b/indra/llcommon/llqueuedthread.cpp index 31bb145fe..27e3dca7c 100644 --- a/indra/llcommon/llqueuedthread.cpp +++ b/indra/llcommon/llqueuedthread.cpp @@ -251,7 +251,7 @@ bool LLQueuedThread::addRequest(QueuedRequest* req) // MAIN thread bool LLQueuedThread::waitForResult(LLQueuedThread::handle_t handle, bool auto_complete) { - llassert (handle != nullHandle()) + llassert (handle != nullHandle()); bool res = false; bool waspaused = isPaused(); bool done = false; diff --git a/indra/llcommon/llqueuedthread.h b/indra/llcommon/llqueuedthread.h index 6a3dc4078..9e2e7d969 100644 --- a/indra/llcommon/llqueuedthread.h +++ b/indra/llcommon/llqueuedthread.h @@ -111,6 +111,8 @@ public: return mPriority > second.mPriority; } + virtual void deleteRequest(); // Only method to delete a request + protected: status_t setStatus(status_t newstatus) { @@ -130,7 +132,6 @@ public: virtual bool processRequest() = 0; // Return true when request has completed virtual void finishRequest(bool completed); // Always called from thread after request has completed or aborted - virtual void deleteRequest(); // Only method to delete a request void setPriority(U32 pri) { diff --git a/indra/llcommon/llrefcount.cpp b/indra/llcommon/llrefcount.cpp index e1876599f..0956e1628 100644 --- a/indra/llcommon/llrefcount.cpp +++ b/indra/llcommon/llrefcount.cpp @@ -38,14 +38,6 @@ LLRefCount::LLRefCount(const LLRefCount& other) : mRef(0) { #if LL_REF_COUNT_DEBUG - if(gAPRPoolp) - { - mMutexp = new LLMutex(gAPRPoolp) ; - } - else - { - mMutexp = NULL ; - } mCrashAtUnlock = FALSE ; #endif } @@ -60,14 +52,6 @@ LLRefCount::LLRefCount() : mRef(0) { #if LL_REF_COUNT_DEBUG - if(gAPRPoolp) - { - mMutexp = new LLMutex(gAPRPoolp) ; - } - else - { - mMutexp = NULL ; - } mCrashAtUnlock = FALSE ; #endif } @@ -78,29 +62,20 @@ LLRefCount::~LLRefCount() { llerrs << "deleting non-zero reference" << llendl; } - -#if LL_REF_COUNT_DEBUG - if(gAPRPoolp) - { - delete mMutexp ; - } -#endif } #if LL_REF_COUNT_DEBUG void LLRefCount::ref() const { - if(mMutexp) - { - if(mMutexp->isLocked()) + if(mMutex.isLocked()) { mCrashAtUnlock = TRUE ; llerrs << "the mutex is locked by the thread: " << mLockedThreadID - << " Current thread: " << LLThread::currentID() << llendl ; + << " Current thread: " << AIThreadID() << llendl ; } - mMutexp->lock() ; - mLockedThreadID = LLThread::currentID() ; + mMutex.lock() ; + mLockedThreadID.reset_inline(); mRef++; @@ -108,27 +83,20 @@ void LLRefCount::ref() const { while(1); //crash here. } - mMutexp->unlock() ; - } - else - { - mRef++; - } + mMutex.unlock() ; } S32 LLRefCount::unref() const { - if(mMutexp) - { - if(mMutexp->isLocked()) + if(mMutex.isLocked()) { mCrashAtUnlock = TRUE ; llerrs << "the mutex is locked by the thread: " << mLockedThreadID - << " Current thread: " << LLThread::currentID() << llendl ; + << " Current thread: " << AIThreadID() << llendl ; } - mMutexp->lock() ; - mLockedThreadID = LLThread::currentID() ; + mMutex.lock() ; + mLockedThreadID.reset_inline(); llassert(mRef >= 1); if (0 == --mRef) @@ -137,7 +105,7 @@ S32 LLRefCount::unref() const { while(1); //crash here. } - mMutexp->unlock() ; + mMutex.unlock() ; delete this; return 0; @@ -147,18 +115,7 @@ S32 LLRefCount::unref() const { while(1); //crash here. } - mMutexp->unlock() ; + mMutex.unlock() ; return mRef; - } - else - { - llassert(mRef >= 1); - if (0 == --mRef) - { - delete this; - return 0; - } - return mRef; - } } #endif diff --git a/indra/llcommon/llrefcount.h b/indra/llcommon/llrefcount.h index 8eb5d53f3..b7831e7fa 100644 --- a/indra/llcommon/llrefcount.h +++ b/indra/llcommon/llrefcount.h @@ -30,7 +30,7 @@ #define LL_REF_COUNT_DEBUG 0 #if LL_REF_COUNT_DEBUG -class LLMutex ; +#include "llthread.h" // LLMutexRootPool #endif //---------------------------------------------------------------------------- @@ -80,8 +80,8 @@ private: mutable S32 mRef; #if LL_REF_COUNT_DEBUG - LLMutex* mMutexp ; - mutable U32 mLockedThreadID ; + mutable LLMutexRootPool mMutex ; + mutable AIThreadID mLockedThreadID ; mutable BOOL mCrashAtUnlock ; #endif }; diff --git a/indra/llcommon/llthread.cpp b/indra/llcommon/llthread.cpp index 3716e5dab..5c1a29f24 100644 --- a/indra/llcommon/llthread.cpp +++ b/indra/llcommon/llthread.cpp @@ -29,6 +29,11 @@ * $/LicenseInfo$ */ +#ifdef __GNUC__ +// Generate code for inlines from llthread.h (needed for is_main_thread()). +#pragma implementation "llthread.h" +#endif + #include "linden_common.h" #include "llapr.h" @@ -62,18 +67,12 @@ // //---------------------------------------------------------------------------- -#if !LL_DARWIN -U32 ll_thread_local local_thread_ID = 0; -#endif - -U32 LLThread::sIDIter = 0; LLAtomicS32 LLThread::sCount = 0; LLAtomicS32 LLThread::sRunning = 0; LL_COMMON_API void assert_main_thread() { - static U32 s_thread_id = LLThread::currentID(); - if (LLThread::currentID() != s_thread_id) + if (!AIThreadID::in_main_thread_inline()) { llerrs << "Illegal execution outside main thread." << llendl; } @@ -90,9 +89,8 @@ void *APR_THREAD_FUNC LLThread::staticRun(apr_thread_t *apr_threadp, void *datap LLThread *threadp = (LLThread *)datap; -#if !LL_DARWIN - local_thread_ID = threadp->mID; -#endif + // Initialize thread-local cache of current thread ID (if supported). + AIThreadID::set_current_thread_id(); // Create a thread local data. LLThreadLocalData::create(threadp); @@ -132,7 +130,6 @@ LLThread::LLThread(std::string const& name) : mStatus(STOPPED), mThreadLocalData(NULL) { - mID = ++sIDIter; sCount++; llassert(sCount <= 50); mRunCondition = new LLCondition; @@ -276,12 +273,6 @@ void LLThread::setQuitting() wake(); } -// static -U32 LLThread::currentID() -{ - return (U32)apr_os_thread_current(); -} - // static void LLThread::yield() { @@ -310,15 +301,19 @@ void LLThread::wakeLocked() } } -#ifdef SHOW_ASSERT -// This allows the use of llassert(is_main_thread()) to assure the current thread is the main thread. -static apr_os_thread_t main_thread_id; -LL_COMMON_API bool is_main_thread(void) { return apr_os_thread_equal(main_thread_id, apr_os_thread_current()); } -#endif - // The thread private handle to access the LLThreadLocalData instance. apr_threadkey_t* LLThreadLocalData::sThreadLocalDataKey; +LLThreadLocalData::LLThreadLocalData(char const* name) : mCurlMultiHandle(NULL), mCurlErrorBuffer(NULL), mName(name) +{ +} + +LLThreadLocalData::~LLThreadLocalData() +{ + delete mCurlMultiHandle; + delete [] mCurlErrorBuffer; +} + //static void LLThreadLocalData::init(void) { @@ -328,6 +323,10 @@ void LLThreadLocalData::init(void) return; } + // This function is called by the main thread (these values are also needed in the next line). + AIThreadID::set_main_thread_id(); + AIThreadID::set_current_thread_id(); + apr_status_t status = apr_threadkey_private_create(&sThreadLocalDataKey, &LLThreadLocalData::destroy, LLAPRRootPool::get()()); ll_apr_assert_status(status); // Or out of memory, or system-imposed limit on the // total number of keys per process {PTHREAD_KEYS_MAX} @@ -335,11 +334,6 @@ void LLThreadLocalData::init(void) // Create the thread-local data for the main thread (this function is called by the main thread). LLThreadLocalData::create(NULL); - -#ifdef SHOW_ASSERT - // This function is called by the main thread. - main_thread_id = apr_os_thread_current(); -#endif } // This is called once for every thread when the thread is destructed. @@ -352,7 +346,7 @@ void LLThreadLocalData::destroy(void* thread_local_data) //static void LLThreadLocalData::create(LLThread* threadp) { - LLThreadLocalData* new_tld = new LLThreadLocalData; + LLThreadLocalData* new_tld = new LLThreadLocalData(threadp ? threadp->mName.c_str() : "main thread"); if (threadp) { threadp->mThreadLocalData = new_tld; @@ -406,27 +400,19 @@ void LLCondition::broadcast() //============================================================================ LLMutexBase::LLMutexBase() : - mLockingThread(NO_THREAD), + mLockingThread(AIThreadID::sNone), mCount(0) { } bool LLMutexBase::isSelfLocked() const { -#if LL_DARWIN - return mLockingThread == LLThread::currentID(); -#else - return mLockingThread == local_thread_ID; -#endif + return mLockingThread.equals_current_thread_inline(); } void LLMutexBase::lock() { -#if LL_DARWIN - if (mLockingThread == LLThread::currentID()) -#else - if (mLockingThread == local_thread_ID) -#endif + if (mLockingThread.equals_current_thread_inline()) { //redundant lock mCount++; return; @@ -434,11 +420,33 @@ void LLMutexBase::lock() apr_thread_mutex_lock(mAPRMutexp); -#if LL_DARWIN - mLockingThread = LLThread::currentID(); -#else - mLockingThread = local_thread_ID; -#endif + mLockingThread.reset_inline(); +} + +bool LLMutexBase::tryLock() +{ + if (mLockingThread.equals_current_thread_inline()) + { //redundant lock + mCount++; + return true; + } + bool success = !APR_STATUS_IS_EBUSY(apr_thread_mutex_trylock(mAPRMutexp)); + if (success) + { + mLockingThread.reset_inline(); + } + return success; +} + +// non-blocking, but does do a lock/unlock so not free +bool LLMutexBase::isLocked() const +{ + if (mLockingThread.equals_current_thread_inline()) + return false; // A call to lock() won't block. + if (APR_STATUS_IS_EBUSY(apr_thread_mutex_trylock(mAPRMutexp))) + return true; + apr_thread_mutex_unlock(mAPRMutexp); + return false; } void LLMutexBase::unlock() @@ -448,7 +456,7 @@ void LLMutexBase::unlock() mCount--; return; } - mLockingThread = NO_THREAD; + mLockingThread = AIThreadID::sNone; apr_thread_mutex_unlock(mAPRMutexp); } diff --git a/indra/llcommon/llthread.h b/indra/llcommon/llthread.h index 8def2c7ea..549a090ec 100644 --- a/indra/llcommon/llthread.h +++ b/indra/llcommon/llthread.h @@ -33,29 +33,29 @@ #ifndef LL_LLTHREAD_H #define LL_LLTHREAD_H +#ifdef __GNUC__ +// Needed for is_main_thread() when compiling with optimization (relwithdebinfo). +// It doesn't hurt to just always specify it though. +#pragma interface +#endif + #include "llapp.h" #include "llapr.h" #include "llmemory.h" #include "apr_thread_cond.h" #include "llaprpool.h" #include "llatomic.h" - -#ifdef SHOW_ASSERT -extern LL_COMMON_API bool is_main_thread(void); -#define ASSERT_SINGLE_THREAD do { static apr_os_thread_t first_thread_id = apr_os_thread_current(); llassert(apr_os_thread_equal(first_thread_id, apr_os_thread_current())); } while(0) -#else -#define ASSERT_SINGLE_THREAD do { } while(0) -#endif +#include "aithreadid.h" class LLThread; class LLMutex; class LLCondition; -#if LL_WINDOWS -#define ll_thread_local __declspec(thread) -#else -#define ll_thread_local __thread -#endif +class LL_COMMON_API LLThreadLocalDataMember +{ +public: + virtual ~LLThreadLocalDataMember() { }; +}; class LL_COMMON_API LLThreadLocalData { @@ -66,13 +66,23 @@ public: // Thread-local memory pool. LLAPRRootPool mRootPool; LLVolatileAPRPool mVolatileAPRPool; + LLThreadLocalDataMember* mCurlMultiHandle; // Initialized by AICurlMultiHandle::getInstance + char* mCurlErrorBuffer; // NULL, or pointing to a buffer used by libcurl. + std::string mName; // "main thread", or a copy of LLThread::mName. static void init(void); static void destroy(void* thread_local_data); static void create(LLThread* pthread); static LLThreadLocalData& tldata(void); + +private: + LLThreadLocalData(char const* name); + ~LLThreadLocalData(); }; +// Print to llerrs if the current thread is not the main thread. +LL_COMMON_API void assert_main_thread(); + class LL_COMMON_API LLThread { private: @@ -95,7 +105,6 @@ public: bool isQuitting() const { return (QUITTING == mStatus); } bool isStopped() const { return (STOPPED == mStatus); } - static U32 currentID(); // Return ID of current thread static S32 getCount() { return sCount; } static S32 getRunning() { return sRunning; } static void yield(); // Static because it can be called by the main thread, which doesn't have an LLThread data structure. @@ -122,8 +131,6 @@ public: // Return thread-local data for the current thread. static LLThreadLocalData& tldata(void) { return LLThreadLocalData::tldata(); } - U32 getID() const { return mID; } - private: bool mPaused; @@ -136,7 +143,6 @@ protected: apr_thread_t *mAPRThreadp; volatile EThreadStatus mStatus; - U32 mID; friend void LLThreadLocalData::create(LLThread* threadp); LLThreadLocalData* mThreadLocalData; @@ -165,6 +171,12 @@ protected: // mRunCondition->unlock(); }; +#ifdef SHOW_ASSERT +#define ASSERT_SINGLE_THREAD do { static AIThreadID first_thread_id; llassert(first_thread_id.equals_current_thread()); } while(0) +#else +#define ASSERT_SINGLE_THREAD do { } while(0) +#endif + //============================================================================ #define MUTEX_DEBUG (LL_DEBUG || LL_RELEASE_WITH_DEBUG_INFO) @@ -180,32 +192,29 @@ protected: class LL_COMMON_API LLMutexBase { public: - typedef enum - { - NO_THREAD = 0xFFFFFFFF - } e_locking_thread; - LLMutexBase() ; void lock(); // blocks void unlock(); // Returns true if lock was obtained successfully. - bool tryLock() { return !APR_STATUS_IS_EBUSY(apr_thread_mutex_trylock(mAPRMutexp)); } + bool tryLock(); - // non-blocking, but does do a lock/unlock so not free - bool isLocked() { bool is_not_locked = tryLock(); if (is_not_locked) unlock(); return !is_not_locked; } + // Returns true if a call to lock() would block (returns false if self-locked()). + bool isLocked() const; // Returns true if locked by this thread. bool isSelfLocked() const; - // get ID of locking thread - U32 lockingThread() const { return mLockingThread; } - protected: // mAPRMutexp is initialized and uninitialized in the derived class. apr_thread_mutex_t* mAPRMutexp; mutable U32 mCount; - mutable U32 mLockingThread; + mutable AIThreadID mLockingThread; + +private: + // Disallow copy construction and assignment. + LLMutexBase(LLMutexBase const&); + LLMutexBase& operator=(LLMutexBase const&); }; class LL_COMMON_API LLMutex : public LLMutexBase @@ -225,10 +234,6 @@ public: protected: LLAPRPool mPool; -private: - // Disable copy construction, as si teh bomb!!! -SG - LLMutex(const LLMutex&); - LLMutex& operator=(const LLMutex&); }; #if APR_HAS_THREADS @@ -289,7 +294,7 @@ private: LLMutexBase* mMutex; }; -class AIRWLock +class LL_COMMON_API AIRWLock { public: AIRWLock(LLAPRPool& parent = LLThread::tldata().mRootPool) : diff --git a/indra/llcommon/llversionviewer.h.in b/indra/llcommon/llversionviewer.h.in index 82285523d..40933fcd4 100644 --- a/indra/llcommon/llversionviewer.h.in +++ b/indra/llcommon/llversionviewer.h.in @@ -35,7 +35,7 @@ const S32 LL_VERSION_MAJOR = 1; const S32 LL_VERSION_MINOR = 7; -const S32 LL_VERSION_PATCH = 1; +const S32 LL_VERSION_PATCH = 2; const S32 LL_VERSION_BUILD = ${vBUILD}; const char * const LL_CHANNEL = "${VIEWER_CHANNEL}"; diff --git a/indra/llcrashlogger/llcrashlogger.cpp b/indra/llcrashlogger/llcrashlogger.cpp index a8be44313..c42697ad9 100644 --- a/indra/llcrashlogger/llcrashlogger.cpp +++ b/indra/llcrashlogger/llcrashlogger.cpp @@ -371,6 +371,9 @@ void LLCrashLogger::updateApplication(const std::string& message) bool LLCrashLogger::init() { + // Initialize curl + AICurlInterface::initCurl(); + // We assume that all the logs we're looking for reside on the current drive gDirUtilp->initAppDirs("SecondLife"); diff --git a/indra/llmessage/CMakeLists.txt b/indra/llmessage/CMakeLists.txt index b09245ba3..aac669137 100644 --- a/indra/llmessage/CMakeLists.txt +++ b/indra/llmessage/CMakeLists.txt @@ -29,7 +29,9 @@ set(llmessage_SOURCE_FILES llchainio.cpp llcircuit.cpp llclassifiedflags.cpp - llcurl.cpp + aicurl.cpp + debug_libcurl.cpp + aicurlthread.cpp lldatapacker.cpp lldispatcher.cpp llfiltersd2xmlrpc.cpp @@ -66,8 +68,6 @@ set(llmessage_SOURCE_FILES llsdmessage.cpp llsdmessagebuilder.cpp llsdmessagereader.cpp - llsdrpcclient.cpp - llsdrpcserver.cpp llservicebuilder.cpp llservice.cpp llstoredmessage.cpp @@ -117,6 +117,10 @@ set(llmessage_HEADER_FILES llcircuit.h llclassifiedflags.h llcurl.h + aicurl.h + debug_libcurl.h + aicurlprivate.h + aicurlthread.h lldatapacker.h lldbstrings.h lldispatcher.h @@ -164,8 +168,6 @@ set(llmessage_HEADER_FILES llsdmessage.h llsdmessagebuilder.h llsdmessagereader.h - llsdrpcclient.h - llsdrpcserver.h llservice.h llservicebuilder.h llstoredmessage.h diff --git a/indra/llmessage/aicurl.cpp b/indra/llmessage/aicurl.cpp new file mode 100644 index 000000000..8237fcaa4 --- /dev/null +++ b/indra/llmessage/aicurl.cpp @@ -0,0 +1,1502 @@ +/** + * @file aicurl.cpp + * @brief Implementation of AICurl. + * + * Copyright (c) 2012, Aleric Inglewood. + * Copyright (C) 2010, Linden Research, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution. + * + * CHANGELOG + * and additional copyright holders. + * + * 17/03/2012 + * Initial version, written by Aleric Inglewood @ SL + * + * 20/03/2012 + * Added copyright notice for Linden Lab for those parts that were + * copied or derived from llcurl.cpp. The code of those parts are + * already in their own llcurl.cpp, so they do not ever need to + * even look at this file; the reason I added the copyright notice + * is to make clear that I am not the author of 100% of this code + * and hence I cannot change the license of it. + */ + +#include "linden_common.h" + +#if LL_WINDOWS +#include //remove classic winsock from windows.h +#endif +#define OPENSSL_THREAD_DEFINES +#include // OPENSSL_THREADS +#include +#include + +#include "aicurl.h" +#include "llbufferstream.h" +#include "llsdserialize.h" +#include "aithreadsafe.h" +#include "llqueuedthread.h" +#include "lltimer.h" // ms_sleep +#include "llproxy.h" +#include "llhttpstatuscodes.h" +#ifdef CWDEBUG +#include +#endif + +//================================================================================== +// Local variables. +// + +namespace { + +struct CertificateAuthority { + std::string file; + std::string path; +}; + +AIThreadSafeSimpleDC gCertificateAuthority; +typedef AIAccess CertificateAuthority_wat; +typedef AIAccessConst CertificateAuthority_rat; + +enum gSSLlib_type { + ssl_unknown, + ssl_openssl, + ssl_gnutls, + ssl_nss +}; + +// No locking needed: initialized before threads are created, and subsequently only read. +gSSLlib_type gSSLlib; +bool gSetoptParamsNeedDup; +void (*statemachines_flush_hook)(void); + +} // namespace + +// See http://www.openssl.org/docs/crypto/threads.html: +// CRYPTO_THREADID and associated functions were introduced in OpenSSL 1.0.0 to replace +// (actually, deprecate) the previous CRYPTO_set_id_callback(), CRYPTO_get_id_callback(), +// and CRYPTO_thread_id() functions which assumed thread IDs to always be represented by +// 'unsigned long'. +#define HAVE_CRYPTO_THREADID (OPENSSL_VERSION_NUMBER >= (1 << 28)) + +//----------------------------------------------------------------------------------- +// Needed for thread-safe openSSL operation. + +// Must be defined in global namespace. +struct CRYPTO_dynlock_value +{ + AIRWLock rwlock; +}; + +namespace { + +AIRWLock* ssl_rwlock_array; + +// OpenSSL locking function. +void ssl_locking_function(int mode, int n, char const* file, int line) +{ + if ((mode & CRYPTO_LOCK)) + { + if ((mode & CRYPTO_READ)) + ssl_rwlock_array[n].rdlock(); + else + ssl_rwlock_array[n].wrlock(); + } + else + { + if ((mode & CRYPTO_READ)) + ssl_rwlock_array[n].rdunlock(); + else + ssl_rwlock_array[n].wrunlock(); + } +} + +#if HAVE_CRYPTO_THREADID +// OpenSSL uniq id function. +void ssl_id_function(CRYPTO_THREADID* thread_id) +{ +#if LL_WINDOWS || LL_DARWIN // apr_os_thread_current() returns a pointer, + CRYPTO_THREADID_set_pointer(thread_id, apr_os_thread_current()); +#else // else it returns an unsigned long. + CRYPTO_THREADID_set_numeric(thread_id, apr_os_thread_current()); +#endif +} +#endif // HAVE_CRYPTO_THREADID + +// OpenSSL allocate and initialize dynamic crypto lock. +CRYPTO_dynlock_value* ssl_dyn_create_function(char const* file, int line) +{ + return new CRYPTO_dynlock_value; +} + +// OpenSSL destroy dynamic crypto lock. +void ssl_dyn_destroy_function(CRYPTO_dynlock_value* l, char const* file, int line) +{ + delete l; +} + +// OpenSSL dynamic locking function. +void ssl_dyn_lock_function(int mode, CRYPTO_dynlock_value* l, char const* file, int line) +{ + if ((mode & CRYPTO_LOCK)) + { + if ((mode & CRYPTO_READ)) + l->rwlock.rdlock(); + else + l->rwlock.wrlock(); + } + else + { + if ((mode & CRYPTO_READ)) + l->rwlock.rdunlock(); + else + l->rwlock.wrunlock(); + } +} + +typedef void (*ssl_locking_function_type)(int, int, char const*, int); +#if HAVE_CRYPTO_THREADID +typedef void (*ssl_id_function_type)(CRYPTO_THREADID*); +#else +typedef unsigned long (*ulong_thread_id_function_type)(void); +#endif +typedef CRYPTO_dynlock_value* (*ssl_dyn_create_function_type)(char const*, int); +typedef void (*ssl_dyn_destroy_function_type)(CRYPTO_dynlock_value*, char const*, int); +typedef void (*ssl_dyn_lock_function_type)(int, CRYPTO_dynlock_value*, char const*, int); + +ssl_locking_function_type old_ssl_locking_function; +#if HAVE_CRYPTO_THREADID +ssl_id_function_type old_ssl_id_function; +#else +ulong_thread_id_function_type old_ulong_thread_id_function; +#endif +ssl_dyn_create_function_type old_ssl_dyn_create_function; +ssl_dyn_destroy_function_type old_ssl_dyn_destroy_function; +ssl_dyn_lock_function_type old_ssl_dyn_lock_function; + +#if LL_WINDOWS +static unsigned long __cdecl apr_os_thread_current_wrapper() +{ + return (unsigned long)apr_os_thread_current(); +} +#endif + +// Set for openssl-1.0.1...1.0.1c. +static bool need_renegotiation_hack = false; + +// Initialize OpenSSL library for thread-safety. +void ssl_init(void) +{ + // The version identifier format is: MMNNFFPPS: major minor fix patch status. + int const compiled_openSSL_major = (OPENSSL_VERSION_NUMBER >> 28) & 0xff; + int const compiled_openSSL_minor = (OPENSSL_VERSION_NUMBER >> 20) & 0xff; + unsigned long const ssleay = SSLeay(); + int const linked_openSSL_major = (ssleay >> 28) & 0xff; + int const linked_openSSL_minor = (ssleay >> 20) & 0xff; + // Check if dynamically loaded version is compatible with the one we compiled against. + // As off version 1.0.0 also minor versions are compatible. + if (linked_openSSL_major != compiled_openSSL_major || + (linked_openSSL_major == 0 && linked_openSSL_minor != compiled_openSSL_minor)) + { + llerrs << "The viewer was compiled against " << OPENSSL_VERSION_TEXT << + " but linked against " << SSLeay_version(SSLEAY_VERSION) << + ". Those versions are not compatible." << llendl; + } + // Static locks vector. + ssl_rwlock_array = new AIRWLock[CRYPTO_num_locks()]; + // Static locks callbacks. + old_ssl_locking_function = CRYPTO_get_locking_callback(); +#if HAVE_CRYPTO_THREADID + old_ssl_id_function = CRYPTO_THREADID_get_callback(); +#else + old_ulong_thread_id_function = CRYPTO_get_id_callback(); +#endif + CRYPTO_set_locking_callback(&ssl_locking_function); + // Setting this avoids the need for a thread-local error number facility, which is hard to check. +#if HAVE_CRYPTO_THREADID + CRYPTO_THREADID_set_callback(&ssl_id_function); +#else +#if LL_WINDOWS + CRYPTO_set_id_callback(&apr_os_thread_current_wrapper); +#else + CRYPTO_set_id_callback(&apr_os_thread_current); +#endif +#endif + // Dynamic locks callbacks. + old_ssl_dyn_create_function = CRYPTO_get_dynlock_create_callback(); + old_ssl_dyn_lock_function = CRYPTO_get_dynlock_lock_callback(); + old_ssl_dyn_destroy_function = CRYPTO_get_dynlock_destroy_callback(); + CRYPTO_set_dynlock_create_callback(&ssl_dyn_create_function); + CRYPTO_set_dynlock_lock_callback(&ssl_dyn_lock_function); + CRYPTO_set_dynlock_destroy_callback(&ssl_dyn_destroy_function); + need_renegotiation_hack = (0x10001000UL <= ssleay); + llinfos << "Successful initialization of " << + SSLeay_version(SSLEAY_VERSION) << " (0x" << std::hex << SSLeay() << ")." << llendl; +} + +// Cleanup OpenSSL library thread-safety. +void ssl_cleanup(void) +{ + // Dynamic locks callbacks. + CRYPTO_set_dynlock_destroy_callback(old_ssl_dyn_destroy_function); + CRYPTO_set_dynlock_lock_callback(old_ssl_dyn_lock_function); + CRYPTO_set_dynlock_create_callback(old_ssl_dyn_create_function); + // Static locks callbacks. +#if HAVE_CRYPTO_THREADID + CRYPTO_THREADID_set_callback(old_ssl_id_function); +#else + CRYPTO_set_id_callback(old_ulong_thread_id_function); +#endif + CRYPTO_set_locking_callback(old_ssl_locking_function); + // Static locks vector. + delete [] ssl_rwlock_array; +} + +} // namespace openSSL +//----------------------------------------------------------------------------------- + +static unsigned int encoded_version(int major, int minor, int patch) +{ + return (major << 16) | (minor << 8) | patch; +} + +//================================================================================== +// External API +// + +#undef AICurlPrivate + +namespace AICurlInterface { + +// MAIN-THREAD +void initCurl(void (*flush_hook)()) +{ + DoutEntering(dc::curl, "AICurlInterface::initCurl(" << (void*)flush_hook << ")"); + + llassert(LLThread::getRunning() == 0); // We must not call curl_global_init unless we are the only thread. + CURLcode res = curl_global_init(CURL_GLOBAL_ALL); + if (res != CURLE_OK) + { + llerrs << "curl_global_init(CURL_GLOBAL_ALL) failed." << llendl; + } + + // Print version and do some feature sanity checks. + { + curl_version_info_data* version_info = curl_version_info(CURLVERSION_NOW); + + llassert_always(version_info->age >= 0); + if (version_info->age < 1) + { + llwarns << "libcurl's age is 0; no ares support." << llendl; + } + llassert_always((version_info->features & CURL_VERSION_SSL)); // SSL support, added in libcurl 7.10. + if (!(version_info->features & CURL_VERSION_ASYNCHDNS)) // Asynchronous name lookups (added in libcurl 7.10.7). + { + llwarns << "libcurl was not compiled with support for asynchronous name lookups!" << llendl; + } + if (!version_info->ssl_version) + { + llerrs << "This libcurl has no SSL support!" << llendl; + } + + llinfos << "Successful initialization of libcurl " << + version_info->version << " (0x" << std::hex << version_info->version_num << "), (" << + version_info->ssl_version; + if (version_info->libz_version) + { + llcont << ", libz/" << version_info->libz_version; + } + llcont << ")." << llendl; + + // Detect SSL library used. + gSSLlib = ssl_unknown; + std::string ssl_version(version_info->ssl_version); + if (ssl_version.find("OpenSSL") != std::string::npos) + gSSLlib = ssl_openssl; // See http://www.openssl.org/docs/crypto/threads.html#DESCRIPTION + else if (ssl_version.find("GnuTLS") != std::string::npos) + gSSLlib = ssl_gnutls; // See http://www.gnu.org/software/gnutls/manual/html_node/Thread-safety.html + else if (ssl_version.find("NSS") != std::string::npos) + gSSLlib = ssl_nss; // Supposedly thread-safe without any requirements. + + // Set up thread-safety requirements of underlaying SSL library. + // See http://curl.haxx.se/libcurl/c/libcurl-tutorial.html + switch (gSSLlib) + { + case ssl_unknown: + { + llerrs << "Unknown SSL library \"" << version_info->ssl_version << "\", required actions for thread-safe handling are unknown! Bailing out." << llendl; + } + case ssl_openssl: + { +#ifndef OPENSSL_THREADS + llerrs << "OpenSSL was not configured with thread support! Bailing out." << llendl; +#endif + ssl_init(); + } + case ssl_gnutls: + { + // I don't think we ever get here, do we? --Aleric + llassert_always(gSSLlib != ssl_gnutls); + // If we do, then didn't curl_global_init already call gnutls_global_init? + // It seems there is nothing to do for us here. + } + case ssl_nss: + { + break; // No requirements. + } + } + + // Before version 7.17.0, strings were not copied. Instead the user was forced keep them available until libcurl no longer needed them. + gSetoptParamsNeedDup = (version_info->version_num < encoded_version(7, 17, 0)); + if (gSetoptParamsNeedDup) + { + llwarns << "Your libcurl version is too old." << llendl; + } + llassert_always(!gSetoptParamsNeedDup); // Might add support later. + } + + // Called in cleanupCurl. + statemachines_flush_hook = flush_hook; +} + +// MAIN-THREAD +void cleanupCurl(void) +{ + using namespace AICurlPrivate; + + DoutEntering(dc::curl, "AICurlInterface::cleanupCurl()"); + + stopCurlThread(); + if (CurlMultiHandle::getTotalMultiHandles() != 0) + llwarns << "Not all CurlMultiHandle objects were destroyed!" << llendl; + if (statemachines_flush_hook) + (*statemachines_flush_hook)(); + Stats::print(); + ssl_cleanup(); + + llassert(LLThread::getRunning() <= (curlThreadIsRunning() ? 1 : 0)); // We must not call curl_global_cleanup unless we are the only thread left. + curl_global_cleanup(); +} + +// THREAD-SAFE +std::string getVersionString(void) +{ + // libcurl is thread safe, no locking needed. + return curl_version(); +} + +// THREAD-SAFE +void setCAFile(std::string const& file) +{ + CertificateAuthority_wat CertificateAuthority_w(gCertificateAuthority); + CertificateAuthority_w->file = file; +} + +// This function is not called from anywhere, but provided as part of AICurlInterface because setCAFile is. +// THREAD-SAFE +void setCAPath(std::string const& path) +{ + CertificateAuthority_wat CertificateAuthority_w(gCertificateAuthority); + CertificateAuthority_w->path = path; +} + +// THREAD-SAFE +std::string strerror(CURLcode errorcode) +{ + // libcurl is thread safe, no locking needed. + return curl_easy_strerror(errorcode); +} + +//----------------------------------------------------------------------------- +// class Responder +// + +Responder::Responder(void) : mReferenceCount(0) +{ + DoutEntering(dc::curl, "AICurlInterface::Responder() with this = " << (void*)this); +} + +Responder::~Responder() +{ + DoutEntering(dc::curl, "AICurlInterface::Responder::~Responder() with this = " << (void*)this << "; mReferenceCount = " << mReferenceCount); + llassert(mReferenceCount == 0); +} + +void Responder::setURL(std::string const& url) +{ + // setURL is called from llhttpclient.cpp (request()), before calling any of the below (of course). + // We don't need locking here therefore; it's a case of initializing before use. + mURL = url; +} + +// Called with HTML header. +// virtual +void Responder::completedHeader(U32, std::string const&, LLSD const&) +{ + // Nothing. +} + +// Called with HTML body. +// virtual +void Responder::completedRaw(U32 status, std::string const& reason, LLChannelDescriptors const& channels, LLIOPipe::buffer_ptr_t const& buffer) +{ + LLSD content; + LLBufferStream istr(channels, buffer.get()); + if (!LLSDSerialize::fromXML(content, istr)) + { + llinfos << "Failed to deserialize LLSD. " << mURL << " [" << status << "]: " << reason << llendl; + } + + // Allow derived class to override at this point. + completed(status, reason, content); +} + +void Responder::fatalError(std::string const& reason) +{ + llwarns << "Responder::fatalError(\"" << reason << "\") is called (" << mURL << "). Passing it to Responder::completed with fake HTML error status and empty HTML body!" << llendl; + completed(U32_MAX, reason, LLSD()); +} + +// virtual +void Responder::completed(U32 status, std::string const& reason, LLSD const& content) +{ + // HTML status good? + if (200 <= status && status < 300) + { + // Allow derived class to override at this point. + result(content); + } + else + { + // Allow derived class to override at this point. + errorWithContent(status, reason, content); + } +} + +// virtual +void Responder::errorWithContent(U32 status, std::string const& reason, LLSD const&) +{ + // Allow derived class to override at this point. + error(status, reason); +} + +// virtual +void Responder::error(U32 status, std::string const& reason) +{ + llinfos << mURL << " [" << status << "]: " << reason << llendl; +} + +// virtual +void Responder::result(LLSD const&) +{ + // Nothing. +} + +// Friend functions. + +void intrusive_ptr_add_ref(Responder* responder) +{ + responder->mReferenceCount++; +} + +void intrusive_ptr_release(Responder* responder) +{ + if (--responder->mReferenceCount == 0) + { + delete responder; + } +} + +} // namespace AICurlInterface +//================================================================================== + + +//================================================================================== +// Local implementation. +// + +namespace AICurlPrivate { + +//static +LLAtomicU32 Stats::easy_calls; +LLAtomicU32 Stats::easy_errors; +LLAtomicU32 Stats::easy_init_calls; +LLAtomicU32 Stats::easy_init_errors; +LLAtomicU32 Stats::easy_cleanup_calls; +LLAtomicU32 Stats::multi_calls; +LLAtomicU32 Stats::multi_errors; + +//static +void Stats::print(void) +{ + llinfos_nf << "============ CURL STATS ============" << llendl; + llinfos_nf << " Curl multi errors/calls : " << std::dec << multi_errors << "/" << multi_calls << llendl; + llinfos_nf << " Curl easy errors/calls : " << std::dec << easy_errors << "/" << easy_calls << llendl; + llinfos_nf << " curl_easy_init() errors/calls : " << std::dec << easy_init_errors << "/" << easy_init_calls << llendl; + llinfos_nf << " Current number of curl easy handles: " << std::dec << (easy_init_calls - easy_init_errors - easy_cleanup_calls) << llendl; + llinfos_nf << "========= END OF CURL STATS =========" << llendl; +} + +// THREAD-SAFE +void handle_multi_error(CURLMcode code) +{ + Stats::multi_errors++; + llinfos << "curl multi error detected: " << curl_multi_strerror(code) << + "; (errors/calls = " << Stats::multi_errors << "/" << Stats::multi_calls << ")" << llendl; +} + +//============================================================================= +// AICurlEasyRequest (base classes) +// + +//----------------------------------------------------------------------------- +// CurlEasyHandle + +// THREAD-SAFE +//static +void CurlEasyHandle::handle_easy_error(CURLcode code) +{ + char* error_buffer = LLThreadLocalData::tldata().mCurlErrorBuffer; + llinfos << "curl easy error detected: " << curl_easy_strerror(code); + if (error_buffer && *error_buffer != '\0') + { + llcont << ": " << error_buffer; + } + Stats::easy_errors++; + llcont << "; (errors/calls = " << Stats::easy_errors << "/" << Stats::easy_calls << ")" << llendl; +} + +// Throws AICurlNoEasyHandle. +CurlEasyHandle::CurlEasyHandle(void) : mActiveMultiHandle(NULL), mErrorBuffer(NULL), mQueuedForRemoval(false) +#ifdef SHOW_ASSERT + , mRemovedPerCommand(true) +#endif +{ + mEasyHandle = curl_easy_init(); +#if 0 + // Fake curl_easy_init() failures: throw once every 10 times (for debugging purposes). + static int count = 0; + if (mEasyHandle && (++count % 10) == 5) + { + curl_easy_cleanup(mEasyHandle); + mEasyHandle = NULL; + } +#endif + Stats::easy_init_calls++; + if (!mEasyHandle) + { + Stats::easy_init_errors++; + throw AICurlNoEasyHandle("curl_easy_init() returned NULL"); + } +} + +#if 0 // Not used +CurlEasyHandle::CurlEasyHandle(CurlEasyHandle const& orig) : mActiveMultiHandle(NULL), mErrorBuffer(NULL) +#ifdef SHOW_ASSERT + , mRemovedPerCommand(true) +#endif +{ + mEasyHandle = curl_easy_duphandle(orig.mEasyHandle); + Stats::easy_init_calls++; + if (!mEasyHandle) + { + Stats::easy_init_errors++; + throw AICurlNoEasyHandle("curl_easy_duphandle() returned NULL"); + } +} +#endif + +CurlEasyHandle::~CurlEasyHandle() +{ + llassert(!mActiveMultiHandle); + curl_easy_cleanup(mEasyHandle); + Stats::easy_cleanup_calls++; +} + +//static +char* CurlEasyHandle::getTLErrorBuffer(void) +{ + LLThreadLocalData& tldata = LLThreadLocalData::tldata(); + if (!tldata.mCurlErrorBuffer) + { + tldata.mCurlErrorBuffer = new char[CURL_ERROR_SIZE]; + } + return tldata.mCurlErrorBuffer; +} + +void CurlEasyHandle::setErrorBuffer(void) +{ + char* error_buffer = getTLErrorBuffer(); + if (mErrorBuffer != error_buffer) + { + mErrorBuffer = error_buffer; + CURLcode res = curl_easy_setopt(mEasyHandle, CURLOPT_ERRORBUFFER, error_buffer); + if (res != CURLE_OK) + { + llwarns << "curl_easy_setopt(" << (void*)mEasyHandle << "CURLOPT_ERRORBUFFER, " << (void*)error_buffer << ") failed with error " << res << llendl; + mErrorBuffer = NULL; + } + } +} + +CURLcode CurlEasyHandle::getinfo_priv(CURLINFO info, void* data) +{ + setErrorBuffer(); + return check_easy_code(curl_easy_getinfo(mEasyHandle, info, data)); +} + +char* CurlEasyHandle::escape(char* url, int length) +{ + return curl_easy_escape(mEasyHandle, url, length); +} + +char* CurlEasyHandle::unescape(char* url, int inlength , int* outlength) +{ + return curl_easy_unescape(mEasyHandle, url, inlength, outlength); +} + +CURLcode CurlEasyHandle::perform(void) +{ + llassert(!mActiveMultiHandle); + setErrorBuffer(); + return check_easy_code(curl_easy_perform(mEasyHandle)); +} + +CURLcode CurlEasyHandle::pause(int bitmask) +{ + setErrorBuffer(); + return check_easy_code(curl_easy_pause(mEasyHandle, bitmask)); +} + +CURLMcode CurlEasyHandle::add_handle_to_multi(AICurlEasyRequest_wat& curl_easy_request_w, CURLM* multi) +{ + llassert_always(!mActiveMultiHandle && multi); + mActiveMultiHandle = multi; + CURLMcode res = check_multi_code(curl_multi_add_handle(multi, mEasyHandle)); + added_to_multi_handle(curl_easy_request_w); + return res; +} + +CURLMcode CurlEasyHandle::remove_handle_from_multi(AICurlEasyRequest_wat& curl_easy_request_w, CURLM* multi) +{ + llassert_always(mActiveMultiHandle && mActiveMultiHandle == multi); + mActiveMultiHandle = NULL; + CURLMcode res = check_multi_code(curl_multi_remove_handle(multi, mEasyHandle)); + removed_from_multi_handle(curl_easy_request_w); + mPostField = NULL; + return res; +} + +void intrusive_ptr_add_ref(ThreadSafeCurlEasyRequest* threadsafe_curl_easy_request) +{ + threadsafe_curl_easy_request->mReferenceCount++; +} + +void intrusive_ptr_release(ThreadSafeCurlEasyRequest* threadsafe_curl_easy_request) +{ + if (--threadsafe_curl_easy_request->mReferenceCount == 0) + { + delete threadsafe_curl_easy_request; + } +} + +CURLcode CurlEasyHandle::setopt(CURLoption option, long parameter) +{ + llassert((CURLOPTTYPE_LONG <= option && option < CURLOPTTYPE_LONG + 1000) || + (sizeof(curl_off_t) == sizeof(long) && + CURLOPTTYPE_OFF_T <= option && option < CURLOPTTYPE_OFF_T + 1000)); + llassert(!mActiveMultiHandle); + setErrorBuffer(); + return check_easy_code(curl_easy_setopt(mEasyHandle, option, parameter)); +} + +// The standard requires that sizeof(long) < sizeof(long long), so it's safe to overload like this. +// We assume that one of them is 64 bit, the size of curl_off_t. +CURLcode CurlEasyHandle::setopt(CURLoption option, long long parameter) +{ + llassert(sizeof(curl_off_t) == sizeof(long long) && + CURLOPTTYPE_OFF_T <= option && option < CURLOPTTYPE_OFF_T + 1000); + llassert(!mActiveMultiHandle); + setErrorBuffer(); + return check_easy_code(curl_easy_setopt(mEasyHandle, option, parameter)); +} + +CURLcode CurlEasyHandle::setopt(CURLoption option, void const* parameter) +{ + llassert(CURLOPTTYPE_OBJECTPOINT <= option && option < CURLOPTTYPE_OBJECTPOINT + 1000); + setErrorBuffer(); + return check_easy_code(curl_easy_setopt(mEasyHandle, option, parameter)); +} + +#define DEFINE_FUNCTION_SETOPT1(function_type, opt1) \ + CURLcode CurlEasyHandle::setopt(CURLoption option, function_type parameter) \ + { \ + llassert(option == opt1); \ + setErrorBuffer(); \ + return check_easy_code(curl_easy_setopt(mEasyHandle, option, parameter)); \ + } + +#define DEFINE_FUNCTION_SETOPT3(function_type, opt1, opt2, opt3) \ + CURLcode CurlEasyHandle::setopt(CURLoption option, function_type parameter) \ + { \ + llassert(option == opt1 || option == opt2 || option == opt3); \ + setErrorBuffer(); \ + return check_easy_code(curl_easy_setopt(mEasyHandle, option, parameter)); \ + } + +#define DEFINE_FUNCTION_SETOPT4(function_type, opt1, opt2, opt3, opt4) \ + CURLcode CurlEasyHandle::setopt(CURLoption option, function_type parameter) \ + { \ + llassert(option == opt1 || option == opt2 || option == opt3 || option == opt4); \ + setErrorBuffer(); \ + return check_easy_code(curl_easy_setopt(mEasyHandle, option, parameter)); \ + } + +DEFINE_FUNCTION_SETOPT1(curl_debug_callback, CURLOPT_DEBUGFUNCTION) +DEFINE_FUNCTION_SETOPT4(curl_write_callback, CURLOPT_HEADERFUNCTION, CURLOPT_WRITEFUNCTION, CURLOPT_INTERLEAVEFUNCTION, CURLOPT_READFUNCTION) +//DEFINE_FUNCTION_SETOPT1(curl_read_callback, CURLOPT_READFUNCTION) +DEFINE_FUNCTION_SETOPT1(curl_ssl_ctx_callback, CURLOPT_SSL_CTX_FUNCTION) +DEFINE_FUNCTION_SETOPT3(curl_conv_callback, CURLOPT_CONV_FROM_NETWORK_FUNCTION, CURLOPT_CONV_TO_NETWORK_FUNCTION, CURLOPT_CONV_FROM_UTF8_FUNCTION) +#if 0 // Not used by the viewer. +DEFINE_FUNCTION_SETOPT1(curl_progress_callback, CURLOPT_PROGRESSFUNCTION) +DEFINE_FUNCTION_SETOPT1(curl_seek_callback, CURLOPT_SEEKFUNCTION) +DEFINE_FUNCTION_SETOPT1(curl_ioctl_callback, CURLOPT_IOCTLFUNCTION) +DEFINE_FUNCTION_SETOPT1(curl_sockopt_callback, CURLOPT_SOCKOPTFUNCTION) +DEFINE_FUNCTION_SETOPT1(curl_opensocket_callback, CURLOPT_OPENSOCKETFUNCTION) +DEFINE_FUNCTION_SETOPT1(curl_closesocket_callback, CURLOPT_CLOSESOCKETFUNCTION) +DEFINE_FUNCTION_SETOPT1(curl_sshkeycallback, CURLOPT_SSH_KEYFUNCTION) +DEFINE_FUNCTION_SETOPT1(curl_chunk_bgn_callback, CURLOPT_CHUNK_BGN_FUNCTION) +DEFINE_FUNCTION_SETOPT1(curl_chunk_end_callback, CURLOPT_CHUNK_END_FUNCTION) +DEFINE_FUNCTION_SETOPT1(curl_fnmatch_callback, CURLOPT_FNMATCH_FUNCTION) +#endif + +//----------------------------------------------------------------------------- +// CurlEasyRequest + +void CurlEasyRequest::setoptString(CURLoption option, std::string const& value) +{ + llassert(!gSetoptParamsNeedDup); + setopt(option, value.c_str()); +} + +void CurlEasyRequest::setPost(AIPostFieldPtr const& postdata, S32 size) +{ + llassert_always(postdata->data()); + + Dout(dc::curl, "POST size is " << size << " bytes: \"" << libcwd::buf2str(postdata->data(), size) << "\"."); + setPostField(postdata); // Make sure the data stays around until we don't need it anymore. + + setPost_raw(size, postdata->data()); +} + +void CurlEasyRequest::setPost_raw(S32 size, char const* data) +{ + if (!data) + { + // data == NULL when we're going to read the data using CURLOPT_READFUNCTION. + Dout(dc::curl, "POST size is " << size << " bytes."); + } + + // The server never replies with 100-continue, so suppress the "Expect: 100-continue" header that libcurl adds by default. + addHeader("Expect:"); + if (size > 0) + { + addHeader("Connection: keep-alive"); + addHeader("Keep-alive: 300"); + } + setopt(CURLOPT_POSTFIELDSIZE, size); + setopt(CURLOPT_POSTFIELDS, data); +} + +ThreadSafeCurlEasyRequest* CurlEasyRequest::get_lockobj(void) +{ + return static_cast(AIThreadSafeSimpleDC::wrapper_cast(this)); +} + +//static +size_t CurlEasyRequest::headerCallback(char* ptr, size_t size, size_t nmemb, void* userdata) +{ + CurlEasyRequest* self = static_cast(userdata); + ThreadSafeCurlEasyRequest* lockobj = self->get_lockobj(); + AICurlEasyRequest_wat lock_self(*lockobj); + return self->mHeaderCallback(ptr, size, nmemb, self->mHeaderCallbackUserData); +} + +void CurlEasyRequest::setHeaderCallback(curl_write_callback callback, void* userdata) +{ + mHeaderCallback = callback; + mHeaderCallbackUserData = userdata; + setopt(CURLOPT_HEADERFUNCTION, callback ? &CurlEasyRequest::headerCallback : NULL); + setopt(CURLOPT_WRITEHEADER, userdata ? this : NULL); +} + +//static +size_t CurlEasyRequest::writeCallback(char* ptr, size_t size, size_t nmemb, void* userdata) +{ + CurlEasyRequest* self = static_cast(userdata); + ThreadSafeCurlEasyRequest* lockobj = self->get_lockobj(); + AICurlEasyRequest_wat lock_self(*lockobj); + return self->mWriteCallback(ptr, size, nmemb, self->mWriteCallbackUserData); +} + +void CurlEasyRequest::setWriteCallback(curl_write_callback callback, void* userdata) +{ + mWriteCallback = callback; + mWriteCallbackUserData = userdata; + setopt(CURLOPT_WRITEFUNCTION, callback ? &CurlEasyRequest::writeCallback : NULL); + setopt(CURLOPT_WRITEDATA, userdata ? this : NULL); +} + +//static +size_t CurlEasyRequest::readCallback(char* ptr, size_t size, size_t nmemb, void* userdata) +{ + CurlEasyRequest* self = static_cast(userdata); + ThreadSafeCurlEasyRequest* lockobj = self->get_lockobj(); + AICurlEasyRequest_wat lock_self(*lockobj); + return self->mReadCallback(ptr, size, nmemb, self->mReadCallbackUserData); +} + +void CurlEasyRequest::setReadCallback(curl_read_callback callback, void* userdata) +{ + mReadCallback = callback; + mReadCallbackUserData = userdata; + setopt(CURLOPT_READFUNCTION, callback ? &CurlEasyRequest::readCallback : NULL); + setopt(CURLOPT_READDATA, userdata ? this : NULL); +} + +//static +CURLcode CurlEasyRequest::SSLCtxCallback(CURL* curl, void* sslctx, void* userdata) +{ + CurlEasyRequest* self = static_cast(userdata); + ThreadSafeCurlEasyRequest* lockobj = self->get_lockobj(); + AICurlEasyRequest_wat lock_self(*lockobj); + return self->mSSLCtxCallback(curl, sslctx, self->mSSLCtxCallbackUserData); +} + +void CurlEasyRequest::setSSLCtxCallback(curl_ssl_ctx_callback callback, void* userdata) +{ + mSSLCtxCallback = callback; + mSSLCtxCallbackUserData = userdata; + setopt(CURLOPT_SSL_CTX_FUNCTION, callback ? &CurlEasyRequest::SSLCtxCallback : NULL); + setopt(CURLOPT_SSL_CTX_DATA, this); +} + +#define llmaybewarns lllog(LLApp::isExiting() ? LLError::LEVEL_INFO : LLError::LEVEL_WARN, NULL, NULL, false, true) + +static size_t noHeaderCallback(char* ptr, size_t size, size_t nmemb, void* userdata) +{ + llmaybewarns << "Calling noHeaderCallback(); curl session aborted." << llendl; + return 0; // Cause a CURL_WRITE_ERROR +} + +static size_t noWriteCallback(char* ptr, size_t size, size_t nmemb, void* userdata) +{ + llmaybewarns << "Calling noWriteCallback(); curl session aborted." << llendl; + return 0; // Cause a CURL_WRITE_ERROR +} + +static size_t noReadCallback(char* ptr, size_t size, size_t nmemb, void* userdata) +{ + llmaybewarns << "Calling noReadCallback(); curl session aborted." << llendl; + return CURL_READFUNC_ABORT; // Cause a CURLE_ABORTED_BY_CALLBACK +} + +static CURLcode noSSLCtxCallback(CURL* curl, void* sslctx, void* parm) +{ + llmaybewarns << "Calling noSSLCtxCallback(); curl session aborted." << llendl; + return CURLE_ABORTED_BY_CALLBACK; +} + +void CurlEasyRequest::revokeCallbacks(void) +{ + if (mHeaderCallback == &noHeaderCallback && + mWriteCallback == &noWriteCallback && + mReadCallback == &noReadCallback && + mSSLCtxCallback == &noSSLCtxCallback) + { + // Already revoked. + return; + } + mHeaderCallback = &noHeaderCallback; + mWriteCallback = &noWriteCallback; + mReadCallback = &noReadCallback; + mSSLCtxCallback = &noSSLCtxCallback; + if (active() && !no_warning()) + { + llwarns << "Revoking callbacks on a still active CurlEasyRequest object!" << llendl; + } + curl_easy_setopt(getEasyHandle(), CURLOPT_HEADERFUNCTION, &noHeaderCallback); + curl_easy_setopt(getEasyHandle(), CURLOPT_WRITEHEADER, &noWriteCallback); + curl_easy_setopt(getEasyHandle(), CURLOPT_READFUNCTION, &noReadCallback); + curl_easy_setopt(getEasyHandle(), CURLOPT_SSL_CTX_FUNCTION, &noSSLCtxCallback); +} + +CurlEasyRequest::~CurlEasyRequest() +{ + // If the CurlEasyRequest object is destructed then we need to revoke all callbacks, because + // we can't set the lock anymore, and neither will mHeaderCallback, mWriteCallback etc, + // be available anymore. + send_events_to(NULL); + revokeCallbacks(); + // This wasn't freed yet if the request never finished. + curl_slist_free_all(mHeaders); +} + +void CurlEasyRequest::resetState(void) +{ + // This function should not revoke the event call backs! + revokeCallbacks(); + reset(); + curl_slist_free_all(mHeaders); + mHeaders = NULL; + mRequestFinalized = false; + mEventsTarget = NULL; + mResult = CURLE_FAILED_INIT; + applyDefaultOptions(); +} + +void CurlEasyRequest::addHeader(char const* header) +{ + llassert(!mRequestFinalized); + mHeaders = curl_slist_append(mHeaders, header); +} + +#if defined(CWDEBUG) || defined(DEBUG_CURLIO) + +static int curl_debug_cb(CURL*, curl_infotype infotype, char* buf, size_t size, void* user_ptr) +{ +#ifdef CWDEBUG + using namespace ::libcwd; + + CurlEasyRequest* request = (CurlEasyRequest*)user_ptr; + std::ostringstream marker; + marker << (void*)request->get_lockobj(); + libcw_do.push_marker(); + libcw_do.marker().assign(marker.str().data(), marker.str().size()); + if (!debug::channels::dc::curlio.is_on()) + debug::channels::dc::curlio.on(); + LibcwDoutScopeBegin(LIBCWD_DEBUGCHANNELS, libcw_do, dc::curlio|cond_nonewline_cf(infotype == CURLINFO_TEXT)) +#else + if (infotype == CURLINFO_TEXT) + { + while (size > 0 && (buf[size - 1] == '\r' || buf[size - 1] == '\n')) + --size; + } + LibcwDoutScopeBegin(LIBCWD_DEBUGCHANNELS, libcw_do, dc::curlio) +#endif + switch (infotype) + { + case CURLINFO_TEXT: + LibcwDoutStream << "* "; + break; + case CURLINFO_HEADER_IN: + LibcwDoutStream << "H> "; + break; + case CURLINFO_HEADER_OUT: + LibcwDoutStream << "H< "; + break; + case CURLINFO_DATA_IN: + LibcwDoutStream << "D> "; + break; + case CURLINFO_DATA_OUT: + LibcwDoutStream << "D< "; + break; + case CURLINFO_SSL_DATA_IN: + LibcwDoutStream << "S> "; + break; + case CURLINFO_SSL_DATA_OUT: + LibcwDoutStream << "S< "; + break; + default: + LibcwDoutStream << "?? "; + } + if (infotype == CURLINFO_TEXT) + LibcwDoutStream.write(buf, size); + else if (infotype == CURLINFO_HEADER_IN || infotype == CURLINFO_HEADER_OUT) + LibcwDoutStream << libcwd::buf2str(buf, size); + else if (infotype == CURLINFO_DATA_IN) + { + LibcwDoutStream << size << " bytes"; + bool finished = false; + size_t i = 0; + while (i < size) + { + char c = buf[i]; + if (!('0' <= c && c <= '9') && !('a' <= c && c <= 'f')) + { + if (0 < i && i + 1 < size && buf[i] == '\r' && buf[i + 1] == '\n') + { + // Binary output: "[0-9a-f]*\r\n ...binary data..." + LibcwDoutStream << ": \"" << libcwd::buf2str(buf, i + 2) << "\"..."; + finished = true; + } + break; + } + ++i; + } + if (!finished && size > 9 && buf[0] == '<') + { + // Human readable output: html, xml or llsd. + if (!strncmp(buf, "", 6)) + { + LibcwDoutStream << ": \"" << libcwd::buf2str(buf, size) << '"'; + finished = true; + } + } + if (!finished) + { + // Unknown format. Only print the first and last 20 characters. + if (size > 40UL) + { + LibcwDoutStream << ": \"" << libcwd::buf2str(buf, 20) << "\"...\"" << libcwd::buf2str(&buf[size - 20], 20) << '"'; + } + else + { + LibcwDoutStream << ": \"" << libcwd::buf2str(buf, size) << '"'; + } + } + } + else if (infotype == CURLINFO_DATA_OUT) + LibcwDoutStream << size << " bytes: \"" << libcwd::buf2str(buf, size) << '"'; + else + LibcwDoutStream << size << " bytes"; + LibcwDoutScopeEnd; +#ifdef CWDEBUG + libcw_do.pop_marker(); +#endif + return 0; +} +#endif + +void CurlEasyRequest::applyProxySettings(void) +{ + LLProxy& proxy = *LLProxy::getInstance(); + + // Do a faster unlocked check to see if we are supposed to proxy. + if (proxy.HTTPProxyEnabled()) + { + // We think we should proxy, read lock the shared proxy members. + LLProxy::Shared_crat proxy_r(proxy.shared_lockobj()); + + // Now test again to verify that the proxy wasn't disabled between the first check and the lock. + if (proxy.HTTPProxyEnabled()) + { + setopt(CURLOPT_PROXY, proxy.getHTTPProxy(proxy_r).getIPString().c_str()); + setopt(CURLOPT_PROXYPORT, proxy.getHTTPProxy(proxy_r).getPort()); + + if (proxy.getHTTPProxyType(proxy_r) == LLPROXY_SOCKS) + { + setopt(CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5); + if (proxy.getSelectedAuthMethod(proxy_r) == METHOD_PASSWORD) + { + std::string auth_string = proxy.getSocksUser(proxy_r) + ":" + proxy.getSocksPwd(proxy_r); + setopt(CURLOPT_PROXYUSERPWD, auth_string.c_str()); + } + } + else + { + setopt(CURLOPT_PROXYTYPE, CURLPROXY_HTTP); + } + } + } +} + +//static +CURLcode CurlEasyRequest::curlCtxCallback(CURL* curl, void* sslctx, void* parm) +{ + SSL_CTX* ctx = (SSL_CTX*)sslctx; + // Turn off TLS v1.1 (which is not supported anyway by Linden Lab) because otherwise we fail to connect. + // Also turn off SSL v2, which is highly broken and strongly discouraged[1]. + // [1] http://www.openssl.org/docs/ssl/SSL_CTX_set_options.html#SECURE_RENEGOTIATION + long options = SSL_OP_NO_SSLv2; +#ifdef SSL_OP_NO_TLSv1_1 // Only defined for openssl version 1.0.1 and up. + if (need_renegotiation_hack) + { + // This option disables openssl to use TLS version 1.1. + // The Linden Lab servers don't support TLS versions later than 1.0, and libopenssl-1.0.1-beta1 up till and including + // libopenssl-1.0.1c have a bug (or feature?) where (re)negotiation fails (see http://rt.openssl.org/Ticket/Display.html?id=2828), + // causing the connection to fail completely without this hack. + // For a commandline test of the same, observe the difference between: + // openssl s_client -connect login.agni.lindenlab.com:443 -CAfile packaged/app_settings/CA.pem -debug + // which gets no response from the server after sending the initial data, and + // openssl s_client -no_tls1_1 -connect login.agni.lindenlab.com:443 -CAfile packaged/app_settings/CA.pem -debug + // which finishes the negotiation and ends with 'Verify return code: 0 (ok)' + options |= SSL_OP_NO_TLSv1_1; + } +#else + llassert_always(!need_renegotiation_hack); +#endif + SSL_CTX_set_options(ctx, options); + return CURLE_OK; +} + +void CurlEasyRequest::applyDefaultOptions(void) +{ + CertificateAuthority_rat CertificateAuthority_r(gCertificateAuthority); + setoptString(CURLOPT_CAINFO, CertificateAuthority_r->file); + setSSLCtxCallback(&curlCtxCallback, NULL); + setopt(CURLOPT_NOSIGNAL, 1); + // The old code did this for the 'buffered' version, but I think it's nonsense. + //setopt(CURLOPT_DNS_CACHE_TIMEOUT, 0); + // Set the CURL options for either SOCKS or HTTP proxy. + applyProxySettings(); + // Cause libcurl to print all it's I/O traffic on the debug channel. + Debug( + if (dc::curlio.is_on()) + { + setopt(CURLOPT_VERBOSE, 1); + setopt(CURLOPT_DEBUGFUNCTION, &curl_debug_cb); + setopt(CURLOPT_DEBUGDATA, this); + } + ); +} + +void CurlEasyRequest::finalizeRequest(std::string const& url) +{ + llassert(!mRequestFinalized); + mResult = CURLE_FAILED_INIT; // General error code, the final code is written here in MultiHandle::check_run_count when msg is CURLMSG_DONE. + lldebugs << url << llendl; +#ifdef SHOW_ASSERT + // Do a sanity check on the headers. + int content_type_count = 0; + for (curl_slist* list = mHeaders; list; list = list->next) + { + if (strncmp(list->data, "Content-Type:", 13) == 0) + { + ++content_type_count; + } + } + if (content_type_count > 1) + { + llwarns << content_type_count << " Content-Type: headers!" << llendl; + } +#endif + mRequestFinalized = true; + setopt(CURLOPT_HTTPHEADER, mHeaders); + setoptString(CURLOPT_URL, url); + // The following line is a bit tricky: we store a pointer to the object without increasing its reference count. + // Of course we could increment the reference count, but that doesn't help much: if then this pointer would + // get "lost" we'd have a memory leak. Either way we must make sure that it is impossible that this pointer + // will be used if the object is deleted [In fact, since this is finalizeRequest() and not addRequest(), + // incrementing the reference counter would be wrong: if addRequest() is never called then the object is + // destroyed shortly after and this pointer is never even used.] + // This pointer is used in MultiHandle::check_run_count, which means that addRequest() was called and + // the reference counter was increased and the object is being kept alive, see the comments above + // command_queue in aicurlthread.cpp. In fact, this object survived until MultiHandle::add_easy_request + // was called and is kept alive by MultiHandle::mAddedEasyRequests. The only way to get deleted after + // that is when MultiHandle::remove_easy_request is called, which first removes the easy handle from + // the multi handle. So that it's (hopefully) no longer possible that info_read() in + // MultiHandle::check_run_count returns this easy handle, after the object is destroyed by deleting + // it from MultiHandle::mAddedEasyRequests. + setopt(CURLOPT_PRIVATE, get_lockobj()); +} + +void CurlEasyRequest::getTransferInfo(AICurlInterface::TransferInfo* info) +{ + // Curl explicitly demands a double for these info's. + double size, total_time, speed; + getinfo(CURLINFO_SIZE_DOWNLOAD, &size); + getinfo(CURLINFO_TOTAL_TIME, &total_time); + getinfo(CURLINFO_SPEED_DOWNLOAD, &speed); + // Convert to F64. + info->mSizeDownload = size; + info->mTotalTime = total_time; + info->mSpeedDownload = speed; +} + +void CurlEasyRequest::getResult(CURLcode* result, AICurlInterface::TransferInfo* info) +{ + *result = mResult; + if (info && mResult != CURLE_FAILED_INIT) + { + getTransferInfo(info); + } +} + +void CurlEasyRequest::added_to_multi_handle(AICurlEasyRequest_wat& curl_easy_request_w) +{ + if (mEventsTarget) + mEventsTarget->added_to_multi_handle(curl_easy_request_w); +} + +void CurlEasyRequest::finished(AICurlEasyRequest_wat& curl_easy_request_w) +{ + if (mEventsTarget) + mEventsTarget->finished(curl_easy_request_w); +} + +void CurlEasyRequest::removed_from_multi_handle(AICurlEasyRequest_wat& curl_easy_request_w) +{ + if (mEventsTarget) + mEventsTarget->removed_from_multi_handle(curl_easy_request_w); +} + +//----------------------------------------------------------------------------- +// CurlResponderBuffer + +static unsigned int const MAX_REDIRECTS = 5; +static S32 const CURL_REQUEST_TIMEOUT = 30; // Seconds per operation. + +LLChannelDescriptors const CurlResponderBuffer::sChannels; + +CurlResponderBuffer::CurlResponderBuffer() +{ + ThreadSafeBufferedCurlEasyRequest* lockobj = get_lockobj(); + AICurlEasyRequest_wat curl_easy_request_w(*lockobj); + curl_easy_request_w->send_events_to(this); +} + +#define llmaybeerrs lllog(LLApp::isRunning() ? LLError::LEVEL_ERROR : LLError::LEVEL_WARN, NULL, NULL, false, true) + +// The callbacks need to be revoked when the CurlResponderBuffer is destructed (because that is what the callbacks use). +// The AIThreadSafeSimple is destructed first (right to left), so when we get here then the +// ThreadSafeCurlEasyRequest base class of ThreadSafeBufferedCurlEasyRequest is still intact and we can create +// and use curl_easy_request_w. +CurlResponderBuffer::~CurlResponderBuffer() +{ + ThreadSafeBufferedCurlEasyRequest* lockobj = get_lockobj(); + AICurlEasyRequest_wat curl_easy_request_w(*lockobj); // Wait 'til possible callbacks have returned. + curl_easy_request_w->send_events_to(NULL); + curl_easy_request_w->revokeCallbacks(); + if (mResponder) + { + // If the responder is still alive, then that means that CurlResponderBuffer::processOutput was + // never called, which means that the removed_from_multi_handle event never happened. + // This is definitely an internal error as it can only happen when libcurl is too slow, + // in which case AICurlEasyRequestStateMachine::mTimer times out, but that already + // calls CurlResponderBuffer::timed_out(). + llmaybeerrs << "Calling ~CurlResponderBuffer() with active responder!" << llendl; + if (!LLApp::isRunning()) + { + // It might happen if some CurlResponderBuffer escaped clean up somehow :/ + mResponder = NULL; + } + else + { + // User chose to continue. + timed_out(); + } + } +} + +void CurlResponderBuffer::timed_out(void) +{ + mResponder->completedRaw(HTTP_INTERNAL_ERROR, "Request timeout, aborted.", sChannels, mOutput); + mResponder = NULL; +} + +void CurlResponderBuffer::resetState(AICurlEasyRequest_wat& curl_easy_request_w) +{ + llassert(!mResponder); + + curl_easy_request_w->resetState(); + + mOutput.reset(); + mInput.reset(); + + mHeaderOutput.str(""); + mHeaderOutput.clear(); +} + +ThreadSafeBufferedCurlEasyRequest* CurlResponderBuffer::get_lockobj(void) +{ + return static_cast(AIThreadSafeSimple::wrapper_cast(this)); +} + +void CurlResponderBuffer::prepRequest(AICurlEasyRequest_wat& curl_easy_request_w, std::vector const& headers, AICurlInterface::ResponderPtr responder, S32 time_out, bool post) +{ + if (post) + { + // Accept everything (send an Accept-Encoding header containing all encodings we support (zlib and gzip)). + curl_easy_request_w->setoptString(CURLOPT_ENCODING, ""); // CURLOPT_ACCEPT_ENCODING + } + + mInput.reset(new LLBufferArray); + mInput->setThreaded(true); + mLastRead = NULL; + + mOutput.reset(new LLBufferArray); + mOutput->setThreaded(true); + + ThreadSafeBufferedCurlEasyRequest* lockobj = get_lockobj(); + curl_easy_request_w->setWriteCallback(&curlWriteCallback, lockobj); + curl_easy_request_w->setReadCallback(&curlReadCallback, lockobj); + curl_easy_request_w->setHeaderCallback(&curlHeaderCallback, lockobj); + + // Allow up to five redirects. + if (responder && responder->followRedir()) + { + curl_easy_request_w->setopt(CURLOPT_FOLLOWLOCATION, 1); + curl_easy_request_w->setopt(CURLOPT_MAXREDIRS, MAX_REDIRECTS); + } + + curl_easy_request_w->setopt(CURLOPT_SSL_VERIFYPEER, true); + // Don't verify host name so urls with scrubbed host names will work (improves DNS performance). + curl_easy_request_w->setopt(CURLOPT_SSL_VERIFYHOST, 0); + + curl_easy_request_w->setopt(CURLOPT_TIMEOUT, llmax(time_out, CURL_REQUEST_TIMEOUT)); + + // Keep responder alive. + mResponder = responder; + + if (!post) + { + // Add extra headers. + for (std::vector::const_iterator iter = headers.begin(); iter != headers.end(); ++iter) + { + curl_easy_request_w->addHeader((*iter).c_str()); + } + } +} + +//static +size_t CurlResponderBuffer::curlWriteCallback(char* data, size_t size, size_t nmemb, void* user_data) +{ + ThreadSafeBufferedCurlEasyRequest* lockobj = static_cast(user_data); + + // We need to lock the curl easy request object too, because that lock is used + // to make sure that callbacks and destruction aren't done simultaneously. + AICurlEasyRequest_wat buffered_easy_request_w(*lockobj); + + AICurlResponderBuffer_wat buffer_w(*lockobj); + S32 n = size * nmemb; + buffer_w->getOutput()->append(sChannels.in(), (U8 const*)data, n); + return n; +} + +//static +size_t CurlResponderBuffer::curlReadCallback(char* data, size_t size, size_t nmemb, void* user_data) +{ + ThreadSafeBufferedCurlEasyRequest* lockobj = static_cast(user_data); + + // We need to lock the curl easy request object too, because that lock is used + // to make sure that callbacks and destruction aren't done simultaneously. + AICurlEasyRequest_wat buffered_easy_request_w(*lockobj); + + S32 bytes = size * nmemb; // The maximum amount to read. + AICurlResponderBuffer_wat buffer_w(*lockobj); + buffer_w->mLastRead = buffer_w->getInput()->readAfter(sChannels.out(), buffer_w->mLastRead, (U8*)data, bytes); + return bytes; // Return the amount actually read. +} + +//static +size_t CurlResponderBuffer::curlHeaderCallback(char* data, size_t size, size_t nmemb, void* user_data) +{ + ThreadSafeBufferedCurlEasyRequest* lockobj = static_cast(user_data); + + // We need to lock the curl easy request object too, because that lock is used + // to make sure that callbacks and destruction aren't done simultaneously. + AICurlEasyRequest_wat buffered_easy_request_w(*lockobj); + + AICurlResponderBuffer_wat buffer_w(*lockobj); + size_t n = size * nmemb; + buffer_w->getHeaderOutput().write(data, n); + return n; +} + +void CurlResponderBuffer::added_to_multi_handle(AICurlEasyRequest_wat& curl_easy_request_w) +{ + Dout(dc::curl, "Calling CurlResponderBuffer::added_to_multi_handle(@" << (void*)&*curl_easy_request_w << ") for this = " << (void*)this); +} + +void CurlResponderBuffer::finished(AICurlEasyRequest_wat& curl_easy_request_w) +{ + Dout(dc::curl, "Calling CurlResponderBuffer::finished(@" << (void*)&*curl_easy_request_w << ") for this = " << (void*)this); +} + +void CurlResponderBuffer::removed_from_multi_handle(AICurlEasyRequest_wat& curl_easy_request_w) +{ + Dout(dc::curl, "Calling CurlResponderBuffer::removed_from_multi_handle(@" << (void*)&*curl_easy_request_w << ") for this = " << (void*)this); + + // Lock self. + ThreadSafeBufferedCurlEasyRequest* lockobj = get_lockobj(); + llassert(dynamic_cast(static_cast(ThreadSafeCurlEasyRequest::wrapper_cast(&*curl_easy_request_w))) == lockobj); + AICurlResponderBuffer_wat buffer_w(*lockobj); + llassert(&*buffer_w == this); + + processOutput(curl_easy_request_w); +} + +void CurlResponderBuffer::processOutput(AICurlEasyRequest_wat& curl_easy_request_w) +{ + U32 responseCode = 0; + std::string responseReason; + + CURLcode code; + curl_easy_request_w->getResult(&code); + if (code == CURLE_OK) + { + curl_easy_request_w->getinfo(CURLINFO_RESPONSE_CODE, &responseCode); + //*TODO: get reason from first line of mHeaderOutput + } + else + { + responseCode = 499; + responseReason = AICurlInterface::strerror(code) + " : "; + if (code == CURLE_FAILED_INIT) + { + responseReason += "Curl Easy Handle initialization failed."; + } + else + { + responseReason += curl_easy_request_w->getErrorString(); + } + curl_easy_request_w->setopt(CURLOPT_FRESH_CONNECT, TRUE); + } + + if (mResponder) + { + mResponder->completedRaw(responseCode, responseReason, sChannels, mOutput); + mResponder = NULL; + } + + resetState(curl_easy_request_w); +} + +//----------------------------------------------------------------------------- +// CurlMultiHandle + +LLAtomicU32 CurlMultiHandle::sTotalMultiHandles; + +CurlMultiHandle::CurlMultiHandle(void) +{ + DoutEntering(dc::curl, "CurlMultiHandle::CurlMultiHandle() [" << (void*)this << "]."); + mMultiHandle = curl_multi_init(); + Stats::multi_calls++; + if (!mMultiHandle) + { + Stats::multi_errors++; + throw AICurlNoMultiHandle("curl_multi_init() returned NULL"); + } + sTotalMultiHandles++; +} + +CurlMultiHandle::~CurlMultiHandle() +{ + curl_multi_cleanup(mMultiHandle); + Stats::multi_calls++; +#ifdef CWDEBUG + int total = --sTotalMultiHandles; + Dout(dc::curl, "Called CurlMultiHandle::~CurlMultiHandle() [" << (void*)this << "], " << total << " remaining."); +#else + --sTotalMultiHandles; +#endif +} + +} // namespace AICurlPrivate diff --git a/indra/llmessage/aicurl.h b/indra/llmessage/aicurl.h new file mode 100644 index 000000000..26491da67 --- /dev/null +++ b/indra/llmessage/aicurl.h @@ -0,0 +1,343 @@ +/** + * @file aicurl.h + * @brief Thread safe wrapper for libcurl. + * + * Copyright (c) 2012, Aleric Inglewood. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution. + * + * CHANGELOG + * and additional copyright holders. + * + * 17/03/2012 + * Initial version, written by Aleric Inglewood @ SL + */ + +#ifndef AICURL_H +#define AICURL_H + +#include +#include +#include +#include +#include +#include + +#include "llpreprocessor.h" +#include // Needed for files that include this header (also for aicurlprivate.h). +#ifdef DEBUG_CURLIO +#include "debug_libcurl.h" +#endif + +// Make sure we don't use this option: it is not thread-safe. +#undef CURLOPT_DNS_USE_GLOBAL_CACHE +#define CURLOPT_DNS_USE_GLOBAL_CACHE do_not_use_CURLOPT_DNS_USE_GLOBAL_CACHE + +#include "stdtypes.h" // U32 +#include "lliopipe.h" // LLIOPipe::buffer_ptr_t +#include "llatomic.h" // LLAtomicU32 +#include "aithreadsafe.h" + +class LLSD; + +//----------------------------------------------------------------------------- +// Exceptions. +// + +// A general curl exception. +// +class AICurlError : public std::runtime_error { + public: + AICurlError(std::string const& message) : std::runtime_error(message) { } +}; + +class AICurlNoEasyHandle : public AICurlError { + public: + AICurlNoEasyHandle(std::string const& message) : AICurlError(message) { } +}; + +class AICurlNoMultiHandle : public AICurlError { + public: + AICurlNoMultiHandle(std::string const& message) : AICurlError(message) { } +}; + +// End Exceptions. +//----------------------------------------------------------------------------- + +// Things defined in this namespace are called from elsewhere in the viewer code. +namespace AICurlInterface { + +// Output parameter of AICurlPrivate::CurlEasyRequest::getResult. +// Only used by LLXMLRPCTransaction::Impl. +struct TransferInfo { + TransferInfo() : mSizeDownload(0.0), mTotalTime(0.0), mSpeedDownload(0.0) { } + F64 mSizeDownload; + F64 mTotalTime; + F64 mSpeedDownload; +}; + +//----------------------------------------------------------------------------- +// Global functions. + +// Called once at start of application (from newview/llappviewer.cpp by main thread (before threads are created)), +// with main purpose to initialize curl. +void initCurl(void (*)(void) = NULL); + +// Called once at start of application (from LLAppViewer::initThreads), starts AICurlThread. +void startCurlThread(void); + +// Called once at end of application (from newview/llappviewer.cpp by main thread), +// with purpose to stop curl threads, free curl resources and deinitialize curl. +void cleanupCurl(void); + +// Called from indra/llmessage/llurlrequest.cpp to print debug output regarding +// an error code returned by EasyRequest::getResult. +// Just returns curl_easy_strerror(errorcode). +std::string strerror(CURLcode errorcode); + +// Called from indra/newview/llfloaterabout.cpp for the About floater, and +// from newview/llappviewer.cpp in behalf of debug output. +// Just returns curl_version(). +std::string getVersionString(void); + +// Called from newview/llappviewer.cpp (and llcrashlogger/llcrashlogger.cpp) to set +// the Certificate Authority file used to verify HTTPS certs. +void setCAFile(std::string const& file); + +// Not called from anywhere. +// Can be used to set the path to the Certificate Authority file. +void setCAPath(std::string const& file); + +//----------------------------------------------------------------------------- +// Global classes. + +// Responder - base class for Request::get* and Request::post API. +// +// The life cycle of classes derived from this class is as follows: +// They are allocated with new on the line where get(), getByteRange() or post() is called, +// and the pointer to the allocated object is then put in a reference counting ResponderPtr. +// This ResponderPtr is passed to CurlResponderBuffer::prepRequest which stores it in its +// member mResponder. Hence, the life time of a Responder is never longer than its +// associated CurlResponderBuffer, however, if everything works correctly, then normally a +// responder is deleted in CurlResponderBuffer::removed_from_multi_handle by setting +// mReponder to NULL. +// +// Note that the lifetime of CurlResponderBuffer is (a bit) shorter than the associated +// CurlEasyRequest (because of the order of base classes of ThreadSafeBufferedCurlEasyRequest) +// and the callbacks, as set by prepRequest, only use those two. +// A callback locks the CurlEasyRequest before actually making the callback, and the +// destruction of CurlResponderBuffer also first locks the CurlEasyRequest, and then revokes +// the callbacks. This assures that a Responder is never used when the objects it uses are +// destructed. Also, if any of those are destructed then the Responder is automatically +// destructed too. +// +class Responder { + protected: + Responder(void); + virtual ~Responder(); + + private: + // Associated URL, used for debug output. + std::string mURL; + + public: + // Called to set the URL of the current request for this Responder, + // used only when printing debug output regarding activity of the Responder. + void setURL(std::string const& url); + + public: + // Called from LLHTTPClientURLAdaptor::complete(): + + // Derived classes can override this to get the HTML header that was received, when the message is completed. + // The default does nothing. + virtual void completedHeader(U32 status, std::string const& reason, LLSD const& content); + + // Derived classes can override this to get the raw data of the body of the HTML message that was received. + // The default is to interpret the content as LLSD and call completed(). + virtual void completedRaw(U32 status, std::string const& reason, LLChannelDescriptors const& channels, LLIOPipe::buffer_ptr_t const& buffer); + + // Called from LLHTTPClient request calls, if an error occurs even before we can call one of the above. + // It calls completed() with a fake status U32_MAX, as that is what some derived clients expect (bad design). + // This means that if a derived class overrides completedRaw() it now STILL has to override completed() to catch this error. + void fatalError(std::string const& reason); + + // A derived class should return true if curl should follow redirections. + // The default is not to follow redirections. + virtual bool followRedir(void) { return false; } + + protected: + // ... or, derived classes can override this to get the LLSD content when the message is completed. + // The default is to call result() (or errorWithContent() in case of a HTML status indicating an error). + virtual void completed(U32 status, std::string const& reason, LLSD const& content); + + // ... or, derived classes can override this to received the content of a body upon success. + // The default does nothing. + virtual void result(LLSD const& content); + + // Derived classes can override this to get informed when a bad HTML status code is received. + // The default calls error(). + virtual void errorWithContent(U32 status, std::string const& reason, LLSD const& content); + + // ... or, derived classes can override this to get informed when a bad HTML statis code is received. + // The default prints the error to llinfos. + virtual void error(U32 status, std::string const& reason); + + public: + // Called from LLSDMessage::ResponderAdapter::listener. + // LLSDMessage::ResponderAdapter is a hack, showing among others by fact that these functions need to be public. + + void pubErrorWithContent(U32 status, std::string const& reason, LLSD const& content) { errorWithContent(status, reason, content); } + void pubResult(LLSD const& content) { result(content); } + + private: + // Used by ResponderPtr. Object is deleted when reference count reaches zero. + LLAtomicU32 mReferenceCount; + + friend void intrusive_ptr_add_ref(Responder* p); // Called by boost::intrusive_ptr when a new copy of a boost::intrusive_ptr is made. + friend void intrusive_ptr_release(Responder* p); // Called by boost::intrusive_ptr when a boost::intrusive_ptr is destroyed. + // This function must delete the Responder object when the reference count reaches zero. +}; + +// A Responder is passed around as ResponderPtr, which causes it to automatically +// destruct when there are no pointers left pointing to it. +typedef boost::intrusive_ptr ResponderPtr; + +} // namespace AICurlInterface + +// Forward declaration (see aicurlprivate.h). +namespace AICurlPrivate { + class CurlEasyRequest; +} // namespace AICurlPrivate + +// Define access types (_crat = Const Read Access Type, _rat = Read Access Type, _wat = Write Access Type). +// Typical usage is: +// AICurlEasyRequest h1; // Create easy handle. +// AICurlEasyRequest h2(h1); // Make lightweight copies. +// AICurlEasyRequest_wat h2_w(*h2); // Lock and obtain write access to the easy handle. +// Use *h2_w, which is a reference to the locked CurlEasyRequest instance. +// Note: As it is not allowed to use curl easy handles in any way concurrently, +// read access would at most give access to a CURL const*, which will turn out +// to be completely useless; therefore it is sufficient and efficient to use +// an AIThreadSafeSimple and it's unlikely that AICurlEasyRequest_rat will be used. +typedef AIAccessConst AICurlEasyRequest_rat; +typedef AIAccess AICurlEasyRequest_wat; + +// Events generated by AICurlPrivate::CurlEasyHandle. +struct AICurlEasyHandleEvents { + // Events. + virtual void added_to_multi_handle(AICurlEasyRequest_wat& curl_easy_request_w) = 0; + virtual void finished(AICurlEasyRequest_wat& curl_easy_request_w) = 0; + virtual void removed_from_multi_handle(AICurlEasyRequest_wat& curl_easy_request_w) = 0; + // Avoid compiler warning. + virtual ~AICurlEasyHandleEvents() { } +}; + +// Pointer to data we're going to POST. +class AIPostField : public LLThreadSafeRefCount { + protected: + char const* mData; + + public: + AIPostField(char const* data) : mData(data) { } + char const* data(void) const { return mData; } +}; + +// The pointer to the data that we have to POST is passed around as AIPostFieldPtr, +// which causes it to automatically clean up when there are no pointers left +// pointing to it. +typedef LLPointer AIPostFieldPtr; + +#include "aicurlprivate.h" + +// AICurlPrivate::CurlEasyRequestPtr, a boost::intrusive_ptr, is no more threadsafe than a +// builtin type, but wrapping it in AIThreadSafe is obviously not going to help here. +// Therefore we use the following trick: we wrap CurlEasyRequestPtr too, and only allow +// read accesses on it. + +// AICurlEasyRequest: a thread safe, reference counting, auto-cleaning curl easy handle. +class AICurlEasyRequest { + public: + // Initial construction is allowed (thread-safe). + // Note: If ThreadSafeCurlEasyRequest() throws then the memory allocated is still freed. + // 'new' never returned however and neither the constructor nor destructor of mCurlEasyRequest is called in this case. + // This might throw AICurlNoEasyHandle. + AICurlEasyRequest(bool buffered) : + mCurlEasyRequest(buffered ? new AICurlPrivate::ThreadSafeBufferedCurlEasyRequest : new AICurlPrivate::ThreadSafeCurlEasyRequest) { } + AICurlEasyRequest(AICurlEasyRequest const& orig) : mCurlEasyRequest(orig.mCurlEasyRequest) { } + + // For the rest, only allow read operations. + AIThreadSafeSimple& operator*(void) const { llassert(mCurlEasyRequest.get()); return *mCurlEasyRequest; } + AIThreadSafeSimple* operator->(void) const { llassert(mCurlEasyRequest.get()); return mCurlEasyRequest.get(); } + AIThreadSafeSimple* get(void) const { return mCurlEasyRequest.get(); } + + // Returns true if this object points to the same CurlEasyRequest object. + bool operator==(AICurlEasyRequest const& cer) const { return mCurlEasyRequest == cer.mCurlEasyRequest; } + + // Returns true if this object points to a different CurlEasyRequest object. + bool operator!=(AICurlEasyRequest const& cer) const { return mCurlEasyRequest != cer.mCurlEasyRequest; } + + // Queue this request for insertion in the multi session. + void addRequest(void); + + // Queue a command to remove this request from the multi session (or cancel a queued command to add it). + void removeRequest(void); + + // Returns true when this AICurlEasyRequest wraps a AICurlPrivate::ThreadSafeBufferedCurlEasyRequest. + bool isBuffered(void) const { return mCurlEasyRequest->isBuffered(); } + + private: + // The actual pointer to the ThreadSafeCurlEasyRequest instance. + AICurlPrivate::CurlEasyRequestPtr mCurlEasyRequest; + + private: + // Assignment would not be thread-safe; we may create this object and read from it. + // Note: Destruction is implicitly assumed thread-safe, as it would be a logic error to + // destruct it while another thread still needs it, concurrent or not. + AICurlEasyRequest& operator=(AICurlEasyRequest const&) { return *this; } + + public: + // The more exotic member functions of this class, to deal with passing this class + // as CURLOPT_PRIVATE pointer to a curl handle and afterwards restore it. + // For "internal use" only; don't use things from AICurlPrivate yourself. + + // It's thread-safe to give read access the underlaying boost::intrusive_ptr. + // It's not OK to then call get() on that and store the AICurlPrivate::ThreadSafeCurlEasyRequest* separately. + AICurlPrivate::CurlEasyRequestPtr const& get_ptr(void) const { return mCurlEasyRequest; } + + // If we have a correct (with regard to reference counting) AICurlPrivate::CurlEasyRequestPtr, + // then it's OK to construct a AICurlEasyRequest from it. + // Note that the external AICurlPrivate::CurlEasyRequestPtr needs its own locking, because + // it's not thread-safe in itself. + AICurlEasyRequest(AICurlPrivate::CurlEasyRequestPtr const& ptr) : mCurlEasyRequest(ptr) { } + + // This one is obviously dangerous. It's for use only in MultiHandle::check_run_count. + // See also the long comment in CurlEasyRequest::finalizeRequest with regard to CURLOPT_PRIVATE. + explicit AICurlEasyRequest(AICurlPrivate::ThreadSafeCurlEasyRequest* ptr) : mCurlEasyRequest(ptr) { } +}; + +// Write Access Type for the buffer. +struct AICurlResponderBuffer_wat : public AIAccess { + explicit AICurlResponderBuffer_wat(AICurlPrivate::ThreadSafeBufferedCurlEasyRequest& lockobj) : + AIAccess(lockobj) { } + AICurlResponderBuffer_wat(AIThreadSafeSimple& lockobj) : + AIAccess(static_cast(lockobj)) { } +}; + +#define AICurlPrivate DONTUSE_AICurlPrivate + +#endif diff --git a/indra/llmessage/aicurlprivate.h b/indra/llmessage/aicurlprivate.h new file mode 100644 index 000000000..1acf08e14 --- /dev/null +++ b/indra/llmessage/aicurlprivate.h @@ -0,0 +1,478 @@ +/** + * @file aicurlprivate.h + * @brief Thread safe wrapper for libcurl. + * + * Copyright (c) 2012, Aleric Inglewood. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution. + * + * CHANGELOG + * and additional copyright holders. + * + * 28/04/2012 + * Initial version, written by Aleric Inglewood @ SL + */ + +#ifndef AICURLPRIVATE_H +#define AICURLPRIVATE_H + +#include +#include "llatomic.h" + +namespace AICurlPrivate { +namespace curlthread { class MultiHandle; } + +struct Stats { + static LLAtomicU32 easy_calls; + static LLAtomicU32 easy_errors; + static LLAtomicU32 easy_init_calls; + static LLAtomicU32 easy_init_errors; + static LLAtomicU32 easy_cleanup_calls; + static LLAtomicU32 multi_calls; + static LLAtomicU32 multi_errors; + + static void print(void); +}; + +void handle_multi_error(CURLMcode code); +inline CURLMcode check_multi_code(CURLMcode code) { Stats::multi_calls++; if (code != CURLM_OK) handle_multi_error(code); return code; } + +bool curlThreadIsRunning(void); +void wakeUpCurlThread(void); +void stopCurlThread(void); + +class ThreadSafeCurlEasyRequest; +class ThreadSafeBufferedCurlEasyRequest; + +#define DECLARE_SETOPT(param_type) \ + CURLcode setopt(CURLoption option, param_type parameter) + +// This class wraps CURL*'s. +// It guarantees that a pointer is cleaned up when no longer needed, as required by libcurl. +class CurlEasyHandle : public boost::noncopyable, protected AICurlEasyHandleEvents { + public: + CurlEasyHandle(void); + ~CurlEasyHandle(); + + private: + // Disallow assignment. + CurlEasyHandle& operator=(CurlEasyHandle const*); + + public: + // Reset all options of a libcurl session handle. + void reset(void) { llassert(!mActiveMultiHandle); curl_easy_reset(mEasyHandle); } + + // Set options for a curl easy handle. + DECLARE_SETOPT(long); + DECLARE_SETOPT(long long); + DECLARE_SETOPT(void const*); + DECLARE_SETOPT(curl_debug_callback); + DECLARE_SETOPT(curl_write_callback); + //DECLARE_SETOPT(curl_read_callback); Same type as curl_write_callback + DECLARE_SETOPT(curl_ssl_ctx_callback); + DECLARE_SETOPT(curl_conv_callback); +#if 0 // Not used by the viewer. + DECLARE_SETOPT(curl_progress_callback); + DECLARE_SETOPT(curl_seek_callback); + DECLARE_SETOPT(curl_ioctl_callback); + DECLARE_SETOPT(curl_sockopt_callback); + DECLARE_SETOPT(curl_opensocket_callback); + DECLARE_SETOPT(curl_closesocket_callback); + DECLARE_SETOPT(curl_sshkeycallback); + DECLARE_SETOPT(curl_chunk_bgn_callback); + DECLARE_SETOPT(curl_chunk_end_callback); + DECLARE_SETOPT(curl_fnmatch_callback); +#endif + // Automatically cast int types to a long. Note that U32/S32 are int and + // that you can overload int and long even if they have the same size. + CURLcode setopt(CURLoption option, U32 parameter) { return setopt(option, (long)parameter); } + CURLcode setopt(CURLoption option, S32 parameter) { return setopt(option, (long)parameter); } + + // Clone a libcurl session handle using all the options previously set. + //CurlEasyHandle(CurlEasyHandle const& orig); + + // URL encode/decode the given string. + char* escape(char* url, int length); + char* unescape(char* url, int inlength , int* outlength); + + // Extract information from a curl handle. + private: + CURLcode getinfo_priv(CURLINFO info, void* data); + public: + // The rest are inlines to provide some type-safety. + CURLcode getinfo(CURLINFO info, char** data) { return getinfo_priv(info, data); } + CURLcode getinfo(CURLINFO info, curl_slist** data) { return getinfo_priv(info, data); } + CURLcode getinfo(CURLINFO info, double* data) { return getinfo_priv(info, data); } + CURLcode getinfo(CURLINFO info, long* data) { return getinfo_priv(info, data); } +#ifdef __LP64__ // sizeof(long) > sizeof(int) ? + // Overload for integer types that are too small (libcurl demands a long). + CURLcode getinfo(CURLINFO info, S32* data) { long ldata; CURLcode res = getinfo_priv(info, &ldata); *data = static_cast(ldata); return res; } + CURLcode getinfo(CURLINFO info, U32* data) { long ldata; CURLcode res = getinfo_priv(info, &ldata); *data = static_cast(ldata); return res; } +#else // sizeof(long) == sizeof(int) + CURLcode getinfo(CURLINFO info, S32* data) { return getinfo_priv(info, reinterpret_cast(data)); } + CURLcode getinfo(CURLINFO info, U32* data) { return getinfo_priv(info, reinterpret_cast(data)); } +#endif + + // Perform a file transfer (blocking). + CURLcode perform(void); + // Pause and unpause a connection. + CURLcode pause(int bitmask); + + // Called when a request is queued for removal. In that case a race between the actual removal + // and revoking of the callbacks is harmless (and happens for the raw non-statemachine version). + void remove_queued(void) { mQueuedForRemoval = true; } + // In case it's added after being removed. + void add_queued(void) { mQueuedForRemoval = false; } + + private: + CURL* mEasyHandle; + CURLM* mActiveMultiHandle; + char* mErrorBuffer; + AIPostFieldPtr mPostField; // This keeps the POSTFIELD data alive for as long as the easy handle exists. + bool mQueuedForRemoval; // Set if the easy handle is (probably) added to the multi handle, but is queued for removal. +#ifdef SHOW_ASSERT + public: + bool mRemovedPerCommand; // Set if mActiveMultiHandle was reset as per command from the main thread. +#endif + + private: + // This should only be called from MultiHandle; add/remove an easy handle to/from a multi handle. + friend class curlthread::MultiHandle; + CURLMcode add_handle_to_multi(AICurlEasyRequest_wat& curl_easy_request_w, CURLM* multi_handle); + CURLMcode remove_handle_from_multi(AICurlEasyRequest_wat& curl_easy_request_w, CURLM* multi_handle); + + public: + // Returns true if this easy handle was added to a curl multi handle. + bool active(void) const { return mActiveMultiHandle; } + + // If there was an error code as result, then this returns a human readable error string. + // Only valid when setErrorBuffer was called and the curl_easy function returned an error. + std::string getErrorString(void) const { return mErrorBuffer ? mErrorBuffer : "(null)"; } + + // Returns true when it is expected that the parent will revoke callbacks before the curl + // easy handle is removed from the multi handle; that usually happens when an external + // error demands termination of the request (ie, an expiration). + bool no_warning(void) const { return mQueuedForRemoval || LLApp::isExiting(); } + + // Used for debugging purposes. + bool operator==(CURL* easy_handle) const { return mEasyHandle == easy_handle; } + + private: + // Call this prior to every curl_easy function whose return value is passed to check_easy_code. + void setErrorBuffer(void); + + static void handle_easy_error(CURLcode code); + + // Always first call setErrorBuffer()! + static inline CURLcode check_easy_code(CURLcode code) + { + Stats::easy_calls++; + if (code != CURLE_OK) + handle_easy_error(code); + return code; + } + + protected: + // Return the underlying curl easy handle. + CURL* getEasyHandle(void) const { return mEasyHandle; } + + // Keep POSTFIELD data alive. + void setPostField(AIPostFieldPtr const& post_field_ptr) { mPostField = post_field_ptr; } + + private: + // Return, and possibly create, the curl (easy) error buffer used by the current thread. + static char* getTLErrorBuffer(void); +}; + +// CurlEasyRequest adds a slightly more powerful interface that can be used +// to set the options on a curl easy handle. +// +// Calling sendRequest() will then connect to the given URL and perform +// the data exchange. If an error occurs related to this handle, it can +// be read by calling getErrorString(). +// +// Note that the life cycle of a CurlEasyRequest is controlled by AICurlEasyRequest: +// a CurlEasyRequest is only ever created as base class of a ThreadSafeCurlEasyRequest, +// which is only created by creating a AICurlEasyRequest. When the last copy of such +// AICurlEasyRequest is deleted, then also the ThreadSafeCurlEasyRequest is deleted +// and the CurlEasyRequest destructed. +class CurlEasyRequest : public CurlEasyHandle { + private: + void setPost_raw(S32 size, char const* data); + public: + void setPost(S32 size) { setPost_raw(size, NULL); } + void setPost(AIPostFieldPtr const& postdata, S32 size); + void setPost(char const* data, S32 size) { setPost(new AIPostField(data), size); } + void setoptString(CURLoption option, std::string const& value); + void addHeader(char const* str); + + private: + // Callback stubs. + static size_t headerCallback(char* ptr, size_t size, size_t nmemb, void* userdata); + static size_t writeCallback(char* ptr, size_t size, size_t nmemb, void* userdata); + static size_t readCallback(char* ptr, size_t size, size_t nmemb, void* userdata); + static CURLcode SSLCtxCallback(CURL* curl, void* sslctx, void* userdata); + + curl_write_callback mHeaderCallback; + void* mHeaderCallbackUserData; + curl_write_callback mWriteCallback; + void* mWriteCallbackUserData; + curl_read_callback mReadCallback; + void* mReadCallbackUserData; + curl_ssl_ctx_callback mSSLCtxCallback; + void* mSSLCtxCallbackUserData; + + public: + void setHeaderCallback(curl_write_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); + + // Call this if the set callbacks are about to be invalidated. + void revokeCallbacks(void); + + // Reset everything to the state it was in when this object was just created. + void resetState(void); + + private: + // Called from applyDefaultOptions. + void applyProxySettings(void); + + // Used in applyProxySettings. + static CURLcode curlCtxCallback(CURL* curl, void* sslctx, void* parm); + + public: + // Set default options that we want applied to all curl easy handles. + void applyDefaultOptions(void); + + // Prepare the request for adding it to a multi session, or calling perform. + // This actually adds the headers that were collected with addHeader. + void finalizeRequest(std::string const& url); + + // Store result code that is returned by getResult. + void store_result(CURLcode result) { mResult = result; } + + // Called when the curl easy handle is done. + void done(AICurlEasyRequest_wat& curl_easy_request_w) { finished(curl_easy_request_w); } + + // Fill info with the transfer info. + void getTransferInfo(AICurlInterface::TransferInfo* info); + + // If result != CURLE_FAILED_INIT then also info was filled. + void getResult(CURLcode* result, AICurlInterface::TransferInfo* info = NULL); + + private: + curl_slist* mHeaders; + bool mRequestFinalized; + AICurlEasyHandleEvents* mEventsTarget; + CURLcode mResult; + + private: + // This class may only be created by constructing a ThreadSafeCurlEasyRequest. + friend class ThreadSafeCurlEasyRequest; + // Throws AICurlNoEasyHandle. + CurlEasyRequest(void) : + mHeaders(NULL), mRequestFinalized(false), mEventsTarget(NULL), mResult(CURLE_FAILED_INIT) + { applyDefaultOptions(); } + public: + ~CurlEasyRequest(); + + public: + // Post-initialization, set the parent to pass the events to. + void send_events_to(AICurlEasyHandleEvents* target) { mEventsTarget = target; } + + // For debugging purposes + bool is_finalized(void) const { return mRequestFinalized; } + + // Return pointer to the ThreadSafe (wrapped) version of this object. + ThreadSafeCurlEasyRequest* get_lockobj(void); + + protected: + // Pass events to parent. + /*virtual*/ void added_to_multi_handle(AICurlEasyRequest_wat& curl_easy_request_w); + /*virtual*/ void finished(AICurlEasyRequest_wat& curl_easy_request_w); + /*virtual*/ void removed_from_multi_handle(AICurlEasyRequest_wat& curl_easy_request_w); +}; + +// Buffers used by the AICurlInterface::Request API. +// Curl callbacks write into and read from these buffers. +// The interface with the rest of the code is through AICurlInterface::Responder. +// +// The lifetime of a CurlResponderBuffer is slightly shorter than its +// associated CurlEasyRequest; this class can only be created as base class +// of ThreadSafeBufferedCurlEasyRequest, and is therefore constructed after +// the construction of the associated CurlEasyRequest and destructed before it. +// Hence, it's safe to use get_lockobj() and through that access the CurlEasyRequest +// object at all times. +// +// A CurlResponderBuffer is thus created when a ThreadSafeBufferedCurlEasyRequest +// is created which only happens by creating a AICurlEasyRequest(true) instance, +// and when the last AICurlEasyRequest is deleted, then the ThreadSafeBufferedCurlEasyRequest +// is deleted and the CurlResponderBuffer destructed. +class CurlResponderBuffer : protected AICurlEasyHandleEvents { + public: + void resetState(AICurlEasyRequest_wat& curl_easy_request_w); + void prepRequest(AICurlEasyRequest_wat& buffered_curl_easy_request_w, std::vector const& headers, AICurlInterface::ResponderPtr responder, S32 time_out = 0, bool post = false); + + LLIOPipe::buffer_ptr_t& getInput(void) { return mInput; } + std::stringstream& getHeaderOutput(void) { return mHeaderOutput; } + LLIOPipe::buffer_ptr_t& getOutput(void) { return mOutput; } + + // Called if libcurl doesn't deliver within CurlRequestTimeOut seconds. + void timed_out(void); + + // Called after removed_from_multi_handle was called. + void processOutput(AICurlEasyRequest_wat& curl_easy_request_w); + + protected: + /*virtual*/ void added_to_multi_handle(AICurlEasyRequest_wat& curl_easy_request_w); + /*virtual*/ void finished(AICurlEasyRequest_wat& curl_easy_request_w); + /*virtual*/ void removed_from_multi_handle(AICurlEasyRequest_wat& curl_easy_request_w); + + private: + LLIOPipe::buffer_ptr_t mInput; + U8* mLastRead; // Pointer into mInput where we last stopped reading (or NULL to start at the beginning). + std::stringstream mHeaderOutput; + LLIOPipe::buffer_ptr_t mOutput; + AICurlInterface::ResponderPtr mResponder; + + public: + static LLChannelDescriptors const sChannels; // Channel object for mInput (channel out()) and mOutput (channel in()). + + private: + // This class may only be created by constructing a ThreadSafeBufferedCurlEasyRequest. + friend class ThreadSafeBufferedCurlEasyRequest; + CurlResponderBuffer(void); + public: + ~CurlResponderBuffer(); + + private: + static size_t curlWriteCallback(char* data, size_t size, size_t nmemb, void* user_data); + static size_t curlReadCallback(char* data, size_t size, size_t nmemb, void* user_data); + static size_t curlHeaderCallback(char* data, size_t size, size_t nmemb, void* user_data); + + public: + // Return pointer to the ThreadSafe (wrapped) version of this object. + ThreadSafeBufferedCurlEasyRequest* get_lockobj(void); + + // Return true when prepRequest was already called and the object has not been + // invalidated as a result of calling timed_out(). + bool isValid(void) const { return mResponder; } +}; + +// This class wraps CurlEasyRequest for thread-safety and adds a reference counter so we can +// copy it around cheaply and it gets destructed automatically when the last instance is deleted. +// It guarantees that the CURL* handle is never used concurrently, which is not allowed by libcurl. +// As AIThreadSafeSimpleDC contains a mutex, it cannot be copied. Therefore we need a reference counter for this object. +class ThreadSafeCurlEasyRequest : public AIThreadSafeSimple { + public: + // Throws AICurlNoEasyHandle. + ThreadSafeCurlEasyRequest(void) : mReferenceCount(0) + { new (ptr()) CurlEasyRequest; + Dout(dc::curl, "Creating ThreadSafeCurlEasyRequest with this = " << (void*)this); } + virtual ~ThreadSafeCurlEasyRequest() + { Dout(dc::curl, "Destructing ThreadSafeCurlEasyRequest with this = " << (void*)this); } + + // Returns true if this is a base class of ThreadSafeBufferedCurlEasyRequest. + virtual bool isBuffered(void) const { return false; } + + private: + LLAtomicU32 mReferenceCount; + + friend void intrusive_ptr_add_ref(ThreadSafeCurlEasyRequest* p); // Called by boost::intrusive_ptr when a new copy of a boost::intrusive_ptr is made. + friend void intrusive_ptr_release(ThreadSafeCurlEasyRequest* p); // Called by boost::intrusive_ptr when a boost::intrusive_ptr is destroyed. +}; + +// Same as the above but adds a CurlResponderBuffer. The latter has its own locking in order to +// allow casting the underlying CurlEasyRequest to ThreadSafeCurlEasyRequest, independent of +// what class it is part of: ThreadSafeCurlEasyRequest or ThreadSafeBufferedCurlEasyRequest. +// The virtual destructor of ThreadSafeCurlEasyRequest allows to treat each easy handle transparently +// as a ThreadSafeCurlEasyRequest object, or optionally dynamic_cast it to a ThreadSafeBufferedCurlEasyRequest. +// Note: the order of these base classes is important: AIThreadSafeSimple is now +// destructed before ThreadSafeCurlEasyRequest is. +class ThreadSafeBufferedCurlEasyRequest : public ThreadSafeCurlEasyRequest, public AIThreadSafeSimple { + public: + // Throws AICurlNoEasyHandle. + ThreadSafeBufferedCurlEasyRequest(void) { new (AIThreadSafeSimple::ptr()) CurlResponderBuffer; } + + /*virtual*/ bool isBuffered(void) const { return true; } +}; + +// The curl easy request type wrapped in a reference counting pointer. +typedef boost::intrusive_ptr CurlEasyRequestPtr; + +// This class wraps CURLM*'s. +// It guarantees that a pointer is cleaned up when no longer needed, as required by libcurl. +class CurlMultiHandle : public boost::noncopyable { + public: + CurlMultiHandle(void); + ~CurlMultiHandle(); + + private: + // Disallow assignment. + CurlMultiHandle& operator=(CurlMultiHandle const*); + + private: + static LLAtomicU32 sTotalMultiHandles; + + protected: + CURLM* mMultiHandle; + + public: + // Set options for a curl multi handle. + CURLMcode setopt(CURLMoption option, long parameter); + CURLMcode setopt(CURLMoption option, curl_socket_callback parameter); + CURLMcode setopt(CURLMoption option, curl_multi_timer_callback parameter); + CURLMcode setopt(CURLMoption option, void* parameter); + + // Returns total number of existing CURLM* handles (excluding ones created outside this class). + static U32 getTotalMultiHandles(void) { return sTotalMultiHandles; } +}; + +// Overload the setopt methods in order to enforce the correct types (ie, convert an int to a long). + +// curl_multi_setopt may only be passed a long, +inline CURLMcode CurlMultiHandle::setopt(CURLMoption option, long parameter) +{ + llassert(option == CURLMOPT_MAXCONNECTS || option == CURLMOPT_PIPELINING); + return check_multi_code(curl_multi_setopt(mMultiHandle, option, parameter)); +} + +// ... or a function pointer, +inline CURLMcode CurlMultiHandle::setopt(CURLMoption option, curl_socket_callback parameter) +{ + llassert(option == CURLMOPT_SOCKETFUNCTION); + return check_multi_code(curl_multi_setopt(mMultiHandle, option, parameter)); +} + +inline CURLMcode CurlMultiHandle::setopt(CURLMoption option, curl_multi_timer_callback parameter) +{ + llassert(option == CURLMOPT_TIMERFUNCTION); + return check_multi_code(curl_multi_setopt(mMultiHandle, option, parameter)); +} + +// ... or an object pointer. +inline CURLMcode CurlMultiHandle::setopt(CURLMoption option, void* parameter) +{ + llassert(option == CURLMOPT_SOCKETDATA || option == CURLMOPT_TIMERDATA); + return check_multi_code(curl_multi_setopt(mMultiHandle, option, parameter)); +} + +} // namespace AICurlPrivate + +#endif diff --git a/indra/llmessage/aicurlthread.cpp b/indra/llmessage/aicurlthread.cpp new file mode 100644 index 000000000..0b1b85c0a --- /dev/null +++ b/indra/llmessage/aicurlthread.cpp @@ -0,0 +1,1710 @@ +/** + * @file aicurlthread.cpp + * @brief Implementation of AICurl, curl thread functions. + * + * Copyright (c) 2012, Aleric Inglewood. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution. + * + * CHANGELOG + * and additional copyright holders. + * + * 28/04/2012 + * Initial version, written by Aleric Inglewood @ SL + */ + +#include "linden_common.h" +#include "aicurlthread.h" +#include "lltimer.h" // ms_sleep +#include +#if !LL_WINDOWS +#include +#include +#include +#endif +#include + +// On linux, add -DDEBUG_WINDOWS_CODE_ON_LINUX to test the windows code used in this file. +#if !defined(DEBUG_WINDOWS_CODE_ON_LINUX) || !defined(LL_LINUX) || defined(LL_RELEASE) +#undef DEBUG_WINDOWS_CODE_ON_LINUX +#define DEBUG_WINDOWS_CODE_ON_LINUX 0 +#endif + +#if DEBUG_WINDOWS_CODE_ON_LINUX + +struct windows_fd_set { + unsigned int fd_count; + curl_socket_t fd_array[64]; +}; + +unsigned int const not_found = (unsigned int)-1; + +static inline unsigned int find_fd(curl_socket_t s, windows_fd_set const* fsp) +{ + for (unsigned int i = 0; i < fsp->fd_count; ++i) + { + if (fsp->fd_array[i] == s) + return i; + } + return not_found; +} + +static int windows_select(int, windows_fd_set* readfds, windows_fd_set* writefds, windows_fd_set*, timeval* val) +{ + fd_set r; + fd_set w; + FD_ZERO(&r); + FD_ZERO(&w); + int mfd = -1; + if (readfds) + { + for (int i = 0; i < readfds->fd_count; ++i) + { + int fd = readfds->fd_array[i]; + FD_SET(fd, &r); + mfd = llmax(mfd, fd); + } + } + if (writefds) + { + for (int i = 0; i < writefds->fd_count; ++i) + { + int fd = writefds->fd_array[i]; + FD_SET(fd, &w); + mfd = llmax(mfd, fd); + } + } + int nfds = select(mfd + 1, readfds ? &r : NULL, writefds ? &w : NULL, NULL, val); + if (readfds) + { + unsigned int fd_count = 0; + for (int i = 0; i < readfds->fd_count; ++i) + { + if (FD_ISSET(readfds->fd_array[i], &r)) + readfds->fd_array[fd_count++] = readfds->fd_array[i]; + } + readfds->fd_count = fd_count; + } + if (writefds) + { + unsigned int fd_count = 0; + for (int i = 0; i < writefds->fd_count; ++i) + { + if (FD_ISSET(writefds->fd_array[i], &w)) + writefds->fd_array[fd_count++] = writefds->fd_array[i]; + } + writefds->fd_count = fd_count; + } + return nfds; +} + +#undef FD_SETSIZE +#undef FD_ZERO +#undef FD_ISSET +#undef FD_SET +#undef FD_CLR + +int const FD_SETSIZE = sizeof(windows_fd_set::fd_array) / sizeof(curl_socket_t); + +static void FD_ZERO(windows_fd_set* fsp) +{ + fsp->fd_count = 0; +} + +static bool FD_ISSET(curl_socket_t s, windows_fd_set const* fsp) +{ + return find_fd(s, fsp) != not_found; +} + +static void FD_SET(curl_socket_t s, windows_fd_set* fsp) +{ + llassert(!FD_ISSET(s, fsp)); + fsp->fd_array[fsp->fd_count++] = s; +} + +static void FD_CLR(curl_socket_t s, windows_fd_set* fsp) +{ + unsigned int i = find_fd(s, fsp); + llassert(i != not_found); + fsp->fd_array[i] = fsp->fd_array[--(fsp->fd_count)]; +} + +#define fd_set windows_fd_set +#define select windows_select + +int WSAGetLastError(void) +{ + return errno; +} + +typedef char* LPTSTR; + +bool FormatMessage(int, void*, int e, int, LPTSTR error_str_ptr, int, void*) +{ + char* error_str = *(LPTSTR*)error_str_ptr; + error_str = strerror(e); + return true; +} + +void LocalFree(LPTSTR) +{ +} + +int const FORMAT_MESSAGE_ALLOCATE_BUFFER = 0; +int const FORMAT_MESSAGE_FROM_SYSTEM = 0; +int const FORMAT_MESSAGE_IGNORE_INSERTS = 0; +int const INVALID_SOCKET = -1; +int const SOCKET_ERROR = -1; +int const WSAEWOULDBLOCK = EWOULDBLOCK; + +int closesocket(curl_socket_t fd) +{ + return close(fd); +} + +int const FIONBIO = 0; + +int ioctlsocket(int fd, int, unsigned long* nonblocking_enable) +{ + int res = fcntl(fd, F_GETFL, 0); + llassert_always(res != -1); + if (*nonblocking_enable) + res |= O_NONBLOCK; + else + res &= ~O_NONBLOCK; + return fcntl(fd, F_SETFD, res); +} + +#endif // DEBUG_WINDOWS_CODE_ON_LINUX + +#define WINDOWS_CODE (LL_WINDOWS || DEBUG_WINDOWS_CODE_ON_LINUX) + +#undef AICurlPrivate + +namespace AICurlPrivate { + +enum command_st { + cmd_none, + cmd_add, + cmd_boost, + cmd_remove +}; + +class Command { + public: + Command(void) : mCommand(cmd_none) { } + Command(AICurlEasyRequest const& easy_request, command_st command) : mCurlEasyRequest(easy_request.get_ptr()), mCommand(command) { } + + command_st command(void) const { return mCommand; } + CurlEasyRequestPtr const& easy_request(void) const { return mCurlEasyRequest; } + + bool operator==(AICurlEasyRequest const& easy_request) const { return mCurlEasyRequest == easy_request.get_ptr(); } + + void reset(void); + + private: + CurlEasyRequestPtr mCurlEasyRequest; + command_st mCommand; +}; + +void Command::reset(void) +{ + mCurlEasyRequest.reset(); + mCommand = cmd_none; +} + +// The following two globals have separate locks for speed considerations (in order not +// to block the main thread unnecessarily) but have the following correlation: +// +// MAIN-THREAD (AICurlEasyRequest::addRequest) +// * command_queue locked +// - A non-active (mActiveMultiHandle is NULL) ThreadSafeCurlEasyRequest (by means of an AICurlEasyRequest pointing to it) is added to command_queue with as command cmd_add. +// * command_queue unlocked +// +// If at this point addRequest is called again, then it is detected that the last command added to the queue +// for this ThreadSafeCurlEasyRequest is cmd_add. +// +// CURL-THREAD (AICurlThread::wakeup): +// * command_queue locked +// * command_being_processed is write-locked +// - command_being_processed is assigned the value of the command in the queue. +// * command_being_processed is unlocked +// - The command is removed from command_queue +// * command_queue unlocked +// +// If at this point addRequest is called again, then it is detected that command_being_processed adds the same ThreadSafeCurlEasyRequest. +// +// * command_being_processed is read-locked +// - mActiveMultiHandle is set to point to the curl multi handle +// - The easy handle is added to the multi handle +// * command_being_processed is write-locked +// - command_being_processed is reset +// * command_being_processed is unlocked +// +// If at this point addRequest is called again, then it is detected that the ThreadSafeCurlEasyRequest is active. + +// Multi-threaded queue for passing Command objects from the main-thread to the curl-thread. +AIThreadSafeSimpleDC > command_queue; +typedef AIAccess > command_queue_wat; + +AIThreadSafeDC command_being_processed; +typedef AIWriteAccess command_being_processed_wat; +typedef AIReadAccess command_being_processed_rat; + +namespace curlthread { +// All functions in this namespace are only run by the curl thread, unless they are marked with MAIN-THREAD. + +//----------------------------------------------------------------------------- +// PollSet + +int const empty = 0x1; +int const complete = 0x2; + +enum refresh_t { + not_complete_not_empty = 0, + complete_not_empty = complete, + empty_and_complete = complete|empty +}; + +class PollSet +{ + public: + PollSet(void); + + // Add/remove a filedescriptor to/from mFileDescriptors. + void add(curl_socket_t s); + void remove(curl_socket_t s); + + // Copy mFileDescriptors to an internal fd_set that is returned by access(). + // Returns if all fds could be copied (complete) and/or if the resulting fd_set is empty. + refresh_t refresh(void); + + // Return a pointer to the underlaying fd_set. + fd_set* access(void) { return &mFdSet; } + +#if !WINDOWS_CODE + // Return the largest fd set in mFdSet by refresh. + curl_socket_t get_max_fd(void) const { return mMaxFdSet; } +#endif + + // Return true if a filedescriptor is set in mFileDescriptors (used for debugging). + bool contains(curl_socket_t s) const; + + // Return true if a filedescriptor is set in mFdSet. + bool is_set(curl_socket_t s) const; + + // Clear filedescriptor in mFdSet. + void clr(curl_socket_t fd); + + // Iterate over all file descriptors that were set by refresh and are still set in mFdSet. + void reset(void); // Reset the iterator. + curl_socket_t get(void) const; // Return next filedescriptor, or CURL_SOCKET_BAD when there are no more. + // Only valid if reset() was called after the last call to refresh(). + void next(void); // Advance to next filedescriptor. + + private: + curl_socket_t* mFileDescriptors; + int mNrFds; // The number of filedescriptors in the array. + int mNext; // The index of the first file descriptor to start copying, the next call to refresh(). + + fd_set mFdSet; // Output variable for select(). (Re)initialized by calling refresh(). + +#if !WINDOWS_CODE + curl_socket_t mMaxFd; // The largest filedescriptor in the array, or CURL_SOCKET_BAD when it is empty. + curl_socket_t mMaxFdSet; // The largest filedescriptor set in mFdSet by refresh(), or CURL_SOCKET_BAD when it was empty. + std::vector mCopiedFileDescriptors; // Filedescriptors copied by refresh to mFdSet. + std::vector::iterator mIter; // Index into mCopiedFileDescriptors for next(); loop variable. +#else + unsigned int mIter; // Index into fd_set::fd_array. +#endif +}; + +// A PollSet can store at least 1024 filedescriptors, or FD_SETSIZE if that is larger than 1024 [MAXSIZE]. +// The number of stored filedescriptors is mNrFds [0 <= mNrFds <= MAXSIZE]. +// The largest filedescriptor is stored is mMaxFd, which is -1 iff mNrFds == 0. +// The file descriptors are stored contiguous in mFileDescriptors[i], with 0 <= i < mNrFds. +// File descriptors with the highest priority should be stored first (low index). +// +// mNext is an index into mFileDescriptors that is copied first, the next call to refresh(). +// It is set to 0 when mNrFds < FD_SETSIZE, even if mNrFds == 0. +// +// After a call to refresh(): +// +// mFdSet has bits set for at most FD_SETSIZE - 1 filedescriptors, copied from mFileDescriptors starting +// at index mNext (wrapping around to 0). If mNrFds < FD_SETSIZE then mNext is reset to 0 before copying starts. +// If mNrFds >= FD_SETSIZE then mNext is set to the next filedescriptor that was not copied (otherwise it is left at 0). +// +// mMaxFdSet is the largest filedescriptor in mFdSet or -1 if it is empty. + +static size_t const MAXSIZE = llmax(1024, FD_SETSIZE); + +// Create an empty PollSet. +PollSet::PollSet(void) : mFileDescriptors(new curl_socket_t [MAXSIZE]), + mNrFds(0), mNext(0) +#if !WINDOWS_CODE + , mMaxFd(-1), mMaxFdSet(-1) +#endif +{ + FD_ZERO(&mFdSet); +} + +// Add filedescriptor s to the PollSet. +void PollSet::add(curl_socket_t s) +{ + llassert_always(mNrFds < (int)MAXSIZE); + mFileDescriptors[mNrFds++] = s; +#if !WINDOWS_CODE + mMaxFd = llmax(mMaxFd, s); +#endif +} + +// Remove filedescriptor s from the PollSet. +void PollSet::remove(curl_socket_t s) +{ + // The number of open filedescriptors is relatively small, + // and on top of that we rather do something CPU intensive + // than bandwidth intensive (lookup table). Hence that this + // is a linear search in an array containing just the open + // filedescriptors. Then, since we are reading this memory + // page anyway, we might as well write to it without losing + // much clock cycles. Therefore, shift the whole vector + // back, keeping it compact and keeping the filedescriptors + // in the same order (which is supposedly their priority). + // + // The general case is where mFileDescriptors contains s at an index + // between 0 and mNrFds: + // mNrFds = 6 + // v + // index: 0 1 2 3 4 5 + // a b c s d e + + // This function should never be called unless s is actually in mFileDescriptors, + // as a result of a previous call to PollSet::add(). + llassert(mNrFds > 0); + + // Correct mNrFds for when the descriptor is removed. + // Make i 'point' to the last entry. + int i = --mNrFds; + // i = NrFds = 5 + // v + // index: 0 1 2 3 4 5 + // a b c s d e + curl_socket_t cur = mFileDescriptors[i]; // cur = 'e' +#if !WINDOWS_CODE + curl_socket_t max = -1; +#endif + while (cur != s) + { + llassert(i > 0); + curl_socket_t next = mFileDescriptors[--i]; // next = 'd' + mFileDescriptors[i] = cur; // Overwrite 'd' with 'e'. +#if !WINDOWS_CODE + max = llmax(max, cur); // max is the maximum value in 'i' or higher. +#endif + cur = next; // cur = 'd' + // i NrFds = 5 + // v v + // index: 0 1 2 3 4 + // a b c s e // cur = 'd' + // + // Next loop iteration: next = 's', overwrite 's' with 'd', cur = 's'; loop terminates. + // i NrFds = 5 + // v v + // index: 0 1 2 3 4 + // a b c d e // cur = 's' + } + llassert(cur == s); + // At this point i was decremented once more and points to the element before the old s. + // i NrFds = 5 + // v v + // index: 0 1 2 3 4 + // a b c d e // max = llmax('d', 'e') + + // If mNext pointed to an element before s, it should be left alone. Otherwise, if mNext pointed + // to s it must now point to 'd', or if it pointed beyond 's' it must be decremented by 1. + if (mNext > i) // i is where s was. + --mNext; + +#if !WINDOWS_CODE + // If s was the largest file descriptor, we have to update mMaxFd. + if (s == mMaxFd) + { + while (i > 0) + { + curl_socket_t next = mFileDescriptors[--i]; + max = llmax(max, next); + } + mMaxFd = max; + llassert(mMaxFd < s); + llassert((mMaxFd == -1) == (mNrFds == 0)); + } +#endif + + // ALSO make sure that s is no longer set in mFdSet, or we might confuse libcurl by + // calling curl_multi_socket_action for a socket that it told us to remove. +#if !WINDOWS_CODE + clr(s); +#else + // We have to use a custom implementation here, because we don't want to invalidate mIter. + // This is the same algorithm as above, but with mFdSet.fd_count instead of mNrFds, + // mFdSet.fd_array instead of mFileDescriptors and mIter instead of mNext. + if (FD_ISSET(s, &mFdSet)) + { + llassert(mFdSet.fd_count > 0); + unsigned int i = --mFdSet.fd_count; + curl_socket_t cur = mFdSet.fd_array[i]; + while (cur != s) + { + llassert(i > 0); + curl_socket_t next = mFdSet.fd_array[--i]; + mFdSet.fd_array[i] = cur; + cur = next; + } + if (mIter > i) + --mIter; + llassert(mIter <= mFdSet.fd_count); + } +#endif +} + +bool PollSet::contains(curl_socket_t fd) const +{ + for (int i = 0; i < mNrFds; ++i) + if (mFileDescriptors[i] == fd) + return true; + return false; +} + +inline bool PollSet::is_set(curl_socket_t fd) const +{ + return FD_ISSET(fd, &mFdSet); +} + +inline void PollSet::clr(curl_socket_t fd) +{ + FD_CLR(fd, &mFdSet); +} + +// This function fills mFdSet with at most FD_SETSIZE - 1 filedescriptors, +// starting at index mNext (updating mNext when not all could be added), +// and updates mMaxFdSet to be the largest fd added to mFdSet, or -1 if it's empty. +refresh_t PollSet::refresh(void) +{ + FD_ZERO(&mFdSet); +#if !WINDOWS_CODE + mCopiedFileDescriptors.clear(); +#endif + + if (mNrFds == 0) + { +#if !WINDOWS_CODE + mMaxFdSet = -1; +#endif + return empty_and_complete; + } + + llassert_always(mNext < mNrFds); + + // Test if mNrFds is larger than or equal to FD_SETSIZE; equal, because we reserve one + // filedescriptor for the wakeup fd: we copy maximal FD_SETSIZE - 1 filedescriptors. + // If not then we're going to copy everything so that we can save on CPU cycles + // by not calculating mMaxFdSet here. + if (mNrFds >= FD_SETSIZE) + { + llwarns << "PollSet::reset: More than FD_SETSIZE (" << FD_SETSIZE << ") file descriptors active!" << llendl; +#if !WINDOWS_CODE + // Calculate mMaxFdSet. + // Run over FD_SETSIZE - 1 elements, starting at mNext, wrapping to 0 when we reach the end. + int max = -1, i = mNext, count = 0; + while (++count < FD_SETSIZE) { max = llmax(max, mFileDescriptors[i]); if (++i == mNrFds) i = 0; } + mMaxFdSet = max; +#endif + } + else + { + mNext = 0; // Start at the beginning if we copy everything anyway. +#if !WINDOWS_CODE + mMaxFdSet = mMaxFd; +#endif + } + int count = 0; + int i = mNext; + for(;;) + { + if (++count == FD_SETSIZE) + { + mNext = i; + return not_complete_not_empty; + } + FD_SET(mFileDescriptors[i], &mFdSet); +#if !WINDOWS_CODE + mCopiedFileDescriptors.push_back(mFileDescriptors[i]); +#endif + if (++i == mNrFds) + { + // If we reached the end and start at the beginning, then we copied everything. + if (mNext == 0) + break; + // When can only come here if mNrFds >= FD_SETSIZE, hence we can just + // wrap around and terminate on count reaching FD_SETSIZE. + i = 0; + } + } + return complete_not_empty; +} + +// The API reset(), get() and next() allows one to run over all filedescriptors +// in mFdSet that are set. This works by running only over the filedescriptors +// that were set initially (by the call to refresh()) and then checking if that +// filedescriptor is (still) set in mFdSet. +// +// A call to reset() resets mIter to the beginning, so that get() returns +// the first filedescriptor that is still set. A call to next() progresses +// the iterator to the next set filedescriptor. If get() return -1, then there +// were no more filedescriptors set. +// +// Note that one should never call next() unless get() didn't return -1, so +// the call sequence is: +// refresh(); +// /* reset some or all bits in mFdSet */ +// reset(); +// while (get() != CURL_SOCKET_BAD) // next(); +// +// Note also that this API is only used by MergeIterator, which wraps it +// and provides a different API to use. + +void PollSet::reset(void) +{ +#if WINDOWS_CODE + mIter = 0; +#else + if (mCopiedFileDescriptors.empty()) + mIter = mCopiedFileDescriptors.end(); + else + { + mIter = mCopiedFileDescriptors.begin(); + if (!FD_ISSET(*mIter, &mFdSet)) + next(); + } +#endif +} + +inline curl_socket_t PollSet::get(void) const +{ +#if WINDOWS_CODE + return (mIter >= mFdSet.fd_count) ? CURL_SOCKET_BAD : mFdSet.fd_array[mIter]; +#else + return (mIter == mCopiedFileDescriptors.end()) ? CURL_SOCKET_BAD : *mIter; +#endif +} + +void PollSet::next(void) +{ +#if WINDOWS_CODE + llassert(mIter < mFdSet.fd_count); + ++mIter; +#else + llassert(mIter != mCopiedFileDescriptors.end()); // Only call next() if the last call to get() didn't return -1. + while (++mIter != mCopiedFileDescriptors.end() && !FD_ISSET(*mIter, &mFdSet)); +#endif +} + +//----------------------------------------------------------------------------- +// MergeIterator +// +// This class takes two PollSet's and allows one to run over all filedescriptors +// that are set in one or both poll sets, returning each filedescriptor only +// once, by calling next() until it returns false. + +class MergeIterator +{ + public: + MergeIterator(PollSet* readPollSet, PollSet* writePollSet); + + bool next(curl_socket_t& fd_out, int& ev_bitmask_out); + + private: + PollSet* mReadPollSet; + PollSet* mWritePollSet; + int readIndx; + int writeIndx; +}; + +MergeIterator::MergeIterator(PollSet* readPollSet, PollSet* writePollSet) : + mReadPollSet(readPollSet), mWritePollSet(writePollSet), readIndx(0), writeIndx(0) +{ + mReadPollSet->reset(); + mWritePollSet->reset(); +} + +bool MergeIterator::next(curl_socket_t& fd_out, int& ev_bitmask_out) +{ + curl_socket_t rfd = mReadPollSet->get(); + curl_socket_t wfd = mWritePollSet->get(); + + if (rfd == CURL_SOCKET_BAD && wfd == CURL_SOCKET_BAD) + return false; + + if (rfd == wfd) + { + fd_out = rfd; + ev_bitmask_out = CURL_CSELECT_IN | CURL_CSELECT_OUT; + mReadPollSet->next(); + } + else if (wfd == CURL_SOCKET_BAD || (rfd != CURL_SOCKET_BAD && rfd < wfd)) // Use and increment smaller one, unless it's CURL_SOCKET_BAD. + { + fd_out = rfd; + ev_bitmask_out = CURL_CSELECT_IN; + mReadPollSet->next(); + if (wfd != CURL_SOCKET_BAD && mWritePollSet->is_set(rfd)) + { + ev_bitmask_out |= CURL_CSELECT_OUT; + mWritePollSet->clr(rfd); + } + } + else + { + fd_out = wfd; + ev_bitmask_out = CURL_CSELECT_OUT; + mWritePollSet->next(); + if (rfd != CURL_SOCKET_BAD && mReadPollSet->is_set(wfd)) + { + ev_bitmask_out |= CURL_CSELECT_IN; + mReadPollSet->clr(wfd); + } + } + + return true; +} + +//----------------------------------------------------------------------------- +// CurlSocketInfo + +// A class with info for each socket that is in use by curl. +class CurlSocketInfo +{ + public: + CurlSocketInfo(MultiHandle& multi_handle, CURL* easy, curl_socket_t s, int action); + ~CurlSocketInfo(); + + void set_action(int action); + + private: + MultiHandle& mMultiHandle; + CURL const* mEasy; + curl_socket_t mSocketFd; + int mAction; +}; + +CurlSocketInfo::CurlSocketInfo(MultiHandle& multi_handle, CURL* easy, curl_socket_t s, int action) : + mMultiHandle(multi_handle), mEasy(easy), mSocketFd(s), mAction(CURL_POLL_NONE) +{ + mMultiHandle.assign(s, this); + llassert(!mMultiHandle.mReadPollSet->contains(s)); + llassert(!mMultiHandle.mWritePollSet->contains(s)); + set_action(action); +} + +CurlSocketInfo::~CurlSocketInfo() +{ + set_action(CURL_POLL_NONE); +} + +void CurlSocketInfo::set_action(int action) +{ + int toggle_action = mAction ^ action; + mAction = action; + if ((toggle_action & CURL_POLL_IN)) + { + if ((action & CURL_POLL_IN)) + mMultiHandle.mReadPollSet->add(mSocketFd); + else + mMultiHandle.mReadPollSet->remove(mSocketFd); + } + if ((toggle_action & CURL_POLL_OUT)) + { + if ((action & CURL_POLL_OUT)) + mMultiHandle.mWritePollSet->add(mSocketFd); + else + mMultiHandle.mWritePollSet->remove(mSocketFd); + } +} + +//----------------------------------------------------------------------------- +// AICurlThread + +class AICurlThread : public LLThread +{ + public: + static AICurlThread* sInstance; + LLMutex mWakeUpMutex; + bool mWakeUpFlag; // Protected by mWakeUpMutex. + + public: + // MAIN-THREAD + AICurlThread(void); + virtual ~AICurlThread(); + + // MAIN-THREAD + void wakeup_thread(void); + + // MAIN-THREAD + void stop_thread(void) { mRunning = false; wakeup_thread(); } + + protected: + virtual void run(void); + void wakeup(AICurlMultiHandle_wat const& multi_handle_w); + void process_commands(AICurlMultiHandle_wat const& multi_handle_w); + + private: + // MAIN-THREAD + void create_wakeup_fds(void); + void cleanup_wakeup_fds(void); + + curl_socket_t mWakeUpFd_in; + curl_socket_t mWakeUpFd; + + int mZeroTimeOut; + + volatile bool mRunning; +}; + +// Only the main thread is accessing this. +AICurlThread* AICurlThread::sInstance = NULL; + +// MAIN-THREAD +AICurlThread::AICurlThread(void) : LLThread("AICurlThread"), + mWakeUpFd_in(CURL_SOCKET_BAD), + mWakeUpFd(CURL_SOCKET_BAD), + mZeroTimeOut(0), mRunning(true), mWakeUpFlag(false) +{ + create_wakeup_fds(); + sInstance = this; +} + +// MAIN-THREAD +AICurlThread::~AICurlThread() +{ + sInstance = NULL; + cleanup_wakeup_fds(); +} + +#if LL_WINDOWS +static std::string formatWSAError() +{ + std::ostringstream r; + int e = WSAGetLastError(); + LPTSTR error_str = 0; + r << e; + if(FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, e, 0, (LPTSTR)&error_str, 0, NULL)) + { + r << " " << utf16str_to_utf8str(error_str); + LocalFree(error_str); + } + else + { + r << " Unknown WinSock error"; + } + return r.str(); +} +#elif WINDOWS_CODE +static std::string formatWSAError() +{ + return strerror(errno); +} +#endif + +#if LL_WINDOWS + /* Copyright 2007, 2010 by Nathan C. Myers + * This code is Free Software. It may be copied freely, in original or + * modified form, subject only to the restrictions that (1) the author is + * relieved from all responsibilities for any use for any purpose, and (2) + * this copyright notice must be retained, unchanged, in its entirety. If + * for any reason the author might be held responsible for any consequences + * of copying or use, license is withheld. + */ +static int dumb_socketpair(SOCKET socks[2], bool make_overlapped) +{ + union { + struct sockaddr_in inaddr; + struct sockaddr addr; + } a; + SOCKET listener; + int e; + socklen_t addrlen = sizeof(a.inaddr); + DWORD flags = (make_overlapped ? WSA_FLAG_OVERLAPPED : 0); + int reuse = 1; + + if (socks == 0) { + WSASetLastError(WSAEINVAL); + return SOCKET_ERROR; + } + + listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (listener == INVALID_SOCKET) + return SOCKET_ERROR; + + memset(&a, 0, sizeof(a)); + a.inaddr.sin_family = AF_INET; + a.inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + a.inaddr.sin_port = 0; + + socks[0] = socks[1] = INVALID_SOCKET; + do { + if (setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, + (char*) &reuse, (socklen_t) sizeof(reuse)) == -1) + break; + if (bind(listener, &a.addr, sizeof(a.inaddr)) == SOCKET_ERROR) + break; + + memset(&a, 0, sizeof(a)); + if (getsockname(listener, &a.addr, &addrlen) == SOCKET_ERROR) + break; + // win32 getsockname may only set the port number, p=0.0005. + // ( http://msdn.microsoft.com/library/ms738543.aspx ): + a.inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + a.inaddr.sin_family = AF_INET; + + if (listen(listener, 1) == SOCKET_ERROR) + break; + + socks[0] = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, flags); + if (socks[0] == INVALID_SOCKET) + break; + if (connect(socks[0], &a.addr, sizeof(a.inaddr)) == SOCKET_ERROR) + break; + + socks[1] = accept(listener, NULL, NULL); + if (socks[1] == INVALID_SOCKET) + break; + + closesocket(listener); + return 0; + + } while (0); + + e = WSAGetLastError(); + closesocket(listener); + closesocket(socks[0]); + closesocket(socks[1]); + WSASetLastError(e); + return SOCKET_ERROR; +} +#elif WINDOWS_CODE +int dumb_socketpair(int socks[2], int dummy) +{ + (void) dummy; + return socketpair(AF_LOCAL, SOCK_STREAM, 0, socks); +} +#endif + +// MAIN-THREAD +void AICurlThread::create_wakeup_fds(void) +{ +#if WINDOWS_CODE + //SGTODO + curl_socket_t socks[2]; + if (dumb_socketpair(socks, false) == SOCKET_ERROR) + { + llerrs << "Failed to generate wake-up socket pair" << formatWSAError() << llendl; + return; + } + u_long nonblocking_enable = TRUE; + int error = ioctlsocket(socks[0], FIONBIO, &nonblocking_enable); + if(error) + { + llerrs << "Failed to set wake-up socket nonblocking: " << formatWSAError() << llendl; + } + llassert(nonblocking_enable); + error = ioctlsocket(socks[1], FIONBIO, &nonblocking_enable); + if(error) + { + llerrs << "Failed to set wake-up input socket nonblocking: " << formatWSAError() << llendl; + } + mWakeUpFd = socks[0]; + mWakeUpFd_in = socks[1]; +#else + int pipefd[2]; + if (pipe(pipefd)) + { + llerrs << "Failed to create wakeup pipe: " << strerror(errno) << llendl; + } + int const flags = O_NONBLOCK; + for (int i = 0; i < 2; ++i) + { + if (fcntl(pipefd[i], F_SETFL, flags)) + { + llerrs << "Failed to set pipe to non-blocking: " << strerror(errno) << llendl; + } + } + mWakeUpFd = pipefd[0]; // Read-end of the pipe. + mWakeUpFd_in = pipefd[1]; // Write-end of the pipe. +#endif +} + +// MAIN-THREAD +void AICurlThread::cleanup_wakeup_fds(void) +{ +#if WINDOWS_CODE + //SGTODO + if (mWakeUpFd != CURL_SOCKET_BAD) + { + int error = closesocket(mWakeUpFd); + if (error) + { + llwarns << "Error closing wake-up socket" << formatWSAError() << llendl; + } + } + if (mWakeUpFd_in != CURL_SOCKET_BAD) + { + int error = closesocket(mWakeUpFd_in); + if (error) + { + llwarns << "Error closing wake-up input socket" << formatWSAError() << llendl; + } + } +#else + if (mWakeUpFd_in != CURL_SOCKET_BAD) + close(mWakeUpFd_in); + if (mWakeUpFd != CURL_SOCKET_BAD) + close(mWakeUpFd); +#endif +} + +// MAIN-THREAD +void AICurlThread::wakeup_thread(void) +{ + DoutEntering(dc::curl, "AICurlThread::wakeup_thread"); + llassert(is_main_thread()); + + // Try if curl thread is still awake and if so, pass the new commands directly. + if (mWakeUpMutex.tryLock()) + { + mWakeUpFlag = true; + mWakeUpMutex.unlock(); + return; + } + +#if WINDOWS_CODE + //SGTODO + int len = send(mWakeUpFd_in, "!", 1, 0); + if (len == SOCKET_ERROR) + { + llerrs << "Send to wake-up socket failed: " << formatWSAError() << llendl; + } + llassert_always(len == 1); + //SGTODO: handle EAGAIN if needed +#else + // If write() is interrupted by a signal before it writes any data, it shall return -1 with errno set to [EINTR]. + // If write() is interrupted by a signal after it successfully writes some data, it shall return the number of bytes written. + // Write requests to a pipe or FIFO shall be handled in the same way as a regular file with the following exceptions: + // If the O_NONBLOCK flag is set, write() requests shall be handled differently, in the following ways: + // A write request for {PIPE_BUF} or fewer bytes shall have the following effect: + // if there is sufficient space available in the pipe, write() shall transfer all the data and return the number + // of bytes requested. Otherwise, write() shall transfer no data and return -1 with errno set to [EAGAIN]. + ssize_t len; + do + { + len = write(mWakeUpFd_in, "!", 1); + if (len == -1 && errno == EAGAIN) + return; // Unread characters are still in the pipe, so no need to add more. + } + while(len == -1 && errno == EINTR); + if (len == -1) + { + llerrs << "write(3) to mWakeUpFd_in: " << strerror(errno) << llendl; + } + llassert_always(len == 1); +#endif +} + +void AICurlThread::wakeup(AICurlMultiHandle_wat const& multi_handle_w) +{ + DoutEntering(dc::curl, "AICurlThread::wakeup"); + +#if WINDOWS_CODE + //SGTODO + char buf[256]; + bool got_data = false; + for(;;) + { + int len = recv(mWakeUpFd, buf, sizeof(buf), 0); + if (len > 0) + { + // Data was read from the pipe. + got_data = true; + if (len < sizeof(buf)) + break; + } + else if (len == SOCKET_ERROR) + { + // An error occurred. + if (errno == EWOULDBLOCK) + { + if (got_data) + break; + // There was no data, even though select() said so. If this ever happens at all(?), lets just return and enter select() again. + return; + } + else if (errno == EINTR) + { + continue; + } + else + { + llerrs << "read(3) from mWakeUpFd: " << formatWSAError() << llendl; + return; + } + } + else + { + // pipe(2) returned 0. + llwarns << "read(3) from mWakeUpFd returned 0, indicating that the pipe on the other end was closed! Shutting down curl thread." << llendl; + closesocket(mWakeUpFd); + mWakeUpFd = CURL_SOCKET_BAD; + mRunning = false; + return; + } + } +#else + // If a read() is interrupted by a signal before it reads any data, it shall return -1 with errno set to [EINTR]. + // If a read() is interrupted by a signal after it has successfully read some data, it shall return the number of bytes read. + // When attempting to read from an empty pipe or FIFO: + // If no process has the pipe open for writing, read() shall return 0 to indicate end-of-file. + // If some process has the pipe open for writing and O_NONBLOCK is set, read() shall return -1 and set errno to [EAGAIN]. + char buf[256]; + bool got_data = false; + for(;;) + { + ssize_t len = read(mWakeUpFd, buf, sizeof(buf)); + if (len > 0) + { + // Data was read from the pipe. + got_data = true; + if (len < sizeof(buf)) + break; + } + else if (len == -1) + { + // An error occurred. + if (errno == EAGAIN) + { + if (got_data) + break; + // There was no data, even though select() said so. If this ever happens at all(?), lets just return and enter select() again. + return; + } + else if (errno == EINTR) + { + continue; + } + else + { + llerrs << "read(3) from mWakeUpFd: " << strerror(errno) << llendl; + return; + } + } + else + { + // pipe(2) returned 0. + llwarns << "read(3) from mWakeUpFd returned 0, indicating that the pipe on the other end was closed! Shutting down curl thread." << llendl; + close(mWakeUpFd); + mWakeUpFd = CURL_SOCKET_BAD; + mRunning = false; + return; + } + } +#endif + // Data was received on mWakeUpFd. This means that the main-thread added one + // or more commands to the command queue and called wakeUpCurlThread(). + process_commands(multi_handle_w); +} + +void AICurlThread::process_commands(AICurlMultiHandle_wat const& multi_handle_w) +{ + DoutEntering(dc::curl, "AICurlThread::process_commands(void)"); + + // If we get here then the main thread called wakeup_thread() recently. + for(;;) + { + // Access command_queue, and move command to command_being_processed. + { + command_queue_wat command_queue_w(command_queue); + if (command_queue_w->empty()) + { + mWakeUpMutex.lock(); + mWakeUpFlag = false; + mWakeUpMutex.unlock(); + break; + } + // Move the next command from the queue into command_being_processed. + *command_being_processed_wat(command_being_processed) = command_queue_w->front(); + command_queue_w->pop_front(); + } + // Access command_being_processed only. + { + command_being_processed_rat command_being_processed_r(command_being_processed); + switch(command_being_processed_r->command()) + { + case cmd_none: + case cmd_boost: // FIXME: future stuff + break; + case cmd_add: + multi_handle_w->add_easy_request(AICurlEasyRequest(command_being_processed_r->easy_request())); + break; + case cmd_remove: + multi_handle_w->remove_easy_request(AICurlEasyRequest(command_being_processed_r->easy_request()), true); + break; + } + // Done processing. + command_being_processed_wat command_being_processed_w(command_being_processed_r); + command_being_processed_w->reset(); // This destroys the CurlEasyRequest in case of a cmd_remove. + } + } +} + +// The main loop of the curl thread. +void AICurlThread::run(void) +{ + DoutEntering(dc::curl, "AICurlThread::run()"); + + { + AICurlMultiHandle_wat multi_handle_w(AICurlMultiHandle::getInstance()); + while(mRunning) + { + // If mRunning is true then we can only get here if mWakeUpFd != CURL_SOCKET_BAD. + llassert(mWakeUpFd != CURL_SOCKET_BAD); + // Copy the next batch of file descriptors from the PollSets mFiledescriptors into their mFdSet. + multi_handle_w->mReadPollSet->refresh(); + refresh_t wres = multi_handle_w->mWritePollSet->refresh(); + // Add wake up fd if any, and pass NULL to select() if a set is empty. + fd_set* read_fd_set = multi_handle_w->mReadPollSet->access(); + FD_SET(mWakeUpFd, read_fd_set); + fd_set* write_fd_set = ((wres & empty)) ? NULL : multi_handle_w->mWritePollSet->access(); + // Calculate nfds (ignored on windows). +#if !WINDOWS_CODE + curl_socket_t const max_rfd = llmax(multi_handle_w->mReadPollSet->get_max_fd(), mWakeUpFd); + curl_socket_t const max_wfd = multi_handle_w->mWritePollSet->get_max_fd(); + int nfds = llmax(max_rfd, max_wfd) + 1; + llassert(0 <= nfds && nfds <= FD_SETSIZE); + llassert((max_rfd == -1) == (read_fd_set == NULL) && + (max_wfd == -1) == (write_fd_set == NULL)); // Needed on Windows. + llassert((max_rfd == -1 || multi_handle_w->mReadPollSet->is_set(max_rfd)) && + (max_wfd == -1 || multi_handle_w->mWritePollSet->is_set(max_wfd))); +#else + int nfds = 64; +#endif + int ready = 0; + // Process every command in command_queue before entering select(). + for(;;) + { + mWakeUpMutex.lock(); + if (mWakeUpFlag) + { + mWakeUpMutex.unlock(); + process_commands(multi_handle_w); + continue; + } + break; + } + // wakeup_thread() is also called after setting mRunning to false. + if (!mRunning) + { + mWakeUpMutex.unlock(); + break; + } + // If we get here then mWakeUpFlag has been false since we grabbed the lock. + // We're now entering select(), during which the main thread will write to the pipe/socket + // to wake us up, because it can't get the lock. + struct timeval timeout; + long timeout_ms = multi_handle_w->getTimeOut(); + // If no timeout is set, sleep 1 second. + if (LL_UNLIKELY(timeout_ms < 0)) + timeout_ms = 1000; + if (LL_UNLIKELY(timeout_ms == 0)) + { + if (mZeroTimeOut >= 10000) + { + if (mZeroTimeOut == 10000) + llwarns << "Detected more than 10000 zero-timeout calls of select() by curl thread (more than 101 seconds)!" << llendl; + } + else if (mZeroTimeOut >= 1000) + timeout_ms = 10; + else if (mZeroTimeOut >= 100) + timeout_ms = 1; + } + else + { + if (LL_UNLIKELY(mZeroTimeOut >= 10000)) + llinfos << "Timeout of select() call by curl thread reset (to " << timeout_ms << " ms)." << llendl; + mZeroTimeOut = 0; + } + timeout.tv_sec = timeout_ms / 1000; + timeout.tv_usec = (timeout_ms % 1000) * 1000; +#ifdef CWDEBUG + static int last_nfds = -1; + static long last_timeout_ms = -1; + static int same_count = 0; + bool same = (nfds == last_nfds && timeout_ms == last_timeout_ms); + if (!same) + { + if (same_count > 1) + Dout(dc::curl, "Last select() call repeated " << same_count << " times."); + Dout(dc::curl|flush_cf|continued_cf, "select(" << nfds << ", ..., timeout = " << timeout_ms << " ms) = "); + same_count = 1; + } + else + { + ++same_count; + } +#endif + ready = select(nfds, read_fd_set, write_fd_set, NULL, &timeout); + mWakeUpMutex.unlock(); +#ifdef CWDEBUG + static int last_ready = -2; + static int last_errno = 0; + if (!same) + Dout(dc::finish|cond_error_cf(ready == -1), ready); + else if (ready != last_ready || (ready == -1 && errno != last_errno)) + { + if (same_count > 1) + Dout(dc::curl, "Last select() call repeated " << same_count << " times."); + Dout(dc::curl|cond_error_cf(ready == -1), "select(" << last_nfds << ", ..., timeout = " << last_timeout_ms << " ms) = " << ready); + same_count = 1; + } + last_nfds = nfds; + last_timeout_ms = timeout_ms; + last_ready = ready; + if (ready == -1) + last_errno = errno; +#endif + // Select returns the total number of bits set in each of the fd_set's (upon return), + // or -1 when an error occurred. A value of 0 means that a timeout occurred. + if (ready == -1) + { + llwarns << "select() failed: " << errno << ", " << strerror(errno) << llendl; + continue; + } + else if (ready == 0) + { + multi_handle_w->socket_action(CURL_SOCKET_TIMEOUT, 0); + } + else + { + if (multi_handle_w->mReadPollSet->is_set(mWakeUpFd)) + { + // Process commands from main-thread. This can add or remove filedescriptors from the poll sets. + wakeup(multi_handle_w); + --ready; + } + // Handle all active filedescriptors. + MergeIterator iter(multi_handle_w->mReadPollSet, multi_handle_w->mWritePollSet); + curl_socket_t fd; + int ev_bitmask; + while (ready > 0 && iter.next(fd, ev_bitmask)) + { + ready -= (ev_bitmask == (CURL_CSELECT_IN|CURL_CSELECT_OUT)) ? 2 : 1; + // This can cause libcurl to do callbacks and remove filedescriptors, causing us to reset their bits in the poll sets. + multi_handle_w->socket_action(fd, ev_bitmask); + llassert(ready >= 0); + } + // Note that ready is not necessarily 0 here, because it's possible + // that libcurl removed file descriptors which we subsequently + // didn't handle. + } + multi_handle_w->check_run_count(); + } + } + AICurlMultiHandle::destroyInstance(); +} + +//----------------------------------------------------------------------------- +// MultiHandle + +MultiHandle::MultiHandle(void) : mHandleAddedOrRemoved(false), mPrevRunningHandles(0), mRunningHandles(0), mTimeOut(-1), mReadPollSet(NULL), mWritePollSet(NULL) +{ + mReadPollSet = new PollSet; + mWritePollSet = new PollSet; + check_multi_code(curl_multi_setopt(mMultiHandle, CURLMOPT_SOCKETFUNCTION, &MultiHandle::socket_callback)); + check_multi_code(curl_multi_setopt(mMultiHandle, CURLMOPT_SOCKETDATA, this)); + check_multi_code(curl_multi_setopt(mMultiHandle, CURLMOPT_TIMERFUNCTION, &MultiHandle::timer_callback)); + check_multi_code(curl_multi_setopt(mMultiHandle, CURLMOPT_TIMERDATA, this)); +} + +MultiHandle::~MultiHandle() +{ + llinfos << "Destructing MultiHandle with " << mAddedEasyRequests.size() << " active curl easy handles." << llendl; + + // This thread was terminated. + // Curl demands that all handles are removed from the multi session handle before calling curl_multi_cleanup. + for(addedEasyRequests_type::iterator iter = mAddedEasyRequests.begin(); iter != mAddedEasyRequests.end(); iter = mAddedEasyRequests.begin()) + { + remove_easy_request(*iter); + } + delete mWritePollSet; + delete mReadPollSet; +} + +#if defined(CWDEBUG) || defined(DEBUG_CURLIO) +#undef AI_CASE_RETURN +#define AI_CASE_RETURN(x) do { case x: return #x; } while(0) +char const* action_str(int action) +{ + switch(action) + { + AI_CASE_RETURN(CURL_POLL_NONE); + AI_CASE_RETURN(CURL_POLL_IN); + AI_CASE_RETURN(CURL_POLL_OUT); + AI_CASE_RETURN(CURL_POLL_INOUT); + AI_CASE_RETURN(CURL_POLL_REMOVE); + } + return ""; +} +#endif + +//static +int MultiHandle::socket_callback(CURL* easy, curl_socket_t s, int action, void* userp, void* socketp) +{ + DoutEntering(dc::curl, "MultiHandle::socket_callback(" << (void*)easy << ", " << s << ", " << action_str(action) << ", " << (void*)userp << ", " << (void*)socketp << ")"); + MultiHandle& self = *static_cast(userp); + CurlSocketInfo* sock_info = static_cast(socketp); + if (action == CURL_POLL_REMOVE) + { + delete sock_info; + } + else + { + if (!sock_info) + { + sock_info = new CurlSocketInfo(self, easy, s, action); + } + else + { + sock_info->set_action(action); + } + } + return 0; +} + +//static +int MultiHandle::timer_callback(CURLM* multi, long timeout_ms, void* userp) +{ + MultiHandle& self = *static_cast(userp); + llassert(multi == self.mMultiHandle); + self.mTimeOut = timeout_ms; + Dout(dc::curl, "MultiHandle::timer_callback(): timeout set to " << timeout_ms << " ms."); + return 0; +} + +CURLMcode MultiHandle::socket_action(curl_socket_t sockfd, int ev_bitmask) +{ + CURLMcode res; + do + { + res = check_multi_code(curl_multi_socket_action(mMultiHandle, sockfd, ev_bitmask, &mRunningHandles)); + } + while(res == CURLM_CALL_MULTI_PERFORM); + return res; +} + +CURLMcode MultiHandle::assign(curl_socket_t sockfd, void* sockptr) +{ + return check_multi_code(curl_multi_assign(mMultiHandle, sockfd, sockptr)); +} + +CURLMsg const* MultiHandle::info_read(int* msgs_in_queue) const +{ + CURLMsg const* ret = curl_multi_info_read(mMultiHandle, msgs_in_queue); + // NULL could be an error, but normally it isn't, so don't print anything and + // never increment Stats::multi_errors. However, lets just increment multi_calls + // when it certainly wasn't an error... + if (ret) + Stats::multi_calls++; + return ret; +} + +CURLMcode MultiHandle::add_easy_request(AICurlEasyRequest const& easy_request) +{ + std::pair res = mAddedEasyRequests.insert(easy_request); + llassert(res.second); // May not have been added before. + CURLMcode ret; + { + AICurlEasyRequest_wat curl_easy_request_w(*easy_request); + ret = curl_easy_request_w->add_handle_to_multi(curl_easy_request_w, mMultiHandle); + } + mHandleAddedOrRemoved = true; + Dout(dc::curl, "MultiHandle::add_easy_request: Added AICurlEasyRequest " << (void*)easy_request.get() << "; now processing " << mAddedEasyRequests.size() << " easy handles."); + return ret; +} + +CURLMcode MultiHandle::remove_easy_request(AICurlEasyRequest const& easy_request, bool as_per_command) +{ + addedEasyRequests_type::iterator iter = mAddedEasyRequests.find(easy_request); + if (iter == mAddedEasyRequests.end()) + return (CURLMcode)-2; // Was already removed before. + CURLMcode res; + { + AICurlEasyRequest_wat curl_easy_request_w(**iter); + res = curl_easy_request_w->remove_handle_from_multi(curl_easy_request_w, mMultiHandle); +#ifdef SHOW_ASSERT + curl_easy_request_w->mRemovedPerCommand = as_per_command; +#endif + } + mAddedEasyRequests.erase(iter); + mHandleAddedOrRemoved = true; + Dout(dc::curl, "MultiHandle::remove_easy_request: Removed AICurlEasyRequest " << (void*)easy_request.get() << "; now processing " << mAddedEasyRequests.size() << " easy handles."); + return res; +} + +void MultiHandle::check_run_count(void) +{ + if (mHandleAddedOrRemoved || mRunningHandles < mPrevRunningHandles) + { + CURLMsg const* msg; + int msgs_left; + while ((msg = info_read(&msgs_left))) + { + if (msg->msg == CURLMSG_DONE) + { + CURL* easy = msg->easy_handle; + ThreadSafeCurlEasyRequest* ptr; + CURLcode rese = curl_easy_getinfo(easy, CURLINFO_PRIVATE, &ptr); + llassert_always(rese == CURLE_OK); + AICurlEasyRequest easy_request(ptr); + llassert(*AICurlEasyRequest_wat(*easy_request) == easy); + // Store the result and transfer info in the easy handle. + { + AICurlEasyRequest_wat curl_easy_request_w(*easy_request); + curl_easy_request_w->store_result(msg->data.result); +#ifdef CWDEBUG + char* eff_url; + curl_easy_request_w->getinfo(CURLINFO_EFFECTIVE_URL, &eff_url); + Dout(dc::curl, "Finished: " << eff_url << " (" << msg->data.result << ")"); +#endif + // Signal that this easy handle finished. + curl_easy_request_w->done(curl_easy_request_w); + } + // This invalidates msg, but not easy_request. + CURLMcode res = remove_easy_request(easy_request); + // This should hold, I think, because the handles are obviously ok and + // the only error we could get is when remove_easy_request() was already + // called before (by this thread); but if that was the case then the easy + // handle should not have been be returned by info_read()... + llassert(res == CURLM_OK); + // Nevertheless, if it was already removed then just ignore it. + if (res == CURLM_OK) + { + } + else if (res == -2) + { + llwarns << "Curl easy handle returned by curl_multi_info_read() that is not (anymore) in MultiHandle::mAddedEasyRequests!?!" << llendl; + } + // Destruction of easy_request at this point, causes the CurlEasyRequest to be deleted. + } + } + mHandleAddedOrRemoved = false; + } + mPrevRunningHandles = mRunningHandles; +} + +} // namespace curlthread +} // namespace AICurlPrivate + +//static +void AICurlMultiHandle::destroyInstance(void) +{ + LLThreadLocalData& tldata = LLThreadLocalData::tldata(); + Dout(dc::curl, "Destroying AICurlMultiHandle [" << (void*)tldata.mCurlMultiHandle << "] for thread \"" << tldata.mName << "\"."); + delete tldata.mCurlMultiHandle; + tldata.mCurlMultiHandle = NULL; +} + +//============================================================================= +// MAIN-THREAD (needing to access the above declarations). + +//static +AICurlMultiHandle& AICurlMultiHandle::getInstance(void) +{ + LLThreadLocalData& tldata = LLThreadLocalData::tldata(); + if (!tldata.mCurlMultiHandle) + { + tldata.mCurlMultiHandle = new AICurlMultiHandle; + Dout(dc::curl, "Created AICurlMultiHandle [" << (void*)tldata.mCurlMultiHandle << "] for thread \"" << tldata.mName << "\"."); + } + return *static_cast(tldata.mCurlMultiHandle); +} + +namespace AICurlPrivate { + +bool curlThreadIsRunning(void) +{ + using curlthread::AICurlThread; + return AICurlThread::sInstance && !AICurlThread::sInstance->isStopped(); +} + +void wakeUpCurlThread(void) +{ + using curlthread::AICurlThread; + if (AICurlThread::sInstance) + AICurlThread::sInstance->wakeup_thread(); +} + +void stopCurlThread(void) +{ + using curlthread::AICurlThread; + if (AICurlThread::sInstance) + { + AICurlThread::sInstance->stop_thread(); + int count = 101; + while(--count && !AICurlThread::sInstance->isStopped()) + { + ms_sleep(10); + } + Dout(dc::curl, "Curl thread" << (curlThreadIsRunning() ? " not" : "") << " stopped after " << ((100 - count) * 10) << "ms."); + // Clear the command queue, for a cleaner cleanup. + command_queue_wat command_queue_w(command_queue); + command_queue_w->clear(); + } +} + +} // namespace AICurlPrivate + +//----------------------------------------------------------------------------- +// AICurlEasyRequest + +void AICurlEasyRequest::addRequest(void) +{ + using namespace AICurlPrivate; + + { + // Write-lock the command queue. + command_queue_wat command_queue_w(command_queue); +#ifdef SHOW_ASSERT + // This debug code checks if we aren't calling addRequest() twice for the same object. + // That means that the main thread already called (and finished, this is also the + // main thread) this function, which also follows from that we just locked command_queue. + // That leaves three options: It's still in the queue, or it was removed and is currently + // processed by the curl thread with again two options: either it was already added + // to the multi session handle or not yet. + + // Find the last command added. + command_st cmd = cmd_none; + for (std::deque::iterator iter = command_queue_w->begin(); iter != command_queue_w->end(); ++iter) + { + if (*iter == *this) + { + cmd = iter->command(); + break; + } + } + llassert(cmd == cmd_none || cmd == cmd_remove); // Not in queue, or last command was to remove it. + if (cmd == cmd_none) + { + // Read-lock command_being_processed. + command_being_processed_rat command_being_processed_r(command_being_processed); + if (*command_being_processed_r == *this) + { + // May not be in-between being removed from the command queue but not added to the multi session handle yet. + llassert(command_being_processed_r->command() == cmd_remove); + } + else + { + // May not already be added to the multi session handle. + llassert(!AICurlEasyRequest_wat(*get())->active()); + } + } +#endif + // Add a command to add the new request to the multi session to the command queue. + command_queue_w->push_back(Command(*this, cmd_add)); + AICurlEasyRequest_wat(*get())->add_queued(); + } + // Something was added to the queue, wake up the thread to get it. + wakeUpCurlThread(); +} + +void AICurlEasyRequest::removeRequest(void) +{ + using namespace AICurlPrivate; + + { + // Write-lock the command queue. + command_queue_wat command_queue_w(command_queue); +#ifdef SHOW_ASSERT + // This debug code checks if we aren't calling removeRequest() twice for the same object. + // That means that the thread calling this function already finished it, following from that + // we just locked command_queue. + // That leaves three options: It's still in the queue, or it was removed and is currently + // processed by the curl thread with again two options: either it was already removed + // from the multi session handle or not yet. + + // Find the last command added. + command_st cmd = cmd_none; + for (std::deque::iterator iter = command_queue_w->begin(); iter != command_queue_w->end(); ++iter) + { + if (*iter == *this) + { + cmd = iter->command(); + break; + } + } + llassert(cmd == cmd_none || cmd != cmd_remove); // Not in queue, or last command was not a remove command. + if (cmd == cmd_none) + { + // Read-lock command_being_processed. + command_being_processed_rat command_being_processed_r(command_being_processed); + if (*command_being_processed_r == *this) + { + // May not be in-between being removed from the command queue but not removed from the multi session handle yet. + llassert(command_being_processed_r->command() != cmd_remove); + } + else + { + // May not already have been removed from multi session handle as per command from the main thread (through this function thus). + { + AICurlEasyRequest_wat curl_easy_request_w(*get()); + llassert(curl_easy_request_w->active() || !curl_easy_request_w->mRemovedPerCommand); + } + } + } +#endif + // Add a command to remove this request from the multi session to the command queue. + command_queue_w->push_back(Command(*this, cmd_remove)); + // Suppress warning that would otherwise happen if the callbacks are revoked before the curl thread removed the request. + AICurlEasyRequest_wat(*get())->remove_queued(); + } + // Something was added to the queue, wake up the thread to get it. + wakeUpCurlThread(); +} + +//----------------------------------------------------------------------------- + +namespace AICurlInterface { + +void startCurlThread(void) +{ + using namespace AICurlPrivate::curlthread; + + llassert(is_main_thread()); + AICurlThread::sInstance = new AICurlThread; + AICurlThread::sInstance->start(); +} + +} // namespace AICurlInterface + diff --git a/indra/llmessage/aicurlthread.h b/indra/llmessage/aicurlthread.h new file mode 100644 index 000000000..a2bd36352 --- /dev/null +++ b/indra/llmessage/aicurlthread.h @@ -0,0 +1,128 @@ +/** + * @file aicurlthread.h + * @brief Thread safe wrapper for libcurl. + * + * Copyright (c) 2012, Aleric Inglewood. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution. + * + * CHANGELOG + * and additional copyright holders. + * + * 28/04/2012 + * Initial version, written by Aleric Inglewood @ SL + */ + +#ifndef AICURLTHREAD_H +#define AICURLTHREAD_H + +#include "aicurl.h" +#include + +#undef AICurlPrivate + +namespace AICurlPrivate { +namespace curlthread { + +class PollSet; + +// For ordering a std::set with AICurlEasyRequest objects. +struct AICurlEasyRequestCompare { + bool operator()(AICurlEasyRequest const& h1, AICurlEasyRequest const& h2) { return h1.get() < h2.get(); } +}; + +//----------------------------------------------------------------------------- +// MultiHandle + +// This class adds member functions that will only be called from the AICurlThread thread. +// This class guarantees that all added easy handles will be removed from the multi handle +// before the multi handle is cleaned up, as is required by libcurl. +class MultiHandle : public CurlMultiHandle +{ + public: + MultiHandle(void); + ~MultiHandle(); + + // Add/remove an easy handle to/from a multi session. + CURLMcode add_easy_request(AICurlEasyRequest const& easy_request); + CURLMcode remove_easy_request(AICurlEasyRequest const& easy_request, bool as_per_command = false); + + // Reads/writes available data from a particular socket (non-blocking). + CURLMcode socket_action(curl_socket_t sockfd, int ev_bitmask); + + // Set data to association with an internal socket. + CURLMcode assign(curl_socket_t sockfd, void* sockptr); + + // Read multi stack informationals. + CURLMsg const* info_read(int* msgs_in_queue) const; + + private: + typedef std::set addedEasyRequests_type; + addedEasyRequests_type mAddedEasyRequests; + + bool mHandleAddedOrRemoved; // Set when an easy handle was added or removed, reset in check_run_count(). + int mPrevRunningHandles; // The last value of mRunningHandles that check_run_count() was called with. + int mRunningHandles; // The last value returned by curl_multi_socket_action. + long mTimeOut; // The last time out in ms as set by the callback CURLMOPT_TIMERFUNCTION. + + private: + static int socket_callback(CURL* easy, curl_socket_t s, int action, void* userp, void* socketp); + static int timer_callback(CURLM* multi, long timeout_ms, void* userp); + + public: + // Returns the number of active easy handles as reported by the last call to curl_multi_socket_action. + int getRunningHandles(void) const { return mRunningHandles; } + + // Returns how long to wait for socket action before calling socket_action(CURL_SOCKET_TIMEOUT, 0), in ms. + int getTimeOut(void) const { return mTimeOut; } + + // This is called before sleeping, after calling (one or more times) socket_action. + void check_run_count(void); + + public: + //----------------------------------------------------------------------------- + // Curl socket administration: + + PollSet* mReadPollSet; + PollSet* mWritePollSet; +}; + +} // namespace curlthread +} // namespace AICurlPrivate + +// Thread safe, noncopyable curl multi handle. +// This class wraps MultiHandle for thread-safety. +// AIThreadSafeSingleThreadDC cannot be copied, but that is OK as we don't need that (or want that); +// this class provides a thread-local singleton (exactly one instance per thread), and because it +// can't be copied, that guarantees that the CURLM* handle is never used concurrently, which is +// not allowed by libcurl. +class AICurlMultiHandle : public AIThreadSafeSingleThreadDC, public LLThreadLocalDataMember { + public: + static AICurlMultiHandle& getInstance(void); + static void destroyInstance(void); + private: + // Use getInstance(). + AICurlMultiHandle(void) { } +}; + +typedef AISTAccessConst AICurlMultiHandle_rat; +typedef AISTAccess AICurlMultiHandle_wat; + +#define AICurlPrivate DONTUSE_AICurlPrivate + +#endif diff --git a/indra/llmessage/debug_libcurl.cpp b/indra/llmessage/debug_libcurl.cpp new file mode 100644 index 000000000..40b75d200 --- /dev/null +++ b/indra/llmessage/debug_libcurl.cpp @@ -0,0 +1,942 @@ +#ifdef DEBUG_CURLIO + +#include "sys.h" +#include +#include +#include +#include +#include +#include "llpreprocessor.h" +#include +#define COMPILING_DEBUG_LIBCURL_CC +#include "debug_libcurl.h" +#include "debug.h" +#include "llerror.h" +#ifdef CWDEBUG +#include +#endif + +#define CURL_VERSION(major, minor, patch) \ + (LIBCURL_VERSION_MAJOR > major || \ + (LIBCURL_VERSION_MAJOR == major && \ + LIBCURL_VERSION_MINOR > minor || \ + (LIBCURL_VERSION_MINOR == minor && \ + LIBCURL_VERSION_PATCH >= patch))) + +static struct curl_slist unchanged_slist; + +std::ostream& operator<<(std::ostream& os, struct curl_slist const& slist) +{ + struct curl_slist const* ptr = &slist; + if (ptr == &unchanged_slist) + os << " "; + else + { + os << "(curl_slist)@0x" << std::hex << (size_t)ptr << std::dec << "{"; + do + { + os << '"' << ptr->data << '"'; + ptr = ptr->next; + if (ptr) + os << ", "; + } + while(ptr); + os << "}"; + } + return os; +} + +#define CASEPRINT(x) case x: os << #x; break + +std::ostream& operator<<(std::ostream& os, CURLINFO info) +{ + switch (info) + { + CASEPRINT(CURLINFO_EFFECTIVE_URL); + CASEPRINT(CURLINFO_RESPONSE_CODE); + CASEPRINT(CURLINFO_TOTAL_TIME); + CASEPRINT(CURLINFO_NAMELOOKUP_TIME); + CASEPRINT(CURLINFO_CONNECT_TIME); + CASEPRINT(CURLINFO_PRETRANSFER_TIME); + CASEPRINT(CURLINFO_SIZE_UPLOAD); + CASEPRINT(CURLINFO_SIZE_DOWNLOAD); + CASEPRINT(CURLINFO_SPEED_DOWNLOAD); + CASEPRINT(CURLINFO_SPEED_UPLOAD); + CASEPRINT(CURLINFO_HEADER_SIZE); + CASEPRINT(CURLINFO_REQUEST_SIZE); + CASEPRINT(CURLINFO_SSL_VERIFYRESULT); + CASEPRINT(CURLINFO_FILETIME); + CASEPRINT(CURLINFO_CONTENT_LENGTH_DOWNLOAD); + CASEPRINT(CURLINFO_CONTENT_LENGTH_UPLOAD); + CASEPRINT(CURLINFO_STARTTRANSFER_TIME); + CASEPRINT(CURLINFO_CONTENT_TYPE); + CASEPRINT(CURLINFO_REDIRECT_TIME); + CASEPRINT(CURLINFO_REDIRECT_COUNT); + CASEPRINT(CURLINFO_PRIVATE); + CASEPRINT(CURLINFO_HTTP_CONNECTCODE); + CASEPRINT(CURLINFO_HTTPAUTH_AVAIL); + CASEPRINT(CURLINFO_PROXYAUTH_AVAIL); + CASEPRINT(CURLINFO_OS_ERRNO); + CASEPRINT(CURLINFO_NUM_CONNECTS); + CASEPRINT(CURLINFO_SSL_ENGINES); + CASEPRINT(CURLINFO_COOKIELIST); + CASEPRINT(CURLINFO_LASTSOCKET); + CASEPRINT(CURLINFO_FTP_ENTRY_PATH); + CASEPRINT(CURLINFO_REDIRECT_URL); + CASEPRINT(CURLINFO_PRIMARY_IP); + CASEPRINT(CURLINFO_APPCONNECT_TIME); + CASEPRINT(CURLINFO_CERTINFO); + CASEPRINT(CURLINFO_CONDITION_UNMET); + CASEPRINT(CURLINFO_RTSP_SESSION_ID); + CASEPRINT(CURLINFO_RTSP_CLIENT_CSEQ); + CASEPRINT(CURLINFO_RTSP_SERVER_CSEQ); + CASEPRINT(CURLINFO_RTSP_CSEQ_RECV); + CASEPRINT(CURLINFO_PRIMARY_PORT); + CASEPRINT(CURLINFO_LOCAL_IP); + CASEPRINT(CURLINFO_LOCAL_PORT); + default: + os << ""; + } + return os; +} + +std::ostream& operator<<(std::ostream& os, CURLcode code) +{ + switch(code) + { + CASEPRINT(CURLE_OK); + CASEPRINT(CURLE_UNSUPPORTED_PROTOCOL); + CASEPRINT(CURLE_FAILED_INIT); + CASEPRINT(CURLE_URL_MALFORMAT); +#if CURL_VERSION(7, 21, 5) + CASEPRINT(CURLE_NOT_BUILT_IN); +#endif + CASEPRINT(CURLE_COULDNT_RESOLVE_PROXY); + CASEPRINT(CURLE_COULDNT_RESOLVE_HOST); + CASEPRINT(CURLE_COULDNT_CONNECT); + CASEPRINT(CURLE_FTP_WEIRD_SERVER_REPLY); + CASEPRINT(CURLE_REMOTE_ACCESS_DENIED); +#if 0 + CASEPRINT(CURLE_FTP_ACCEPT_FAILED); +#endif + CASEPRINT(CURLE_FTP_WEIRD_PASS_REPLY); +#if 0 + CASEPRINT(CURLE_FTP_ACCEPT_TIMEOUT); +#endif + CASEPRINT(CURLE_FTP_WEIRD_PASV_REPLY); + CASEPRINT(CURLE_FTP_WEIRD_227_FORMAT); + CASEPRINT(CURLE_FTP_CANT_GET_HOST); + CASEPRINT(CURLE_OBSOLETE16); + CASEPRINT(CURLE_FTP_COULDNT_SET_TYPE); + CASEPRINT(CURLE_PARTIAL_FILE); + CASEPRINT(CURLE_FTP_COULDNT_RETR_FILE); + CASEPRINT(CURLE_OBSOLETE20); + CASEPRINT(CURLE_QUOTE_ERROR); + CASEPRINT(CURLE_HTTP_RETURNED_ERROR); + CASEPRINT(CURLE_WRITE_ERROR); + CASEPRINT(CURLE_OBSOLETE24); + CASEPRINT(CURLE_UPLOAD_FAILED); + CASEPRINT(CURLE_READ_ERROR); + CASEPRINT(CURLE_OUT_OF_MEMORY); + CASEPRINT(CURLE_OPERATION_TIMEDOUT); + CASEPRINT(CURLE_OBSOLETE29); + CASEPRINT(CURLE_FTP_PORT_FAILED); + CASEPRINT(CURLE_FTP_COULDNT_USE_REST); + CASEPRINT(CURLE_OBSOLETE32); + CASEPRINT(CURLE_RANGE_ERROR); + CASEPRINT(CURLE_HTTP_POST_ERROR); + CASEPRINT(CURLE_SSL_CONNECT_ERROR); + CASEPRINT(CURLE_BAD_DOWNLOAD_RESUME); + CASEPRINT(CURLE_FILE_COULDNT_READ_FILE); + CASEPRINT(CURLE_LDAP_CANNOT_BIND); + CASEPRINT(CURLE_LDAP_SEARCH_FAILED); + CASEPRINT(CURLE_OBSOLETE40); + CASEPRINT(CURLE_FUNCTION_NOT_FOUND); + CASEPRINT(CURLE_ABORTED_BY_CALLBACK); + CASEPRINT(CURLE_BAD_FUNCTION_ARGUMENT); + CASEPRINT(CURLE_OBSOLETE44); + CASEPRINT(CURLE_INTERFACE_FAILED); + CASEPRINT(CURLE_OBSOLETE46); + CASEPRINT(CURLE_TOO_MANY_REDIRECTS ); +#if CURL_VERSION(7, 21, 5) + CASEPRINT(CURLE_UNKNOWN_OPTION); +#else + CASEPRINT(CURLE_UNKNOWN_TELNET_OPTION); +#endif + CASEPRINT(CURLE_TELNET_OPTION_SYNTAX ); + CASEPRINT(CURLE_OBSOLETE50); + CASEPRINT(CURLE_PEER_FAILED_VERIFICATION); + CASEPRINT(CURLE_GOT_NOTHING); + CASEPRINT(CURLE_SSL_ENGINE_NOTFOUND); + CASEPRINT(CURLE_SSL_ENGINE_SETFAILED); + CASEPRINT(CURLE_SEND_ERROR); + CASEPRINT(CURLE_RECV_ERROR); + CASEPRINT(CURLE_OBSOLETE57); + CASEPRINT(CURLE_SSL_CERTPROBLEM); + CASEPRINT(CURLE_SSL_CIPHER); + CASEPRINT(CURLE_SSL_CACERT); + CASEPRINT(CURLE_BAD_CONTENT_ENCODING); + CASEPRINT(CURLE_LDAP_INVALID_URL); + CASEPRINT(CURLE_FILESIZE_EXCEEDED); + CASEPRINT(CURLE_USE_SSL_FAILED); + CASEPRINT(CURLE_SEND_FAIL_REWIND); + CASEPRINT(CURLE_SSL_ENGINE_INITFAILED); + CASEPRINT(CURLE_LOGIN_DENIED); + CASEPRINT(CURLE_TFTP_NOTFOUND); + CASEPRINT(CURLE_TFTP_PERM); + CASEPRINT(CURLE_REMOTE_DISK_FULL); + CASEPRINT(CURLE_TFTP_ILLEGAL); + CASEPRINT(CURLE_TFTP_UNKNOWNID); + CASEPRINT(CURLE_REMOTE_FILE_EXISTS); + CASEPRINT(CURLE_TFTP_NOSUCHUSER); + CASEPRINT(CURLE_CONV_FAILED); + CASEPRINT(CURLE_CONV_REQD); + CASEPRINT(CURLE_SSL_CACERT_BADFILE); + CASEPRINT(CURLE_REMOTE_FILE_NOT_FOUND); + CASEPRINT(CURLE_SSH); + CASEPRINT(CURLE_SSL_SHUTDOWN_FAILED); + CASEPRINT(CURLE_AGAIN); + CASEPRINT(CURLE_SSL_CRL_BADFILE); + CASEPRINT(CURLE_SSL_ISSUER_ERROR); + CASEPRINT(CURLE_FTP_PRET_FAILED); + CASEPRINT(CURLE_RTSP_CSEQ_ERROR); + CASEPRINT(CURLE_RTSP_SESSION_ERROR); + CASEPRINT(CURLE_FTP_BAD_FILE_LIST); + CASEPRINT(CURLE_CHUNK_FAILED); + default: + os << (code == CURL_LAST ? ""; + } + return os; +} + +struct AICURL; +struct AICURLM; + +std::ostream& operator<<(std::ostream& os, AICURL* curl) +{ + os << "(CURL*)0x" << std::hex << (size_t)curl << std::dec; + return os; +} + +std::ostream& operator<<(std::ostream& os, AICURLM* curl) +{ + os << "(CURLM*)0x" << std::hex << (size_t)curl << std::dec; + return os; +} + +std::ostream& operator<<(std::ostream& os, CURLoption option) +{ + switch(option) + { + CASEPRINT(CURLOPT_WRITEDATA); + CASEPRINT(CURLOPT_URL); + CASEPRINT(CURLOPT_PORT); + CASEPRINT(CURLOPT_PROXY); + CASEPRINT(CURLOPT_USERPWD); + CASEPRINT(CURLOPT_PROXYUSERPWD); + CASEPRINT(CURLOPT_RANGE); + CASEPRINT(CURLOPT_READDATA); + CASEPRINT(CURLOPT_ERRORBUFFER); + CASEPRINT(CURLOPT_WRITEFUNCTION); + CASEPRINT(CURLOPT_READFUNCTION); + CASEPRINT(CURLOPT_TIMEOUT); + CASEPRINT(CURLOPT_INFILESIZE); + CASEPRINT(CURLOPT_POSTFIELDS); + CASEPRINT(CURLOPT_REFERER); + CASEPRINT(CURLOPT_FTPPORT); + CASEPRINT(CURLOPT_USERAGENT); + CASEPRINT(CURLOPT_LOW_SPEED_LIMIT); + CASEPRINT(CURLOPT_LOW_SPEED_TIME); + CASEPRINT(CURLOPT_RESUME_FROM); + CASEPRINT(CURLOPT_COOKIE); + CASEPRINT(CURLOPT_RTSPHEADER); + CASEPRINT(CURLOPT_HTTPPOST); + CASEPRINT(CURLOPT_SSLCERT); + CASEPRINT(CURLOPT_KEYPASSWD); + CASEPRINT(CURLOPT_CRLF); + CASEPRINT(CURLOPT_QUOTE); + CASEPRINT(CURLOPT_HEADERDATA); + CASEPRINT(CURLOPT_COOKIEFILE); + CASEPRINT(CURLOPT_SSLVERSION); + CASEPRINT(CURLOPT_TIMECONDITION); + CASEPRINT(CURLOPT_TIMEVALUE); + CASEPRINT(CURLOPT_CUSTOMREQUEST); + CASEPRINT(CURLOPT_STDERR); + CASEPRINT(CURLOPT_POSTQUOTE); + CASEPRINT(CURLOPT_WRITEINFO); + CASEPRINT(CURLOPT_VERBOSE); + CASEPRINT(CURLOPT_HEADER); + CASEPRINT(CURLOPT_NOPROGRESS); + CASEPRINT(CURLOPT_NOBODY); + CASEPRINT(CURLOPT_FAILONERROR); + CASEPRINT(CURLOPT_UPLOAD); + CASEPRINT(CURLOPT_POST); + CASEPRINT(CURLOPT_DIRLISTONLY); + CASEPRINT(CURLOPT_APPEND); + CASEPRINT(CURLOPT_NETRC); + CASEPRINT(CURLOPT_FOLLOWLOCATION); + CASEPRINT(CURLOPT_TRANSFERTEXT); + CASEPRINT(CURLOPT_PUT); + CASEPRINT(CURLOPT_PROGRESSFUNCTION); + CASEPRINT(CURLOPT_PROGRESSDATA); + CASEPRINT(CURLOPT_AUTOREFERER); + CASEPRINT(CURLOPT_PROXYPORT); + CASEPRINT(CURLOPT_POSTFIELDSIZE); + CASEPRINT(CURLOPT_HTTPPROXYTUNNEL); + CASEPRINT(CURLOPT_INTERFACE); + CASEPRINT(CURLOPT_KRBLEVEL); + CASEPRINT(CURLOPT_SSL_VERIFYPEER); + CASEPRINT(CURLOPT_CAINFO); + CASEPRINT(CURLOPT_MAXREDIRS); + CASEPRINT(CURLOPT_FILETIME); + CASEPRINT(CURLOPT_TELNETOPTIONS); + CASEPRINT(CURLOPT_MAXCONNECTS); + CASEPRINT(CURLOPT_CLOSEPOLICY); + CASEPRINT(CURLOPT_FRESH_CONNECT); + CASEPRINT(CURLOPT_FORBID_REUSE); + CASEPRINT(CURLOPT_RANDOM_FILE); + CASEPRINT(CURLOPT_EGDSOCKET); + CASEPRINT(CURLOPT_CONNECTTIMEOUT); + CASEPRINT(CURLOPT_HEADERFUNCTION); + CASEPRINT(CURLOPT_HTTPGET); + CASEPRINT(CURLOPT_SSL_VERIFYHOST); + CASEPRINT(CURLOPT_COOKIEJAR); + CASEPRINT(CURLOPT_SSL_CIPHER_LIST); + CASEPRINT(CURLOPT_HTTP_VERSION); + CASEPRINT(CURLOPT_FTP_USE_EPSV); + CASEPRINT(CURLOPT_SSLCERTTYPE); + CASEPRINT(CURLOPT_SSLKEY); + CASEPRINT(CURLOPT_SSLKEYTYPE); + CASEPRINT(CURLOPT_SSLENGINE); + CASEPRINT(CURLOPT_SSLENGINE_DEFAULT); + CASEPRINT(CURLOPT_DNS_USE_GLOBAL_CACHE); + CASEPRINT(CURLOPT_DNS_CACHE_TIMEOUT); + CASEPRINT(CURLOPT_PREQUOTE); + CASEPRINT(CURLOPT_DEBUGFUNCTION); + CASEPRINT(CURLOPT_DEBUGDATA); + CASEPRINT(CURLOPT_COOKIESESSION); + CASEPRINT(CURLOPT_CAPATH); + CASEPRINT(CURLOPT_BUFFERSIZE); + CASEPRINT(CURLOPT_NOSIGNAL); + CASEPRINT(CURLOPT_SHARE); + CASEPRINT(CURLOPT_PROXYTYPE); +#if CURL_VERSION(7, 21, 6) + CASEPRINT(CURLOPT_ACCEPT_ENCODING); +#else + CASEPRINT(CURLOPT_ENCODING); +#endif + CASEPRINT(CURLOPT_PRIVATE); + CASEPRINT(CURLOPT_HTTP200ALIASES); + CASEPRINT(CURLOPT_UNRESTRICTED_AUTH); + CASEPRINT(CURLOPT_FTP_USE_EPRT); + CASEPRINT(CURLOPT_HTTPAUTH); + CASEPRINT(CURLOPT_SSL_CTX_FUNCTION); + CASEPRINT(CURLOPT_SSL_CTX_DATA); + CASEPRINT(CURLOPT_FTP_CREATE_MISSING_DIRS); + CASEPRINT(CURLOPT_PROXYAUTH); + CASEPRINT(CURLOPT_FTP_RESPONSE_TIMEOUT); + CASEPRINT(CURLOPT_IPRESOLVE); + CASEPRINT(CURLOPT_MAXFILESIZE); + CASEPRINT(CURLOPT_INFILESIZE_LARGE); + CASEPRINT(CURLOPT_RESUME_FROM_LARGE); + CASEPRINT(CURLOPT_MAXFILESIZE_LARGE); + CASEPRINT(CURLOPT_NETRC_FILE); + CASEPRINT(CURLOPT_USE_SSL); + CASEPRINT(CURLOPT_POSTFIELDSIZE_LARGE); + CASEPRINT(CURLOPT_TCP_NODELAY); + CASEPRINT(CURLOPT_FTPSSLAUTH); + CASEPRINT(CURLOPT_IOCTLFUNCTION); + CASEPRINT(CURLOPT_IOCTLDATA); + CASEPRINT(CURLOPT_FTP_ACCOUNT); + CASEPRINT(CURLOPT_COOKIELIST); + CASEPRINT(CURLOPT_IGNORE_CONTENT_LENGTH); + CASEPRINT(CURLOPT_FTP_SKIP_PASV_IP); + CASEPRINT(CURLOPT_FTP_FILEMETHOD); + CASEPRINT(CURLOPT_LOCALPORT); + CASEPRINT(CURLOPT_LOCALPORTRANGE); + CASEPRINT(CURLOPT_CONNECT_ONLY); + CASEPRINT(CURLOPT_CONV_FROM_NETWORK_FUNCTION); + CASEPRINT(CURLOPT_CONV_TO_NETWORK_FUNCTION); + CASEPRINT(CURLOPT_CONV_FROM_UTF8_FUNCTION); + CASEPRINT(CURLOPT_MAX_SEND_SPEED_LARGE); + CASEPRINT(CURLOPT_MAX_RECV_SPEED_LARGE); + CASEPRINT(CURLOPT_FTP_ALTERNATIVE_TO_USER); + CASEPRINT(CURLOPT_SOCKOPTFUNCTION); + CASEPRINT(CURLOPT_SOCKOPTDATA); + CASEPRINT(CURLOPT_SSL_SESSIONID_CACHE); + CASEPRINT(CURLOPT_SSH_AUTH_TYPES); + CASEPRINT(CURLOPT_SSH_PUBLIC_KEYFILE); + CASEPRINT(CURLOPT_SSH_PRIVATE_KEYFILE); + CASEPRINT(CURLOPT_FTP_SSL_CCC); + CASEPRINT(CURLOPT_TIMEOUT_MS); + CASEPRINT(CURLOPT_CONNECTTIMEOUT_MS); + CASEPRINT(CURLOPT_HTTP_TRANSFER_DECODING); + CASEPRINT(CURLOPT_HTTP_CONTENT_DECODING); + CASEPRINT(CURLOPT_NEW_FILE_PERMS); + CASEPRINT(CURLOPT_NEW_DIRECTORY_PERMS); + CASEPRINT(CURLOPT_POSTREDIR); + CASEPRINT(CURLOPT_SSH_HOST_PUBLIC_KEY_MD5); + CASEPRINT(CURLOPT_OPENSOCKETFUNCTION); + CASEPRINT(CURLOPT_OPENSOCKETDATA); + CASEPRINT(CURLOPT_COPYPOSTFIELDS); + CASEPRINT(CURLOPT_PROXY_TRANSFER_MODE); + CASEPRINT(CURLOPT_SEEKFUNCTION); + CASEPRINT(CURLOPT_SEEKDATA); + CASEPRINT(CURLOPT_CRLFILE); + CASEPRINT(CURLOPT_ISSUERCERT); + CASEPRINT(CURLOPT_ADDRESS_SCOPE); + CASEPRINT(CURLOPT_CERTINFO); + CASEPRINT(CURLOPT_USERNAME); + CASEPRINT(CURLOPT_PASSWORD); + CASEPRINT(CURLOPT_PROXYUSERNAME); + CASEPRINT(CURLOPT_PROXYPASSWORD); + CASEPRINT(CURLOPT_NOPROXY); + CASEPRINT(CURLOPT_TFTP_BLKSIZE); + CASEPRINT(CURLOPT_SOCKS5_GSSAPI_SERVICE); + CASEPRINT(CURLOPT_SOCKS5_GSSAPI_NEC); + CASEPRINT(CURLOPT_PROTOCOLS); + CASEPRINT(CURLOPT_REDIR_PROTOCOLS); + CASEPRINT(CURLOPT_SSH_KNOWNHOSTS); + CASEPRINT(CURLOPT_SSH_KEYFUNCTION); + CASEPRINT(CURLOPT_SSH_KEYDATA); + CASEPRINT(CURLOPT_MAIL_FROM); + CASEPRINT(CURLOPT_MAIL_RCPT); + CASEPRINT(CURLOPT_FTP_USE_PRET); + CASEPRINT(CURLOPT_RTSP_REQUEST); + CASEPRINT(CURLOPT_RTSP_SESSION_ID); + CASEPRINT(CURLOPT_RTSP_STREAM_URI); + CASEPRINT(CURLOPT_RTSP_TRANSPORT); + CASEPRINT(CURLOPT_RTSP_CLIENT_CSEQ); + CASEPRINT(CURLOPT_RTSP_SERVER_CSEQ); + CASEPRINT(CURLOPT_INTERLEAVEDATA); + CASEPRINT(CURLOPT_INTERLEAVEFUNCTION); + CASEPRINT(CURLOPT_WILDCARDMATCH); + CASEPRINT(CURLOPT_CHUNK_BGN_FUNCTION); + CASEPRINT(CURLOPT_CHUNK_END_FUNCTION); + CASEPRINT(CURLOPT_FNMATCH_FUNCTION); + CASEPRINT(CURLOPT_CHUNK_DATA); + CASEPRINT(CURLOPT_FNMATCH_DATA); +#if CURL_VERSION(7, 21, 3) + CASEPRINT(CURLOPT_RESOLVE); +#endif +#if CURL_VERSION(7, 21, 4) + CASEPRINT(CURLOPT_TLSAUTH_USERNAME); + CASEPRINT(CURLOPT_TLSAUTH_PASSWORD); + CASEPRINT(CURLOPT_TLSAUTH_TYPE); +#endif +#if CURL_VERSION(7, 21, 6) + CASEPRINT(CURLOPT_TRANSFER_ENCODING); +#endif +#if CURL_VERSION(7, 21, 7) + CASEPRINT(CURLOPT_CLOSESOCKETFUNCTION); + CASEPRINT(CURLOPT_CLOSESOCKETDATA); +#endif +#if CURL_VERSION(7, 22, 0) + CASEPRINT(CURLOPT_GSSAPI_DELEGATION); +#endif +#if CURL_VERSION(7, 24, 0) + CASEPRINT(CURLOPT_DNS_SERVERS); + CASEPRINT(CURLOPT_ACCEPTTIMEOUT_MS); +#endif +#if CURL_VERSION(7, 25, 0) + CASEPRINT(CURLOPT_TCP_KEEPALIVE); + CASEPRINT(CURLOPT_TCP_KEEPIDLE); + CASEPRINT(CURLOPT_TCP_KEEPINTVL); +#endif +#if CURL_VERSION(7, 25, 0) + CASEPRINT(CURLOPT_SSL_OPTIONS); + CASEPRINT(CURLOPT_MAIL_AUTH); +#endif + default: + os << ""; + } + return os; +} + +std::ostream& operator<<(std::ostream& os, CURLMoption option) +{ + switch(option) + { + CASEPRINT(CURLMOPT_SOCKETFUNCTION); + CASEPRINT(CURLMOPT_SOCKETDATA); + CASEPRINT(CURLMOPT_PIPELINING); + CASEPRINT(CURLMOPT_TIMERFUNCTION); + CASEPRINT(CURLMOPT_TIMERDATA); + CASEPRINT(CURLMOPT_MAXCONNECTS); + default: + os << ""; + } + return os; +} + +std::ostream& operator<<(std::ostream& os, CURLMsg* msg) +{ + if (msg) + { + os << "(CURLMsg*){"; + if (msg->msg == CURLMSG_DONE) + os << "CURLMSG_DONE"; + else + os << msg->msg; + os << ", " << (AICURL*)msg->easy_handle << ", 0x" << std::hex << (size_t)msg->data.whatever << std::dec << '}'; + } + else + os << "(CURLMsg*)NULL"; + return os; +} + +struct Socket { + curl_socket_t mSocket; + Socket(curl_socket_t sockfd) : mSocket(sockfd) { } +}; + +std::ostream& operator<<(std::ostream& os, Socket const& sock) +{ + if (sock.mSocket == CURL_SOCKET_TIMEOUT) + os << "CURL_SOCKET_TIMEOUT"; + else + os << sock.mSocket; + return os; +} + +struct EvBitmask { + int mBitmask; + EvBitmask(int mask) : mBitmask(mask) { } +}; + +std::ostream& operator<<(std::ostream& os, EvBitmask const& bitmask) +{ + int m = bitmask.mBitmask; + if (m == 0) + os << '0'; + if ((m & CURL_CSELECT_IN)) + { + os << "CURL_CSELECT_IN"; + if ((m & (CURL_CSELECT_OUT|CURL_CSELECT_ERR))) + os << '|'; + } + if ((m & CURL_CSELECT_OUT)) + { + os << "CURL_CSELECT_OUT"; + if ((m & CURL_CSELECT_ERR)) + os << '|'; + } + if ((m & CURL_CSELECT_ERR)) + { + os << "CURL_CSELECT_ERR"; + } + return os; +} + +extern "C" { + +void debug_curl_easy_cleanup(CURL* handle) +{ + curl_easy_cleanup(handle); + Dout(dc::curl, "curl_easy_cleanup(" << (AICURL*)handle << ")"); +} + +CURL* debug_curl_easy_duphandle(CURL* handle) +{ + CURL* ret; + ret = curl_easy_duphandle(handle); + Dout(dc::curl, "curl_easy_duphandle(" << (AICURL*)handle << ") = " << (AICURL*)ret); + return ret; +} + +char* debug_curl_easy_escape(CURL* curl, char* url, int length) +{ + char* ret; + ret = curl_easy_escape(curl, url, length); + Dout(dc::curl, "curl_easy_escape(" << curl << ", \"" << url << "\", " << length << ") = \"" << ret << '"'); + return ret; +} + +CURLcode debug_curl_easy_getinfo(CURL* curl, CURLINFO info, ...) +{ + CURLcode ret; + va_list ap; + union param_type { + void* some_ptr; + long* long_ptr; + char** char_ptr; + curl_slist** curl_slist_ptr; + double* double_ptr; + } param; + va_start(ap, info); + param.some_ptr = va_arg(ap, void*); + va_end(ap); + ret = curl_easy_getinfo(curl, info, param.some_ptr); + if (info == CURLINFO_PRIVATE) + { + Dout(dc::curl, "curl_easy_getinfo(" << curl << ", " << info << ", 0x" << std::hex << (size_t)param.some_ptr << std::dec << ") = " << ret); + } + else + { + switch((info & CURLINFO_TYPEMASK)) + { + case CURLINFO_STRING: + Dout(dc::curl, "curl_easy_getinfo(" << curl << ", " << info << ", (char**){ \"" << (ret == CURLE_OK ? *param.char_ptr : " ") << "\" }) = " << ret); + break; + case CURLINFO_LONG: + Dout(dc::curl, "curl_easy_getinfo(" << curl << ", " << info << ", (long*){ " << (ret == CURLE_OK ? *param.long_ptr : 0L) << "L }) = " << ret); + break; + case CURLINFO_DOUBLE: + Dout(dc::curl, "curl_easy_getinfo(" << curl << ", " << info << ", (double*){" << (ret == CURLE_OK ? *param.double_ptr : 0.) << "}) = " << ret); + break; + case CURLINFO_SLIST: + Dout(dc::curl, "curl_easy_getinfo(" << curl << ", " << info << ", (curl_slist**){ " << (ret == CURLE_OK ? **param.curl_slist_ptr : unchanged_slist) << " }) = " << ret); + break; + } + } + return ret; +} + +CURL* debug_curl_easy_init(void) +{ + CURL* ret; + ret = curl_easy_init(); + Dout(dc::curl, "curl_easy_init() = " << (AICURL*)ret); + return ret; +} + +CURLcode debug_curl_easy_pause(CURL* handle, int bitmask) +{ + CURLcode ret; + ret = curl_easy_pause(handle, bitmask); + Dout(dc::curl, "curl_easy_pause(" << (AICURL*)handle << ", 0x" << std::hex << bitmask << std::dec << ") = " << ret); + return ret; +} + +CURLcode debug_curl_easy_perform(CURL* handle) +{ + CURLcode ret; + ret = curl_easy_perform(handle); + Dout(dc::curl, "curl_easy_perform(" << (AICURL*)handle << ") = " << ret); + return ret; +} + +void debug_curl_easy_reset(CURL* handle) +{ + curl_easy_reset(handle); + Dout(dc::curl, "curl_easy_reset(" << (AICURL*)handle << ")"); +} + +CURLcode debug_curl_easy_setopt(CURL* handle, CURLoption option, ...) +{ + CURLcode ret; + va_list ap; + union param_type { + long along; + void* ptr; + curl_off_t offset; + } param; + unsigned int param_type = (option / 10000) * 10000; + va_start(ap, option); + switch (param_type) + { + case CURLOPTTYPE_LONG: + param.along = va_arg(ap, long); + break; + case CURLOPTTYPE_OBJECTPOINT: + case CURLOPTTYPE_FUNCTIONPOINT: + param.ptr = va_arg(ap, void*); + break; + case CURLOPTTYPE_OFF_T: + param.offset = va_arg(ap, curl_off_t); + break; + default: + std::cerr << "Extracting param_type failed; option = " << option << "; param_type = " << param_type << std::endl; + std::exit(EXIT_FAILURE); + } + va_end(ap); + static long postfieldsize; // Cache. Assumes only one thread sets CURLOPT_POSTFIELDSIZE. + switch (param_type) + { + case CURLOPTTYPE_LONG: + { + ret = curl_easy_setopt(handle, option, param.along); + Dout(dc::curl, "curl_easy_setopt(" << (AICURL*)handle << ", " << option << ", " << param.along << "L) = " << ret); + if (option == CURLOPT_POSTFIELDSIZE) + { + postfieldsize = param.along; + } + break; + } + case CURLOPTTYPE_OBJECTPOINT: + { + ret = curl_easy_setopt(handle, option, param.ptr); + LibcwDoutScopeBegin(LIBCWD_DEBUGCHANNELS, libcwd::libcw_do, dc::curl) + LibcwDoutStream << "curl_easy_setopt(" << (AICURL*)handle << ", " << option << ", "; + // For a subset of all options that take a char*, print the string passed. + if (option == CURLOPT_PROXY || // Set HTTP proxy to use. The parameter should be a char* to a zero terminated string holding the host name or dotted IP address. + option == CURLOPT_PROXYUSERPWD || // Pass a char* as parameter, which should be [user name]:[password] to use for the connection to the HTTP proxy. + option == CURLOPT_CAINFO || // Pass a char * to a zero terminated string naming a file holding one or more certificates to verify the peer with. + option == CURLOPT_URL || // Pass in a pointer to the actual URL to deal with. The parameter should be a char * to a zero terminated string [...] + option == CURLOPT_COOKIEFILE || // Pass a pointer to a zero terminated string as parameter. It should contain the name of your file holding cookie data to read. + option == CURLOPT_CUSTOMREQUEST || // Pass a pointer to a zero terminated string as parameter. It can be used to specify the request instead of GET or HEAD when performing HTTP based requests + option == CURLOPT_ENCODING || // Sets the contents of the Accept-Encoding: header sent in a HTTP request, and enables decoding of a response when a Content-Encoding: header is received. + option == CURLOPT_POSTFIELDS || + option == CURLOPT_COPYPOSTFIELDS) // Full data to post in a HTTP POST operation. + { + bool const is_postfield = option == CURLOPT_POSTFIELDS || option == CURLOPT_COPYPOSTFIELDS; + char* str = (char*)param.ptr; + long size; + LibcwDoutStream << "(char*)0x" << std::hex << (size_t)param.ptr << std::dec << ")"; + if (is_postfield) + { + size = postfieldsize < 32 ? postfieldsize : 32; // Only print first 32 characters (this was already written to debug output before). + } + else + { + size = strlen(str); + } + LibcwDoutStream << "["; + if (str) + { + LibcwDoutStream << libcwd::buf2str(str, size); + if (is_postfield && postfieldsize > 32) + LibcwDoutStream << "..."; + } + else + { + LibcwDoutStream << "NULL"; + } + LibcwDoutStream << "](" << (is_postfield ? postfieldsize : size) << " bytes))"; + } + else + { + LibcwDoutStream << "(object*)0x" << std::hex << (size_t)param.ptr << std::dec << ")"; + } + LibcwDoutStream << " = " << ret; + LibcwDoutScopeEnd; + if (option == CURLOPT_HTTPHEADER && param.ptr) + { + debug::Indent indent(2); + Dout(dc::curl, "HTTP Headers:"); + struct curl_slist* list = (struct curl_slist*)param.ptr; + while (list) + { + Dout(dc::curl, '"' << list->data << '"'); + list = list->next; + } + } + break; + } + case CURLOPTTYPE_FUNCTIONPOINT: + ret = curl_easy_setopt(handle, option, param.ptr); + Dout(dc::curl, "curl_easy_setopt(" << (AICURL*)handle << ", " << option << ", (function*)0x" << std::hex << (size_t)param.ptr << std::dec << ") = " << ret); + break; + case CURLOPTTYPE_OFF_T: + { + ret = curl_easy_setopt(handle, option, param.offset); + Dout(dc::curl, "curl_easy_setopt(" << (AICURL*)handle << ", " << option << ", (curl_off_t)" << param.offset << ") = " << ret); + if (option == CURLOPT_POSTFIELDSIZE_LARGE) + { + postfieldsize = (long)param.offset; + } + break; + } + default: + break; + } + return ret; +} + +char const* debug_curl_easy_strerror(CURLcode errornum) +{ + char const* ret; + ret = curl_easy_strerror(errornum); + Dout(dc::curl, "curl_easy_strerror(" << errornum << ") = \"" << ret << '"'); + return ret; +} + +char* debug_curl_easy_unescape(CURL* curl, char* url, int inlength, int* outlength) +{ + char* ret; + ret = curl_easy_unescape(curl, url, inlength, outlength); + Dout(dc::curl, "curl_easy_unescape(" << curl << ", \"" << url << "\", " << inlength << ", " << ((ret && outlength) ? *outlength : 1) << ") = \"" << ret << '"'); + return ret; +} + +void debug_curl_free(char* ptr) +{ + curl_free(ptr); + Dout(dc::curl, "curl_free(0x" << std::hex << (size_t)ptr << std::dec << ")"); +} + +time_t debug_curl_getdate(char const* datestring, time_t* now) +{ + time_t ret; + ret = curl_getdate(datestring, now); + Dout(dc::curl, "curl_getdate(\"" << datestring << "\", " << (now == NULL ? "NULL" : "") << ") = " << ret); + return ret; +} + +void debug_curl_global_cleanup(void) +{ + curl_global_cleanup(); + Dout(dc::curl, "curl_global_cleanup()"); +} + +CURLcode debug_curl_global_init(long flags) +{ + CURLcode ret; + ret = curl_global_init(flags); + Dout(dc::curl, "curl_global_init(0x" << std::hex << flags << std::dec << ") = " << ret); + return ret; +} + +CURLMcode debug_curl_multi_add_handle(CURLM* multi_handle, CURL* easy_handle) +{ + CURLMcode ret; + ret = curl_multi_add_handle(multi_handle, easy_handle); + Dout(dc::curl, "curl_multi_add_handle(" << (AICURLM*)multi_handle << ", " << (AICURL*)easy_handle << ") = " << ret); + return ret; +} + +CURLMcode debug_curl_multi_assign(CURLM* multi_handle, curl_socket_t sockfd, void* sockptr) +{ + CURLMcode ret; + ret = curl_multi_assign(multi_handle, sockfd, sockptr); + Dout(dc::curl, "curl_multi_assign(" << (AICURLM*)multi_handle << ", " << Socket(sockfd) << ", " << sockptr << ") = " << ret); + return ret; +} + +CURLMcode debug_curl_multi_cleanup(CURLM* multi_handle) +{ + CURLMcode ret; + ret = curl_multi_cleanup(multi_handle); + Dout(dc::curl, "curl_multi_cleanup(" << (AICURLM*)multi_handle << ") = " << ret); + return ret; +} + +CURLMsg* debug_curl_multi_info_read(CURLM* multi_handle, int* msgs_in_queue) +{ + CURLMsg* ret; + ret = curl_multi_info_read(multi_handle, msgs_in_queue); + Dout(dc::curl, "curl_multi_info_read(" << (AICURLM*)multi_handle << ", {" << *msgs_in_queue << "}) = " << ret); + return ret; +} + +CURLM* debug_curl_multi_init(void) +{ + CURLM* ret; + ret = curl_multi_init(); + Dout(dc::curl, "curl_multi_init() = " << (AICURLM*)ret); + return ret; +} + +CURLMcode debug_curl_multi_remove_handle(CURLM* multi_handle, CURL* easy_handle) +{ + CURLMcode ret; + ret = curl_multi_remove_handle(multi_handle, easy_handle); + Dout(dc::curl, "curl_multi_remove_handle(" << (AICURLM*)multi_handle << ", " << (AICURL*)easy_handle << ") = " << ret); + return ret; +} + +CURLMcode debug_curl_multi_setopt(CURLM* multi_handle, CURLMoption option, ...) +{ + CURLMcode ret; + va_list ap; + union param_type { + long along; + void* ptr; + curl_off_t offset; + } param; + unsigned int param_type = (option / 10000) * 10000; + va_start(ap, option); + switch (param_type) + { + case CURLOPTTYPE_LONG: + param.along = va_arg(ap, long); + break; + case CURLOPTTYPE_OBJECTPOINT: + case CURLOPTTYPE_FUNCTIONPOINT: + param.ptr = va_arg(ap, void*); + break; + case CURLOPTTYPE_OFF_T: + param.offset = va_arg(ap, curl_off_t); + break; + default: + std::cerr << "Extracting param_type failed; option = " << option << "; param_type = " << param_type << std::endl; + std::exit(EXIT_FAILURE); + } + va_end(ap); + switch (param_type) + { + case CURLOPTTYPE_LONG: + ret = curl_multi_setopt(multi_handle, option, param.along); + Dout(dc::curl, "curl_easy_setopt(" << (AICURLM*)multi_handle << ", " << option << ", " << param.along << "L) = " << ret); + break; + case CURLOPTTYPE_OBJECTPOINT: + ret = curl_multi_setopt(multi_handle, option, param.ptr); + Dout(dc::curl, "curl_easy_setopt(" << (AICURLM*)multi_handle << ", " << option << ", (object*)0x" << std::hex << (size_t)param.ptr << std::dec << ") = " << ret); + break; + case CURLOPTTYPE_FUNCTIONPOINT: + ret = curl_multi_setopt(multi_handle, option, param.ptr); + Dout(dc::curl, "curl_easy_setopt(" << (AICURLM*)multi_handle << ", " << option << ", (function*)0x" << std::hex << (size_t)param.ptr << std::dec << ") = " << ret); + break; + case CURLOPTTYPE_OFF_T: + ret = curl_multi_setopt(multi_handle, option, param.offset); + Dout(dc::curl, "curl_easy_setopt(" << (AICURLM*)multi_handle << ", " << option << ", (curl_off_t)" << param.offset << ") = " << ret); + break; + default: // Stop compiler complaining about no default. + break; + } + return ret; +} + +CURLMcode debug_curl_multi_socket_action(CURLM* multi_handle, curl_socket_t sockfd, int ev_bitmask, int* running_handles) +{ + CURLMcode ret; + ret = curl_multi_socket_action(multi_handle, sockfd, ev_bitmask, running_handles); + Dout(dc::curl, "curl_multi_socket_action(" << (AICURLM*)multi_handle << ", " << Socket(sockfd) << + ", " << EvBitmask(ev_bitmask) << ", {" << (ret == CURLM_OK ? *running_handles : 0) << "}) = " << ret); + return ret; +} + +char const* debug_curl_multi_strerror(CURLMcode errornum) +{ + char const* ret; + ret = curl_multi_strerror(errornum); + Dout(dc::curl, "curl_multi_strerror(" << errornum << ") = \"" << ret << '"'); + return ret; +} + +struct curl_slist* debug_curl_slist_append(struct curl_slist* list, char const* string) +{ + struct curl_slist* ret; + ret = curl_slist_append(list, string); + Dout(dc::curl, "curl_slist_append((curl_slist)@0x" << std::hex << (size_t)list << std::dec << ", \"" << string << "\") = " << *ret); + return ret; +} + +void debug_curl_slist_free_all(struct curl_slist* list) +{ + curl_slist_free_all(list); + Dout(dc::curl, "curl_slist_free_all((curl_slist)@0x" << std::hex << (size_t)list << std::dec << ")"); +} + +char* debug_curl_unescape(char const* url, int length) +{ + char* ret; + ret = curl_unescape(url, length); + Dout(dc::curl, "curl_unescape(\"" << url << "\", " << length << ") = \"" << ret << '"'); + return ret; +} + +char* debug_curl_version(void) +{ + char* ret; + ret = curl_version(); + Dout(dc::curl, "curl_version() = \"" << ret << '"'); + return ret; +} + +} + +#else // DEBUG_CURLIO +int debug_libcurl_dummy; // I thought some OS didn't like empty source files. +#endif // DEBUG_CURLIO + diff --git a/indra/llmessage/debug_libcurl.h b/indra/llmessage/debug_libcurl.h new file mode 100644 index 000000000..27becb1c6 --- /dev/null +++ b/indra/llmessage/debug_libcurl.h @@ -0,0 +1,89 @@ +#ifndef DEBUG_LIBCURL +#define DEBUG_LIBCURL + +#ifndef DEBUG_CURLIO +#error "Don't include debug_libcurl.h unless DEBUG_CURLIO is defined." +#endif + +#ifndef CURLINFO_TYPEMASK +#error " must be included before including debug_libcurl.h!" +#endif + +#ifndef LLPREPROCESSOR_H +// CURL_STATICLIB is needed on windows namely, which is defined in llpreprocessor.h (but only on windows). +#error "llpreprocessor.h must be included before ." +#endif + +extern "C" { + +extern void debug_curl_easy_cleanup(CURL* handle); +extern CURL* debug_curl_easy_duphandle(CURL* handle); +extern char* debug_curl_easy_escape(CURL* curl, char* url, int length); +extern CURLcode debug_curl_easy_getinfo(CURL* curl, CURLINFO info, ...); +extern CURL* debug_curl_easy_init(void); +extern CURLcode debug_curl_easy_pause(CURL* handle, int bitmask); +extern CURLcode debug_curl_easy_perform(CURL* handle); +extern void debug_curl_easy_reset(CURL* handle); +extern CURLcode debug_curl_easy_setopt(CURL* handle, CURLoption option, ...); +extern char const* debug_curl_easy_strerror(CURLcode errornum); +extern char* debug_curl_easy_unescape(CURL* curl, char* url, int inlength, int* outlength); +extern void debug_curl_free(char* ptr); +extern time_t debug_curl_getdate(char const* datestring, time_t* now); +extern void debug_curl_global_cleanup(void); +extern CURLcode debug_curl_global_init(long flags); +extern CURLMcode debug_curl_multi_add_handle(CURLM* multi_handle, CURL* easy_handle); +extern CURLMcode debug_curl_multi_assign(CURLM* multi_handle, curl_socket_t sockfd, void* sockptr); +extern CURLMcode debug_curl_multi_cleanup(CURLM* multi_handle); +extern CURLMsg* debug_curl_multi_info_read(CURLM* multi_handle, int* msgs_in_queue); +extern CURLM* debug_curl_multi_init(void); +extern CURLMcode debug_curl_multi_remove_handle(CURLM* multi_handle, CURL* easy_handle); +extern CURLMcode debug_curl_multi_setopt(CURLM* multi_handle, CURLMoption option, ...); +extern CURLMcode debug_curl_multi_socket_action(CURLM* multi_handle, curl_socket_t sockfd, int ev_bitmask, int* running_handles); +extern char const* debug_curl_multi_strerror(CURLMcode errornum); +extern struct curl_slist* debug_curl_slist_append(struct curl_slist* list, char const* string); +extern void debug_curl_slist_free_all(struct curl_slist* list); +extern char* debug_curl_unescape(char const* url, int length); +extern char* debug_curl_version(void); + +} + +#ifndef COMPILING_DEBUG_LIBCURL_CC + +#ifdef curl_easy_setopt +#undef curl_easy_setopt +#undef curl_easy_getinfo +#undef curl_multi_setopt +#endif + +#define curl_easy_cleanup(handle) debug_curl_easy_cleanup(handle) +#define curl_easy_duphandle(handle) debug_curl_easy_duphandle(handle) +#define curl_easy_escape(curl, url, length) debug_curl_easy_escape(curl, url, length) +#define curl_easy_getinfo(curl, info, param) debug_curl_easy_getinfo(curl, info, param) +#define curl_easy_init() debug_curl_easy_init() +#define curl_easy_pause(handle, bitmask) debug_curl_easy_pause(handle, bitmask) +#define curl_easy_perform(handle) debug_curl_easy_perform(handle) +#define curl_easy_reset(handle) debug_curl_easy_reset(handle) +#define curl_easy_setopt(handle, option, param) debug_curl_easy_setopt(handle, option, param) +#define curl_easy_strerror(errornum) debug_curl_easy_strerror(errornum) +#define curl_easy_unescape(curl, url, inlength, outlength) debug_curl_easy_unescape(curl, url, inlength, outlength) +#define curl_free(ptr) debug_curl_free(ptr) +#define curl_getdate(datestring, now) debug_curl_getdate(datestring, now) +#define curl_global_cleanup() debug_curl_global_cleanup() +#define curl_global_init(flags) debug_curl_global_init(flags) +#define curl_multi_add_handle(multi_handle, easy_handle) debug_curl_multi_add_handle(multi_handle, easy_handle) +#define curl_multi_assign(multi_handle, sockfd, sockptr) debug_curl_multi_assign(multi_handle, sockfd, sockptr) +#define curl_multi_cleanup(multi_handle) debug_curl_multi_cleanup(multi_handle) +#define curl_multi_info_read(multi_handle, msgs_in_queue) debug_curl_multi_info_read(multi_handle, msgs_in_queue) +#define curl_multi_init() debug_curl_multi_init() +#define curl_multi_remove_handle(multi_handle, easy_handle) debug_curl_multi_remove_handle(multi_handle, easy_handle) +#define curl_multi_setopt(multi_handle, option, param) debug_curl_multi_setopt(multi_handle, option, param) +#define curl_multi_socket_action(multi_handle, sockfd, ev_bitmask, running_handles) debug_curl_multi_socket_action(multi_handle, sockfd, ev_bitmask, running_handles) +#define curl_multi_strerror(errornum) debug_curl_multi_strerror(errornum) +#define curl_slist_append(list, string) debug_curl_slist_append(list, string) +#define curl_slist_free_all(list) debug_curl_slist_free_all(list) +#define curl_unescape(url, length) debug_curl_unescape(url, length) +#define curl_version() debug_curl_version() + +#endif // !COMPILING_DEBUG_LIBCURL_CC + +#endif // DEBUG_LIBCURL diff --git a/indra/llmessage/llares.cpp b/indra/llmessage/llares.cpp index 48072d0e4..91d13dac3 100644 --- a/indra/llmessage/llares.cpp +++ b/indra/llmessage/llares.cpp @@ -28,7 +28,6 @@ #include "linden_common.h" #include "llares.h" -#include "llscopedvolatileaprpool.h" #include #include @@ -38,6 +37,7 @@ #include "apr_poll.h" #include "llapr.h" +#include "llaprpool.h" #include "llareslistener.h" #if defined(LL_WINDOWS) diff --git a/indra/llmessage/llassetstorage.cpp b/indra/llmessage/llassetstorage.cpp index 2729f199d..fd30f9249 100644 --- a/indra/llmessage/llassetstorage.cpp +++ b/indra/llmessage/llassetstorage.cpp @@ -401,7 +401,7 @@ bool LLAssetStorage::findInStaticVFSAndInvokeCallback(const LLUUID& uuid, LLAsse if (user_data) { // The *user_data should not be passed without a callback to clean it up. - llassert(callback != NULL) + llassert(callback != NULL); } BOOL exists = mStaticVFS->getExists(uuid, type); @@ -441,7 +441,7 @@ void LLAssetStorage::getAssetData(const LLUUID uuid, LLAssetType::EType type, LL if (user_data) { // The *user_data should not be passed without a callback to clean it up. - llassert(callback != NULL) + llassert(callback != NULL); } if (mShutDown) diff --git a/indra/llmessage/llcircuit.cpp b/indra/llmessage/llcircuit.cpp index 4b41abd45..56249db41 100644 --- a/indra/llmessage/llcircuit.cpp +++ b/indra/llmessage/llcircuit.cpp @@ -56,6 +56,7 @@ #include "llstl.h" #include "lltransfermanager.h" #include "llmodularmath.h" +#include "llpacketring.h" const S32 PING_START_BLOCK = 3; // How many pings behind we have to be to consider ourself blocked. const S32 PING_RELEASE_BLOCK = 2; // How many pings behind we have to be to consider ourself unblocked. @@ -346,7 +347,7 @@ S32 LLCircuitData::resendUnackedPackets(const F64 now) packetp->mBuffer[0] |= LL_RESENT_FLAG; // tag packet id as being a resend - gMessageSystem->mPacketRing.sendPacket(packetp->mSocket, + gMessageSystem->mPacketRing->sendPacket(packetp->mSocket, (char *)packetp->mBuffer, packetp->mBufferLength, packetp->mHost); diff --git a/indra/llmessage/llcurl.cpp b/indra/llmessage/llcurl.cpp index be35e97f4..585e4644b 100644 --- a/indra/llmessage/llcurl.cpp +++ b/indra/llmessage/llcurl.cpp @@ -26,32 +26,6 @@ * $/LicenseInfo$ */ -#if LL_WINDOWS -#define SAFE_SSL 1 -#elif LL_DARWIN -#define SAFE_SSL 1 -#else -#define SAFE_SSL 1 -#endif - -#include "linden_common.h" - -#include "llcurl.h" - -#include -#include -#include -#if SAFE_SSL -#include -#endif - -#include "llbufferstream.h" -#include "llproxy.h" -#include "llsdserialize.h" -#include "llstl.h" -#include "llthread.h" -#include "lltimer.h" - ////////////////////////////////////////////////////////////////////////////// /* The trick to getting curl to do keep-alives is to reuse the @@ -72,177 +46,40 @@ static const U32 EASY_HANDLE_POOL_SIZE = 5; static const S32 MULTI_PERFORM_CALL_REPEAT = 5; -static const S32 CURL_REQUEST_TIMEOUT = 30; // seconds +static const S32 CURL_REQUEST_TIMEOUT = 30; // seconds per operation static const S32 MAX_ACTIVE_REQUEST_COUNT = 100; -static -// DEBUG // -S32 gCurlEasyCount = 0; -S32 gCurlMultiCount = 0; +//static +F32 LLCurl::sCurlRequestTimeOut = 120.f; //seconds +S32 LLCurl::sMaxHandles = 256; //max number of handles, (multi handles and easy handles combined). ////////////////////////////////////////////////////////////////////////////// -//static -std::vector LLCurl::sSSLMutex; -std::string LLCurl::sCAPath; -std::string LLCurl::sCAFile; - -bool LLCurl::sMultiThreaded = false; -static U32 sMainThreadID = 0; - -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). - llinfos << "curl error detected: " << curl_easy_strerror(code) << llendl; - } -} - -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). - llinfos << "curl multi error detected: " << curl_multi_strerror(code) << llendl; - } -} - -//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() - : mReferenceCount(0) -{ -} - -LLCurl::Responder::~Responder() -{ -} - -// virtual -void LLCurl::Responder::errorWithContent( - U32 status, - const std::string& reason, - const LLSD&) -{ - error(status, reason); -} - -// virtual -void LLCurl::Responder::error(U32 status, const std::string& reason) -{ - llinfos << mURL << " [" << status << "]: " << reason << llendl; -} - -// virtual -void LLCurl::Responder::result(const LLSD& content) -{ -} - -void LLCurl::Responder::setURL(const std::string& url) -{ - mURL = url; -} - -// virtual -void LLCurl::Responder::completedRaw( - U32 status, - const std::string& reason, - const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer) -{ - LLSD content; - LLBufferStream istr(channels, buffer.get()); - if (!LLSDSerialize::fromXML(content, istr)) - { - llinfos << "Failed to deserialize LLSD. " << mURL << " [" << status << "]: " << reason << llendl; - } - completed(status, reason, content); -} - -// virtual -void LLCurl::Responder::completed(U32 status, const std::string& reason, const LLSD& content) -{ - if (isGoodStatus(status)) - { - result(content); - } - else - { - errorWithContent(status, reason, content); - } -} - -//virtual -void LLCurl::Responder::completedHeader(U32 status, const std::string& reason, const LLSD& content) -{ - -} - -// DONT UNCOMMENT THIS IT BREAKS GCC47 -//namespace boost -//{ - void intrusive_ptr_add_ref(LLCurl::Responder* p) - { - ++p->mReferenceCount; - } - - void intrusive_ptr_release(LLCurl::Responder* p) - { - if(p && 0 == --p->mReferenceCount) - { - delete p; - } - } -//}; - - -////////////////////////////////////////////////////////////////////////////// - -std::set LLCurl::Easy::sFreeHandles; -std::set LLCurl::Easy::sActiveHandles; -LLMutex* LLCurl::Easy::sHandleMutex = NULL; -LLMutex* LLCurl::Easy::sMultiMutex = NULL; +AIThreadSafeSimpleDC LLCurl::Easy::sHandles; //static CURL* LLCurl::Easy::allocEasyHandle() { + llassert(*AIAccess(LLCurl::getCurlThread())) ; + CURL* ret = NULL; - LLMutexLock lock(sHandleMutex); - if (sFreeHandles.empty()) + + //*** Multi-threaded. + AIAccess handles_w(sHandles); + + if (handles_w->free.empty()) { - ret = curl_easy_init(); + ret = LLCurl::newEasyHandle(); } else { - ret = *(sFreeHandles.begin()); - sFreeHandles.erase(ret); - curl_easy_reset(ret); + ret = *(handles_w->free.begin()); + handles_w->free.erase(ret); } if (ret) { - sActiveHandles.insert(ret); + handles_w->active.insert(ret); } return ret; @@ -251,17 +88,33 @@ CURL* LLCurl::Easy::allocEasyHandle() //static void LLCurl::Easy::releaseEasyHandle(CURL* handle) { + DoutEntering(dc::curl, "LLCurl::Easy::releaseEasyHandle(" << (void*)handle << ")"); + BACKTRACE; + + static const S32 MAX_NUM_FREE_HANDLES = 32 ; + if (!handle) { - llerrs << "handle cannot be NULL!" << llendl; + return ; //handle allocation failed. + //llerrs << "handle cannot be NULL!" << llendl; } - LLMutexLock lock(sHandleMutex); - - if (sActiveHandles.find(handle) != sActiveHandles.end()) + //*** Multi-Threaded (logout only?) + AIAccess handles_w(sHandles); + + if (handles_w->active.find(handle) != handles_w->active.end()) { - sActiveHandles.erase(handle); - sFreeHandles.insert(handle); + handles_w->active.erase(handle); + + if (handles_w->free.size() < MAX_NUM_FREE_HANDLES) + { + curl_easy_reset(handle); + handles_w->free.insert(handle); + } + else + { + LLCurl::deleteEasyHandle(handle) ; + } } else { @@ -269,550 +122,16 @@ void LLCurl::Easy::releaseEasyHandle(CURL* handle) } } -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) - llwarns << "allocEasyHandle() returned NULL! Easy handles: " << gCurlEasyCount << " Multi handles: " << gCurlMultiCount << llendl; - delete easy; - return NULL; - } - - // set no DNS caching as default for all easy handles. This prevents them adopting a - // multi handles cache if they are added to one. - CURLcode result = curl_easy_setopt(easy->mCurlEasyHandle, CURLOPT_DNS_CACHE_TIMEOUT, 10); - check_curl_code(result); - result = curl_easy_setopt(easy->mCurlEasyHandle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); - check_curl_code(result); - - // Disable SSL/TLS session caching. Some servers refuse to talk to us when session ids are enabled. - // id.secondlife.com is such a server, when greeted with a SSL HELLO and a session id, it immediatly returns a RST packet and closes - // the connections. - // Fixes: FIRE-5368, FIRE-5756, VWR-28039, VWR-28629 - result = curl_easy_setopt(easy->mCurlEasyHandle, CURLOPT_SSL_SESSIONID_CACHE, 0); - check_curl_code(result); - - ++gCurlEasyCount; - return easy; -} - LLCurl::Easy::~Easy() { - releaseEasyHandle(mCurlEasyHandle); - --gCurlEasyCount; - curl_slist_free_all(mHeaders); - for_each(mStrings.begin(), mStrings.end(), DeletePointerArray()); -} - -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)); -} - -U32 LLCurl::Easy::report(CURLcode code) -{ - U32 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 = 499; - responseReason = strerror(code) + " : " + mErrorBuffer; - setopt(CURLOPT_FRESH_CONNECT, TRUE); - } - - if (mResponder) + AISTAccess responder_w(mResponder); + if (*responder_w && LLCurl::getNotQuitting()) //aborted { - mResponder->completedRaw(responseCode, responseReason, mChannels, mOutput); - mResponder = NULL; + std::string reason("Request timeout, aborted.") ; + (*responder_w)->completedRaw(408, //HTTP_REQUEST_TIME_OUT, timeout, abort + reason, mChannels, mOutput); } - - 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 char* str) -{ - mHeaders = curl_slist_append(mHeaders, str); -} - -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& 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); - 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)); - - setoptString(CURLOPT_URL, url); - - mResponder = responder; - - if (!post) - { - slist_append("Connection: keep-alive"); - slist_append("Keep-alive: 300"); - // Accept and other headers - for (std::vector::const_iterator iter = headers.begin(); - iter != headers.end(); ++iter) - { - slist_append((*iter).c_str()); - } - } -} - -//////////////////////////////////////////////////////////////////////////// - -LLCurl::Multi::Multi() - : LLThread("Curl Multi"), - mQueued(0), - mErrorCount(0), - mPerformState(PERFORM_STATE_READY) -{ - mQuitting = false; - - mThreaded = LLCurl::sMultiThreaded && LLThread::currentID() == sMainThreadID; - if (mThreaded) - { - mSignal = new LLCondition; - } - else - { - mSignal = NULL; - } - - mCurlMultiHandle = curl_multi_init(); - if (!mCurlMultiHandle) - { - llwarns << "curl_multi_init() returned NULL! Easy handles: " << gCurlEasyCount << " Multi handles: " << gCurlMultiCount << llendl; - mCurlMultiHandle = curl_multi_init(); - } - - llassert_always(mCurlMultiHandle); - ++gCurlMultiCount; -} - -LLCurl::Multi::~Multi() -{ - llassert(isStopped()); - - if (LLCurl::sMultiThreaded) - { - LLCurl::Easy::sMultiMutex->lock(); - } - - delete mSignal; - mSignal = NULL; - - // Clean up active - for(easy_active_list_t::iterator iter = mEasyActiveList.begin(); - iter != mEasyActiveList.end(); ++iter) - { - Easy* easy = *iter; - check_curl_multi_code(curl_multi_remove_handle(mCurlMultiHandle, easy->getCurlHandle())); - delete easy; - } - mEasyActiveList.clear(); - mEasyActiveMap.clear(); - - // Clean up freed - for_each(mEasyFreeList.begin(), mEasyFreeList.end(), DeletePointer()); - mEasyFreeList.clear(); - - check_curl_multi_code(curl_multi_cleanup(mCurlMultiHandle)); - --gCurlMultiCount; - - if (LLCurl::sMultiThreaded) - { - LLCurl::Easy::sMultiMutex->unlock(); - } -} - -CURLMsg* LLCurl::Multi::info_read(S32* msgs_in_queue) -{ - CURLMsg* curlmsg = curl_multi_info_read(mCurlMultiHandle, msgs_in_queue); - return curlmsg; -} - -void LLCurl::Multi::perform() -{ - if (mThreaded) - { - mSignal->lock(); - if (mPerformState == PERFORM_STATE_READY) - { - mSignal->signal(); - } - mSignal->unlock(); - } - else - { - doPerform(); - } -} - -void LLCurl::Multi::run() -{ - llassert(mThreaded); - - mSignal->lock(); - while (!mQuitting) - { - mSignal->wait(); - mPerformState = PERFORM_STATE_PERFORMING; - if (!mQuitting) - { - LLMutexLock lock(LLCurl::Easy::sMultiMutex); - doPerform(); - } - } - mSignal->unlock(); -} - -void LLCurl::Multi::doPerform() -{ - S32 q = 0; - if (mThreaded) - mSignal->unlock(); - for (S32 call_count = 0; - call_count < MULTI_PERFORM_CALL_REPEAT; - call_count += 1) - { - CURLMcode code = curl_multi_perform(mCurlMultiHandle, &q); - if (CURLM_CALL_MULTI_PERFORM != code || q == 0) - { - check_curl_multi_code(code); - break; - } - - } - if (mThreaded) - mSignal->lock(); - mQueued = q; - mPerformState = PERFORM_STATE_COMPLETED; -} - -S32 LLCurl::Multi::process() -{ - perform(); - - if (mPerformState != PERFORM_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) - { - U32 response = 0; - easy_active_map_t::iterator iter = mEasyActiveMap.find(msg->easy_handle); - if (iter != mEasyActiveMap.end()) - { - Easy* easy = iter->second; - response = easy->report(msg->data.result); - removeEasy(easy); - } - else - { - response = 499; - // *TODO: change to llwarns - llerrs << "cleaned up curl request completed!" << llendl; - } - if (response >= 400) - { - // failure of some sort, inc mErrorCount for debugging and flagging multi for destruction - ++mErrorCount; - } - } - } - - mPerformState = PERFORM_STATE_READY; - return processed; -} - -LLCurl::Easy* LLCurl::Multi::allocEasy() -{ - Easy* easy = 0; - - if (mEasyFreeList.empty()) - { - easy = Easy::getEasy(); - } - else - { - easy = *(mEasyFreeList.begin()); - mEasyFreeList.erase(easy); - } - if (easy) - { - mEasyActiveList.insert(easy); - mEasyActiveMap[easy->getCurlHandle()] = easy; - } - return easy; -} - -bool LLCurl::Multi::addEasy(Easy* easy) -{ - CURLMcode mcode = curl_multi_add_handle(mCurlMultiHandle, easy->getCurlHandle()); - check_curl_multi_code(mcode); - //if (mcode != CURLM_OK) - //{ - // llwarns << "Curl Error: " << curl_multi_strerror(mcode) << llendl; - // return false; - //} - return true; -} - -void LLCurl::Multi::easyFree(Easy* easy) -{ - mEasyActiveList.erase(easy); - mEasyActiveMap.erase(easy->getCurlHandle()); - if (mEasyFreeList.size() < EASY_HANDLE_POOL_SIZE) - { - easy->resetState(); - mEasyFreeList.insert(easy); - } - else - { - delete easy; - } -} - -void LLCurl::Multi::removeEasy(Easy* easy) -{ - check_curl_multi_code(curl_multi_remove_handle(mCurlMultiHandle, easy->getCurlHandle())); - easyFree(easy); -} - -//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) -{ - mThreadID = LLThread::currentID(); - mProcessing = FALSE; -} - -LLCurlRequest::~LLCurlRequest() -{ - llassert_always(mThreadID == LLThread::currentID()); - - //stop all Multi handle background threads - for (curlmulti_set_t::iterator iter = mMultiSet.begin(); iter != mMultiSet.end(); ++iter) - { - LLCurl::Multi* multi = *iter; - if (multi->mThreaded) - multi->mSignal->lock(); - multi->mQuitting = true; - if (multi->mThreaded) - { - while (!multi->isStopped()) - { - multi->mSignal->signal(); - multi->mSignal->unlock(); - apr_sleep(1000); - multi->mSignal->lock(); - } - } - if (multi->mThreaded) - multi->mSignal->unlock(); - } - for_each(mMultiSet.begin(), mMultiSet.end(), DeletePointer()); -} - -void LLCurlRequest::addMulti() -{ - llassert_always(mThreadID == LLThread::currentID()); - LLCurl::Multi* multi = new LLCurl::Multi(); - if (multi->mThreaded) - { - multi->start(); - } - mMultiSet.insert(multi); - mActiveMulti = multi; - mActiveRequestCount = 0; + *responder_w = NULL; } LLCurl::Easy* LLCurlRequest::allocEasy() @@ -823,451 +142,13 @@ LLCurl::Easy* LLCurlRequest::allocEasy() { addMulti(); } - llassert_always(mActiveMulti); + 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) - { - llerrs << "Posting to a LLCurlRequest instance from within a responder is not allowed (causes DNS timeouts)." << llendl; - } - bool res = mActiveMulti->addEasy(easy); - return res; -} - -void LLCurlRequest::get(const std::string& url, LLCurl::ResponderPtr responder) -{ - getByteRange(url, headers_t(), 0, -1, responder); -} - -bool LLCurlRequest::getByteRange(const std::string& url, - const headers_t& headers, - S32 offset, S32 length, - LLCurl::ResponderPtr responder) -{ - 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("Range: bytes=%d-%d", offset,offset+length-1); - easy->slist_append(range.c_str()); - } - 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) -{ - 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("Content-Type: application/llsd+xml"); - easy->setHeaders(); - - lldebugs << "POSTING: " << bytes << " bytes." << llendl; - 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) -{ - 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("Content-Type: application/octet-stream"); - easy->setHeaders(); - - lldebugs << "POSTING: " << bytes << " bytes." << llendl; - bool res = addEasy(easy); - return res; -} - -// Note: call once per frame -S32 LLCurlRequest::process() -{ - llassert_always(mThreadID == LLThread::currentID()); - 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; - S32 tres = multi->process(); - res += tres; - if (multi != mActiveMulti && tres == 0 && multi->mQueued == 0) - { - mMultiSet.erase(curiter); - if (multi->mThreaded) - multi->mSignal->lock(); - multi->mQuitting = true; - if (multi->mThreaded) - { - while (!multi->isStopped()) - { - multi->mSignal->signal(); - multi->mSignal->unlock(); - apr_sleep(1000); - multi->mSignal->unlock(); - } - } - if (multi->mThreaded) - multi->mSignal->unlock(); - - delete multi; - } - } - mProcessing = FALSE; - return res; -} - -S32 LLCurlRequest::getQueued() -{ - llassert_always(mThreadID == LLThread::currentID()); - S32 queued = 0; - for (curlmulti_set_t::iterator iter = mMultiSet.begin(); - iter != mMultiSet.end(); ) - { - curlmulti_set_t::iterator curiter = iter++; - LLCurl::Multi* multi = *curiter; - queued += multi->mQueued; - if (multi->mPerformState != LLCurl::Multi::PERFORM_STATE_READY) - { - ++queued; - } - } - return queued; -} - -//////////////////////////////////////////////////////////////////////////// -// For generating one easy request -// associated with a single multi request - -LLCurlEasyRequest::LLCurlEasyRequest() - : mRequestSent(false), - mResultReturned(false) -{ - mMulti = new LLCurl::Multi(); - if (mMulti->mThreaded) - { - mMulti->start(); - } - mEasy = mMulti->allocEasy(); - if (mEasy) - { - mEasy->setErrorBuffer(); - mEasy->setCA(); - } -} - -LLCurlEasyRequest::~LLCurlEasyRequest() -{ - if (mMulti->mThreaded) - mMulti->mSignal->lock(); - mMulti->mQuitting = true; - if (mMulti->mThreaded) - { - while (!mMulti->isStopped()) - { - mMulti->mSignal->signal(); - mMulti->mSignal->unlock(); - apr_sleep(1000); - mMulti->mSignal->lock(); - } - } - if (mMulti->mThreaded) - mMulti->mSignal->unlock(); - delete mMulti; -} - -void LLCurlEasyRequest::setopt(CURLoption option, S32 value) -{ - if (mEasy) - { - mEasy->setopt(option, value); - } -} - -void LLCurlEasyRequest::setoptString(CURLoption option, const std::string& value) -{ - if (mEasy) - { - mEasy->setoptString(option, value); - } -} - -void LLCurlEasyRequest::setPost(char* postdata, S32 size) -{ - if (mEasy) - { - mEasy->setopt(CURLOPT_POST, 1); - mEasy->setopt(CURLOPT_POSTFIELDS, postdata); - mEasy->setopt(CURLOPT_POSTFIELDSIZE, size); - } -} - -void LLCurlEasyRequest::setHeaderCallback(curl_header_callback callback, void* userdata) -{ - if (mEasy) - { - mEasy->setopt(CURLOPT_HEADERFUNCTION, (void*)callback); - mEasy->setopt(CURLOPT_HEADERDATA, userdata); // aka CURLOPT_WRITEHEADER - } -} - -void LLCurlEasyRequest::setWriteCallback(curl_write_callback callback, void* userdata) -{ - if (mEasy) - { - mEasy->setopt(CURLOPT_WRITEFUNCTION, (void*)callback); - mEasy->setopt(CURLOPT_WRITEDATA, userdata); - } -} - -void LLCurlEasyRequest::setReadCallback(curl_read_callback callback, void* userdata) -{ - if (mEasy) - { - mEasy->setopt(CURLOPT_READFUNCTION, (void*)callback); - mEasy->setopt(CURLOPT_READDATA, userdata); - } -} - -void LLCurlEasyRequest::setSSLCtxCallback(curl_ssl_ctx_callback callback, void* userdata) -{ - if (mEasy) - { - mEasy->setopt(CURLOPT_SSL_CTX_FUNCTION, (void*)callback); - mEasy->setopt(CURLOPT_SSL_CTX_DATA, userdata); - } -} - -void LLCurlEasyRequest::slist_append(const char* str) -{ - if (mEasy) - { - mEasy->slist_append(str); - } -} - -void LLCurlEasyRequest::sendRequest(const std::string& url) -{ - llassert_always(!mRequestSent); - mRequestSent = true; - lldebugs << url << llendl; - if (mEasy) - { - mEasy->setHeaders(); - mEasy->setoptString(CURLOPT_URL, url); - mMulti->addEasy(mEasy); - } -} - -void LLCurlEasyRequest::requestComplete() -{ - llassert_always(mRequestSent); - mRequestSent = false; - if (mEasy) - { - mMulti->removeEasy(mEasy); - } -} - -void LLCurlEasyRequest::perform() -{ - mMulti->perform(); -} - -// Usage: Call getRestult until it returns false (no more messages) -bool LLCurlEasyRequest::getResult(CURLcode* result, LLCurl::TransferInfo* info) -{ - if (mMulti->mPerformState != LLCurl::Multi::PERFORM_STATE_COMPLETED) - { //we're busy, try again later - return false; - } - mMulti->mPerformState = LLCurl::Multi::PERFORM_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 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(bool multi_threaded) -{ - sMainThreadID = LLThread::currentID(); - sMultiThreaded = multi_threaded; - // 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); - - Easy::sHandleMutex = new LLMutex; - Easy::sMultiMutex = new LLMutex; - -#if SAFE_SSL - S32 mutex_count = CRYPTO_num_locks(); - for (S32 i=0; i::iterator iter = Easy::sFreeHandles.begin(); iter != Easy::sFreeHandles.end(); ++iter) - { - CURL* curl = *iter; - curl_easy_cleanup(curl); - } - - Easy::sFreeHandles.clear(); - - llassert(Easy::sActiveHandles.empty()); -} - -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); -} diff --git a/indra/llmessage/llcurl.h b/indra/llmessage/llcurl.h index 67abb30ff..32bb6e45f 100644 --- a/indra/llmessage/llcurl.h +++ b/indra/llmessage/llcurl.h @@ -1,383 +1,39 @@ -/** +/** * @file llcurl.h - * @author Zero / Donovan - * @date 2006-10-15 - * @brief A wrapper around libcurl. + * @brief Drop in replacement for old llcurl.h. * - * $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, + * Copyright (c) 2012, Aleric Inglewood. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program 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$ + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution. + * + * CHANGELOG + * and additional copyright holders. + * + * 22/06/2012 + * Initial version, written by Aleric Inglewood @ SL */ - + #ifndef LL_LLCURL_H #define LL_LLCURL_H -#include "linden_common.h" +#include "aicurl.h" -#include -#include -#include - -#include -#include // TODO: remove dependency - -#include "llbuffer.h" -#include "lliopipe.h" -#include "llsd.h" -#include "llthread.h" - -class LLMutex; - -// For whatever reason, this is not typedef'd in curl.h -typedef size_t (*curl_header_callback)(void *ptr, size_t size, size_t nmemb, void *stream); - -class LLCurl -{ - LOG_CLASS(LLCurl); - -public: - class Easy; - class Multi; - - static bool sMultiThreaded; - - struct TransferInfo - { - TransferInfo() : mSizeDownload(0.0), mTotalTime(0.0), mSpeedDownload(0.0) {} - F64 mSizeDownload; - F64 mTotalTime; - F64 mSpeedDownload; - }; - - class Responder - { - //LOG_CLASS(Responder); - public: - - Responder(); - virtual ~Responder(); - - /** - * @brief return true if the status code indicates success. - */ - static bool isGoodStatus(U32 status) - { - return((200 <= status) && (status < 300)); - } - - virtual void errorWithContent( - U32 status, - const std::string& reason, - const LLSD& content); - //< called by completed() on bad status - - virtual void error(U32 status, const std::string& reason); - //< called by default error(status, reason, content) - - virtual void result(const LLSD& content); - //< called by completed for good status codes. - - virtual void completedRaw( - U32 status, - const std::string& reason, - 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 - */ - - virtual void completed( - U32 status, - const std::string& reason, - const LLSD& content); - /**< The default implemetnation calls - either: - * result(), or - * error() - */ - - // Override to handle parsing of the header only. Note: this is the only place where the contents - // of the header can be parsed. In the ::completed call above only the body is contained in the LLSD. - virtual void completedHeader(U32 status, const std::string& reason, const LLSD& content); - - // Used internally to set the url for debugging later. - void setURL(const std::string& url); - - virtual bool followRedir() - { - return false; - } - public: /* but not really -- don't touch this */ - U32 mReferenceCount; - - private: - std::string mURL; - }; - typedef boost::intrusive_ptr 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(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 sSSLMutex; - - // OpenSSL callbacks - static void ssl_locking_callback(int mode, int type, const char *file, int line); - static unsigned long ssl_thread_id(void); - -private: - static std::string sCAPath; - static std::string sCAFile; - static const unsigned int MAX_REDIRECTS; -}; - -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 gauranteed to stick around - void setoptString(CURLoption option, const std::string& value); - - void slist_append(const char* str); - void setHeaders(); - - U32 report(CURLcode); - void getTransferInfo(LLCurl::TransferInfo* info); - - void prepRequest(const std::string& url, const std::vector& headers, 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 mStrings; - - ResponderPtr mResponder; - - static std::set sFreeHandles; - static std::set sActiveHandles; - static LLMutex* sHandleMutex; - static LLMutex* sMultiMutex; -}; - -class LLCurl::Multi : public LLThread -{ - LOG_CLASS(Multi); -public: - - typedef enum - { - PERFORM_STATE_READY=0, - PERFORM_STATE_PERFORMING=1, - PERFORM_STATE_COMPLETED=2 - } ePerformState; - - Multi(); - ~Multi(); - - Easy* allocEasy(); - bool addEasy(Easy* easy); - - void removeEasy(Easy* easy); - - S32 process(); - void perform(); - void doPerform(); - - virtual void run(); - - CURLMsg* info_read(S32* msgs_in_queue); - - S32 mQueued; - S32 mErrorCount; - - S32 mPerformState; - - LLCondition* mSignal; - bool mQuitting; - bool mThreaded; - -private: - void easyFree(Easy*); - - CURLM* mCurlMultiHandle; - - typedef std::set easy_active_list_t; - easy_active_list_t mEasyActiveList; - typedef std::map easy_active_map_t; - easy_active_map_t mEasyActiveMap; - typedef std::set easy_free_list_t; - easy_free_list_t mEasyFreeList; -}; - -// DONT UNCOMMENT BREAKS GCC47 -//namespace boost -//{ - void intrusive_ptr_add_ref(LLCurl::Responder* p); - void intrusive_ptr_release(LLCurl::Responder* p); -//}; - - -class LLCurlRequest -{ -public: - typedef std::vector 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 curlmulti_set_t; - curlmulti_set_t mMultiSet; - LLCurl::Multi* mActiveMulti; - S32 mActiveRequestCount; - BOOL mProcessing; - U32 mThreadID; // debug -}; - -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 char* str); - void sendRequest(const std::string& url); - void requestComplete(); - void perform(); - bool getResult(CURLcode* result, LLCurl::TransferInfo* info = NULL); - std::string getErrorString(); - - 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); -} +// Map interface to old LLCurl names so this can be used as a drop-in replacement. +namespace LLCurl = AICurlInterface; #endif // LL_LLCURL_H diff --git a/indra/llmessage/llhttpclient.cpp b/indra/llmessage/llhttpclient.cpp index 2662eb349..c04bad5e4 100644 --- a/indra/llmessage/llhttpclient.cpp +++ b/indra/llmessage/llhttpclient.cpp @@ -25,7 +25,6 @@ */ #include "linden_common.h" - #include "llhttpclient.h" #include "llassetstorage.h" @@ -38,10 +37,8 @@ #include "lluri.h" #include "message.h" -#include const F32 HTTP_REQUEST_EXPIRY_SECS = 60.0f; - //////////////////////////////////////////////////////////////////////////// // Responder class moved to LLCurl @@ -122,7 +119,7 @@ namespace { public: RawInjector(const U8* data, S32 size) : mData(data), mSize(size) {} - virtual ~RawInjector() {delete mData;} + virtual ~RawInjector() {delete [] mData;} const char* contentType() { return "application/octet-stream"; } @@ -156,9 +153,9 @@ namespace if(fstream.is_open()) { fstream.seekg(0, std::ios::end); - U32 fileSize = (U32)fstream.tellg(); + U32 fileSize = fstream.tellg(); fstream.seekg(0, std::ios::beg); - std::vector fileBuffer(fileSize); //Mem leak fix'd + std::vector fileBuffer(fileSize); fstream.read(&fileBuffer[0], fileSize); ostream.write(&fileBuffer[0], fileSize); fstream.close(); @@ -209,20 +206,45 @@ static void request( const F32 timeout = HTTP_REQUEST_EXPIRY_SECS, const LLSD& headers = LLSD()) { + if (responder) + { + // For possible debug output from within the responder. + responder->setURL(url); + } + if (!LLHTTPClient::hasPump()) { - responder->completed(U32_MAX, "No pump", LLSD()); + responder->fatalError("No pump"); return; } LLPumpIO::chain_t chain; - LLURLRequest* req = new LLURLRequest(method, url); + LLURLRequest* req; + try + { + req = new LLURLRequest(method, url); + } + catch(AICurlNoEasyHandle& error) + { + llwarns << "Failed to create LLURLRequest: " << error.what() << llendl; + // This is what the old LL code did: no recovery whatsoever (but also no leaks or crash). + return ; + } req->checkRootCertificate(true); - // Insert custom headers is the caller sent any - if (headers.isMap()) - { + + lldebugs << LLURLRequest::actionAsVerb(method) << " " << url << " " + << headers << llendl; + + // Insert custom headers if the caller sent any + if (headers.isMap()) + { + if (headers.has("Cookie")) + { + req->allowCookies(); + } + LLSD::map_const_iterator iter = headers.beginMap(); LLSD::map_const_iterator end = headers.endMap(); @@ -257,11 +279,6 @@ static void request( } } - if (responder) - { - responder->setURL(url); - } - req->setCallback(new LLHTTPClientURLAdaptor(responder)); if (method == LLURLRequest::HTTP_POST && gMessageSystem) @@ -308,7 +325,7 @@ void LLHTTPClient::getByteRange( std::string range = llformat("bytes=%d-%d", offset, offset+bytes-1); headers["Range"] = range; } - request(url,LLURLRequest::HTTP_GET, NULL, responder, timeout, headers); + request(url, LLURLRequest::HTTP_GET, NULL, responder, timeout, headers); } void LLHTTPClient::head( @@ -347,12 +364,12 @@ class LLHTTPBuffer public: LLHTTPBuffer() { } - static size_t curl_write( void *ptr, size_t size, size_t nmemb, void *user_data) + static size_t curl_write(char* 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); + self->mBuffer.append(ptr,bytes); return nmemb; } @@ -403,99 +420,82 @@ static LLSD blocking_request( ) { lldebugs << "blockingRequest of " << url << llendl; - char curl_error_buffer[CURL_ERROR_SIZE] = "\0"; - CURL* curlp = curl_easy_init(); - LLHTTPBuffer http_buffer; - std::string body_str; - - // other request method checks root cert first, we skip? - - // * 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()) + + S32 http_status = 499; + LLSD response = LLSD::emptyMap(); + + try { - LLSD::map_const_iterator iter = headers.beginMap(); - LLSD::map_const_iterator end = headers.endMap(); - for (; iter != end; ++iter) + AICurlEasyRequest easy_request(false); + AICurlEasyRequest_wat curlEasyRequest_w(*easy_request); + + LLHTTPBuffer http_buffer; + + // * Set curl handle options + curlEasyRequest_w->setopt(CURLOPT_TIMEOUT, (long)timeout); // seconds, see warning at top of function. + curlEasyRequest_w->setWriteCallback(&LLHTTPBuffer::curl_write, &http_buffer); + + // * Setup headers. + if (headers.isMap()) { - std::ostringstream header; - header << iter->first << ": " << iter->second.asString() ; - lldebugs << "header = " << header.str() << llendl; - headers_list = curl_slist_append(headers_list, header.str().c_str()); + 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() ; + lldebugs << "header = " << header.str() << llendl; + curlEasyRequest_w->addHeader(header.str().c_str()); + } + } + + // Needs to stay alive until after the call to perform(). + std::ostringstream ostr; + + // * Setup specific method / "verb" for the URI (currently only GET and POST supported + poppy) + if (method == LLURLRequest::HTTP_GET) + { + curlEasyRequest_w->setopt(CURLOPT_HTTPGET, 1); + } + else if (method == LLURLRequest::HTTP_POST) + { + //copied from PHP libs, correct? + curlEasyRequest_w->addHeader("Content-Type: application/llsd+xml"); + LLSDSerialize::toXML(body, ostr); + curlEasyRequest_w->setPost(ostr.str().c_str(), ostr.str().length()); + } + + // * Do the action using curl, handle results + curlEasyRequest_w->addHeader("Accept: application/llsd+xml"); + curlEasyRequest_w->finalizeRequest(url); + + S32 curl_success = curlEasyRequest_w->perform(); + curlEasyRequest_w->getinfo(CURLINFO_RESPONSE_CODE, &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 != 404 && (http_status != 200 || curl_success != 0) ) + { + // We expect 404s, don't spam for them. + llwarns << "CURL REQ URL: " << url << llendl; + llwarns << "CURL REQ METHOD TYPE: " << method << llendl; + llwarns << "CURL REQ HEADERS: " << headers.asString() << llendl; + llwarns << "CURL REQ BODY: " << ostr.str() << llendl; + llwarns << "CURL HTTP_STATUS: " << http_status << llendl; + llwarns << "CURL ERROR: " << curlEasyRequest_w->getErrorString() << llendl; + llwarns << "CURL ERROR BODY: " << http_buffer.asString() << llendl; + response["body"] = http_buffer.asString(); + } + else + { + response["body"] = http_buffer.asLLSD(); + lldebugs << "CURL response: " << http_buffer.asString() << llendl; } } - - // * Setup specific method / "verb" for the URI (currently only GET and POST supported + poppy) - if (method == LLURLRequest::HTTP_GET) + catch(AICurlNoEasyHandle const& error) { - curl_easy_setopt(curlp, CURLOPT_HTTPGET, 1); - } - else if (method == LLURLRequest::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, "Content-Type: application/llsd+xml"); - - // 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, "Expect:"); - } - - // * Do the action using curl, handle results - lldebugs << "HTTP body: " << body_str << llendl; - headers_list = curl_slist_append(headers_list, "Accept: application/llsd+xml"); - CURLcode curl_result = curl_easy_setopt(curlp, CURLOPT_HTTPHEADER, headers_list); - if ( curl_result != CURLE_OK ) - { - llinfos << "Curl is hosed - can't add headers" << llendl; + response["body"] = error.what(); } - LLSD response = LLSD::emptyMap(); - S32 curl_success = curl_easy_perform(curlp); - S32 http_status = 499; - 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 != 404 && (http_status != 200 || curl_success != 0) ) - { - // We expect 404s, don't spam for them. - llwarns << "CURL REQ URL: " << url << llendl; - llwarns << "CURL REQ METHOD TYPE: " << method << llendl; - llwarns << "CURL REQ HEADERS: " << headers.asString() << llendl; - llwarns << "CURL REQ BODY: " << body_str << llendl; - llwarns << "CURL HTTP_STATUS: " << http_status << llendl; - llwarns << "CURL ERROR: " << curl_error_buffer << llendl; - llwarns << "CURL ERROR BODY: " << http_buffer.asString() << llendl; - response["body"] = http_buffer.asString(); - } - else - { - response["body"] = http_buffer.asLLSD(); - lldebugs << "CURL response: " << http_buffer.asString() << llendl; - } - - if(headers_list) - { // free the header list - curl_slist_free_all(headers_list); - } - - // * Cleanup - curl_easy_cleanup(curlp); return response; } @@ -595,7 +595,8 @@ bool LLHTTPClient::hasPump() return theClientPump != NULL; } -LLPumpIO &LLHTTPClient::getPump() +//static +LLPumpIO& LLHTTPClient::getPump() { return *theClientPump; } diff --git a/indra/llmessage/llhttpclient.h b/indra/llmessage/llhttpclient.h index b4541c99e..ae22fd30a 100644 --- a/indra/llmessage/llhttpclient.h +++ b/indra/llmessage/llhttpclient.h @@ -34,7 +34,6 @@ #include #include - #include "llassettype.h" #include "llcurl.h" #include "lliopipe.h" @@ -56,6 +55,9 @@ public: typedef LLCurl::Responder Responder; typedef LLCurl::ResponderPtr ResponderPtr; + // The default actually already ignores responses. + class ResponderIgnore : public Responder { }; + /** @name non-blocking API */ //@{ static void head( diff --git a/indra/llmessage/lliopipe.cpp b/indra/llmessage/lliopipe.cpp index 8f827f7a3..c8bef05c6 100644 --- a/indra/llmessage/lliopipe.cpp +++ b/indra/llmessage/lliopipe.cpp @@ -76,7 +76,14 @@ LLIOPipe::~LLIOPipe() } //virtual -bool LLIOPipe::isValid() +bool LLIOPipe::hasExpiration(void) const +{ + // LLIOPipe::hasNotExpired always returns true. + return false; +} + +//virtual +bool LLIOPipe::hasNotExpired(void) const { return true ; } diff --git a/indra/llmessage/lliopipe.h b/indra/llmessage/lliopipe.h index 2def3229f..0e71d7a00 100644 --- a/indra/llmessage/lliopipe.h +++ b/indra/llmessage/lliopipe.h @@ -149,7 +149,7 @@ public: // The connection was lost. STATUS_LOST_CONNECTION = -5, - // The totoal process time has exceeded the timeout. + // The total process time has exceeded the timeout. STATUS_EXPIRED = -6, // Keep track of the count of codes here. @@ -231,7 +231,16 @@ public: */ virtual ~LLIOPipe(); - virtual bool isValid() ; + /** + * @brief External expiration facility. + * + * If hasExpiration() returns true, then we need to check hasNotExpired() + * to see if the LLIOPipe is still valid. In the legacy LL code the + * latter was called isValid() and was overloaded for two purposes: + * either expiration or failure to initialize. + */ + virtual bool hasExpiration(void) const; + virtual bool hasNotExpired(void) const; protected: /** diff --git a/indra/llmessage/llpacketring.h b/indra/llmessage/llpacketring.h index b214271e7..63d272308 100644 --- a/indra/llmessage/llpacketring.h +++ b/indra/llmessage/llpacketring.h @@ -32,7 +32,7 @@ #include "llhost.h" #include "llpacketbuffer.h" -#include "llproxy.h" +//#include "llproxy.h" #include "llthrottle.h" #include "net.h" diff --git a/indra/llmessage/llproxy.cpp b/indra/llmessage/llproxy.cpp index 4a7d326c0..448b6606b 100644 --- a/indra/llmessage/llproxy.cpp +++ b/indra/llmessage/llproxy.cpp @@ -29,7 +29,6 @@ #include "llproxy.h" #include -#include #include "llapr.h" #include "llcurl.h" @@ -47,23 +46,22 @@ static apr_status_t tcp_blocking_handshake(LLSocket::ptr_t handle, char * dataou static LLSocket::ptr_t tcp_open_channel(LLHost host); // Open a TCP channel to a given host static void tcp_close_channel(LLSocket::ptr_t* handle_ptr); // Close an open TCP channel -LLProxy::LLProxy(): - mHTTPProxyEnabled(false), - mProxyMutex(), - mUDPProxy(), - mTCPProxy(), - mHTTPProxy(), +ProxyShared::ProxyShared(void): mProxyType(LLPROXY_SOCKS), - mAuthMethodSelected(METHOD_NOAUTH), - mSocksUsername(), - mSocksPassword() + mAuthMethodSelected(METHOD_NOAUTH) +{ +} + +LLProxy::LLProxy(): + mHTTPProxyEnabled(false) { } LLProxy::~LLProxy() { stopSOCKSProxy(); - disableHTTPProxy(); + Shared_wat shared_w(mShared); + disableHTTPProxy(shared_w); } /** @@ -78,15 +76,18 @@ S32 LLProxy::proxyHandshake(LLHost proxy) { S32 result; + Unshared_rat unshared_r(mUnshared); + Shared_rat shared_r(mShared); + /* SOCKS 5 Auth request */ socks_auth_request_t socks_auth_request; socks_auth_response_t socks_auth_response; socks_auth_request.version = SOCKS_VERSION; // SOCKS version 5 socks_auth_request.num_methods = 1; // Sending 1 method. - socks_auth_request.methods = getSelectedAuthMethod(); // Send only the selected method. + socks_auth_request.methods = getSelectedAuthMethod(shared_r); // Send only the selected method. - result = tcp_blocking_handshake(mProxyControlChannel, + result = tcp_blocking_handshake(unshared_r->mProxyControlChannel, static_cast(static_cast(&socks_auth_request)), sizeof(socks_auth_request), static_cast(static_cast(&socks_auth_response)), @@ -109,8 +110,8 @@ S32 LLProxy::proxyHandshake(LLHost proxy) if (socks_auth_response.method == METHOD_PASSWORD) { // The server has requested a username/password combination - std::string socks_username(getSocksUser()); - std::string socks_password(getSocksPwd()); + std::string socks_username(getSocksUser(shared_r)); + std::string socks_password(getSocksPwd(shared_r)); U32 request_size = socks_username.size() + socks_password.size() + 3; char * password_auth = new char[request_size]; password_auth[0] = 0x01; @@ -121,7 +122,7 @@ S32 LLProxy::proxyHandshake(LLHost proxy) authmethod_password_reply_t password_reply; - result = tcp_blocking_handshake(mProxyControlChannel, + result = tcp_blocking_handshake(unshared_r->mProxyControlChannel, password_auth, request_size, static_cast(static_cast(&password_reply)), @@ -157,7 +158,7 @@ S32 LLProxy::proxyHandshake(LLHost proxy) // "If the client is not in possession of the information at the time of the UDP ASSOCIATE, // the client MUST use a port number and address of all zeros. RFC 1928" - result = tcp_blocking_handshake(mProxyControlChannel, + result = tcp_blocking_handshake(unshared_r->mProxyControlChannel, static_cast(static_cast(&connect_request)), sizeof(connect_request), static_cast(static_cast(&connect_reply)), @@ -176,10 +177,14 @@ S32 LLProxy::proxyHandshake(LLHost proxy) return SOCKS_UDP_FWD_NOT_GRANTED; } - mUDPProxy.setPort(ntohs(connect_reply.port)); // reply port is in network byte order - mUDPProxy.setAddress(proxy.getAddress()); + { + // Write access type and read access type are really the same, so unshared_w must be simply a reference. + Unshared_wat& unshared_w = unshared_r; + unshared_w->mUDPProxy.setPort(ntohs(connect_reply.port)); // reply port is in network byte order + unshared_w->mUDPProxy.setAddress(proxy.getAddress()); + } // The connection was successful. We now have the UDP port to send requests that need forwarding to. - LL_INFOS("Proxy") << "SOCKS 5 UDP proxy connected on " << mUDPProxy << LL_ENDL; + LL_INFOS("Proxy") << "SOCKS 5 UDP proxy connected on " << unshared_r->mUDPProxy << LL_ENDL; return SOCKS_OK; } @@ -197,9 +202,11 @@ S32 LLProxy::proxyHandshake(LLHost proxy) */ S32 LLProxy::startSOCKSProxy(LLHost host) { + Unshared_wat unshared_w(mUnshared); + if (host.isOk()) { - mTCPProxy = host; + unshared_w->mTCPProxy = host; } else { @@ -209,13 +216,13 @@ S32 LLProxy::startSOCKSProxy(LLHost host) // Close any running SOCKS connection. stopSOCKSProxy(); - mProxyControlChannel = tcp_open_channel(mTCPProxy); - if (!mProxyControlChannel) + unshared_w->mProxyControlChannel = tcp_open_channel(unshared_w->mTCPProxy); + if (!unshared_w->mProxyControlChannel) { return SOCKS_HOST_CONNECT_FAILED; } - S32 status = proxyHandshake(mTCPProxy); + S32 status = proxyHandshake(unshared_w->mTCPProxy); if (status != SOCKS_OK) { @@ -246,14 +253,16 @@ void LLProxy::stopSOCKSProxy() // then we must shut down any HTTP proxy operations. But it is allowable if web // proxy is being used to continue proxying HTTP. - if (LLPROXY_SOCKS == getHTTPProxyType()) + Shared_rat shared_r(mShared); + if (LLPROXY_SOCKS == getHTTPProxyType(shared_r)) { - disableHTTPProxy(); + Shared_wat shared_w(shared_r); + disableHTTPProxy(shared_w); } - - if (mProxyControlChannel) + Unshared_wat unshared_w(mUnshared); + if (unshared_w->mProxyControlChannel) { - tcp_close_channel(&mProxyControlChannel); + tcp_close_channel(&unshared_w->mProxyControlChannel); } } @@ -262,9 +271,7 @@ void LLProxy::stopSOCKSProxy() */ void LLProxy::setAuthNone() { - LLMutexLock lock(&mProxyMutex); - - mAuthMethodSelected = METHOD_NOAUTH; + Shared_wat(mShared)->mAuthMethodSelected = METHOD_NOAUTH; } /** @@ -288,11 +295,10 @@ bool LLProxy::setAuthPassword(const std::string &username, const std::string &pa return false; } - LLMutexLock lock(&mProxyMutex); - - mAuthMethodSelected = METHOD_PASSWORD; - mSocksUsername = username; - mSocksPassword = password; + Shared_wat shared_w(mShared); + shared_w->mAuthMethodSelected = METHOD_PASSWORD; + shared_w->mSocksUsername = username; + shared_w->mSocksPassword = password; return true; } @@ -314,12 +320,10 @@ bool LLProxy::enableHTTPProxy(LLHost httpHost, LLHttpProxyType type) return false; } - LLMutexLock lock(&mProxyMutex); - - mHTTPProxy = httpHost; - mProxyType = type; - + Shared_wat shared_w(mShared); mHTTPProxyEnabled = true; + shared_w->mHTTPProxy = httpHost; + shared_w->mProxyType = type; return true; } @@ -335,9 +339,8 @@ bool LLProxy::enableHTTPProxy() { bool ok; - LLMutexLock lock(&mProxyMutex); - - ok = (mHTTPProxy.isOk()); + Shared_rat shared_r(mShared); + ok = (shared_r->mHTTPProxy.isOk()); if (ok) { mHTTPProxyEnabled = true; @@ -346,54 +349,6 @@ bool LLProxy::enableHTTPProxy() return ok; } -/** - * @brief Disable the HTTP proxy. - */ -void LLProxy::disableHTTPProxy() -{ - LLMutexLock lock(&mProxyMutex); - - mHTTPProxyEnabled = false; -} - -/** - * @brief Get the currently selected HTTP proxy type - */ -LLHttpProxyType LLProxy::getHTTPProxyType() const -{ - LLMutexLock lock(&mProxyMutex); - return mProxyType; -} - -/** - * @brief Get the SOCKS 5 password. - */ -std::string LLProxy::getSocksPwd() const -{ - LLMutexLock lock(&mProxyMutex); - return mSocksPassword; -} - -/** - * @brief Get the SOCKS 5 username. - */ -std::string LLProxy::getSocksUser() const -{ - LLMutexLock lock(&mProxyMutex); - return mSocksUsername; -} - -/** - * @brief Get the currently selected SOCKS 5 authentication method. - * - * @return Returns either none or password. - */ -LLSocks5AuthType LLProxy::getSelectedAuthMethod() const -{ - LLMutexLock lock(&mProxyMutex); - return mAuthMethodSelected; -} - /** * @brief Stop the LLProxy and make certain that any APR pools and classes are deleted before terminating APR. * @@ -406,57 +361,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. - * - * This method has been designed to be safe to call from - * any thread in the viewer. This allows requests in the - * texture fetch thread to be aware of the proxy settings. - * When the HTTP proxy is enabled, the proxy mutex will - * be locked every time this method is called. - * - * @param handle A pointer to a valid CURL request, before it has been performed. - */ -void LLProxy::applyProxySettings(CURL* handle) -{ - // Do a faster unlocked check to see if we are supposed to proxy. - if (mHTTPProxyEnabled) - { - // We think we should proxy, lock the proxy mutex. - LLMutexLock lock(&mProxyMutex); - // 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())); - - if (mProxyType == LLPROXY_SOCKS) - { - LLCurlFF::check_easy_code(curl_easy_setopt(handle, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5)); - if (mAuthMethodSelected == METHOD_PASSWORD) - { - std::string auth_string = mSocksUsername + ":" + mSocksPassword; - LLCurlFF::check_easy_code(curl_easy_setopt(handle, CURLOPT_PROXYUSERPWD, auth_string.c_str())); - } - } - else - { - LLCurlFF::check_easy_code(curl_easy_setopt(handle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP)); - } - } - } -} - /** * @brief Send one TCP packet and receive one in return. * diff --git a/indra/llmessage/llproxy.h b/indra/llmessage/llproxy.h index a91937054..00bfa1ad4 100644 --- a/indra/llmessage/llproxy.h +++ b/indra/llmessage/llproxy.h @@ -33,6 +33,7 @@ #include "llmemory.h" #include "llsingleton.h" #include "llthread.h" +#include "aithreadsafe.h" #include // SOCKS error codes returned from the StartProxy method @@ -206,41 +207,92 @@ enum LLSocks5AuthType * The implementation of HTTP proxying is handled by libcurl. LLProxy * is responsible for managing the HTTP proxy options and provides a * 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. + * (LLProxy::applyProxySettings()). * * To ensure thread safety, all LLProxy members that relate to the HTTP * proxy require the LLProxyMutex to be locked before accessing. */ + +struct ProxyUnshared +{ + /*########################################################################################### + MEMBERS READ AND WRITTEN ONLY IN THE MAIN THREAD. + ###########################################################################################*/ + + // UDP proxy address and port + LLHost mUDPProxy; + + // TCP proxy control channel address and port + LLHost mTCPProxy; + + // socket handle to proxy TCP control channel + LLSocket::ptr_t mProxyControlChannel; + + /*########################################################################################### + END OF UNSHARED MEMBERS + ###########################################################################################*/ +}; + +struct ProxyShared +{ + ProxyShared(void); + + /*########################################################################################### + MEMBERS WRITTEN IN MAIN THREAD AND READ IN ANY THREAD. + ###########################################################################################*/ + + // HTTP proxy address and port + LLHost mHTTPProxy; + + // Currently selected HTTP proxy type. Can be web or SOCKS. + LLHttpProxyType mProxyType; + + // SOCKS 5 selected authentication method. + LLSocks5AuthType mAuthMethodSelected; + + // SOCKS 5 username + std::string mSocksUsername; + // SOCKS 5 password + std::string mSocksPassword; + + /*########################################################################################### + END OF SHARED MEMBERS + ###########################################################################################*/ +}; + class LLProxy: public LLSingleton { LOG_CLASS(LLProxy); + public: + typedef AISTAccessConst Unshared_crat; // Constant Read Access Type for Unshared (cannot be converted to write access). + typedef AISTAccess Unshared_rat; // Read Access Type for Unshared (same as write access type, since we don't lock at all). + typedef AISTAccess Unshared_wat; // Write Access Type, for Unshared. + typedef AIReadAccessConst Shared_crat; // Constant Read Access Type for Shared (cannot be converted to write access). + typedef AIReadAccess Shared_rat; // Read Access Type for Shared. + typedef AIWriteAccess Shared_wat; // Write Access Type for Shared. + /*########################################################################################### - METHODS THAT DO NOT LOCK mProxyMutex! + Public methods that only access variables not shared between threads. ###########################################################################################*/ // Constructor, cannot have parameters due to LLSingleton parent class. Call from main thread only. LLProxy(); - // Static check for enabled status for UDP packets. Call from main thread only. - static bool isSOCKSProxyEnabled() { return sUDPProxyEnabled; } + // Static check for enabled status for UDP packets. Called from main thread only. + static bool isSOCKSProxyEnabled(void) { llassert(is_main_thread()); return sUDPProxyEnabled; } - // Get the UDP proxy address and port. Call from main thread only. - LLHost getUDPProxy() const { return mUDPProxy; } + // Get the UDP proxy address and port. Called from main thread only. + LLHost getUDPProxy(void) const { return Unshared_crat(mUnshared)->mUDPProxy; } /*########################################################################################### - END OF NON-LOCKING METHODS + End of methods that only access variables not shared between threads. ###########################################################################################*/ + // Return true if there is a good chance that the HTTP proxy is currently enabled. + bool HTTPProxyEnabled(void) const { return mHTTPProxyEnabled; } + /*########################################################################################### - METHODS THAT LOCK mProxyMutex! DO NOT CALL WHILE mProxyMutex IS LOCKED! + Public methods that access variables shared between threads. ###########################################################################################*/ // Destructor, closes open connections. Do not call directly, use cleanupClass(). ~LLProxy(); @@ -251,9 +303,7 @@ 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); + void applyProxySettings(AICurlEasyRequest_wat const& curlEasyRequest_w); // Start a connection to the SOCKS 5 proxy. Call from main thread only. S32 startSOCKSProxy(LLHost host); @@ -273,30 +323,37 @@ public: bool enableHTTPProxy(); // Stop proxying HTTP packets. Call from main thread only. - void disableHTTPProxy(); + // Note that this needs shared_w to be passed because we want the shared members to be locked when this is reset to false. + void disableHTTPProxy(Shared_wat const& shared_w) { mHTTPProxyEnabled = false; } + void disableHTTPProxy(void) { disableHTTPProxy(Shared_wat(mShared)); } + + // Get the currently selected HTTP proxy address and port + LLHost const& getHTTPProxy(Shared_crat const& shared_r) const { return shared_r->mHTTPProxy; } + + // Get the currently selected HTTP proxy type + LLHttpProxyType getHTTPProxyType(Shared_crat const& shared_r) const { return shared_r->mProxyType; } + + // Get the currently selected auth method. + LLSocks5AuthType getSelectedAuthMethod(Shared_crat const& shared_r) const { return shared_r->mAuthMethodSelected; } + + // SOCKS 5 username and password accessors. + std::string getSocksUser(Shared_crat const& shared_r) const { return shared_r->mSocksUsername; } + std::string getSocksPwd(Shared_crat const& shared_r) const { return shared_r->mSocksPassword; } /*########################################################################################### - END OF LOCKING METHODS + End of methods that access variables shared between threads. ###########################################################################################*/ + private: /*########################################################################################### - METHODS THAT LOCK mProxyMutex! DO NOT CALL WHILE mProxyMutex IS LOCKED! + Private methods that access variables shared between threads. ###########################################################################################*/ // Perform a SOCKS 5 authentication and UDP association with the proxy server. S32 proxyHandshake(LLHost proxy); - // Get the currently selected auth method. - LLSocks5AuthType getSelectedAuthMethod() const; - - // Get the currently selected HTTP proxy type - LLHttpProxyType getHTTPProxyType() const; - - std::string getSocksPwd() const; - std::string getSocksUser() const; - /*########################################################################################### - END OF LOCKING METHODS + End of methods that access variables shared between threads. ###########################################################################################*/ private: @@ -304,49 +361,16 @@ private: // Instead use enableHTTPProxy() and disableHTTPProxy() instead. mutable LLAtomic32 mHTTPProxyEnabled; - // Mutex to protect shared members in non-main thread calls to applyProxySettings(). - mutable LLMutex mProxyMutex; - - /*########################################################################################### - MEMBERS READ AND WRITTEN ONLY IN THE MAIN THREAD. DO NOT SHARE! - ###########################################################################################*/ - // Is the UDP proxy enabled? static bool sUDPProxyEnabled; - // UDP proxy address and port - LLHost mUDPProxy; - // TCP proxy control channel address and port - LLHost mTCPProxy; + AIThreadSafeSingleThreadDC mUnshared; + AIThreadSafeDC mShared; - // socket handle to proxy TCP control channel - LLSocket::ptr_t mProxyControlChannel; - - /*########################################################################################### - END OF UNSHARED MEMBERS - ###########################################################################################*/ - - /*########################################################################################### - MEMBERS WRITTEN IN MAIN THREAD AND READ IN ANY THREAD. ONLY READ OR WRITE AFTER LOCKING mProxyMutex! - ###########################################################################################*/ - - // HTTP proxy address and port - LLHost mHTTPProxy; - - // Currently selected HTTP proxy type. Can be web or socks. - LLHttpProxyType mProxyType; - - // SOCKS 5 selected authentication method. - LLSocks5AuthType mAuthMethodSelected; - - // SOCKS 5 username - std::string mSocksUsername; - // SOCKS 5 password - std::string mSocksPassword; - - /*########################################################################################### - END OF SHARED MEMBERS - ###########################################################################################*/ +public: + // For thread-safe read access. Use the _crat access types with these. + AIThreadSafeSingleThreadDC const& unshared_lockobj(void) const { return mUnshared; } + AIThreadSafeDC const& shared_lockobj(void) const { return mShared; } }; #endif diff --git a/indra/llmessage/llpumpio.cpp b/indra/llmessage/llpumpio.cpp index 48e581c64..e6c6a42d6 100644 --- a/indra/llmessage/llpumpio.cpp +++ b/indra/llmessage/llpumpio.cpp @@ -4,31 +4,25 @@ * @date 2004-11-21 * @brief Implementation of the i/o pump and related functions. * - * $LicenseInfo:firstyear=2004&license=viewergpl$ - * - * Copyright (c) 2004-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2004&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * Copyright (C) 2010, Linden Research, Inc. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * 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. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * 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. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * 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$ */ @@ -40,12 +34,11 @@ #include "apr_poll.h" #include "llapr.h" +#include "llfasttimer.h" #include "llmemtype.h" #include "llstl.h" #include "llstat.h" #include "llthread.h" -#include "llfasttimer.h" -#include //VS2010 // These should not be enabled in production, but they can be // intensely useful during development for finding certain kinds of @@ -191,17 +184,28 @@ LLPumpIO::LLPumpIO(void) : LLPumpIO::~LLPumpIO() { LLMemType m1(LLMemType::MTYPE_IO_PUMP); - cleanup(); +#if LL_THREADS_APR + if (mChainsMutex) apr_thread_mutex_destroy(mChainsMutex); + if (mCallbackMutex) apr_thread_mutex_destroy(mCallbackMutex); +#endif + mChainsMutex = NULL; + mCallbackMutex = NULL; + if(mPollset) + { +// lldebugs << "cleaning up pollset" << llendl; + apr_pollset_destroy(mPollset); + mPollset = NULL; + } } -bool LLPumpIO::addChain(const chain_t& chain, F32 timeout) +bool LLPumpIO::addChain(chain_t const& chain, F32 timeout) { LLMemType m1(LLMemType::MTYPE_IO_PUMP); - if(chain.empty()) return false; -#if LL_THREADS_APR - LLScopedLock lock(mChainsMutex); -#endif + chain_t::const_iterator it = chain.begin(); + chain_t::const_iterator const end = chain.end(); + if (it == end) return false; + LLChainInfo info; info.setTimeoutSeconds(timeout); info.mData = LLIOPipe::buffer_ptr_t(new LLBufferArray); @@ -212,14 +216,17 @@ bool LLPumpIO::addChain(const chain_t& chain, F32 timeout) #else lldebugs << "LLPumpIO::addChain() " << chain[0] <hasExpiration(); link.mPipe = (*it); link.mChannels = info.mData->nextChannel(); info.mChainLinks.push_back(link); } + +#if LL_THREADS_APR + LLScopedLock lock(mChainsMutex); +#endif mPendingChains.push_back(info); return true; } @@ -236,11 +243,10 @@ bool LLPumpIO::addChain( // description, we need to have that description matched to a // particular buffer. if(!data) return false; - if(links.empty()) return false; + links_t::const_iterator link = links.begin(); + links_t::const_iterator const end = links.end(); + if (link == end) return false; -#if LL_THREADS_APR - LLScopedLock lock(mChainsMutex); -#endif #if LL_DEBUG_PIPE_TYPE_IN_PUMP lldebugs << "LLPumpIO::addChain() " << links[0].mPipe << " '" << typeid(*(links[0].mPipe)).name() << "'" << llendl; @@ -252,6 +258,17 @@ bool LLPumpIO::addChain( info.mChainLinks = links; info.mData = data; info.mContext = context; + for (; link != end; ++link) + { + if (link->mPipe->hasExpiration()) + { + info.mHasExpiration = true; + break; + } + } +#if LL_THREADS_APR + LLScopedLock lock(mChainsMutex); +#endif mPendingChains.push_back(info); return true; } @@ -438,6 +455,15 @@ void LLPumpIO::pump() static LLFastTimer::DeclareTimer FTM_PUMP_IO("Pump IO"); +LLPumpIO::current_chain_t LLPumpIO::removeRunningChain(LLPumpIO::current_chain_t& run_chain) +{ + std::for_each( + (*run_chain).mDescriptors.begin(), + (*run_chain).mDescriptors.end(), + ll_delete_apr_pollset_fd_client_data()); + return mRunningChains.erase(run_chain); +} + //timeout is in microseconds void LLPumpIO::pump(const S32& poll_timeout) { @@ -583,10 +609,16 @@ void LLPumpIO::pump(const S32& poll_timeout) // << (*run_chain).mChainLinks[0].mPipe // << " because we reached the end." << llendl; #endif - run_chain = mRunningChains.erase(run_chain); + run_chain = removeRunningChain(run_chain); continue; } } + else if(isChainExpired(*run_chain)) + { + run_chain = removeRunningChain(run_chain); + continue; + } + PUMP_DEBUG; if((*run_chain).mLock) { @@ -694,11 +726,7 @@ void LLPumpIO::pump(const S32& poll_timeout) PUMP_DEBUG; // This chain is done. Clean up any allocated memory and // erase the chain info. - std::for_each( - (*run_chain).mDescriptors.begin(), - (*run_chain).mDescriptors.end(), - ll_delete_apr_pollset_fd_client_data()); - run_chain = mRunningChains.erase(run_chain); + run_chain = removeRunningChain(run_chain); // *NOTE: may not always need to rebuild the pollset. mRebuildPollset = true; @@ -833,22 +861,6 @@ void LLPumpIO::initialize(void) apr_thread_mutex_create(&mCallbackMutex, APR_THREAD_MUTEX_UNNESTED, mPool()); #endif } -void LLPumpIO::cleanup() -{ - LLMemType m1(LLMemType::MTYPE_IO_PUMP); -#if LL_THREADS_APR - if (mChainsMutex) apr_thread_mutex_destroy(mChainsMutex); - if (mCallbackMutex) apr_thread_mutex_destroy(mCallbackMutex); -#endif - mChainsMutex = NULL; - mCallbackMutex = NULL; - if(mPollset) - { -// lldebugs << "cleaning up pollset" << llendl; - apr_pollset_destroy(mPollset); - mPollset = NULL; - } -} void LLPumpIO::rebuildPollset() { @@ -1083,10 +1095,30 @@ void LLPumpIO::processChain(LLChainInfo& chain) PUMP_DEBUG; } +bool LLPumpIO::isChainExpired(LLChainInfo& chain) +{ + if(!chain.mHasExpiration) + { + return false ; + } + + for(links_t::iterator iter = chain.mChainLinks.begin(); iter != chain.mChainLinks.end(); ++iter) + { + if(!(*iter).mPipe->hasNotExpired()) + { + return true ; + } + } + + return false ; +} + bool LLPumpIO::handleChainError( LLChainInfo& chain, LLIOPipe::EStatus error) { + DoutEntering(dc::notice, "LLPumpIO::handleChainError(" << (void*)&chain << ", " << LLIOPipe::lookupStatusString(error) << ")"); + LLMemType m1(LLMemType::MTYPE_IO_PUMP); links_t::reverse_iterator rit; if(chain.mHead == chain.mChainLinks.end()) @@ -1124,6 +1156,9 @@ bool LLPumpIO::handleChainError( #endif keep_going = false; break; + case LLIOPipe::STATUS_EXPIRED: + keep_going = false; + break ; default: if(LLIOPipe::isSuccess(error)) { @@ -1146,6 +1181,7 @@ LLPumpIO::LLChainInfo::LLChainInfo() : mInit(false), mLock(0), mEOS(false), + mHasExpiration(false), mDescriptorsPool(new LLAPRPool(LLThread::tldata().mRootPool)) { LLMemType m1(LLMemType::MTYPE_IO_PUMP); diff --git a/indra/llmessage/llpumpio.h b/indra/llmessage/llpumpio.h index 4e6d59b39..0d1387257 100644 --- a/indra/llmessage/llpumpio.h +++ b/indra/llmessage/llpumpio.h @@ -4,31 +4,25 @@ * @date 2004-11-19 * @brief Declaration of pump class which manages io chains. * - * $LicenseInfo:firstyear=2004&license=viewergpl$ - * - * Copyright (c) 2004-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2004&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * Copyright (C) 2010, Linden Research, Inc. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * 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. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * 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. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * 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$ */ @@ -351,12 +345,13 @@ protected: // basic member data bool mInit; + bool mEOS; + bool mHasExpiration; S32 mLock; LLFrameTimer mTimer; links_t::iterator mHead; links_t mChainLinks; - LLIOPipe::buffer_ptr_t mData; - bool mEOS; + LLIOPipe::buffer_ptr_t mData; LLSD mContext; // tracking inside the pump @@ -397,8 +392,8 @@ protected: protected: void initialize(); - void cleanup(); + current_chain_t removeRunningChain(current_chain_t& chain) ; /** * @brief Given the internal state of the chains, rebuild the pollset * @see setConditional() @@ -425,6 +420,9 @@ protected: */ bool handleChainError(LLChainInfo& chain, LLIOPipe::EStatus error); + //if the chain is expired, remove it + bool isChainExpired(LLChainInfo& chain) ; + public: /** * @brief Return number of running chains. diff --git a/indra/llmessage/llsdmessage.cpp b/indra/llmessage/llsdmessage.cpp index 1c93c12d9..67da908b7 100644 --- a/indra/llmessage/llsdmessage.cpp +++ b/indra/llmessage/llsdmessage.cpp @@ -151,11 +151,11 @@ bool LLSDMessage::ResponderAdapter::listener(const LLSD& payload, bool success) { if (success) { - mResponder->result(payload); + mResponder->pubResult(payload); } else { - mResponder->errorWithContent(payload["status"].asInteger(), payload["reason"], payload["content"]); + mResponder->pubErrorWithContent(payload["status"].asInteger(), payload["reason"], payload["content"]); } /*---------------- MUST BE LAST STATEMENT BEFORE RETURN ----------------*/ diff --git a/indra/llmessage/llsdrpcclient.cpp b/indra/llmessage/llsdrpcclient.cpp deleted file mode 100644 index dbc511f9f..000000000 --- a/indra/llmessage/llsdrpcclient.cpp +++ /dev/null @@ -1,255 +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 "llmemtype.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) -{ - LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT); -} - -// virtual -LLSDRPCResponse::~LLSDRPCResponse() -{ - LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT); -} - -bool LLSDRPCResponse::extractResponse(const LLSD& sd) -{ - LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT); - 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 LLFastTimer::DeclareTimer FTM_SDRPC_RESPONSE("SDRPC Response"); - -// virtual -LLIOPipe::EStatus LLSDRPCResponse::process_impl( - const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, - bool& eos, - LLSD& context, - LLPumpIO* pump) -{ - LLFastTimer t(FTM_SDRPC_RESPONSE); - PUMP_DEBUG; - LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT); - 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) -{ - LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT); -} - -// virtual -LLSDRPCClient::~LLSDRPCClient() -{ - LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT); -} - -bool LLSDRPCClient::call( - const std::string& uri, - const std::string& method, - const LLSD& parameter, - LLSDRPCResponse* response, - EPassBackQueue queue) -{ - LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT); - //llinfos << "RPC: " << uri << "." << method << "(" << *parameter << ")" - // << llendl; - 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) -{ - LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT); - //llinfos << "RPC: " << uri << "." << method << "(" << parameter << ")" - // << llendl; - 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 LLFastTimer::DeclareTimer 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) -{ - LLFastTimer t(FTM_PROCESS_SDRPC_CLIENT); - PUMP_DEBUG; - LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT); - 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; -// lldebugs << "LLSDRPCClient::process_impl STATE_READY" << llendl; - 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. - //lldebugs << "LLSDRPCClient::process_impl STATE_WAITING_FOR_RESPONSE" - // << llendl; - 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; - llinfos << "invalid state to process" << llendl; - rv = STATUS_ERROR; - break; - } - return rv; -} diff --git a/indra/llmessage/llsdrpcclient.h b/indra/llmessage/llsdrpcclient.h deleted file mode 100644 index 0cecf4f68..000000000 --- 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 - * extractResponse method will deal with the llsdrpc method - * call overhead, and keep track of what to call during the next call - * into process. If you use this as a base class, you - * need to implement response, fault, and - * error 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: - * - * response = uri.method(parameter) - * - * where you pass in everything to call 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 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: - * - * class LLUsefulService : public LLService { ... } - * LLService::registerCreator( - * "useful", - * LLService::creator_t(new LLSDRPCClientFactory)) - * - */ -template -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 - { - lldebugs << "LLSDRPCClientFactory::build" << llendl; - LLURLRequest* http(new LLURLRequest(LLURLRequest::HTTP_POST)); - if(!http->isValid()) - { - llwarns << "Creating LLURLRequest failed." << llendl ; - delete http; - return false; - } - - LLIOPipe::ptr_t service(new Client); - chain.push_back(service); - LLIOPipe::ptr_t http_pipe(http); - http->addHeader("Content-Type: 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: - * - * class LLUsefulService : public LLService { ... } - * LLService::registerCreator( - * "useful", - * LLService::creator_t(new LLXMLSDRPCClientFactory)) - * - */ -template -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 - { - lldebugs << "LLXMLSDRPCClientFactory::build" << llendl; - - LLURLRequest* http(new LLURLRequest(LLURLRequest::HTTP_POST)); - if(!http->isValid()) - { - llwarns << "Creating LLURLRequest failed." << llendl ; - delete http; - return false ; - } - LLIOPipe::ptr_t service(new Client); - chain.push_back(service); - LLIOPipe::ptr_t http_pipe(http); - http->addHeader("Content-Type: 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 6ee5bb508..000000000 --- a/indra/llmessage/llsdrpcserver.cpp +++ /dev/null @@ -1,347 +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 "llmemtype.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) -{ - LLMemType m1(LLMemType::MTYPE_IO_SD_SERVER); -} - -LLSDRPCServer::~LLSDRPCServer() -{ - LLMemType m1(LLMemType::MTYPE_IO_SD_SERVER); - std::for_each( - mMethods.begin(), - mMethods.end(), - llcompose1( - DeletePointerFunctor(), - llselect2nd())); - std::for_each( - mCallbackMethods.begin(), - mCallbackMethods.end(), - llcompose1( - DeletePointerFunctor(), - llselect2nd())); -} - - -// 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 LLFastTimer::DeclareTimer 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) -{ - LLFastTimer t(FTM_PROCESS_SDRPC_SERVER); - PUMP_DEBUG; - LLMemType m1(LLMemType::MTYPE_IO_SD_SERVER); -// lldebugs << "LLSDRPCServer::process_impl" << llendl; - // 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: -// lldebugs << "STATE_DONE" << llendl; - break; - case STATE_CALLBACK: -// lldebugs << "STATE_CALLBACK" << llendl; - 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: -// lldebugs << "STATE_NONE" << llendl; - 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) -{ - LLMemType m1(LLMemType::MTYPE_IO_SD_SERVER); - // 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) -{ - LLMemType m1(LLMemType::MTYPE_IO_SD_SERVER); - // 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) -{ - LLMemType m1(LLMemType::MTYPE_IO_SD_SERVER); - LLBufferStream ostr(channels, data); - ostr << FAULT_PART_1 << code << FAULT_PART_2 << msg << FAULT_PART_3; - llinfos << "LLSDRPCServer::buildFault: " << code << ", " << msg << llendl; -} - -// static -void LLSDRPCServer::buildResponse( - const LLChannelDescriptors& channels, - LLBufferArray* data, - const LLSD& response) -{ - LLMemType m1(LLMemType::MTYPE_IO_SD_SERVER); - 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); - llinfos << debug_ostr.str() << llendl; -#endif -} diff --git a/indra/llmessage/llsdrpcserver.h b/indra/llmessage/llsdrpcserver.h deleted file mode 100644 index 9e56e4ea4..000000000 --- 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: - * - * - * class LLMessageAgents : public LLSDRPCServer {
- * public:
- * typedef LLSDRPCServer mem_fn_t;
- * LLMessageAgents() {
- * mMethods["message"] = new mem_fn_t(this, &LLMessageAgents::rpc_IM);
- * mMethods["alert"] = new mem_fn_t(this, &LLMessageAgents::rpc_Alrt);
- * }
- * protected:
- * rpc_IM(const LLSD& params, - * const LLChannelDescriptors& channels, - * LLBufferArray* data) - * {...}
- * rpc_Alert(const LLSD& params, - * const LLChannelDescriptors& channels, - * LLBufferArray* data) - * {...}
- * };
- *
- * - * 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 -#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 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 - * process() and callback() to specify which - * thread you want to work on when handling a method call. The - * mMethods methods are called from - * process(), while the mCallbackMethods are - * called when a pump is in a callback() 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 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); - * root.addNode("xmlrpc/useful", new LLXMLRPCNode); - * \endcode - */ -//@{ - -template -class LLSDRPCServerFactory : public LLChainIOFactory -{ -public: - virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const - { - lldebugs << "LLXMLSDRPCServerFactory::build" << llendl; - chain.push_back(LLIOPipe::ptr_t(new Server)); - return true; - } -}; - -template -class LLSDRPCNode : public LLHTTPNodeForFactory< - LLSDRPCServerFactory > -{ -}; - -template -class LLXMLRPCServerFactory : public LLChainIOFactory -{ -public: - virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const - { - lldebugs << "LLXMLSDRPCServerFactory::build" << llendl; - 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 LLXMLRPCNode : public LLHTTPNodeForFactory< - LLXMLRPCServerFactory > -{ -}; - -//@} - -#endif // LL_LLSDRPCSERVER_H diff --git a/indra/llmessage/llurlrequest.cpp b/indra/llmessage/llurlrequest.cpp index 6b6fd445f..4948f10d6 100644 --- a/indra/llmessage/llurlrequest.cpp +++ b/indra/llmessage/llurlrequest.cpp @@ -29,6 +29,10 @@ #include "linden_common.h" #include "llurlrequest.h" +#ifdef CWDEBUG +#include +#endif + #include #include #include @@ -48,13 +52,10 @@ static const U32 HTTP_STATUS_PIPE_ERROR = 499; /** * String constants */ -const std::string CONTEXT_DEST_URI_SD_LABEL("dest_uri"); const std::string CONTEXT_TRANSFERED_BYTES("transfered_bytes"); -static size_t headerCallback(void* data, size_t size, size_t nmemb, void* user); - - +static size_t headerCallback(char* data, size_t size, size_t nmemb, void* user); /** * class LLURLRequestDetail @@ -65,33 +66,26 @@ public: LLURLRequestDetail(); ~LLURLRequestDetail(); std::string mURL; - LLCurlEasyRequest* mCurlRequest; - LLBufferArray* mResponseBuffer; + AICurlEasyRequest mCurlEasyRequest; + LLIOPipe::buffer_ptr_t mResponseBuffer; LLChannelDescriptors mChannels; U8* mLastRead; U32 mBodyLimit; S32 mByteAccumulator; bool mIsBodyLimitSet; - LLURLRequest::SSLCertVerifyCallback mSSLVerifyCallback; }; LLURLRequestDetail::LLURLRequestDetail() : - mCurlRequest(NULL), - mResponseBuffer(NULL), + mCurlEasyRequest(false), mLastRead(NULL), mBodyLimit(0), mByteAccumulator(0), mIsBodyLimitSet(false) { - LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST); - mCurlRequest = new LLCurlEasyRequest(); } LLURLRequestDetail::~LLURLRequestDetail() { - LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST); - delete mCurlRequest; - mResponseBuffer = NULL; mLastRead = NULL; } @@ -123,6 +117,7 @@ LLURLRequest::LLURLRequest(LLURLRequest::ERequestAction action) : mAction(action) { LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST); + // This might throw AICurlNoEasyHandle. initialize(); } @@ -132,6 +127,7 @@ LLURLRequest::LLURLRequest( mAction(action) { LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST); + // This might throw AICurlNoEasyHandle. initialize(); setURL(url); } @@ -139,13 +135,16 @@ LLURLRequest::LLURLRequest( LLURLRequest::~LLURLRequest() { LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST); + { + AICurlEasyRequest_wat curl_easy_request_w(*mDetail->mCurlEasyRequest); + curl_easy_request_w->revokeCallbacks(); + curl_easy_request_w->send_events_to(NULL); + } delete mDetail; - mDetail = NULL ; } void LLURLRequest::setURL(const std::string& url) { - LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST); mDetail->mURL = url; } @@ -153,19 +152,21 @@ std::string LLURLRequest::getURL() const { return mDetail->mURL; } + void LLURLRequest::addHeader(const char* header) { LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST); - mDetail->mCurlRequest->slist_append(header); + AICurlEasyRequest_wat curlEasyRequest_w(*mDetail->mCurlEasyRequest); + curlEasyRequest_w->addHeader(header); } void LLURLRequest::checkRootCertificate(bool check) { - mDetail->mCurlRequest->setopt(CURLOPT_SSL_VERIFYPEER, (check? TRUE : FALSE)); - mDetail->mCurlRequest->setoptString(CURLOPT_ENCODING, ""); + AICurlEasyRequest_wat curlEasyRequest_w(*mDetail->mCurlEasyRequest); + curlEasyRequest_w->setopt(CURLOPT_SSL_VERIFYPEER, check ? 1L : 0L); + curlEasyRequest_w->setoptString(CURLOPT_ENCODING, ""); } - void LLURLRequest::setBodyLimit(U32 size) { mDetail->mBodyLimit = size; @@ -176,7 +177,8 @@ void LLURLRequest::setCallback(LLURLRequestComplete* callback) { LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST); mCompletionCallback = callback; - mDetail->mCurlRequest->setHeaderCallback(&headerCallback, (void*)callback); + AICurlEasyRequest_wat curlEasyRequest_w(*mDetail->mCurlEasyRequest); + curlEasyRequest_w->setHeaderCallback(&headerCallback, (void*)callback); } // Added to mitigate the effect of libcurl looking @@ -210,26 +212,41 @@ void LLURLRequest::useProxy(bool use_proxy) } } - LL_DEBUGS("Proxy") << "use_proxy = " << (use_proxy?'Y':'N') << ", env_proxy = " << (!env_proxy.empty() ? env_proxy : "(null)") << LL_ENDL; + LL_DEBUGS("Proxy") << "use_proxy = " << (use_proxy?'Y':'N') << ", env_proxy = " << (!env_proxy.empty() ? env_proxy : "(null)") << LL_ENDL; - if (use_proxy && !env_proxy.empty()) - { - mDetail->mCurlRequest->setoptString(CURLOPT_PROXY, env_proxy); - } - else - { - mDetail->mCurlRequest->setoptString(CURLOPT_PROXY, ""); - } + AICurlEasyRequest_wat curlEasyRequest_w(*mDetail->mCurlEasyRequest); + curlEasyRequest_w->setoptString(CURLOPT_PROXY, (use_proxy && !env_proxy.empty()) ? env_proxy : std::string("")); } void LLURLRequest::useProxy(const std::string &proxy) { - mDetail->mCurlRequest->setoptString(CURLOPT_PROXY, proxy); + AICurlEasyRequest_wat curlEasyRequest_w(*mDetail->mCurlEasyRequest); + curlEasyRequest_w->setoptString(CURLOPT_PROXY, proxy); } void LLURLRequest::allowCookies() { - mDetail->mCurlRequest->setoptString(CURLOPT_COOKIEFILE, ""); + AICurlEasyRequest_wat curlEasyRequest_w(*mDetail->mCurlEasyRequest); + curlEasyRequest_w->setoptString(CURLOPT_COOKIEFILE, ""); +} + +//virtual +bool LLURLRequest::hasExpiration(void) const +{ + // Currently, this ALWAYS returns false -- because only AICurlEasyRequestStateMachine uses buffered + // AICurlEasyRequest objects, and LLURLRequest uses (unbuffered) AICurlEasyRequest directly, which + // have no expiration facility. + return mDetail->mCurlEasyRequest.isBuffered(); +} + +//virtual +bool LLURLRequest::hasNotExpired(void) const +{ + if (!mDetail->mCurlEasyRequest.isBuffered()) + return true; + AICurlEasyRequest_wat buffered_easy_request_w(*mDetail->mCurlEasyRequest); + AICurlResponderBuffer_wat buffer_w(*mDetail->mCurlEasyRequest); + return buffer_w->isValid(); } // virtual @@ -237,7 +254,27 @@ LLIOPipe::EStatus LLURLRequest::handleError( LLIOPipe::EStatus status, LLPumpIO* pump) { + DoutEntering(dc::curl, "LLURLRequest::handleError(" << LLIOPipe::lookupStatusString(status) << ", " << (void*)pump << ")"); LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST); + + if (LL_LIKELY(!mDetail->mCurlEasyRequest.isBuffered())) // Currently always true. + { + // The last reference will be deleted when the pump that this chain belongs to + // is removed from the running chains vector, upon returning from this function. + // This keeps the CurlEasyRequest object alive until the curl thread cleanly removed it. + Dout(dc::curl, "Calling mDetail->mCurlEasyRequest.removeRequest()"); + mDetail->mCurlEasyRequest.removeRequest(); + } + else if (!hasNotExpired()) + { + // The buffered version has it's own time out handling, and that already expired, + // so we can ignore the expiration of this timer (currently never happens). + // I left it here because it's what LL did (in the form if (!isValid() ...), + // and it would be relevant if this characteristic of mDetail->mCurlEasyRequest + // would change. --Aleric + return STATUS_EXPIRED ; + } + if(mCompletionCallback && pump) { LLURLRequestComplete* complete = NULL; @@ -252,6 +289,19 @@ LLIOPipe::EStatus LLURLRequest::handleError( return status; } +void LLURLRequest::added_to_multi_handle(AICurlEasyRequest_wat&) +{ +} + +void LLURLRequest::finished(AICurlEasyRequest_wat&) +{ +} + +void LLURLRequest::removed_from_multi_handle(AICurlEasyRequest_wat&) +{ + mRemoved = true; +} + static LLFastTimer::DeclareTimer FTM_PROCESS_URL_REQUEST("URL Request"); // virtual @@ -267,9 +317,10 @@ LLIOPipe::EStatus LLURLRequest::process_impl( LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST); //llinfos << "LLURLRequest::process_impl()" << llendl; if (!buffer) return STATUS_ERROR; + if (!mDetail) return STATUS_ERROR; //Seems to happen on occasion. Need to hunt down why. - // we're still waiting or prcessing, check how many + // we're still waiting or processing, check how many // bytes we have accumulated. const S32 MIN_ACCUMULATION = 100000; if(pump && (mDetail->mByteAccumulator > MIN_ACCUMULATION)) @@ -307,47 +358,42 @@ LLIOPipe::EStatus LLURLRequest::process_impl( // *FIX: bit of a hack, but it should work. The configure and // callback method expect this information to be ready. - mDetail->mResponseBuffer = buffer.get(); + mDetail->mResponseBuffer = buffer; mDetail->mChannels = channels; if(!configure()) { return STATUS_ERROR; } + mRemoved = false; mState = STATE_WAITING_FOR_RESPONSE; + mDetail->mCurlEasyRequest.addRequest(); // Add easy handle to multi handle. - // *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; - static LLFastTimer::DeclareTimer FTM_URL_PERFORM("Perform"); + if (!mRemoved) // Not removed from multi handle yet? { - LLFastTimer t(FTM_URL_PERFORM); - mDetail->mCurlRequest->perform(); + // Easy handle is still being processed. + return STATUS_BREAK; } + // Curl thread finished with this easy handle. + mState = STATE_CURL_FINISHED; + } + case STATE_CURL_FINISHED: + { + PUMP_DEBUG; + LLIOPipe::EStatus status = STATUS_NO_CONNECTION; // Catch-all failure code. - while(1) + // Left braces in order not to change indentation. { CURLcode result; static LLFastTimer::DeclareTimer FTM_PROCESS_URL_REQUEST_GET_RESULT("Get Result"); - bool newmsg = false; - { - LLFastTimer t(FTM_PROCESS_URL_REQUEST_GET_RESULT); - newmsg = mDetail->mCurlRequest->getResult(&result); - } + AICurlEasyRequest_wat(*mDetail->mCurlEasyRequest)->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; @@ -379,6 +425,7 @@ LLIOPipe::EStatus LLURLRequest::process_impl( } mCompletionCallback = NULL; } + status = STATUS_BREAK; // This is what the old code returned. Does it make sense? break; case CURLE_FAILED_INIT: case CURLE_COULDNT_CONNECT: @@ -420,10 +467,15 @@ void LLURLRequest::initialize() { LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST); mState = STATE_INITIALIZED; + // This might throw AICurlNoEasyHandle. mDetail = new LLURLRequestDetail; - mDetail->mCurlRequest->setopt(CURLOPT_NOSIGNAL, 1); - mDetail->mCurlRequest->setWriteCallback(&downCallback, (void*)this); - mDetail->mCurlRequest->setReadCallback(&upCallback, (void*)this); + + { + AICurlEasyRequest_wat curlEasyRequest_w(*mDetail->mCurlEasyRequest); + curlEasyRequest_w->setWriteCallback(&downCallback, (void*)this); + curlEasyRequest_w->setReadCallback(&upCallback, (void*)this); + } + mRequestTransferedBytes = 0; mResponseTransferedBytes = 0; } @@ -438,70 +490,66 @@ bool LLURLRequest::configure() 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); - mDetail->mCurlRequest->setopt(CURLOPT_FOLLOWLOCATION, 1); - rv = true; - break; - case HTTP_GET: - mDetail->mCurlRequest->setopt(CURLOPT_HTTPGET, 1); - mDetail->mCurlRequest->setopt(CURLOPT_FOLLOWLOCATION, 1); + AICurlEasyRequest_wat curlEasyRequest_w(*mDetail->mCurlEasyRequest); + switch(mAction) + { + case HTTP_HEAD: + curlEasyRequest_w->setopt(CURLOPT_HEADER, 1); + curlEasyRequest_w->setopt(CURLOPT_NOBODY, 1); + curlEasyRequest_w->setopt(CURLOPT_FOLLOWLOCATION, 1); + rv = true; + break; - // Set Accept-Encoding to allow response compression - mDetail->mCurlRequest->setoptString(CURLOPT_ENCODING, ""); - rv = true; - break; + case HTTP_GET: + curlEasyRequest_w->setopt(CURLOPT_HTTPGET, 1); + curlEasyRequest_w->setopt(CURLOPT_FOLLOWLOCATION, 1); - 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("Expect:"); + // Set Accept-Encoding to allow response compression + curlEasyRequest_w->setoptString(CURLOPT_ENCODING, ""); + rv = true; + break; - mDetail->mCurlRequest->setopt(CURLOPT_UPLOAD, 1); - mDetail->mCurlRequest->setopt(CURLOPT_INFILESIZE, bytes); - 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. + curlEasyRequest_w->addHeader("Expect:"); + curlEasyRequest_w->setopt(CURLOPT_UPLOAD, 1); + curlEasyRequest_w->setopt(CURLOPT_INFILESIZE, bytes); + 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("Expect:"); + case HTTP_POST: + // Set the handle for an http post + curlEasyRequest_w->setPost(bytes); - // Disable the content type http header. - // *FIX: what should it be? - addHeader("Content-Type:"); + // Set Accept-Encoding to allow response compression + curlEasyRequest_w->setoptString(CURLOPT_ENCODING, ""); + rv = true; + break; - // Set the handle for an http post - mDetail->mCurlRequest->setPost(NULL, bytes); + case HTTP_DELETE: + // Set the handle for an http post + curlEasyRequest_w->setoptString(CURLOPT_CUSTOMREQUEST, "DELETE"); + rv = true; + break; - // Set Accept-Encoding to allow response compression - mDetail->mCurlRequest->setoptString(CURLOPT_ENCODING, ""); - rv = true; - break; + case HTTP_MOVE: + // Set the handle for an http post + curlEasyRequest_w->setoptString(CURLOPT_CUSTOMREQUEST, "MOVE"); + // *NOTE: should we check for the Destination header? + rv = true; + break; - case HTTP_DELETE: - // Set the handle for an http post - mDetail->mCurlRequest->setoptString(CURLOPT_CUSTOMREQUEST, "DELETE"); - rv = true; - break; - - case HTTP_MOVE: - // Set the handle for an http post - mDetail->mCurlRequest->setoptString(CURLOPT_CUSTOMREQUEST, "MOVE"); - // *NOTE: should we check for the Destination header? - rv = true; - break; - - default: - llwarns << "Unhandled URLRequest action: " << mAction << llendl; - break; - } - if(rv) - { - mDetail->mCurlRequest->sendRequest(mDetail->mURL); + default: + llwarns << "Unhandled URLRequest action: " << mAction << llendl; + break; + } + if(rv) + { + curlEasyRequest_w->finalizeRequest(mDetail->mURL); + curlEasyRequest_w->send_events_to(this); + } } return rv; } @@ -565,9 +613,8 @@ size_t LLURLRequest::upCallback( return bytes; } -static size_t headerCallback(void* data, size_t size, size_t nmemb, void* user) +static size_t headerCallback(char* header_line, 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; @@ -633,42 +680,6 @@ static size_t headerCallback(void* data, size_t size, size_t nmemb, void* user) return header_len; } -static LLFastTimer::DeclareTimer 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) -{ - LLFastTimer t(FTM_PROCESS_URL_EXTRACTOR); - PUMP_DEBUG; - LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST); - // 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 */ diff --git a/indra/llmessage/llurlrequest.h b/indra/llmessage/llurlrequest.h index 625f11afd..fdf7d873e 100644 --- a/indra/llmessage/llurlrequest.h +++ b/indra/llmessage/llurlrequest.h @@ -41,7 +41,6 @@ #include "llcurl.h" extern const std::string CONTEXT_REQUEST; -extern const std::string CONTEXT_DEST_URI_SD_LABEL; extern const std::string CONTEXT_RESPONSE; extern const std::string CONTEXT_TRANSFERED_BYTES; @@ -65,7 +64,7 @@ typedef struct x509_store_ctx_st X509_STORE_CTX; * worth the time and effort to eventually port this to a raw client * socket. */ -class LLURLRequest : public LLIOPipe +class LLURLRequest : public LLIOPipe, protected AICurlEasyHandleEvents { LOG_CLASS(LLURLRequest); public: @@ -188,6 +187,9 @@ public: */ void allowCookies(); + /*virtual*/ bool hasExpiration(void) const; + /*virtual*/ bool hasNotExpired(void) const; + public: /** * @brief Give this pipe a chance to handle a generated error @@ -212,6 +214,7 @@ protected: STATE_INITIALIZED, STATE_WAITING_FOR_RESPONSE, STATE_PROCESSING_RESPONSE, + STATE_CURL_FINISHED, STATE_HAVE_RESPONSE, }; EState mState; @@ -221,6 +224,14 @@ protected: S32 mRequestTransferedBytes; S32 mResponseTransferedBytes; + // mRemoved is used instead of changing mState directly, because I'm not convinced the latter is atomic. + // Set to false before adding curl request and then only tested. + // Reset in removed_from_multi_handle (by another thread), this is thread-safe. + bool mRemoved; + /*virtual*/ void added_to_multi_handle(AICurlEasyRequest_wat&); + /*virtual*/ void finished(AICurlEasyRequest_wat&); + /*virtual*/ void removed_from_multi_handle(AICurlEasyRequest_wat&); + private: /** * @brief Initialize the object. Called during construction. @@ -259,42 +270,6 @@ private: 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 @@ -367,11 +342,4 @@ protected: EStatus mRequestStatus; }; - - -/** - * External constants - */ -extern const std::string CONTEXT_DEST_URI_SD_LABEL; - #endif // LL_LLURLREQUEST_H diff --git a/indra/llmessage/message.cpp b/indra/llmessage/message.cpp index e50bb604a..1fe40f6bc 100644 --- a/indra/llmessage/message.cpp +++ b/indra/llmessage/message.cpp @@ -81,6 +81,7 @@ #include "v4math.h" #include "lltransfertargetvfile.h" #include "llmemtype.h" +#include "llpacketring.h" // Constants //const char* MESSAGE_LOG_FILENAME = "message.log"; @@ -243,7 +244,8 @@ LLMessageSystem::LLMessageSystem(const std::string& filename, U32 port, bool failure_is_fatal, const F32 circuit_heartbeat_interval, const F32 circuit_timeout) : mCircuitInfo(circuit_heartbeat_interval, circuit_timeout), - mLastMessageFromTrustedMessageService(false) + mLastMessageFromTrustedMessageService(false), + mPacketRing(new LLPacketRing) { init(); @@ -383,6 +385,9 @@ LLMessageSystem::~LLMessageSystem() delete mPollInfop; mPollInfop = NULL; + delete mPacketRing; + mPacketRing = NULL; + mIncomingCompressedSize = 0; mCurrentRecvPacketID = 0; } @@ -548,13 +553,13 @@ BOOL LLMessageSystem::checkMessages( S64 frame_count ) U8* buffer = mTrueReceiveBuffer; - mTrueReceiveSize = mPacketRing.receivePacket(mSocket, (char *)mTrueReceiveBuffer); + mTrueReceiveSize = mPacketRing->receivePacket(mSocket, (char *)mTrueReceiveBuffer); // If you want to dump all received packets into SecondLife.log, uncomment this //dumpPacketToLog(); receive_size = mTrueReceiveSize; - mLastSender = mPacketRing.getLastSender(); - mLastReceivingIF = mPacketRing.getLastReceivingInterface(); + mLastSender = mPacketRing->getLastSender(); + mLastReceivingIF = mPacketRing->getLastReceivingInterface(); if (receive_size < (S32) LL_MINIMUM_VALID_PACKET_SIZE) { @@ -1129,7 +1134,7 @@ S32 LLMessageSystem::flushReliable(const LLHost &host) return send_bytes; } -LLHTTPClient::ResponderPtr LLMessageSystem::createResponder(const std::string& name) +LLFnPtrResponder* LLMessageSystem::createResponder(const std::string& name) { if(mSendReliable) { @@ -1328,7 +1333,7 @@ S32 LLMessageSystem::sendMessage(const LLHost &host) } BOOL success; - success = mPacketRing.sendPacket(mSocket, (char *)buf_ptr, buffer_length, host); + success = mPacketRing->sendPacket(mSocket, (char *)buf_ptr, buffer_length, host); if (!success) { @@ -3361,7 +3366,7 @@ void LLMessageSystem::establishBidirectionalTrust(const LLHost &host, S64 frame_ void LLMessageSystem::dumpPacketToLog() { - LL_WARNS("Messaging") << "Packet Dump from:" << mPacketRing.getLastSender() << llendl; + LL_WARNS("Messaging") << "Packet Dump from:" << mPacketRing->getLastSender() << llendl; LL_WARNS("Messaging") << "Packet Size:" << mTrueReceiveSize << llendl; char line_buffer[256]; /* Flawfinder: ignore */ S32 i; diff --git a/indra/llmessage/message.h b/indra/llmessage/message.h index 1589ea29c..dee687d47 100644 --- a/indra/llmessage/message.h +++ b/indra/llmessage/message.h @@ -48,9 +48,9 @@ #include "string_table.h" #include "llcircuit.h" #include "lltimer.h" -#include "llpacketring.h" +//#include "llpacketring.h" #include "llhost.h" -#include "llhttpclient.h" +//#include "llhttpclient.h" #include "llhttpnode.h" #include "llpacketack.h" #include "llsingleton.h" @@ -61,6 +61,12 @@ #include "llstoredmessage.h" +class LLPacketRing; +namespace +{ + class LLFnPtrResponder; +} + const U32 MESSAGE_MAX_STRINGS_LENGTH = 64; const U32 MESSAGE_NUMBER_OF_HASH_BUCKETS = 8192; @@ -213,7 +219,7 @@ class LLMessageSystem : public LLMessageSenderInterface LLHost mUntrustedInterface; public: - LLPacketRing mPacketRing; + LLPacketRing* mPacketRing; LLReliablePacketParams mReliablePacketParams; // Set this flag to TRUE when you want *very* verbose logs. @@ -494,7 +500,7 @@ public: void (*callback)(void **,S32), void ** callback_data); - LLHTTPClient::ResponderPtr createResponder(const std::string& name); + LLFnPtrResponder* createResponder(const std::string& name); S32 sendMessage(const LLHost &host); S32 sendMessage(const U32 circuit); private: diff --git a/indra/llplugin/llplugininstance.cpp b/indra/llplugin/llplugininstance.cpp index a53f67087..19d41f201 100644 --- a/indra/llplugin/llplugininstance.cpp +++ b/indra/llplugin/llplugininstance.cpp @@ -125,7 +125,7 @@ int LLPluginInstance::load(const std::string& plugin_dir, std::string &plugin_fi buf[0] = 0; if (error) { - strncpy(buf, dlerror(), sizeof(buf)); + strncpy(buf, error, sizeof(buf)); } buf[sizeof(buf) - 1] = 0; } diff --git a/indra/llplugin/llpluginprocessparent.cpp b/indra/llplugin/llpluginprocessparent.cpp index cdb8ec88a..c3fa0fb53 100644 --- a/indra/llplugin/llpluginprocessparent.cpp +++ b/indra/llplugin/llpluginprocessparent.cpp @@ -379,6 +379,7 @@ void LLPluginProcessParent::idle(void) } else { + // Set PluginAttachDebuggerToPlugins to TRUE to use this. You might also want to set DebugPluginDisableTimeout to TRUE. if(mDebug) { // If we're set to debug, start up a gdb instance in a new terminal window and have it attach to the plugin process and continue. @@ -400,14 +401,37 @@ void LLPluginProcessParent::idle(void) mDebugger.launch(); #elif LL_LINUX // The command we're constructing would look like this on the command line: - // /usr/bin/xterm -geometry 160x24-0+0 -e '/usr/bin/gdb -n /proc/12345/exe 12345' - // This can be changed by setting the following environment variables, for example: - // export LL_DEBUG_TERMINAL_COMMAND="/usr/bin/gnome-terminal --geometry=165x24-0+0 -e %s" + // /usr/bin/xterm -geometry 160x24-0+0 -e /usr/bin/gdb -n /proc/12345/exe 12345 + // Note that most terminals demand that all arguments to the process that is + // started with -e are passed as arguments to the terminal: there are no quotes + // around '/usr/bin/gdb -n /proc/12345/exe 12345'. This is the case for xterm, + // uxterm, konsole etc. The exception might be gnome-terminal. + // + // The constructed command can be changed by setting the following environment + // variables, for example: + // // export LL_DEBUG_GDB_PATH=/usr/bin/gdb + // export LL_DEBUG_TERMINAL_COMMAND='/usr/bin/gnome-terminal --geometry=165x24-0+0 -e "%s"' + // + // Or, as second example, if you are running the viewer on host 'A', and you want + // to open the gdb terminal on the X display of host 'B', you would run on host B: + // 'ssh -X A' (and then start the viewer, or just leave the terminal open), and + // then use: + // + // export LL_DEBUG_TERMINAL_COMMAND="/usr/bin/uxterm -fs 9 -fa 'DejaVu Sans Mono' -display localhost:10 -geometry 209x31+0-50 -e %s" + // + // which would open the terminal on B (no quotes around the %s, since this uses uxterm!). + // For a list of available strings to pass to the -fa, run in a terminal: fc-list :scalable=true:spacing=mono: family + char const* env; - std::string const terminal_command = (env = getenv("LL_DEBUG_TERMINAL_COMMAND")) ? env : "/usr/bin/xterm -geometry 160x24+0+0 -e %s"; + std::string terminal_command = (env = getenv("LL_DEBUG_TERMINAL_COMMAND")) ? env : "/usr/bin/xterm -geometry 160x24+0+0 -e %s"; char const* const gdb_path = (env = getenv("LL_DEBUG_GDB_PATH")) ? env : "/usr/bin/gdb"; cmd << gdb_path << " -n /proc/" << mProcess.getProcessID() << "/exe " << mProcess.getProcessID(); + std::string::size_type pos = terminal_command.find("%s"); + if (pos != std::string::npos) + { + terminal_command.replace(pos, 2, cmd.str()); + } typedef boost::tokenizer< boost::escaped_list_separator< char>, std::basic_string< @@ -429,14 +453,7 @@ void LLPluginProcessParent::idle(void) mDebugger.setExecutable(*token); while (++token != tokens.end()) { - if (*token == "%s") - { - mDebugger.addArgument(cmd.str()); - } - else - { - mDebugger.addArgument(*token); - } + mDebugger.addArgument(*token); } mDebugger.launch(); #endif diff --git a/indra/llplugin/slplugin/CMakeLists.txt b/indra/llplugin/slplugin/CMakeLists.txt index ea9afec16..e377434b1 100644 --- a/indra/llplugin/slplugin/CMakeLists.txt +++ b/indra/llplugin/slplugin/CMakeLists.txt @@ -52,7 +52,14 @@ add_executable(SLPlugin set_target_properties(SLPlugin PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/slplugin_info.plist +) + +if (WINDOWS) + set_target_properties(SLPlugin + PROPERTIES + LINK_FLAGS "/OPT:NOREF" ) +endif() target_link_libraries(SLPlugin ${LLPLUGIN_LIBRARIES} diff --git a/indra/llrender/llglslshader.cpp b/indra/llrender/llglslshader.cpp index 1c90d7658..ce130cf30 100644 --- a/indra/llrender/llglslshader.cpp +++ b/indra/llrender/llglslshader.cpp @@ -244,14 +244,16 @@ BOOL LLGLSLShader::createShader(vector * attributes, BOOL LLGLSLShader::attachObject(std::string object) { - if (LLShaderMgr::instance()->mShaderObjects.count(object) > 0) + std::multimap::iterator it = LLShaderMgr::instance()->mShaderObjects.begin(); + for(; it!=LLShaderMgr::instance()->mShaderObjects.end(); it++) { - stop_glerror(); - glAttachObjectARB(mProgramObject, LLShaderMgr::instance()->mShaderObjects[object]); - stop_glerror(); - return TRUE; + if((*it).first == object) + { + glAttachObjectARB(mProgramObject, (*it).second.mHandle); + stop_glerror(); + return TRUE; + } } - else { LL_WARNS("ShaderLoading") << "Attempting to attach shader object that hasn't been compiled: " << object << LL_ENDL; return FALSE; @@ -262,6 +264,20 @@ void LLGLSLShader::attachObject(GLhandleARB object) { if (object != 0) { + std::multimap::iterator it = LLShaderMgr::instance()->mShaderObjects.begin(); + for(; it!=LLShaderMgr::instance()->mShaderObjects.end(); it++) + { + if((*it).second.mHandle == object) + { + LL_DEBUGS("ShaderLoading") << "Attached: " << (*it).first << llendl; + break; + } + } + if(it == LLShaderMgr::instance()->mShaderObjects.end()) + { + LL_WARNS("ShaderLoading") << "Attached unknown shader!" << llendl; + } + stop_glerror(); glAttachObjectARB(mProgramObject, object); stop_glerror(); diff --git a/indra/llrender/llrendertarget.cpp b/indra/llrender/llrendertarget.cpp index c75304957..6454f92d9 100644 --- a/indra/llrender/llrendertarget.cpp +++ b/indra/llrender/llrendertarget.cpp @@ -775,11 +775,8 @@ bool LLMultisampleBuffer::allocate(U32 resx, U32 resy, U32 color_fmt, bool depth //Restrict to valid sample count { mSamples = samples; - mSamples = llmin(mSamples, (U32)4); //Cap to prevent memory bloat. - mSamples = llmin(mSamples, (U32) gGLManager.mMaxIntegerSamples);//GL_RGBA - - if(depth && !stencil) - mSamples = llmin(mSamples, (U32) gGLManager.mMaxSamples); //GL_DEPTH_COMPONENT16_ARB + //mSamples = llmin(mSamples, (U32)4); //Cap to prevent memory bloat. + mSamples = llmin(mSamples, (U32) gGLManager.mMaxSamples); } if (mSamples <= 1) diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp index 2531df4b6..234d2e141 100644 --- a/indra/llrender/llshadermgr.cpp +++ b/indra/llrender/llshadermgr.cpp @@ -525,6 +525,14 @@ void LLShaderMgr::dumpObjectLog(GLhandleARB ret, BOOL warns) GLhandleARB LLShaderMgr::loadShaderFile(const std::string& filename, S32 & shader_level, GLenum type, S32 texture_index_channels) { + std::pair::iterator, std::multimap::iterator> range; + range = mShaderObjects.equal_range(filename); + for (std::multimap::iterator it = range.first; it != range.second;++it) + { + if((*it).second.mLevel == shader_level && (*it).second.mType == type) + return (*it).second.mHandle; + } + GLenum error = GL_NO_ERROR; if (gDebugGL) { @@ -888,7 +896,7 @@ GLhandleARB LLShaderMgr::loadShaderFile(const std::string& filename, S32 & shade if (ret) { // Add shader file to map - mShaderObjects[filename] = ret; + mShaderObjects.insert(make_pair(filename,CachedObjectInfo(ret,try_gpu_class,type))); shader_level = try_gpu_class; } else diff --git a/indra/llrender/llshadermgr.h b/indra/llrender/llshadermgr.h index b06d61b3e..fc9242f7c 100644 --- a/indra/llrender/llshadermgr.h +++ b/indra/llrender/llshadermgr.h @@ -185,8 +185,16 @@ public: virtual void updateShaderUniforms(LLGLSLShader * shader) = 0; // Pure Virtual public: + struct CachedObjectInfo + { + CachedObjectInfo(GLhandleARB handle, U32 level, GLenum type) : + mHandle(handle), mLevel(level), mType(type) {} + GLhandleARB mHandle; //Actual handle of the opengl shader object. + U32 mLevel; //Level /might/ not be needed, but it's stored to ensure there's no change in behavior. + GLenum mType; //GL_VERTEX_SHADER_ARB or GL_FRAGMENT_SHADER_ARB. Tracked because some utility shaders can be loaded as both types (carefully). + }; // Map of shader names to compiled - std::map mShaderObjects; + std::multimap mShaderObjects; //Singu Note: Packing more info here. Doing such provides capability to skip unneeded duplicate loading.. //global (reserved slot) shader parameters std::vector mReservedAttribs; diff --git a/indra/llvfs/CMakeLists.txt b/indra/llvfs/CMakeLists.txt index e5889805b..8dabf5b91 100644 --- a/indra/llvfs/CMakeLists.txt +++ b/indra/llvfs/CMakeLists.txt @@ -63,8 +63,8 @@ add_library (llvfs ${llvfs_SOURCE_FILES}) add_dependencies(llvfs prepare) target_link_libraries(llvfs - ${BOOST_FILESYSTEM_LIBRARY} - ${BOOST_SYSTEM_LIBRARY} + ${Boost_FILESYSTEM_LIBRARY} + ${Boost_SYSTEM_LIBRARY} ) if (DARWIN) diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp index 70c6f771f..53ba137d0 100644 --- a/indra/llwindow/llwindowwin32.cpp +++ b/indra/llwindow/llwindowwin32.cpp @@ -1416,7 +1416,7 @@ BOOL LLWindowWin32::switchContext(BOOL fullscreen, const LLCoordScreen &size, BO { found_format = TRUE; } - else if(cur_format >= num_formats-1) + else if(cur_format >= (S32)num_formats-1) { cur_format = 0; found_format = TRUE; diff --git a/indra/llxml/llcontrol.h b/indra/llxml/llcontrol.h index 47df59d5e..6ce9745cc 100644 --- a/indra/llxml/llcontrol.h +++ b/indra/llxml/llcontrol.h @@ -42,6 +42,7 @@ #include "v4color.h" #include "v4coloru.h" #include "llinstancetracker.h" +#include "llrefcount.h" #include "llcontrolgroupreader.h" diff --git a/indra/lscript/lscript_compile/indra.l b/indra/lscript/lscript_compile/indra.l index 60245757a..d7d591cc5 100644 --- a/indra/lscript/lscript_compile/indra.l +++ b/indra/lscript/lscript_compile/indra.l @@ -56,12 +56,6 @@ void parse_string(); #define ECHO do { } while (0) -#if defined(__cplusplus) -extern "C" { int yylex( void ); } -extern "C" { int yyparse( void ); } -extern "C" { int yyerror(const char *fmt, ...); } -#endif - %} %% diff --git a/indra/lscript/lscript_compile/indra.y b/indra/lscript/lscript_compile/indra.y index e4b10ffdd..febf5f675 100644 --- a/indra/lscript/lscript_compile/indra.y +++ b/indra/lscript/lscript_compile/indra.y @@ -2,10 +2,6 @@ #include "linden_common.h" #include "lscript_tree.h" - #ifdef __cplusplus - extern "C" { - #endif - int yylex(void); int yyparse( void ); int yyerror(const char *fmt, ...); @@ -19,10 +15,6 @@ #pragma warning (disable : 4702) // warning C4702: unreachable code #pragma warning( disable : 4065 ) // warning: switch statement contains 'default' but no 'case' labels #endif - - #ifdef __cplusplus - } - #endif %} %union diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index e1742fc20..b380af7d2 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -8,10 +8,10 @@ include(DBusGlib) include(DirectX) include(ELFIO) if(FMODEX) - include(FMODEX) - set(FMOD OFF) + include(FMODEX) + set(FMOD OFF) else(FMODEX) - include(FMOD) + include(FMOD) endif(FMODEX) include(OPENAL) include(FindOpenGL) @@ -132,6 +132,7 @@ set(viewer_SOURCE_FILES llconfirmationmanager.cpp llconsole.cpp llcontainerview.cpp + llcurlrequest.cpp llcurrencyuimanager.cpp llcylinder.cpp lldaycyclemanager.cpp @@ -633,6 +634,7 @@ set(viewer_HEADER_FILES llconfirmationmanager.h llconsole.h llcontainerview.h + llcurlrequest.h llcurrencyuimanager.h llcylinder.h lldaycyclemanager.h @@ -1560,10 +1562,10 @@ target_link_libraries(${VIEWER_BINARY_NAME} ${LLCOMMON_LIBRARIES} ${NDOF_LIBRARY} ${viewer_LIBRARIES} - ${BOOST_FILESYSTEM_LIBRARY} - ${BOOST_PROGRAM_OPTIONS_LIBRARY} - ${BOOST_REGEX_LIBRARY} - ${BOOST_SYSTEM_LIBRARY} + ${Boost_FILESYSTEM_LIBRARY} + ${Boost_PROGRAM_OPTIONS_LIBRARY} + ${Boost_REGEX_LIBRARY} + ${Boost_SYSTEM_LIBRARY} ${DBUSGLIB_LIBRARIES} ${OPENGL_LIBRARIES} ${FMODWRAPPER_LIBRARY} # must come after LLAudio diff --git a/indra/newview/NACLantispam.cpp b/indra/newview/NACLantispam.cpp index cabd47ed0..a46378268 100644 --- a/indra/newview/NACLantispam.cpp +++ b/indra/newview/NACLantispam.cpp @@ -19,7 +19,9 @@ #include "llnotificationsutil.h" #include "llviewerobjectlist.h" #include "llagent.h" +#include "lltrans.h" #include +#include U32 NACLAntiSpamRegistry::globalAmount; U32 NACLAntiSpamRegistry::globalTime; @@ -28,6 +30,7 @@ NACLAntiSpamQueue* NACLAntiSpamRegistry::queues[NACLAntiSpamRegistry::QUEUE_MAX] std::tr1::unordered_map NACLAntiSpamRegistry::globalEntries; std::tr1::unordered_map::iterator NACLAntiSpamRegistry::it2; +// The following sounds will be ignored for purposes of spam protection. They have been gathered from wiki documentation of frequent official sounds. const std::string COLLISION_SOUNDS[] ={"dce5fdd4-afe4-4ea1-822f-dd52cac46b08","51011582-fbca-4580-ae9e-1a5593f094ec","68d62208-e257-4d0c-bbe2-20c9ea9760bb","75872e8c-bc39-451b-9b0b-042d7ba36cba","6a45ba0b-5775-4ea8-8513-26008a17f873","992a6d1b-8c77-40e0-9495-4098ce539694","2de4da5a-faf8-46be-bac6-c4d74f1e5767","6e3fb0f7-6d9c-42ca-b86b-1122ff562d7d","14209133-4961-4acc-9649-53fc38ee1667","bc4a4348-cfcc-4e5e-908e-8a52a8915fe6","9e5c1297-6eed-40c0-825a-d9bcd86e3193","e534761c-1894-4b61-b20c-658a6fb68157","8761f73f-6cf9-4186-8aaa-0948ed002db1","874a26fd-142f-4173-8c5b-890cd846c74d","0e24a717-b97e-4b77-9c94-b59a5a88b2da","75cf3ade-9a5b-4c4d-bb35-f9799bda7fb2","153c8bf7-fb89-4d89-b263-47e58b1b4774","55c3e0ce-275a-46fa-82ff-e0465f5e8703","24babf58-7156-4841-9a3f-761bdbb8e237","aca261d8-e145-4610-9e20-9eff990f2c12","0642fba6-5dcf-4d62-8e7b-94dbb529d117","25a863e8-dc42-4e8a-a357-e76422ace9b5","9538f37c-456e-4047-81be-6435045608d4","8c0f84c3-9afd-4396-b5f5-9bca2c911c20","be582e5d-b123-41a2-a150-454c39e961c8","c70141d4-ba06-41ea-bcbc-35ea81cb8335","7d1826f4-24c4-4aac-8c2e-eff45df37783","063c97d3-033a-4e9b-98d8-05c8074922cb","00000000-0000-0000-0000-000000000120"}; const int COLLISION_SOUNDS_SIZE=29; @@ -45,11 +48,11 @@ void NACLAntiSpamQueueEntry::clearEntry() entryAmount=0; blocked=false; } -U32 NACLAntiSpamQueueEntry::getEntryAmount() +U32 NACLAntiSpamQueueEntry::getEntryAmount() const { return entryAmount; } -U32 NACLAntiSpamQueueEntry::getEntryTime() +U32 NACLAntiSpamQueueEntry::getEntryTime() const { return entryTime; } @@ -65,7 +68,7 @@ void NACLAntiSpamQueueEntry::setBlocked() { blocked=true; } -bool NACLAntiSpamQueueEntry::getBlocked() +bool NACLAntiSpamQueueEntry::getBlocked() const { return blocked; } @@ -85,6 +88,14 @@ void NACLAntiSpamQueue::setTime(U32 time) { queueTime=time; } +U32 NACLAntiSpamQueue::getAmount() const +{ + return queueAmount; +} +U32 NACLAntiSpamQueue::getTime() const +{ + return queueTime; +} void NACLAntiSpamQueue::clearEntries() { for(it = entries.begin(); it != entries.end(); it++) @@ -110,6 +121,7 @@ void NACLAntiSpamQueue::blockEntry(LLUUID& source) entries[source.asString()]->setBlocked(); } int NACLAntiSpamQueue::checkEntry(LLUUID& name, U32 multiplier) +// Returns 0 if unblocked/disabled, 1 if check results in a new block, 2 if by an existing block { static LLCachedControl enabled(gSavedSettings,"AntiSpamEnabled",false); if(!enabled) return 0; @@ -141,6 +153,7 @@ int NACLAntiSpamQueue::checkEntry(LLUUID& name, U32 multiplier) } else { + //lldebugs << "[antispam] New queue entry:" << name.asString() << llendl; entries[name.asString()]=new NACLAntiSpamQueueEntry(); entries[name.asString()]->updateEntryAmount(); entries[name.asString()]->updateEntryTime(); @@ -154,8 +167,8 @@ static const char* QUEUE_NAME[NACLAntiSpamRegistry::QUEUE_MAX] = { "Chat", "Inventory", "Instant Message", -"Calling Card", -"Sound", +"calling card", +"sound", "Sound Preload", "Script Dialog", "Teleport"}; @@ -171,12 +184,14 @@ NACLAntiSpamRegistry::NACLAntiSpamRegistry(U32 time, U32 amount) queues[queue] = new NACLAntiSpamQueue(time,amount); } } +//static const char* NACLAntiSpamRegistry::getQueueName(U32 queue_id) { if(queue_id >= QUEUE_MAX) return "Unknown"; return QUEUE_NAME[queue_id]; } +//static void NACLAntiSpamRegistry::registerQueues(U32 time, U32 amount) { globalTime=time; @@ -188,16 +203,16 @@ void NACLAntiSpamRegistry::registerQueues(U32 time, U32 amount) queues[queue] = new NACLAntiSpamQueue(time,amount); } } -void NACLAntiSpamRegistry::registerQueue(U32 name, U32 time, U32 amount) +//static +/*void NACLAntiSpamRegistry::registerQueue(U32 name, U32 time, U32 amount) { - /* it=queues.find(name); if(it == queues.end()) { queues[name]=new NACLAntiSpamQueue(time,amount); } - */ -} +}*/ +//static void NACLAntiSpamRegistry::setRegisteredQueueTime(U32 name, U32 time) { if(name >= QUEUE_MAX || queues[name] == 0) @@ -208,6 +223,7 @@ void NACLAntiSpamRegistry::setRegisteredQueueTime(U32 name, U32 time) queues[name]->setTime(time); } +//static void NACLAntiSpamRegistry::setRegisteredQueueAmount(U32 name, U32 amount) { if(name >= QUEUE_MAX || queues[name] == 0) @@ -218,23 +234,28 @@ void NACLAntiSpamRegistry::setRegisteredQueueAmount(U32 name, U32 amount) queues[name]->setAmount(amount); } +//static void NACLAntiSpamRegistry::setAllQueueTimes(U32 time) { globalTime=time; for(int queue = 0; queue < QUEUE_MAX; ++queue) - queues[queue]->setTime(time); + if( queues[queue] ) + queues[queue]->setTime(time); } +//static void NACLAntiSpamRegistry::setAllQueueAmounts(U32 amount) { globalAmount=amount; for(int queue = 0; queue < QUEUE_MAX; ++queue) { + if(!queues[queue]) continue; if(queue == QUEUE_SOUND || queue == QUEUE_SOUND_PRELOAD) queues[queue]->setAmount(amount*5); else queues[queue]->setAmount(amount); } } +//static void NACLAntiSpamRegistry::clearRegisteredQueue(U32 name) { if(name >= QUEUE_MAX || queues[name] == 0) @@ -245,6 +266,7 @@ void NACLAntiSpamRegistry::clearRegisteredQueue(U32 name) queues[name]->clearEntries(); } +//static void NACLAntiSpamRegistry::purgeRegisteredQueue(U32 name) { if(name >= QUEUE_MAX || queues[name] == 0) @@ -255,6 +277,7 @@ void NACLAntiSpamRegistry::purgeRegisteredQueue(U32 name) queues[name]->purgeEntries(); } +//static void NACLAntiSpamRegistry::blockOnQueue(U32 name, LLUUID& source) { if(bGlobalQueue) @@ -271,6 +294,7 @@ void NACLAntiSpamRegistry::blockOnQueue(U32 name, LLUUID& source) queues[name]->blockEntry(source); } } +//static void NACLAntiSpamRegistry::blockGlobalEntry(LLUUID& source) { it2=globalEntries.find(source.asString()); @@ -280,13 +304,13 @@ void NACLAntiSpamRegistry::blockGlobalEntry(LLUUID& source) } globalEntries[source.asString()]->setBlocked(); } +//static bool NACLAntiSpamRegistry::checkQueue(U32 name, LLUUID& source, U32 multiplier) +//returns true if blocked { - if(source.isNull()) return false; - if(gAgent.getID() == source) return false; + if(source.isNull() || gAgent.getID() == source) return false; LLViewerObject *obj=gObjectList.findObject(source); - if(obj) - if(obj->permYouOwner()) return false; + if(obj && obj->permYouOwner()) return false; int result; if(bGlobalQueue) @@ -302,40 +326,45 @@ bool NACLAntiSpamRegistry::checkQueue(U32 name, LLUUID& source, U32 multiplier) } result=queues[name]->checkEntry(source,multiplier); } - if(result==0) - { + if(result == 0) //Safe return false; - } - else if(result==2) + else if(result == 2) //Previously blocked { return true; } - else + else //Just blocked! { if(gSavedSettings.getBOOL("AntiSpamNotify")) { - LLSD args; - args["MESSAGE"] = std::string(getQueueName(name))+": Blocked object "+source.asString(); - LLNotificationsUtil::add("SystemMessageTip", args); + LLSD args; + args["SOURCE"] = source.asString().c_str(); + args["TYPE"] = LLTrans::getString(getQueueName(name)); + args["AMOUNT"] = boost::lexical_cast(multiplier * queues[name]->getAmount()); + args["TIME"] = boost::lexical_cast(queues[name]->getTime()); + LLNotificationsUtil::add("AntiSpamBlock", args); } return true; } } // Global queue stoof +//static void NACLAntiSpamRegistry::setGlobalQueue(bool value) { NACLAntiSpamRegistry::purgeAllQueues(); bGlobalQueue=value; } +//static void NACLAntiSpamRegistry::setGlobalAmount(U32 amount) { globalAmount=amount; } +//static void NACLAntiSpamRegistry::setGlobalTime(U32 time) { globalTime=time; } +//static void NACLAntiSpamRegistry::clearAllQueues() { if(bGlobalQueue) @@ -343,9 +372,10 @@ void NACLAntiSpamRegistry::clearAllQueues() else for(int queue = 0; queue < QUEUE_MAX; ++queue) { - queues[queue]->clearEntries(); + if(queues[queue]) queues[queue]->clearEntries(); } } +//static void NACLAntiSpamRegistry::purgeAllQueues() { if(bGlobalQueue) @@ -353,9 +383,11 @@ void NACLAntiSpamRegistry::purgeAllQueues() else for(int queue = 0; queue < QUEUE_MAX; ++queue) { - queues[queue]->purgeEntries(); + if(queues[queue]) queues[queue]->purgeEntries(); } + llinfos << "AntiSpam Queues Purged" << llendl; } +//static int NACLAntiSpamRegistry::checkGlobalEntry(LLUUID& name, U32 multiplier) { static LLCachedControl enabled(gSavedSettings,"AntiSpamEnabled",false); @@ -391,6 +423,7 @@ int NACLAntiSpamRegistry::checkGlobalEntry(LLUUID& name, U32 multiplier) return 0; } } +//static void NACLAntiSpamRegistry::clearGlobalEntries() { for(it2 = globalEntries.begin(); it2 != globalEntries.end(); it2++) @@ -398,6 +431,7 @@ void NACLAntiSpamRegistry::clearGlobalEntries() it2->second->clearEntry(); } } +//static void NACLAntiSpamRegistry::purgeGlobalEntries() { for(it2 = globalEntries.begin(); it2 != globalEntries.end(); it2++) @@ -407,16 +441,21 @@ void NACLAntiSpamRegistry::purgeGlobalEntries() } globalEntries.clear(); } + +//Handlers +//static bool NACLAntiSpamRegistry::handleNaclAntiSpamGlobalQueueChanged(const LLSD& newvalue) { setGlobalQueue(newvalue.asBoolean()); return true; } +//static bool NACLAntiSpamRegistry::handleNaclAntiSpamTimeChanged(const LLSD& newvalue) { setAllQueueTimes(newvalue.asInteger()); return true; } +//static bool NACLAntiSpamRegistry::handleNaclAntiSpamAmountChanged(const LLSD& newvalue) { setAllQueueAmounts(newvalue.asInteger()); diff --git a/indra/newview/NACLantispam.h b/indra/newview/NACLantispam.h index 6da32f5c1..e0476e267 100644 --- a/indra/newview/NACLantispam.h +++ b/indra/newview/NACLantispam.h @@ -25,11 +25,11 @@ class NACLAntiSpamQueueEntry protected: NACLAntiSpamQueueEntry(); void clearEntry(); - U32 getEntryAmount(); - U32 getEntryTime(); + U32 getEntryAmount() const; + U32 getEntryTime() const; void updateEntryAmount(); void updateEntryTime(); - bool getBlocked(); + bool getBlocked() const; void setBlocked(); private: U32 entryAmount; @@ -39,6 +39,9 @@ private: class NACLAntiSpamQueue { friend class NACLAntiSpamRegistry; +public: + U32 getAmount() const; + U32 getTime() const; protected: NACLAntiSpamQueue(U32 time, U32 amount); void setAmount(U32 amount); @@ -58,7 +61,7 @@ class NACLAntiSpamRegistry public: NACLAntiSpamRegistry(U32 time=2, U32 amount=10); static void registerQueues(U32 time=2, U32 amount=10); - static void registerQueue(U32 name, U32 time, U32 amount); +// static void registerQueue(U32 name, U32 time, U32 amount); static void setRegisteredQueueTime(U32 name, U32 time); static void setRegisteredQueueAmount(U32 name,U32 amount); static void setAllQueueTimes(U32 amount); diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 36279ab1f..c0ec36c53 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -9,6 +9,17 @@ settings_rlv.xml + SGAllowRiggedMeshSelection + + Comment + Allow selection of your worn rigged meshes in build mode + Persist + 1 + Type + Boolean + Value + 0 + SGShiftCrouchToggle Comment @@ -20,7 +31,6 @@ Value 1 - SGServerVersionChangedNotification Comment @@ -1032,7 +1042,7 @@ _NACL_AntiSpamGlobalQueue Comment - Collect spamtypes together, instead of individually counting them + Collect spamtypes together, instead of individually counting them; Use in cases of extreme spam, only. Persist 1 Type @@ -1043,7 +1053,7 @@ _NACL_AntiSpamTime Comment - Time in seconds for spamtype to accumulate the _NACL_AntiSpamAmount and be blocked + Time in seconds for spamtype to accumulate to more than _NACL_AntiSpamAmount and be blocked Persist 1 Type @@ -1054,7 +1064,7 @@ _NACL_AntiSpamAmount Comment - Amount of spamtype to be reached before the blocking will occur. + Count of spamtype to be reached during _NACL_AntiSpamTime before the spam blocking will occur. Persist 1 Type @@ -1065,7 +1075,7 @@ _NACL_AntiSpamSoundMulti Comment - + Multiplier for _NACL_AntiSpamTime for sounds heard in _NACL_AntiSpamTime interval needed to trigger a block, since sounds are more common than messages Persist 1 Type @@ -1076,18 +1086,18 @@ _NACL_AntiSpamNewlines Comment - How many newlines a message can have before it's considered spam. + How many newlines a message is allowed to have without being considered spam. Persist 1 Type U32 Value - 20 + 70 _NACL_AntiSpamSoundPreloadMulti Comment - + Multiplier for _NACL_AntiSpamTime for sounds preloaded in _NACL_AntiSpamTime interval needed to trigger a block, since sound preloads are more common than messages Persist 1 Type @@ -4217,17 +4227,6 @@ Value 120.0 - CurlUseMultipleThreads - - Comment - Use background threads for executing curl_multi_perform (requires restart) - Persist - 1 - Type - Boolean - Value - 1 - Cursor3D Comment diff --git a/indra/newview/hipporestrequest.cpp b/indra/newview/hipporestrequest.cpp index 8953dec87..12e112bcf 100644 --- a/indra/newview/hipporestrequest.cpp +++ b/indra/newview/hipporestrequest.cpp @@ -6,15 +6,18 @@ #ifndef CURL_STATICLIB #define CURL_STATICLIB 1 #endif -#include #include -#include -#include -#include -#include -#include +#include "llbufferstream.h" +#include "llerror.h" +#include "llhttpclient.h" +#include "llurlrequest.h" +#include "llxmltree.h" +#include +#ifdef DEBUG_CURLIO +#include "debug_libcurl.h" +#endif // ******************************************************************** @@ -255,7 +258,16 @@ static void request(const std::string &url, } LLPumpIO::chain_t chain; - LLURLRequest *req = new LLURLRequest(method, url); + LLURLRequest *req; + try + { + req = new LLURLRequest(method, url); + } + catch(AICurlNoEasyHandle const& error) + { + llwarns << "Failed to create LLURLRequest: " << error.what() << llendl; + return; + } req->checkRootCertificate(true); /* @@ -324,10 +336,11 @@ int HippoRestRequest::getBlocking(const std::string &url, std::string *result) char curlErrorBuffer[CURL_ERROR_SIZE]; CURL* curlp = curl_easy_init(); + llassert_always(curlp); curl_easy_setopt(curlp, CURLOPT_NOSIGNAL, 1); // don't use SIGALRM for timeouts curl_easy_setopt(curlp, CURLOPT_TIMEOUT, 5); // seconds - curl_easy_setopt(curlp, CURLOPT_CAINFO, gDirUtilp->getCAFile().c_str()); + curl_easy_setopt(curlp, CURLOPT_CAINFO, gDirUtilp->getCAFile().c_str()); curl_easy_setopt(curlp, CURLOPT_WRITEFUNCTION, curlWrite); curl_easy_setopt(curlp, CURLOPT_WRITEDATA, result); @@ -337,7 +350,7 @@ int HippoRestRequest::getBlocking(const std::string &url, std::string *result) *result = ""; S32 curlSuccess = curl_easy_perform(curlp); - S32 httpStatus = 499; + long httpStatus = 499L; // curl_easy_getinfo demands pointer to long. curl_easy_getinfo(curlp, CURLINFO_RESPONSE_CODE, &httpStatus); if (curlSuccess != 0) { diff --git a/indra/newview/linux_tools/wrapper.sh b/indra/newview/linux_tools/wrapper.sh index 04edf2a76..e406e3c71 100755 --- a/indra/newview/linux_tools/wrapper.sh +++ b/indra/newview/linux_tools/wrapper.sh @@ -139,15 +139,11 @@ fi export VIEWER_BINARY='singularity-do-not-run-directly' BINARY_TYPE=$(expr match "$(file -b bin/$VIEWER_BINARY)" '\(.*executable\)') -QPP=qt4/plugins/imageformats/ if [ "${BINARY_TYPE}" == "ELF 64-bit LSB executable" ]; then - QTPLUGINS=/usr/lib64/$QPP:/lib64/$QPP:/usr/local/lib64/$QPP - SL_ENV+='LD_LIBRARY_PATH="`pwd`/lib64:`pwd`/lib32:$QTPLUGINS:$LD_LIBRARY_PATH"' + SL_ENV+='LD_LIBRARY_PATH="`pwd`/lib64:`pwd`/lib32:$LD_LIBRARY_PATH"' else - QTPLUGINS=/usr/lib/$QPP:/lib/$QPP:/usr/local/lib/$QPP - SL_ENV+='LD_LIBRARY_PATH="`pwd`/lib:$QTPLUGINS:$LD_LIBRARY_PATH"' + SL_ENV+='LD_LIBRARY_PATH="`pwd`/lib:$LD_LIBRARY_PATH"' fi - export SL_CMD='$LL_WRAPPER bin/$VIEWER_BINARY' export SL_OPT="`cat gridargs.dat` $@" diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index 9f85f680f..a0798dcd8 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -2401,7 +2401,7 @@ bool LLAgent::sendMaturityPreferenceToServer(int preferredMaturity) body["access_prefs"] = access_prefs; llinfos << "Sending access prefs update to " << (access_prefs["max"].asString()) << " via capability to: " << url << llendl; - LLHTTPClient::post(url, body, new LLHTTPClient::Responder()); // Ignore response + LLHTTPClient::post(url, body, new LLHTTPClient::ResponderIgnore); // Ignore response return true; } return false; diff --git a/indra/newview/llagentlanguage.cpp b/indra/newview/llagentlanguage.cpp index 04e1228c7..4dbc7eb70 100644 --- a/indra/newview/llagentlanguage.cpp +++ b/indra/newview/llagentlanguage.cpp @@ -61,7 +61,7 @@ bool LLAgentLanguage::update() body["language"] = language; body["language_is_public"] = gSavedSettings.getBOOL("LanguageIsPublic"); - LLHTTPClient::post(url, body, new LLHTTPClient::Responder); + LLHTTPClient::post(url, body, new LLHTTPClient::ResponderIgnore); } return true; } diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 955a420c6..2fcf37fdb 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -63,7 +63,6 @@ #include "llviewerjoystick.h" #include "llfloaterjoystick.h" #include "llares.h" -#include "llcurl.h" #include "llfloatersnapshot.h" #include "lltexturestats.h" #include "llviewerwindow.h" @@ -611,6 +610,9 @@ bool LLAppViewer::init() initLogging(); + // Curl must be initialized before any thread is running. + AICurlInterface::initCurl(&AIStateMachine::flush); + // Logging is initialized. Now it's safe to start the error thread. startErrorThread(); @@ -635,11 +637,6 @@ bool LLAppViewer::init() LLPrivateMemoryPoolManager::initClass((BOOL)gSavedSettings.getBOOL("MemoryPrivatePoolEnabled"), (U32)gSavedSettings.getU32("MemoryPrivatePoolSize")) ; mAlloc.setProfilingEnabled(gSavedSettings.getBOOL("MemProfiling")); - // *NOTE:Mani - LLCurl::initClass is not thread safe. - // Called before threads are created. - LLCurl::initClass(gSavedSettings.getF32("CurlRequestTimeOut")); - - LL_INFOS("InitInfo") << "LLCurl initialized." << LL_ENDL ; initThreads(); LL_INFOS("InitInfo") << "Threads initialized." << LL_ENDL ; @@ -1739,22 +1736,20 @@ bool LLAppViewer::cleanup() delete gVFS; gVFS = NULL; - // Cleanup settings last in case other clases reference them - gSavedSettings.cleanup(); - gColors.cleanup(); - gCrashSettings.cleanup(); - LLWatchdog::getInstance()->cleanup(); llinfos << "Shutting down message system" << llendflush; end_messaging_system(); llinfos << "Message system deleted." << llendflush; - LLUserAuth::getInstance()->reset(); //reset before LLCurl::cleanupClass, else LLCURL::sHandleMutex == NULL - // *NOTE:Mani - The following call is not thread safe. - LLCurl::cleanupClass(); - llinfos << "LLCurl cleaned up." << llendflush; + LLApp::stopErrorThread(); // The following call is not thread-safe. Have to stop all threads. + AICurlInterface::cleanupCurl(); + // Cleanup settings last in case other classes reference them. + gSavedSettings.cleanup(); + gColors.cleanup(); + gCrashSettings.cleanup(); + // If we're exiting to launch an URL, do that here so the screen // is at the right resolution before we launch IE. if (!gLaunchFileOnQuit.empty()) @@ -1822,6 +1817,8 @@ bool LLAppViewer::initThreads() LLWatchdog::getInstance()->init(watchdog_killer_callback); } + AICurlInterface::startCurlThread(); + LLImage::initClass(); LLVFSThread::initClass(enable_threads && false); diff --git a/indra/newview/llcurlrequest.cpp b/indra/newview/llcurlrequest.cpp new file mode 100644 index 000000000..4084b729f --- /dev/null +++ b/indra/newview/llcurlrequest.cpp @@ -0,0 +1,146 @@ +/** + * @file llcurlrequest.cpp + * @brief Implementation of Request. + * + * Copyright (c) 2012, Aleric Inglewood. + * Copyright (C) 2010, Linden Research, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution. + * + * CHANGELOG + * and additional copyright holders. + * + * 17/03/2012 + * Initial version, written by Aleric Inglewood @ SL + * + * 20/03/2012 + * Added copyright notice for Linden Lab for those parts that were + * copied or derived from llcurl.cpp. The code of those parts are + * already in their own llcurl.cpp, so they do not ever need to + * even look at this file; the reason I added the copyright notice + * is to make clear that I am not the author of 100% of this code + * and hence I cannot change the license of it. + */ + +#include "llviewerprecompiledheaders.h" + +#include "llsdserialize.h" +#include "llcurlrequest.h" +#include "llbuffer.h" +#include "llbufferstream.h" +#include "statemachine/aicurleasyrequeststatemachine.h" + +//----------------------------------------------------------------------------- +// class Request +// + +namespace AICurlInterface { + +bool Request::get(std::string const& url, ResponderPtr responder) +{ + return getByteRange(url, headers_t(), 0, -1, responder); +} + +bool Request::getByteRange(std::string const& url, headers_t const& headers, S32 offset, S32 length, ResponderPtr responder) +{ + DoutEntering(dc::curl, "Request::getByteRange(" << url << ", ...)"); + + // This might throw AICurlNoEasyHandle. + AICurlEasyRequestStateMachine* buffered_easy_request = new AICurlEasyRequestStateMachine(true); + + { + AICurlEasyRequest_wat buffered_easy_request_w(*buffered_easy_request->mCurlEasyRequest); + + AICurlResponderBuffer_wat(*buffered_easy_request->mCurlEasyRequest)->prepRequest(buffered_easy_request_w, headers, responder); + + buffered_easy_request_w->setopt(CURLOPT_HTTPGET, 1); + if (length > 0) + { + std::string range = llformat("Range: bytes=%d-%d", offset, offset + length - 1); + buffered_easy_request_w->addHeader(range.c_str()); + } + + buffered_easy_request_w->finalizeRequest(url); + } + + buffered_easy_request->run(); + + return true; // We throw in case of problems. +} + +bool Request::post(std::string const& url, headers_t const& headers, std::string const& data, ResponderPtr responder, S32 time_out) +{ + DoutEntering(dc::curl, "Request::post(" << url << ", ...)"); + + // This might throw AICurlNoEasyHandle. + AICurlEasyRequestStateMachine* buffered_easy_request = new AICurlEasyRequestStateMachine(true); + + { + AICurlEasyRequest_wat buffered_easy_request_w(*buffered_easy_request->mCurlEasyRequest); + AICurlResponderBuffer_wat buffer_w(*buffered_easy_request->mCurlEasyRequest); + + buffer_w->prepRequest(buffered_easy_request_w, headers, responder); + + U32 bytes = data.size(); + bool success = buffer_w->getInput()->append(buffer_w->sChannels.out(), (U8 const*)data.data(), bytes); + llassert_always(success); // AIFIXME: Maybe throw an error. + if (!success) + return false; + buffered_easy_request_w->setPost(bytes); + buffered_easy_request_w->addHeader("Content-Type: application/octet-stream"); + buffered_easy_request_w->finalizeRequest(url); + + lldebugs << "POSTING: " << bytes << " bytes." << llendl; + } + + buffered_easy_request->run(); + + return true; // We throw in case of problems. +} + +bool Request::post(std::string const& url, headers_t const& headers, LLSD const& data, ResponderPtr responder, S32 time_out) +{ + DoutEntering(dc::curl, "Request::post(" << url << ", ...)"); + + // This might throw AICurlNoEasyHandle. + AICurlEasyRequestStateMachine* buffered_easy_request = new AICurlEasyRequestStateMachine(true); + + { + AICurlEasyRequest_wat buffered_easy_request_w(*buffered_easy_request->mCurlEasyRequest); + AICurlResponderBuffer_wat buffer_w(*buffered_easy_request->mCurlEasyRequest); + + buffer_w->prepRequest(buffered_easy_request_w, headers, responder); + + LLBufferStream buffer_stream(buffer_w->sChannels, buffer_w->getInput().get()); + LLSDSerialize::toXML(data, buffer_stream); + S32 bytes = buffer_w->getInput()->countAfter(buffer_w->sChannels.out(), NULL); + buffered_easy_request_w->setPost(bytes); + buffered_easy_request_w->addHeader("Content-Type: application/llsd+xml"); + buffered_easy_request_w->finalizeRequest(url); + + lldebugs << "POSTING: " << bytes << " bytes." << llendl; + } + + buffered_easy_request->run(); + + return true; // We throw in case of problems. +} + +} // namespace AICurlInterface +//================================================================================== + diff --git a/indra/newview/llcurlrequest.h b/indra/newview/llcurlrequest.h new file mode 100644 index 000000000..d10c20b98 --- /dev/null +++ b/indra/newview/llcurlrequest.h @@ -0,0 +1,57 @@ +/** + * @file llcurlrequest.h + * @brief Declaration of class Request + * + * Copyright (c) 2012, Aleric Inglewood. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution. + * + * CHANGELOG + * and additional copyright holders. + * + * 17/03/2012 + * Initial version, written by Aleric Inglewood @ SL + */ + +#ifndef AICURLREQUEST_H +#define AICURLREQUEST_H + +#include +#include +#include + +// Things defined in this namespace are called from elsewhere in the viewer code. +namespace AICurlInterface { + +// Forward declaration. +class Responder; +typedef boost::intrusive_ptr ResponderPtr; + +class Request { + public: + typedef std::vector headers_t; + + bool get(std::string const& url, ResponderPtr responder); + bool getByteRange(std::string const& url, headers_t const& headers, S32 offset, S32 length, ResponderPtr responder); + bool post(std::string const& url, headers_t const& headers, std::string const& data, ResponderPtr responder, S32 time_out = 0); + bool post(std::string const& url, headers_t const& headers, LLSD const& data, ResponderPtr responder, S32 time_out = 0); +}; + +} // namespace AICurlInterface + +#endif diff --git a/indra/newview/llcurrencyuimanager.cpp b/indra/newview/llcurrencyuimanager.cpp index 6b72cc922..e31e9d114 100644 --- a/indra/newview/llcurrencyuimanager.cpp +++ b/indra/newview/llcurrencyuimanager.cpp @@ -266,7 +266,7 @@ bool LLCurrencyUIManager::Impl::checkTransaction() return false; } - if (!mTransaction->process()) + if (!mTransaction->is_finished()) { return false; } diff --git a/indra/newview/lldrawpoolbump.cpp b/indra/newview/lldrawpoolbump.cpp index 60af91ac8..dd8b4e80c 100644 --- a/indra/newview/lldrawpoolbump.cpp +++ b/indra/newview/lldrawpoolbump.cpp @@ -1598,16 +1598,18 @@ void LLDrawPoolInvisible::renderDeferred( S32 pass ) } LLFastTimer t(FTM_RENDER_INVISIBLE); - + + gOcclusionProgram.bind(); + U32 invisi_mask = LLVertexBuffer::MAP_VERTEX; glStencilMask(0); - glStencilOp(GL_ZERO, GL_KEEP, GL_REPLACE); gGL.setColorMask(false, false); pushBatches(LLRenderPass::PASS_INVISIBLE, invisi_mask, FALSE); gGL.setColorMask(true, true); - glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); glStencilMask(0xFFFFFFFF); + gOcclusionProgram.unbind(); + if (gPipeline.hasRenderBatches(LLRenderPass::PASS_INVISI_SHINY)) { beginShiny(true); diff --git a/indra/newview/llenvmanager.cpp b/indra/newview/llenvmanager.cpp index f3ffe732f..7e5377bd1 100644 --- a/indra/newview/llenvmanager.cpp +++ b/indra/newview/llenvmanager.cpp @@ -494,11 +494,13 @@ void LLEnvManagerNew::onRegionSettingsResponse(const LLSD& content) // Load region sky presets. LLWLParamManager::instance().refreshRegionPresets(); - // Not possible to assume M7WL should take precidence as OpenSim will send both - // bool bOverridden = M7WindlightInterface::getInstance()->hasOverride(); + bool bOverridden = M7WindlightInterface::getInstance()->hasOverride(); // If using server settings, update managers. - if (getUseRegionSettings()) +// if (getUseRegionSettings()) +// [RLVa:KB] - Checked: 2011-08-29 (RLVa-1.4.1a) | Added: RLVa-1.4.1a + if (!bOverridden && (getUseRegionSettings()) && (LLWLParamManager::getInstance()->mAnimator.getIsRunning()) ) +// [/RLVa:KB] { updateManagersFromPrefs(mInterpNextChangeMessage); } diff --git a/indra/newview/llfasttimerview.cpp b/indra/newview/llfasttimerview.cpp index 42a14b7f6..eeb53a98d 100644 --- a/indra/newview/llfasttimerview.cpp +++ b/indra/newview/llfasttimerview.cpp @@ -103,6 +103,10 @@ LLFastTimerView::LLFastTimerView(const std::string& name, const LLRect& rect) FTV_NUM_TIMERS = LLFastTimer::NamedTimer::instanceCount(); mPrintStats = -1; mAverageCyclesPerTimer = 0; + // Making the ledgend part of fast timers scrollable + mOverLegend = false; + mScrollOffset = 0; + // LLUICtrlFactory::getInstance()->buildFloater(this, "floater_fast_timers.xml"); } @@ -258,6 +262,7 @@ BOOL LLFastTimerView::handleHover(S32 x, S32 y, MASK mask) } mHoverTimer = NULL; mHoverID = NULL; + mOverLegend = false; // Making the ledgend part of fast timers scrollable if(LLFastTimer::sPauseHistory && mBarRect.pointInRect(x, y)) { @@ -311,6 +316,7 @@ BOOL LLFastTimerView::handleHover(S32 x, S32 y, MASK mask) { mHoverID = timer_id; } + mOverLegend = true; // Making the ledgend part of fast timers scrollable } return LLFloater::handleHover(x, y, mask); @@ -358,10 +364,36 @@ BOOL LLFastTimerView::handleToolTip(S32 x, S32 y, std::string& msg, LLRect* stic BOOL LLFastTimerView::handleScrollWheel(S32 x, S32 y, S32 clicks) { - LLFastTimer::sPauseHistory = TRUE; - mScrollIndex = llclamp( mScrollIndex + clicks, - 0, - llmin(LLFastTimer::getLastFrameIndex(), (S32)LLFastTimer::NamedTimer::HISTORY_NUM - MAX_VISIBLE_HISTORY)); + //LLFastTimer::sPauseHistory = TRUE; + //mScrollIndex = llclamp( mScrollIndex + clicks, + //0, + //llmin(LLFastTimer::getLastFrameIndex(), (S32)LLFastTimer::NamedTimer::HISTORY_NUM - MAX_VISIBLE_HISTORY)); + // Making the ledgend part of fast timers scrollable + if(mOverLegend) + { + mScrollOffset += clicks; + S32 count = 0; + for (timer_tree_iterator_t it = begin_timer_tree(LLFastTimer::NamedTimer::getRootNamedTimer()); + it != timer_tree_iterator_t(); + ++it) + { + count++; + LLFastTimer::NamedTimer* idp = (*it); + if (idp->getCollapsed()) + { + it.skipDescendants(); + } + } + mScrollOffset = llclamp(mScrollOffset,0,count-5); + } + else + { + LLFastTimer::sPauseHistory = TRUE; + mScrollIndex = llclamp( mScrollIndex + clicks, + 0, + llmin(LLFastTimer::getLastFrameIndex(), (S32)LLFastTimer::NamedTimer::HISTORY_NUM - MAX_VISIBLE_HISTORY)); + } + // return TRUE; } @@ -479,11 +511,23 @@ void LLFastTimerView::draw() S32 cur_line = 0; ft_display_idx.clear(); std::map display_line; + S32 mScrollOffset_tmp = mScrollOffset; // Making the ledgend part of fast timers scrollable for (timer_tree_iterator_t it = begin_timer_tree(LLFastTimer::NamedTimer::getRootNamedTimer()); it != timer_tree_iterator_t(); ++it) { LLFastTimer::NamedTimer* idp = (*it); + // Making the ledgend part of fast timers scrollable + if(mScrollOffset_tmp) + { + --mScrollOffset_tmp; + if (idp->getCollapsed()) + { + it.skipDescendants(); + } + continue; + } + // display_line[idp] = cur_line; ft_display_idx.push_back(idp); cur_line++; diff --git a/indra/newview/llfasttimerview.h b/indra/newview/llfasttimerview.h index 04fa500e1..47972b272 100644 --- a/indra/newview/llfasttimerview.h +++ b/indra/newview/llfasttimerview.h @@ -99,6 +99,11 @@ private: S32 mPrintStats; S32 mAverageCyclesPerTimer; LLRect mGraphRect; + + // Making the ledgend part of fast timers scrollable + bool mOverLegend; + S32 mScrollOffset; + // }; #endif diff --git a/indra/newview/llfloateractivespeakers.cpp b/indra/newview/llfloateractivespeakers.cpp index f0f856ef0..1d1fb1ebf 100644 --- a/indra/newview/llfloateractivespeakers.cpp +++ b/indra/newview/llfloateractivespeakers.cpp @@ -35,7 +35,9 @@ #include "llfloateractivespeakers.h" #include "llagent.h" -#include "llvoavatar.h" +#include "llappviewer.h" +#include "llimview.h" +#include "llsdutil.h" #include "llfloateravatarinfo.h" #include "lluictrlfactory.h" #include "llviewercontrol.h" @@ -44,12 +46,10 @@ #include "lltextbox.h" #include "llmutelist.h" #include "llviewerobjectlist.h" +#include "llvoavatar.h" #include "llimpanel.h" // LLVoiceChannel -#include "llsdutil.h" -#include "llimview.h" #include "llviewerwindow.h" #include "llworld.h" -#include "llappviewer.h" // [RLVa:KB] #include "rlvhandler.h" @@ -90,9 +90,7 @@ LLSpeaker::LLSpeaker(const LLUUID& id, const std::string& name, const ESpeakerTy mDisplayName = name; mLegacyName = name; } - gVoiceClient->setUserVolume(id, LLMuteList::getInstance()->getSavedResidentVolume(id)); - mActivityTimer.reset(SPEAKER_TIMEOUT); } @@ -104,7 +102,7 @@ void LLSpeaker::lookupName() // [/Ansariel: Display name support] } -//static +//static // [Ansariel: Display name support] void LLSpeaker::onAvatarNameLookup(const LLUUID& id, const LLAvatarName& avatar_name, void* user_data) // [/Ansariel: Display name support] @@ -122,12 +120,12 @@ void LLSpeaker::onAvatarNameLookup(const LLUUID& id, const LLAvatarName& avatar_ case 2 : speaker_ptr->mDisplayName = avatar_name.mDisplayName; break; default : speaker_ptr->mDisplayName = avatar_name.getLegacyName(); break; } - + // Also set the legacy name. We will need it to initiate a new // IM session. speaker_ptr->mLegacyName = LLCacheName::cleanFullName(avatar_name.getLegacyName()); // [/Ansariel: Display name support] - + // [RLVa:KB] - Checked: 2009-07-10 (RLVa-1.0.0g) | Added: RLVa-1.0.0g // TODO-RLVa: this seems to get called per frame which is very likely an LL bug that will eventuall get fixed if (gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES)) @@ -500,7 +498,7 @@ void LLPanelActiveSpeakers::refreshSpeakers() } LLColor4 icon_color; - + if (speakerp->mStatus == LLSpeaker::STATUS_MUTED) { icon_cell->setValue(mute_icon_image); @@ -605,7 +603,7 @@ void LLPanelActiveSpeakers::refreshSpeakers() speaking_status_cell->setValue(speaking_order_sort_string); } } - + // we potentially modified the sort order by touching the list items mSpeakerList->setSorted(FALSE); @@ -860,25 +858,25 @@ void LLPanelActiveSpeakers::onModeratorMuteVoice(LLUICtrl* ctrl, void* user_data if ( gIMMgr ) { - //403 == you're not a mod - //should be disabled if you're not a moderator LLFloaterIMPanel* floaterp; floaterp = gIMMgr->findFloaterBySession(mSessionID); if ( floaterp ) { + //403 == you're not a mod + //should be disabled if you're not a moderator if ( 403 == status ) { floaterp->showSessionEventError( "mute", - "not_a_moderator"); + "not_a_mod_error"); } else { floaterp->showSessionEventError( "mute", - "generic"); + "generic_request_error"); } } } @@ -925,25 +923,25 @@ void LLPanelActiveSpeakers::onModeratorMuteText(LLUICtrl* ctrl, void* user_data) if ( gIMMgr ) { - //403 == you're not a mod - //should be disabled if you're not a moderator LLFloaterIMPanel* floaterp; floaterp = gIMMgr->findFloaterBySession(mSessionID); if ( floaterp ) { + //403 == you're not a mod + //should be disabled if you're not a moderator if ( 403 == status ) { floaterp->showSessionEventError( "mute", - "not_a_moderator"); + "not_a_mod_error"); } else { floaterp->showSessionEventError( "mute", - "generic"); + "generic_request_error"); } } } @@ -1194,6 +1192,9 @@ void LLSpeakerMgr::updateSpeakerList() const LLPointer LLSpeakerMgr::findSpeaker(const LLUUID& speaker_id) { + //In some conditions map causes crash if it is empty(Windows only), adding check (EK) + if (mSpeakers.size() == 0) + return NULL; speaker_map_t::iterator found_it = mSpeakers.find(speaker_id); if (found_it == mSpeakers.end()) { diff --git a/indra/newview/llfloaterbuyland.cpp b/indra/newview/llfloaterbuyland.cpp index 852b439fe..909111ee3 100644 --- a/indra/newview/llfloaterbuyland.cpp +++ b/indra/newview/llfloaterbuyland.cpp @@ -866,7 +866,7 @@ bool LLFloaterBuyLandUI::checkTransaction() return false; } - if (!mTransaction->process()) + if (!mTransaction->is_finished()) { return false; } diff --git a/indra/newview/llfloatergodtools.cpp b/indra/newview/llfloatergodtools.cpp index 37bcb873b..ba047ae41 100644 --- a/indra/newview/llfloatergodtools.cpp +++ b/indra/newview/llfloatergodtools.cpp @@ -74,6 +74,7 @@ #include "llsurface.h" #include "llviewercontrol.h" #include "lluictrlfactory.h" +#include "lltrans.h" #include "lltransfertargetfile.h" #include "lltransfersourcefile.h" @@ -172,6 +173,7 @@ LLFloaterGodTools::~LLFloaterGodTools() // children automatically deleted } + U32 LLFloaterGodTools::computeRegionFlags() const { U32 flags = gAgent.getRegion()->getRegionFlags(); @@ -245,14 +247,16 @@ void LLFloaterGodTools::showPanel(const std::string& panel_name) void LLFloaterGodTools::onTabChanged(LLUICtrl* ctrl, const LLSD& param) { LLPanel* panel = (LLPanel*)ctrl->getChildView(param.asString(),false,false); - if(panel) + if (panel) panel->setFocus(TRUE); } - // static void LLFloaterGodTools::processRegionInfo(LLMessageSystem* msg) { + llassert(msg); + if (!msg) return; + //const S32 SIM_NAME_BUF = 256; U32 region_flags; U8 sim_access; @@ -307,12 +311,12 @@ void LLFloaterGodTools::processRegionInfo(LLMessageSystem* msg) regionp->setBillableFactor(billable_factor); } + if (!sGodTools) return; + // push values to god tools, if available - if (sGodTools + if ( gAgent.isGodlike() && sGodTools->mPanelRegionTools - && sGodTools->mPanelObjectTools - && msg - && gAgent.isGodlike()) + && sGodTools->mPanelObjectTools) { LLPanelRegionTools* rtool = sGodTools->mPanelRegionTools; sGodTools->mCurrentHost = host; @@ -371,6 +375,8 @@ void LLFloaterGodTools::sendRegionInfoRequest() void LLFloaterGodTools::sendGodUpdateRegionInfo() { + if (!sGodTools) return; + LLViewerRegion *regionp = gAgent.getRegion(); if (gAgent.isGodlike() && sGodTools->mPanelRegionTools @@ -777,9 +783,7 @@ void LLPanelRegionTools::setPricePerMeter(S32 price) // static void LLPanelRegionTools::onChangeAnything(LLUICtrl* ctrl, void* userdata) { - if (sGodTools - && userdata - && gAgent.isGodlike()) + if (sGodTools && userdata && gAgent.isGodlike()) { LLPanelRegionTools* region_tools = (LLPanelRegionTools*) userdata; region_tools->childEnable("Apply"); @@ -803,9 +807,7 @@ void LLPanelRegionTools::onChangePrelude(LLUICtrl* ctrl, void* data) // static void LLPanelRegionTools::onChangeSimName(LLLineEditor* caller, void* userdata ) { - if (sGodTools - && userdata - && gAgent.isGodlike()) + if (sGodTools && userdata && gAgent.isGodlike()) { LLPanelRegionTools* region_tools = (LLPanelRegionTools*) userdata; region_tools->childEnable("Apply"); @@ -815,10 +817,9 @@ void LLPanelRegionTools::onChangeSimName(LLLineEditor* caller, void* userdata ) //static void LLPanelRegionTools::onRefresh(void* userdata) { + if(!sGodTools) return; LLViewerRegion *region = gAgent.getRegion(); - if (region - && sGodTools - && gAgent.isGodlike()) + if (region && gAgent.isGodlike()) { sGodTools->sendRegionInfoRequest(); } @@ -827,11 +828,9 @@ void LLPanelRegionTools::onRefresh(void* userdata) // static void LLPanelRegionTools::onApplyChanges(void* userdata) { + if(!sGodTools) return; LLViewerRegion *region = gAgent.getRegion(); - if (region - && sGodTools - && userdata - && gAgent.isGodlike()) + if (region && userdata && gAgent.isGodlike()) { LLPanelRegionTools* region_tools = (LLPanelRegionTools*) userdata; @@ -840,19 +839,19 @@ void LLPanelRegionTools::onApplyChanges(void* userdata) } } -// static +// static void LLPanelRegionTools::onBakeTerrain(void *userdata) { LLPanelRequestTools::sendRequest("terrain", "bake", gAgent.getRegionHost()); } -// static +// static void LLPanelRegionTools::onRevertTerrain(void *userdata) { LLPanelRequestTools::sendRequest("terrain", "revert", gAgent.getRegionHost()); } -// static +// static void LLPanelRegionTools::onSwapTerrain(void *userdata) { LLPanelRequestTools::sendRequest("terrain", "swap", gAgent.getRegionHost()); @@ -953,7 +952,6 @@ bool LLPanelGridTools::finishKick(const LLSD& notification, const LLSD& response { S32 option = LLNotification::getSelectedOption(notification, response); - if (option == 0) { LLMessageSystem* msg = gMessageSystem; @@ -1027,7 +1025,8 @@ bool LLPanelGridTools::flushMapVisibilityCachesConfirm(const LLSD& notification, // Default constructor LLPanelObjectTools::LLPanelObjectTools(const std::string& title) -: LLPanel(title), mTargetAvatar() + : LLPanel(title), + mTargetAvatar() { } @@ -1063,7 +1062,7 @@ void LLPanelObjectTools::setTargetAvatar(const LLUUID &target_id) mTargetAvatar = target_id; if (target_id.isNull()) { - childSetValue("target_avatar_name", "(no target)"); + childSetValue("target_avatar_name", getString("no_target")); } } @@ -1146,8 +1145,9 @@ void LLPanelObjectTools::enableAllWidgets() // static void LLPanelObjectTools::onGetTopColliders(void* userdata) { - if (sGodTools - && gAgent.isGodlike()) + if(!sGodTools) return; + + if (gAgent.isGodlike()) { LLFloaterTopObjects::show(); LLFloaterTopObjects::setMode(STAT_REPORT_TOP_COLLIDERS); @@ -1158,8 +1158,9 @@ void LLPanelObjectTools::onGetTopColliders(void* userdata) // static void LLPanelObjectTools::onGetTopScripts(void* userdata) { - if (sGodTools - && gAgent.isGodlike()) + if(!sGodTools) return; + + if (gAgent.isGodlike()) { LLFloaterTopObjects::show(); LLFloaterTopObjects::setMode(STAT_REPORT_TOP_SCRIPTS); @@ -1170,8 +1171,7 @@ void LLPanelObjectTools::onGetTopScripts(void* userdata) // static void LLPanelObjectTools::onGetScriptDigest(void* userdata) { - if (sGodTools - && gAgent.isGodlike()) + if (sGodTools && gAgent.isGodlike()) { // get the list of scripts and number of occurences of each // (useful for finding self-replicating objects) @@ -1281,7 +1281,10 @@ void LLPanelObjectTools::onClickSetBySelection(void* data) LLSelectMgr::getInstance()->selectGetOwner(owner_id, owner_name); panelp->mTargetAvatar = owner_id; - std::string name = "Object " + node->mName + " owned by " + owner_name; + LLStringUtil::format_map_t args; + args["[OBJECT]"] = node->mName; + args["[OWNER]"] = owner_name; + std::string name = LLTrans::getString("GodToolsObjectOwnedBy", args); panelp->childSetValue("target_avatar_name", name); } @@ -1295,11 +1298,10 @@ void LLPanelObjectTools::callbackAvatarID(const std::vector& names, object_tools->refresh(); } - // static void LLPanelObjectTools::onChangeAnything(LLUICtrl* ctrl, void* userdata) { - if (sGodTools + if (sGodTools && userdata && gAgent.isGodlike()) { @@ -1311,15 +1313,12 @@ void LLPanelObjectTools::onChangeAnything(LLUICtrl* ctrl, void* userdata) // static void LLPanelObjectTools::onApplyChanges(void* userdata) { + if(!sGodTools) return; LLViewerRegion *region = gAgent.getRegion(); - if (region - && sGodTools - && userdata - && gAgent.isGodlike()) + if (region && userdata && gAgent.isGodlike()) { LLPanelObjectTools* object_tools = (LLPanelObjectTools*) userdata; // TODO -- implement this - object_tools->childDisable("Apply"); sGodTools->sendGodUpdateRegionInfo(); } diff --git a/indra/newview/llfloaterland.cpp b/indra/newview/llfloaterland.cpp index 20f60e920..ac3e2f7cc 100644 --- a/indra/newview/llfloaterland.cpp +++ b/indra/newview/llfloaterland.cpp @@ -2260,8 +2260,8 @@ void LLPanelLandOptions::onCommitAny(LLUICtrl *ctrl, void *userdata) BOOL allow_damage = !self->mCheckSafe->get(); BOOL allow_fly = self->mCheckFly->get(); BOOL allow_landmark = self->mCheckLandmark->get(); - BOOL allow_group_scripts = self->mCheckGroupScripts->get() || self->mCheckOtherScripts->get(); BOOL allow_other_scripts = self->mCheckOtherScripts->get(); + BOOL allow_group_scripts = self->mCheckGroupScripts->get() || allow_other_scripts; BOOL allow_publish = FALSE; BOOL mature_publish = self->mMatureCtrl->get(); BOOL push_restriction = self->mPushRestrictionCtrl->get(); @@ -2424,7 +2424,7 @@ void LLPanelLandAccess::refresh() mListAccess->deleteAllItems(); if (mListBanned) mListBanned->deleteAllItems(); - + LLParcel *parcel = mParcel->getParcel(); // Display options diff --git a/indra/newview/llfloaternamedesc.cpp b/indra/newview/llfloaternamedesc.cpp index 1d930c337..9ac324468 100644 --- a/indra/newview/llfloaternamedesc.cpp +++ b/indra/newview/llfloaternamedesc.cpp @@ -157,7 +157,10 @@ BOOL LLFloaterNameDesc::postBuild() // OK button childSetLabelArg("ok_btn", "[UPLOADFEE]", gHippoGridManager->getConnectedGrid()->getUploadFee()); - //childSetAction("ok_btn", onBtnOK, this); + if (exten == "wav") + { + childSetAction("ok_btn", onBtnOK, this); + } setDefaultBtn("ok_btn"); return TRUE; diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp index 794e774fe..8395f842c 100644 --- a/indra/newview/llfloaterregioninfo.cpp +++ b/indra/newview/llfloaterregioninfo.cpp @@ -165,6 +165,7 @@ bool estate_dispatch_initialized = false; //S32 LLFloaterRegionInfo::sRequestSerial = 0; LLUUID LLFloaterRegionInfo::sRequestInvoice; + LLFloaterRegionInfo::LLFloaterRegionInfo(const LLSD& seed) { LLUICtrlFactory::getInstance()->buildFloater(this, "floater_region_info.xml", NULL, FALSE); @@ -176,26 +177,6 @@ BOOL LLFloaterRegionInfo::postBuild() // contruct the panels LLPanelRegionInfo* panel; - panel = new LLPanelRegionGeneralInfo; - mInfoPanels.push_back(panel); - LLUICtrlFactory::getInstance()->buildPanel(panel, "panel_region_general.xml"); - mTab->addTabPanel(panel, panel->getLabel(), TRUE); - - panel = new LLPanelRegionDebugInfo; - mInfoPanels.push_back(panel); - LLUICtrlFactory::getInstance()->buildPanel(panel, "panel_region_debug.xml"); - mTab->addTabPanel(panel, panel->getLabel(), FALSE); - - panel = new LLPanelRegionTextureInfo; - mInfoPanels.push_back(panel); - LLUICtrlFactory::getInstance()->buildPanel(panel, "panel_region_texture.xml"); - mTab->addTabPanel(panel, panel->getLabel(), FALSE); - - panel = new LLPanelRegionTerrainInfo; - mInfoPanels.push_back(panel); - LLUICtrlFactory::getInstance()->buildPanel(panel, "panel_region_terrain.xml"); - mTab->addTabPanel(panel, panel->getLabel(), FALSE); - panel = new LLPanelEstateInfo; mInfoPanels.push_back(panel); LLUICtrlFactory::getInstance()->buildPanel(panel, "panel_region_estate.xml"); @@ -206,6 +187,26 @@ BOOL LLFloaterRegionInfo::postBuild() LLUICtrlFactory::getInstance()->buildPanel(panel, "panel_region_covenant.xml"); mTab->addTabPanel(panel, panel->getLabel(), FALSE); + panel = new LLPanelRegionGeneralInfo; + mInfoPanels.push_back(panel); + LLUICtrlFactory::getInstance()->buildPanel(panel, "panel_region_general.xml"); + mTab->addTabPanel(panel, panel->getLabel(), TRUE); + + panel = new LLPanelRegionTerrainInfo; + mInfoPanels.push_back(panel); + LLUICtrlFactory::getInstance()->buildPanel(panel, "panel_region_terrain.xml"); + mTab->addTabPanel(panel, panel->getLabel(), FALSE); + + panel = new LLPanelRegionTextureInfo; + mInfoPanels.push_back(panel); + LLUICtrlFactory::getInstance()->buildPanel(panel, "panel_region_texture.xml"); + mTab->addTabPanel(panel, panel->getLabel(), FALSE); + + panel = new LLPanelRegionDebugInfo; + mInfoPanels.push_back(panel); + LLUICtrlFactory::getInstance()->buildPanel(panel, "panel_region_debug.xml"); + mTab->addTabPanel(panel, panel->getLabel(), FALSE); + gMessageSystem->setHandlerFunc( "EstateOwnerMessage", &processEstateOwnerRequest); @@ -331,6 +332,7 @@ void LLFloaterRegionInfo::processRegionInfo(LLMessageSystem* msg) msg->getSize("RegionInfo2", "ProductName") > 0) { msg->getString("RegionInfo2", "ProductName", sim_type); + LLTrans::findString(sim_type, sim_type); // try localizing sim product name } // GENERAL PANEL @@ -760,7 +762,7 @@ BOOL LLPanelRegionGeneralInfo::sendUpdate() body["allow_parcel_changes"] = childGetValue("allow_parcel_changes_check"); body["block_parcel_search"] = childGetValue("block_parcel_search_check"); - LLHTTPClient::post(url, body, new LLHTTPClient::Responder()); + LLHTTPClient::post(url, body, new LLHTTPClient::ResponderIgnore); } else { @@ -1234,6 +1236,7 @@ bool LLPanelRegionTerrainInfo::refreshFromRegion(LLViewerRegion* region) return LLPanelRegionInfo::refreshFromRegion(region); } + // virtual BOOL LLPanelRegionTerrainInfo::sendUpdate() { @@ -1390,6 +1393,7 @@ bool LLPanelRegionTerrainInfo::callbackBakeTerrain(const LLSD& notification, con strings.push_back("bake"); LLUUID invoice(LLFloaterRegionInfo::getLastInvoice()); sendEstateOwnerMessage(gMessageSystem, "terrain", invoice, strings); + return false; } @@ -1669,26 +1673,29 @@ bool LLPanelEstateInfo::kickUserConfirm(const LLSD& notification, const LLSD& re std::string all_estates_text() { LLPanelEstateInfo* panel = LLFloaterRegionInfo::getPanelEstate(); - if (!panel) return "(error)"; + if (!panel) return "(" + LLTrans::getString("RegionInfoError") + ")"; + LLStringUtil::format_map_t args; std::string owner = panel->getOwnerName(); LLViewerRegion* region = gAgent.getRegion(); if (gAgent.isGodlike()) { - return llformat("all estates\nowned by %s", owner.c_str()); + args["[OWNER]"] = owner.c_str(); + return LLTrans::getString("RegionInfoAllEstatesOwnedBy", args); } else if (region && region->getOwner() == gAgent.getID()) { - return "all estates you own"; + return LLTrans::getString("RegionInfoAllEstatesYouOwn"); } else if (region && region->isEstateManager()) { - return llformat("all estates that\nyou manage for %s", owner.c_str()); + args["[OWNER]"] = owner.c_str(); + return LLTrans::getString("RegionInfoAllEstatesYouManage", args); } else { - return "(error)"; + return "(" + LLTrans::getString("RegionInfoError") + ")"; } } @@ -2254,6 +2261,7 @@ bool LLPanelEstateInfo::callbackChangeLindenEstate(const LLSD& notification, con LLFloaterRegionInfo::nextInvoice(); commitEstateInfoDataserver(); } + // we don't want to do this because we'll get it automatically from the sim // after the spaceserver processes it // else @@ -2723,7 +2731,6 @@ bool LLPanelEstateCovenant::refreshFromRegion(LLViewerRegion* region) region_landtype->setText(region->getLocalizedSimProductName()); } - // let the parent class handle the general data collection. bool rv = LLPanelRegionInfo::refreshFromRegion(region); LLMessageSystem *msg = gMessageSystem; @@ -2863,7 +2870,7 @@ void LLPanelEstateCovenant::loadInvItem(LLInventoryItem *itemp) else { mAssetStatus = ASSET_LOADED; - setCovenantTextEditor("There is no Covenant provided for this Estate."); + setCovenantTextEditor(LLTrans::getString("RegionNoCovenant")); sendChangeCovenantID(LLUUID::null); } } @@ -3179,9 +3186,10 @@ bool LLDispatchSetEstateAccess::operator()( totalAllowedAgents += allowed_agent_name_list->getItemCount(); } - std::string msg = llformat("Allowed residents: (%d, max %d)", - totalAllowedAgents, - ESTATE_MAX_ACCESS_IDS); + LLStringUtil::format_map_t args; + args["[ALLOWEDAGENTS]"] = llformat ("%d", totalAllowedAgents); + args["[MAXACCESS]"] = llformat ("%d", ESTATE_MAX_ACCESS_IDS); + std::string msg = LLTrans::getString("RegionInfoAllowedResidents", args); panel->childSetValue("allow_resident_label", LLSD(msg)); if (allowed_agent_name_list) @@ -3203,9 +3211,10 @@ bool LLDispatchSetEstateAccess::operator()( LLNameListCtrl* allowed_group_name_list; allowed_group_name_list = panel->getChild("allowed_group_name_list"); - std::string msg = llformat("Allowed groups: (%d, max %d)", - num_allowed_groups, - (S32) ESTATE_MAX_GROUP_IDS); + LLStringUtil::format_map_t args; + args["[ALLOWEDGROUPS]"] = llformat ("%d", num_allowed_groups); + args["[MAXACCESS]"] = llformat ("%d", ESTATE_MAX_GROUP_IDS); + std::string msg = LLTrans::getString("RegionInfoAllowedGroups", args); panel->childSetValue("allow_group_label", LLSD(msg)); if (allowed_group_name_list) diff --git a/indra/newview/llfloaterurlentry.cpp b/indra/newview/llfloaterurlentry.cpp index 35dc9ddda..c56e49206 100644 --- a/indra/newview/llfloaterurlentry.cpp +++ b/indra/newview/llfloaterurlentry.cpp @@ -43,6 +43,7 @@ #include "lluictrlfactory.h" #include "llwindow.h" #include "llviewerwindow.h" +#include "llhttpclient.h" static LLFloaterURLEntry* sInstance = NULL; diff --git a/indra/newview/llfloaterworldmap.cpp b/indra/newview/llfloaterworldmap.cpp index 8cd9ea972..d541a93e5 100644 --- a/indra/newview/llfloaterworldmap.cpp +++ b/indra/newview/llfloaterworldmap.cpp @@ -64,6 +64,7 @@ #include "llscrolllistctrl.h" #include "lltextbox.h" #include "lltracker.h" +#include "lltrans.h" #include "llurldispatcher.h" #include "llviewermenu.h" #include "llviewerregion.h" @@ -1625,7 +1626,8 @@ void LLFloaterWorldMap::updateSims(bool found_null_sim) } else { - list->addCommentText(std::string("None found.")); + // if we found nothing, say "none" + list->addCommentText(LLTrans::getString("worldmap_results_none_found")); list->operateOnAll(LLCtrlListInterface::OP_DESELECT); } } diff --git a/indra/newview/llimpanel.cpp b/indra/newview/llimpanel.cpp index 27774273a..4ba053a40 100644 --- a/indra/newview/llimpanel.cpp +++ b/indra/newview/llimpanel.cpp @@ -50,19 +50,20 @@ #include "llchat.h" #include "llconsole.h" #include "llfloater.h" -#include "llfloatergroupinfo.h" -#include "llimview.h" -#include "llfloaterinventory.h" -#include "llinventory.h" -#include "llinventoryfunctions.h" #include "llfloateractivespeakers.h" #include "llfloateravatarinfo.h" #include "llfloaterchat.h" +#include "llfloatergroupinfo.h" +#include "llimview.h" +#include "llinventory.h" +#include "llinventoryfunctions.h" +#include "llfloaterinventory.h" +#include "llcheckboxctrl.h" #include "llkeyboard.h" #include "lllineeditor.h" -#include "llcheckboxctrl.h" #include "llnotify.h" #include "llresmgr.h" +#include "lltrans.h" #include "lltabcontainer.h" #include "llviewertexteditor.h" #include "llviewermessage.h" @@ -2544,7 +2545,7 @@ void LLFloaterIMPanel::chatFromLogFile(LLLogChat::ELogLineType type, std::string // add log end message if (gSavedPerAccountSettings.getBOOL("LogInstantMessages")) { - message = LLFloaterChat::getInstance()->getString("IM_logging_string"); + message = LLFloaterChat::getInstance()->getString("IM_end_log_string"); } break; case LLLogChat::LOG_LINE: @@ -2562,19 +2563,8 @@ void LLFloaterIMPanel::chatFromLogFile(LLLogChat::ELogLineType type, std::string void LLFloaterIMPanel::showSessionStartError( const std::string& error_string) { - //the error strings etc. should be really be static and local - //to this file instead of in the LLFloaterIM - //but they were in llimview.cpp first and unfortunately - //some translations into non English languages already occurred - //thus making it a tad harder to change over to a - //"correct" solution. The best solution - //would be to store all of the misc. strings into - //their own XML file which would be read in by any LLIMPanel - //post build function instead of repeating the same info - //in the group, adhoc and normal IM xml files. LLSD args; - args["REASON"] = - LLFloaterIM::sErrorStringsMap[error_string]; + args["REASON"] = LLTrans::getString(error_string); args["RECIPIENT"] = getTitle(); LLSD payload; @@ -2592,13 +2582,14 @@ void LLFloaterIMPanel::showSessionEventError( const std::string& error_string) { LLSD args; - std::string recipient = getTitle(); - std::string reason = LLFloaterIM::sErrorStringsMap[error_string]; - boost::replace_all(reason, "[RECIPIENT]", recipient); - std::string event = LLFloaterIM::sEventStringsMap[event_string]; - boost::replace_all(event, "[RECIPIENT]", recipient); - args["REASON"] = reason; - args["EVENT"] = event; + LLStringUtil::format_map_t event_args; + + event_args["RECIPIENT"] = getTitle(); + + args["REASON"] = + LLTrans::getString(error_string); + args["EVENT"] = + LLTrans::getString(event_string, event_args); LLNotifications::instance().add( "ChatterBoxSessionEventError", @@ -2611,7 +2602,7 @@ void LLFloaterIMPanel::showSessionForceClose( LLSD args; args["NAME"] = getTitle(); - args["REASON"] = LLFloaterIM::sForceCloseSessionMap[reason_string]; + args["REASON"] = LLTrans::getString(reason_string); LLSD payload; payload["session_id"] = mSessionUUID; diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index ba794068a..9783badf7 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -41,6 +41,7 @@ #include "llhttpclient.h" #include "llsdutil_math.h" #include "llstring.h" +#include "lltrans.h" #include "lluictrlfactory.h" #include "llagent.h" @@ -81,19 +82,6 @@ // LLIMMgr* gIMMgr = NULL; -// -// Statics -// -// *FIXME: make these all either UIStrings or Strings -static std::string sOnlyUserMessage; -static LLUIString sOfflineMessage; -static std::string sMutedMessage; -static LLUIString sInviteMessage; - -std::map LLFloaterIM::sEventStringsMap; -std::map LLFloaterIM::sErrorStringsMap; -std::map LLFloaterIM::sForceCloseSessionMap; - // // Helper Functions // @@ -172,18 +160,16 @@ public: gIMMgr->clearPendingAgentListUpdates(mSessionID); gIMMgr->clearPendingInvitation(mSessionID); - LLFloaterIMPanel* floaterp = - gIMMgr->findFloaterBySession(mSessionID); + LLFloaterIMPanel* floaterp = gIMMgr->findFloaterBySession(mSessionID); if ( floaterp ) { if ( 404 == statusNum ) { std::string error_string; - error_string = "does not exist"; + error_string = "session_does_not_exist_error"; - floaterp->showSessionStartError( - error_string); + floaterp->showSessionStartError(error_string); } } } @@ -250,86 +236,6 @@ LLFloaterIM::LLFloaterIM() LLUICtrlFactory::getInstance()->buildFloater(this, "floater_im.xml"); } -BOOL LLFloaterIM::postBuild() -{ - // IM session initiation warnings - sOnlyUserMessage = getString("only_user_message"); - sOfflineMessage = getString("offline_message"); - sMutedMessage = getString("muted_message"); - - sInviteMessage = getString("invite_message"); - - if ( sErrorStringsMap.find("generic") == sErrorStringsMap.end() ) - { - sErrorStringsMap["generic"] = - getString("generic_request_error"); - } - - if ( sErrorStringsMap.find("unverified") == - sErrorStringsMap.end() ) - { - sErrorStringsMap["unverified"] = - getString("insufficient_perms_error"); - } - - if ( sErrorStringsMap.end() == - sErrorStringsMap.find("no_ability") ) - { - sErrorStringsMap["no_ability"] = - getString("no_ability_error"); - } - - if ( sErrorStringsMap.end() == - sErrorStringsMap.find("muted") ) - { - sErrorStringsMap["muted"] = - getString("muted_error"); - } - - if ( sErrorStringsMap.end() == - sErrorStringsMap.find("not_a_moderator") ) - { - sErrorStringsMap["not_a_moderator"] = - getString("not_a_mod_error"); - } - - if ( sErrorStringsMap.end() == - sErrorStringsMap.find("does not exist") ) - { - sErrorStringsMap["does not exist"] = - getString("session_does_not_exist_error"); - } - - if ( sEventStringsMap.end() == sEventStringsMap.find("add") ) - { - sEventStringsMap["add"] = - getString("add_session_event"); - } - - if ( sEventStringsMap.end() == sEventStringsMap.find("message") ) - { - sEventStringsMap["message"] = - getString("message_session_event"); - } - - - if ( sForceCloseSessionMap.end() == - sForceCloseSessionMap.find("removed") ) - { - sForceCloseSessionMap["removed"] = - getString("removed_from_group"); - } - - if ( sForceCloseSessionMap.end() == - sForceCloseSessionMap.find("no ability") ) - { - sForceCloseSessionMap["no ability"] = - getString("close_on_no_ability"); - } - - return TRUE; -} - //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Class LLIMViewFriendObserver // @@ -645,10 +551,10 @@ void LLIMMgr::addMessage( { // *TODO:translate (low priority, god ability) std::ostringstream bonus_info; - bonus_info << "*** parent estate: " + bonus_info << LLTrans::getString("***")+ " "+ LLTrans::getString("IMParentEstate") + LLTrans::getString(":") + " " << parent_estate_id - << ((parent_estate_id == 1) ? ", mainland" : "") - << ((parent_estate_id == 5) ? ", teen" : ""); + << ((parent_estate_id == 1) ? LLTrans::getString(",") + LLTrans::getString("IMMainland") : "") + << ((parent_estate_id == 5) ? LLTrans::getString(",") + LLTrans::getString ("IMTeen") : ""); // once we have web-services (or something) which returns // information about a region id, we can print this out @@ -717,23 +623,20 @@ void LLIMMgr::addSystemMessage(const LLUUID& session_id, const std::string& mess // null session id means near me (chat history) if (session_id.isNull()) { - LLFloaterChat* floaterp = LLFloaterChat::getInstance(); - - message = floaterp->getString(message_name); + message = LLTrans::getString(message_name); message.setArgs(args); LLChat chat(message); chat.mSourceType = CHAT_SOURCE_SYSTEM; + LLFloaterChat::getInstance()->addChatHistory(chat); } else // going to IM session { - LLFloaterIMPanel* floaterp = findFloaterBySession(session_id); - if (floaterp) + message = LLTrans::getString(message_name + "-im"); + message.setArgs(args); + if (hasSession(session_id)) { - message = floaterp->getString(message_name); - message.setArgs(args); - gIMMgr->addMessage(session_id, LLUUID::null, SYSTEM_FROM, message.getString()); } } @@ -764,7 +667,7 @@ int LLIMMgr::getIMUnreadCount() } // This method returns TRUE if the local viewer has a session -// currently open keyed to the uuid. +// currently open keyed to the uuid. BOOL LLIMMgr::isIMSessionOpen(const LLUUID& uuid) { LLFloaterIMPanel* floater = findFloaterBySession(uuid); @@ -780,12 +683,14 @@ LLUUID LLIMMgr::addP2PSession(const std::string& name, LLUUID session_id = addSession(name, IM_NOTHING_SPECIAL, other_participant_id); LLFloaterIMPanel* floater = findFloaterBySession(session_id); - if(floater) + if (floater) { - LLVoiceChannelP2P* voice_channelp = (LLVoiceChannelP2P*)floater->getVoiceChannel(); - voice_channelp->setSessionHandle(voice_session_handle, caller_uri); + LLVoiceChannelP2P* voice_channel = dynamic_cast(floater->getVoiceChannel()); + if (voice_channel) + { + voice_channel->setSessionHandle(voice_session_handle, caller_uri); + } } - return session_id; } @@ -951,7 +856,7 @@ void LLIMMgr::inviteToSession( payload["session_handle"] = session_handle; payload["session_uri"] = session_uri; payload["notify_box_type"] = notify_box_type; - + LLVoiceChannel* channelp = LLVoiceChannel::getChannelByID(session_id); if (channelp && channelp->callStarted()) { @@ -962,17 +867,13 @@ void LLIMMgr::inviteToSession( if (type == IM_SESSION_P2P_INVITE || ad_hoc_invite) { - // is the inviter a friend? - if (LLAvatarTracker::instance().getBuddyInfo(caller_id) == NULL) + if ( // we're rejecting non-friend voice calls and this isn't a friend + (gSavedSettings.getBOOL("VoiceCallsFriendsOnly") && (LLAvatarTracker::instance().getBuddyInfo(caller_id) == NULL)) + ) { - // if not, and we are ignoring voice invites from non-friends - // then silently decline - if (gSavedSettings.getBOOL("VoiceCallsFriendsOnly")) - { - // invite not from a friend, so decline - LLNotifications::instance().forceResponse(LLNotification::Params("VoiceInviteP2P").payload(payload), 1); - return; - } + // silently decline the call + LLNotifications::instance().forceResponse(LLNotification::Params("VoiceInviteP2P").payload(payload), 1); + return; } } @@ -980,7 +881,8 @@ void LLIMMgr::inviteToSession( { if (caller_name.empty()) { - gCacheName->get(caller_id, true, boost::bind(&LLIMMgr::onInviteNameLookup,_1,_2,_3,payload)); + gCacheName->get(caller_id, true, // voice + boost::bind(&LLIMMgr::onInviteNameLookup, _1, _2, _3, payload)); } else { @@ -998,7 +900,7 @@ void LLIMMgr::inviteToSession( } } -//static +//static void LLIMMgr::onInviteNameLookup(const LLUUID& id, const std::string& full_name, bool is_group, LLSD payload) { payload["caller_name"] = full_name; @@ -1234,7 +1136,8 @@ void LLIMMgr::noteOfflineUsers( S32 count = ids.count(); if(count == 0) { - floater->addHistoryLine(sOnlyUserMessage, gSavedSettings.getColor4("SystemChatColor")); + const std::string& only_user = LLTrans::getString("only_user_message"); + floater->addHistoryLine(only_user, gSavedSettings.getColor4("SystemChatColor")); } else { @@ -1244,10 +1147,11 @@ void LLIMMgr::noteOfflineUsers( { info = at.getBuddyInfo(ids.get(i)); std::string full_name; - if(info && !info->isOnline() - && gCacheName->getFullName(ids.get(i), full_name)) + if (info + && !info->isOnline() + && gCacheName->getFullName(ids.get(i), full_name)) { - LLUIString offline = sOfflineMessage; + LLUIString offline = LLTrans::getString("offline_message"); offline.setArg("[NAME]", full_name); floater->addHistoryLine(offline, gSavedSettings.getColor4("SystemChatColor")); } @@ -1272,7 +1176,8 @@ void LLIMMgr::noteMutedUsers(LLFloaterIMPanel* floater, { if( ml->isMuted(ids.get(i)) ) { - LLUIString muted = sMutedMessage; + LLUIString muted = LLTrans::getString("muted_message"); + floater->addHistoryLine(muted); break; } @@ -1474,10 +1379,8 @@ public: } else { - //throw an error dialog and close the temp session's - //floater - LLFloaterIMPanel* floater = - gIMMgr->findFloaterBySession(temp_session_id); + //throw an error dialog and close the temp session's floater + LLFloaterIMPanel* floater = gIMMgr->findFloaterBySession(temp_session_id); if ( floater ) { diff --git a/indra/newview/llimview.h b/indra/newview/llimview.h index be8db650a..fc2dd33b7 100644 --- a/indra/newview/llimview.h +++ b/indra/newview/llimview.h @@ -221,7 +221,7 @@ private: std::list mIgnoreGroupList; public: - + S32 getIgnoreGroupListCount() { return mIgnoreGroupList.size(); } }; @@ -230,11 +230,6 @@ class LLFloaterIM : public LLMultiFloater { public: LLFloaterIM(); - /*virtual*/ BOOL postBuild(); - - static std::map sEventStringsMap; - static std::map sErrorStringsMap; - static std::map sForceCloseSessionMap; }; // Globals diff --git a/indra/newview/lllogchat.cpp b/indra/newview/lllogchat.cpp index 574d70f4b..e80032239 100644 --- a/indra/newview/lllogchat.cpp +++ b/indra/newview/lllogchat.cpp @@ -99,7 +99,7 @@ std::string LLLogChat::timestamp(bool withdate) //static -void LLLogChat::saveHistory(std::string filename, std::string line) +void LLLogChat::saveHistory(std::string const& filename, std::string line) { if(!filename.size()) { @@ -120,7 +120,7 @@ void LLLogChat::saveHistory(std::string filename, std::string line) } } -void LLLogChat::loadHistory(std::string filename , void (*callback)(ELogLineType,std::string,void*), void* userdata) +void LLLogChat::loadHistory(std::string const& filename , void (*callback)(ELogLineType,std::string,void*), void* userdata) { if(!filename.size()) { diff --git a/indra/newview/lllogchat.h b/indra/newview/lllogchat.h index ad903b66f..84f6760ab 100644 --- a/indra/newview/lllogchat.h +++ b/indra/newview/lllogchat.h @@ -34,6 +34,8 @@ #ifndef LL_LLLOGCHAT_H #define LL_LLLOGCHAT_H +#include + class LLLogChat { public: @@ -44,9 +46,9 @@ public: LOG_END }; static std::string timestamp(bool withdate = false); - static std::string makeLogFileName(std::string(filename)); - static void saveHistory(std::string filename, std::string line); - static void loadHistory(std::string filename, + static std::string makeLogFileName(std::string filename); + static void saveHistory(std::string const& filename, std::string line); + static void loadHistory(std::string const& filename, void (*callback)(ELogLineType,std::string,void*), void* userdata); private: diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index caf2ce7da..33439f10e 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -35,7 +35,7 @@ #include "llappviewer.h" #include "llbufferstream.h" #include "llcallbacklist.h" -#include "llcurl.h" +#include "llcurlrequest.h" #include "lldatapacker.h" #include "llfasttimer.h" #if MESH_IMPORT @@ -465,7 +465,6 @@ public: LLMeshRepoThread::LLMeshRepoThread() : LLThread("mesh repo") { - mWaiting = false; mMutex = new LLMutex(); mHeaderMutex = new LLMutex(); mSignal = new LLCondition(); @@ -483,7 +482,7 @@ LLMeshRepoThread::~LLMeshRepoThread() void LLMeshRepoThread::run() { - mCurlRequest = new LLCurlRequest(); + mCurlRequest = new AICurlInterface::Request; #if MESH_IMPORT LLCDResult res = LLConvexDecomposition::initThread(); if (res != LLCD_OK) @@ -492,13 +491,10 @@ void LLMeshRepoThread::run() } #endif //MESH_IMPORT + mSignal->lock(); while (!LLApp::isQuitting()) { - mWaiting = true; - mSignal->wait(); - mWaiting = false; - - if (!LLApp::isQuitting()) + // Left braces in order not to change the indentation. { static U32 count = 0; @@ -511,38 +507,53 @@ void LLMeshRepoThread::run() } // NOTE: throttling intentionally favors LOD requests over header requests - + while (!mLODReqQ.empty() && count < MAX_MESH_REQUESTS_PER_SECOND && sActiveLODRequests < (S32)sMaxConcurrentRequests) { - if (mMutex) { mMutex->lock(); LODRequest req = mLODReqQ.front(); mLODReqQ.pop(); LLMeshRepository::sLODProcessing--; mMutex->unlock(); - if (!fetchMeshLOD(req.mMeshParams, req.mLOD, count))//failed, resubmit + try { + fetchMeshLOD(req.mMeshParams, req.mLOD, count); + } + catch(AICurlNoEasyHandle const& error) + { + llwarns << "fetchMeshLOD() failed: " << error.what() << llendl; mMutex->lock(); - mLODReqQ.push(req) ; + LLMeshRepository::sLODProcessing++; + mLODReqQ.push(req); mMutex->unlock(); + break; } } } while (!mHeaderReqQ.empty() && count < MAX_MESH_REQUESTS_PER_SECOND && sActiveHeaderRequests < (S32)sMaxConcurrentRequests) { - if (mMutex) { mMutex->lock(); HeaderRequest req = mHeaderReqQ.front(); mHeaderReqQ.pop(); mMutex->unlock(); - if (!fetchMeshHeader(req.mMeshParams, count))//failed, resubmit + bool success = false; + try + { + success = fetchMeshHeader(req.mMeshParams, count); + } + catch(AICurlNoEasyHandle const& error) + { + llwarns << "fetchMeshHeader() failed: " << error.what() << llendl; + } + if (!success) { mMutex->lock(); mHeaderReqQ.push(req) ; mMutex->unlock(); + break; } } } @@ -552,7 +563,16 @@ void LLMeshRepoThread::run() for (std::set::iterator iter = mSkinRequests.begin(); iter != mSkinRequests.end(); ++iter) { LLUUID mesh_id = *iter; - if (!fetchMeshSkinInfo(mesh_id)) + bool success = false; + try + { + success = fetchMeshSkinInfo(mesh_id); + } + catch(AICurlNoEasyHandle const& error) + { + llwarns << "fetchMeshSkinInfo(" << mesh_id << ") failed: " << error.what() << llendl; + } + if (!success) { incomplete.insert(mesh_id); } @@ -565,7 +585,16 @@ void LLMeshRepoThread::run() for (std::set::iterator iter = mDecompositionRequests.begin(); iter != mDecompositionRequests.end(); ++iter) { LLUUID mesh_id = *iter; - if (!fetchMeshDecomposition(mesh_id)) + bool success = false; + try + { + success = fetchMeshDecomposition(mesh_id); + } + catch(AICurlNoEasyHandle const& error) + { + llwarns << "fetchMeshDecomposition(" << mesh_id << ") failed: " << error.what() << llendl; + } + if (!success) { incomplete.insert(mesh_id); } @@ -578,7 +607,16 @@ void LLMeshRepoThread::run() for (std::set::iterator iter = mPhysicsShapeRequests.begin(); iter != mPhysicsShapeRequests.end(); ++iter) { LLUUID mesh_id = *iter; - if (!fetchMeshPhysicsShape(mesh_id)) + bool success = false; + try + { + success = fetchMeshPhysicsShape(mesh_id); + } + catch(AICurlNoEasyHandle const& error) + { + llwarns << "fetchMeshPhysicsShape(" << mesh_id << ") failed: " << error.what() << llendl; + } + if (!success) { incomplete.insert(mesh_id); } @@ -586,14 +624,11 @@ void LLMeshRepoThread::run() mPhysicsShapeRequests = incomplete; } - mCurlRequest->process(); } + + mSignal->wait(); } - - if (mSignal->isLocked()) - { //make sure to let go of the mutex associated with the given signal before shutting down - mSignal->unlock(); - } + mSignal->unlock(); #if MESH_IMPORT res = LLConvexDecomposition::quitThread(); @@ -645,7 +680,7 @@ void LLMeshRepoThread::loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod) if (pending != mPendingLOD.end()) { //append this lod request to existing header request pending->second.push_back(lod); - llassert(pending->second.size() <= LLModel::NUM_LODS) + llassert(pending->second.size() <= LLModel::NUM_LODS); } else { //if no header request is pending, fetch header @@ -681,21 +716,15 @@ std::string LLMeshRepoThread::constructUrl(LLUUID mesh_id) bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id) { //protected by mMutex - - if (!mHeaderMutex) - { - return false; - } - mHeaderMutex->lock(); if (mMeshHeader.find(mesh_id) == mMeshHeader.end()) - { //we have no header info for this mesh, do nothing + { + // We have no header info for this mesh, try again later. mHeaderMutex->unlock(); return false; } - bool ret = true ; U32 header_size = mMeshHeaderSize[mesh_id]; if (header_size > 0) @@ -743,12 +772,10 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id) std::string http_url = constructUrl(mesh_id); if (!http_url.empty()) { - ret = mCurlRequest->getByteRange(http_url, headers, offset, size, - new LLMeshSkinInfoResponder(mesh_id, offset, size)); - if(ret) - { - LLMeshRepository::sHTTPRequestCount++; - } + // This might throw AICurlNoEasyHandle. + mCurlRequest->getByteRange(http_url, headers, offset, size, + new LLMeshSkinInfoResponder(mesh_id, offset, size)); + LLMeshRepository::sHTTPRequestCount++; } } } @@ -758,26 +785,22 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id) } //early out was not hit, effectively fetched - return ret; + return true; } +//return false if failed to get header bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id) { //protected by mMutex - if (!mHeaderMutex) - { - return false; - } - mHeaderMutex->lock(); if (mMeshHeader.find(mesh_id) == mMeshHeader.end()) - { //we have no header info for this mesh, do nothing + { + // We have no header info for this mesh, try again later. mHeaderMutex->unlock(); return false; } U32 header_size = mMeshHeaderSize[mesh_id]; - bool ret = true ; if (header_size > 0) { @@ -824,12 +847,10 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id) std::string http_url = constructUrl(mesh_id); if (!http_url.empty()) { - ret = mCurlRequest->getByteRange(http_url, headers, offset, size, - new LLMeshDecompositionResponder(mesh_id, offset, size)); - if(ret) - { - LLMeshRepository::sHTTPRequestCount++; - } + // This might throw AICurlNoEasyHandle. + mCurlRequest->getByteRange(http_url, headers, offset, size, + new LLMeshDecompositionResponder(mesh_id, offset, size)); + LLMeshRepository::sHTTPRequestCount++; } } } @@ -839,26 +860,22 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id) } //early out was not hit, effectively fetched - return ret; + return true; } +//return false if failed to get header bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id) { //protected by mMutex - if (!mHeaderMutex) - { - return false; - } - mHeaderMutex->lock(); if (mMeshHeader.find(mesh_id) == mMeshHeader.end()) - { //we have no header info for this mesh, do nothing + { + // We have no header info for this mesh, retry later. mHeaderMutex->unlock(); return false; } U32 header_size = mMeshHeaderSize[mesh_id]; - bool ret = true ; if (header_size > 0) { @@ -905,13 +922,10 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id) std::string http_url = constructUrl(mesh_id); if (!http_url.empty()) { - ret = mCurlRequest->getByteRange(http_url, headers, offset, size, - new LLMeshPhysicsShapeResponder(mesh_id, offset, size)); - - if(ret) - { - LLMeshRepository::sHTTPRequestCount++; - } + // This might throw AICurlNoEasyHandle. + mCurlRequest->getByteRange(http_url, headers, offset, size, + new LLMeshPhysicsShapeResponder(mesh_id, offset, size)); + LLMeshRepository::sHTTPRequestCount++; } } else @@ -925,7 +939,7 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id) } //early out was not hit, effectively fetched - return ret; + return true; } //return false if failed to get header @@ -944,14 +958,14 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, U32& c LLMeshRepository::sCacheBytesRead += bytes; file.read(buffer, bytes); if (headerReceived(mesh_params, buffer, bytes)) - { //did not do an HTTP request, return false + { + // Already have header, no need to retry. return true; } } } //either cache entry doesn't exist or is corrupt, request header from simulator - bool retval = true ; std::vector headers; headers.push_back("Accept: application/octet-stream"); @@ -961,29 +975,19 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, U32& c //grab first 4KB if we're going to bother with a fetch. Cache will prevent future fetches if a full mesh fits //within the first 4KB //NOTE -- this will break of headers ever exceed 4KB - retval = mCurlRequest->getByteRange(http_url, headers, 0, 4096, new LLMeshHeaderResponder(mesh_params)); - if(retval) - { - LLMeshRepository::sHTTPRequestCount++; - } + // This might throw AICurlNoEasyHandle. + mCurlRequest->getByteRange(http_url, headers, 0, 4096, new LLMeshHeaderResponder(mesh_params)); + LLMeshRepository::sHTTPRequestCount++; count++; } - return retval; + return true; } -//return false if failed to get mesh lod. -bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, U32& count) +void LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, U32& count) { //protected by mMutex - if (!mHeaderMutex) - { - return false; - } - mHeaderMutex->lock(); - bool retval = true; - LLUUID mesh_id = mesh_params.getSculptID(); U32 header_size = mMeshHeaderSize[mesh_id]; @@ -1019,7 +1023,7 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, if (lodReceived(mesh_params, lod, buffer, size)) { delete[] buffer; - return true; + return; } } @@ -1033,13 +1037,10 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, std::string http_url = constructUrl(mesh_id); if (!http_url.empty()) { - retval = mCurlRequest->getByteRange(constructUrl(mesh_id), headers, offset, size, + // This might throw AICurlNoEasyHandle. + mCurlRequest->getByteRange(constructUrl(mesh_id), headers, offset, size, new LLMeshLODResponder(mesh_params, lod, offset, size)); - - if(retval) - { - LLMeshRepository::sHTTPRequestCount++; - } + LLMeshRepository::sHTTPRequestCount++; count++; } else @@ -1056,8 +1057,6 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, { mHeaderMutex->unlock(); } - - return retval; } bool LLMeshRepoThread::headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size) @@ -1102,21 +1101,26 @@ bool LLMeshRepoThread::headerReceived(const LLVolumeParams& mesh_params, U8* dat LLMutexLock lock(mHeaderMutex); mMeshHeaderSize[mesh_id] = header_size; mMeshHeader[mesh_id] = header; - } + } + + + LLMutexLock lock(mMutex); // FIRE-7182, make sure only one thread access mPendingLOD at the same time. //check for pending requests pending_lod_map::iterator iter = mPendingLOD.find(mesh_params); if (iter != mPendingLOD.end()) { - LLMutexLock lock(mMutex); + // LLMutexLock lock(mMutex); FIRE-7182, lock was moved up, before calling mPendingLOD.find for (U32 i = 0; i < iter->second.size(); ++i) { LODRequest req(mesh_params, iter->second[i]); mLODReqQ.push(req); LLMeshRepository::sLODProcessing++; } + + mPendingLOD.erase(iter); // FIRE-7182, only call erase if iter is really valid. } - mPendingLOD.erase(iter); + // mPendingLOD.erase(iter); // avoid crash by moving erase up. } return true; @@ -1599,7 +1603,7 @@ void LLMeshUploadThread::generateHulls() void LLMeshUploadThread::doWholeModelUpload() { - mCurlRequest = new LLCurlRequest(); + mCurlRequest = new AICurlInterface::Request(); if (mWholeModelUploadURL.empty()) { @@ -1614,11 +1618,14 @@ void LLMeshUploadThread::doWholeModelUpload() LLSD body = full_model_data["asset_resources"]; dump_llsd_to_file(body,make_dump_name("whole_model_body_",dump_num)); LLCurlRequest::headers_t headers; + // This might throw AICurlNoEasyHandle. mCurlRequest->post(mWholeModelUploadURL, headers, body, new LLWholeModelUploadResponder(this, full_model_data, mUploadObserverHandle), mMeshUploadTimeOut); do { - mCurlRequest->process(); + mCurlRequest->process(); // FIXME: This function does not exist anymore. The post() gets CPU time from AICurlEasyRequestStateMachine. + // Therefore, if we do not want to continue here unless this upload is done... no wait, that would + // be blocking and we don't want blocking... //sleep for 10ms to prevent eating a whole core apr_sleep(10000); } while (mCurlRequest->getQueued() > 0); @@ -1635,7 +1642,7 @@ void LLMeshUploadThread::requestWholeModelFee() { dump_num++; - mCurlRequest = new LLCurlRequest(); + mCurlRequest = new AICurlInterface::Request; generateHulls(); @@ -1645,6 +1652,7 @@ void LLMeshUploadThread::requestWholeModelFee() mPendingUploads++; LLCurlRequest::headers_t headers; + // This might throw AICurlNoEasyHandle. mCurlRequest->post(mWholeModelFeeCapability, headers, model_data, new LLWholeModelFeeResponder(this,model_data, mFeeObserverHandle), mMeshUploadTimeOut); @@ -1665,11 +1673,6 @@ void LLMeshUploadThread::requestWholeModelFee() void LLMeshRepoThread::notifyLoadedMeshes() {//called via gMeshRepo.notifyLoadedMeshes(). mMutex already locked - if (!mMutex) - { - return; - } - while (!mLoadedQ.empty()) { mMutex->lock(); @@ -2373,17 +2376,17 @@ void LLMeshRepository::notifyLoadedMeshes() mInventoryQ.pop(); } - } - #endif //MESH_IMPORT //call completed callbacks on finished decompositions mDecompThread->notifyCompleted(); - if (!mThread->mWaiting) - { //curl thread is churning, wait for it to go idle + if (!mThread->mSignal->tryLock()) + { + // Curl thread is churning, wait for it to go idle. return; } + mThread->mSignal->unlock(); static std::string region_name("never name a region this"); @@ -3329,6 +3332,7 @@ void LLPhysicsDecomp::run() mStageID[stages[i].mName] = i; } + mSignal->lock(); while (!mQuitting) { mSignal->wait(); @@ -3357,14 +3361,10 @@ void LLPhysicsDecomp::run() } } } + mSignal->unlock(); decomp->quitThread(); - if (mSignal->isLocked()) - { //let go of mSignal's associated mutex - mSignal->unlock(); - } - mDone = true; #endif //MESH_IMPORT } diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index c106f810f..5d225cfe4 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -32,6 +32,7 @@ #include "lluuid.h" #include "llviewertexture.h" #include "llvolume.h" +#include "llcurlrequest.h" #if MESH_IMPORT #define LLCONVEXDECOMPINTER_STATIC 1 @@ -67,7 +68,6 @@ struct LLCDHull class LLVOVolume; class LLMeshResponder; -class LLCurlRequest; class LLMutex; class LLCondition; class LLVFS; @@ -246,13 +246,11 @@ public: static S32 sActiveLODRequests; static U32 sMaxConcurrentRequests; - LLCurlRequest* mCurlRequest; + AICurlInterface::Request* mCurlRequest; LLMutex* mMutex; LLMutex* mHeaderMutex; LLCondition* mSignal; - bool mWaiting; - //map of known mesh headers typedef std::map mesh_header_map; mesh_header_map mMeshHeader; @@ -351,7 +349,7 @@ public: void loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod); bool fetchMeshHeader(const LLVolumeParams& mesh_params, U32& count); - bool fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, U32& count); + void fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, U32& count); bool headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size); bool lodReceived(const LLVolumeParams& mesh_params, S32 lod, U8* data, S32 data_size); bool skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 data_size); diff --git a/indra/newview/llnotify.h b/indra/newview/llnotify.h index 439cae239..4047430a5 100644 --- a/indra/newview/llnotify.h +++ b/indra/newview/llnotify.h @@ -152,7 +152,7 @@ public: public: Matcher(){} virtual ~Matcher() {} - virtual BOOL matches(const LLNotificationPtr) const = 0; + virtual bool matches(const LLNotificationPtr) const = 0; }; // Walks the list and removes any stacked messages for which the given matcher returns TRUE. // Useful when muting people and things in order to clear out any similar previously queued messages. diff --git a/indra/newview/llpanelclassified.cpp b/indra/newview/llpanelclassified.cpp index 97c162cd8..1d3e50360 100644 --- a/indra/newview/llpanelclassified.cpp +++ b/indra/newview/llpanelclassified.cpp @@ -999,7 +999,7 @@ void LLPanelClassified::sendClassifiedClickMessage(const std::string& type) std::string url = gAgent.getRegion()->getCapability("SearchStatTracking"); llinfos << "LLPanelClassified::sendClassifiedClickMessage via capability" << llendl; - LLHTTPClient::post(url, body, new LLHTTPClient::Responder()); + LLHTTPClient::post(url, body, new LLHTTPClient::ResponderIgnore); } //////////////////////////////////////////////////////////////////////////////////////////// diff --git a/indra/newview/llpaneldisplay.cpp b/indra/newview/llpaneldisplay.cpp index 45214ca05..d2f3bdd01 100644 --- a/indra/newview/llpaneldisplay.cpp +++ b/indra/newview/llpaneldisplay.cpp @@ -877,7 +877,7 @@ void LLPanelDisplay::cancel() void LLPanelDisplay::apply() { U32 fsaa_value = childGetValue("fsaa").asInteger(); - bool apply_fsaa_change = !LLRenderTarget::sUseFBO && (mFSAASamples != fsaa_value); + bool apply_fsaa_change = !gSavedSettings.getBOOL("RenderUseFBO") && (mFSAASamples != fsaa_value); gSavedSettings.setU32("RenderFSAASamples", fsaa_value); applyResolution(); diff --git a/indra/newview/llpanelgrouplandmoney.cpp b/indra/newview/llpanelgrouplandmoney.cpp index 4baa6cd8b..9b7ee5c78 100644 --- a/indra/newview/llpanelgrouplandmoney.cpp +++ b/indra/newview/llpanelgrouplandmoney.cpp @@ -1105,7 +1105,7 @@ void LLGroupMoneyDetailsTabEventHandler::processReply(LLMessageSystem* msg, text.append(1, '\n'); - text.append(llformat("%-24s %6d\n", "Total", total_amount)); + text.append(llformat("%-24s %6d\n", LLTrans::getString("GroupMoneyTotal").c_str(), total_amount)); if ( mImplementationp->mTextEditorp ) { @@ -1227,7 +1227,7 @@ void LLGroupMoneySalesTabEventHandler::processReply(LLMessageSystem* msg, S32 transactions = msg->getNumberOfBlocksFast(_PREHASH_HistoryData); if (transactions == 0) { - text.append("(none)"); + text.append(LLTrans::getString("none_text")); } else { @@ -1252,22 +1252,22 @@ void LLGroupMoneySalesTabEventHandler::processReply(LLMessageSystem* msg, switch(type) { case TRANS_OBJECT_SALE: - verb = "bought"; + verb = LLTrans::getString("GroupMoneyBought").c_str(); break; case TRANS_GIFT: - verb = "paid you"; + verb = LLTrans::getString("GroupMoneyPaidYou").c_str(); break; case TRANS_PAY_OBJECT: - verb = "paid into"; + verb = LLTrans::getString("GroupMoneyPaidInto").c_str(); break; case TRANS_LAND_PASS_SALE: - verb = "bought pass to"; + verb = LLTrans::getString("GroupMoneyBoughtPassTo").c_str(); break; case TRANS_EVENT_FEE: - verb = "paid fee for event"; + verb = LLTrans::getString("GroupMoneyPaidFeeForEvent").c_str(); break; case TRANS_EVENT_PRIZE: - verb = "paid prize for event"; + verb = LLTrans::getString("GroupMoneyPaidPrizeForEvent").c_str(); break; default: verb = ""; @@ -1429,15 +1429,18 @@ void LLGroupMoneyPlanningTabEventHandler::processReply(LLMessageSystem* msg, return; } - text.append("Summary for this week, beginning on "); + text.append(LLTrans::getString("SummaryForTheWeek")); + text.append(start_date); + text.append(". "); if (current_interval == 0) { - text.append("The next stipend day is "); + text.append(LLTrans::getString("NextStipendDay")); + text.append(next_stipend_date); - text.append("\n\n"); - text.append(llformat("%-24s%s%6d\n", "Balance", + text.append(".\n\n"); + text.append(llformat("%-24s%s%6d\n", LLTrans::getString("GroupMoneyBalance").c_str(), gHippoGridManager->getConnectedGrid()->getCurrencySymbol().c_str(), balance)); text.append(1, '\n'); @@ -1445,15 +1448,15 @@ void LLGroupMoneyPlanningTabEventHandler::processReply(LLMessageSystem* msg, // [DEV-29503] Hide the individual info since // non_exempt_member here is a wrong choice to calculate individual shares. - // text.append( " Group Individual Share\n"); - // text.append(llformat( "%-24s %6d %6d \n", "Credits", total_credits, (S32)floor((F32)total_credits/(F32)non_exempt_members))); - // text.append(llformat( "%-24s %6d %6d \n", "Debits", total_debits, (S32)floor((F32)total_debits/(F32)non_exempt_members))); - // text.append(llformat( "%-24s %6d %6d \n", "Total", total_credits + total_debits, (S32)floor((F32)(total_credits + total_debits)/(F32)non_exempt_members))); +// text.append( LLTrans::getString("GroupIndividualShare")); +// text.append(llformat( "%-24s %6d %6d \n", LLTrans::getString("GroupMoneyCredits").c_str(), total_credits, (S32)floor((F32)total_credits/(F32)non_exempt_members))); +// text.append(llformat( "%-24s %6d %6d \n", LLTrans::getString("GroupMoneyDebits").c_str(), total_debits, (S32)floor((F32)total_debits/(F32)non_exempt_members))); +// text.append(llformat( "%-24s %6d %6d \n", LLTrans::getString("GroupMoneyTotal").c_str(), total_credits + total_debits, (S32)floor((F32)(total_credits + total_debits)/(F32)non_exempt_members))); - text.append( " Group\n"); - text.append(llformat( "%-24s %6d\n", "Credits", total_credits)); - text.append(llformat( "%-24s %6d\n", "Debits", total_debits)); - text.append(llformat( "%-24s %6d\n", "Total", total_credits + total_debits)); + text.append(llformat( "%s\n", LLTrans::getString("GroupColumn").c_str())); + text.append(llformat( "%-24s %6d\n", LLTrans::getString("GroupMoneyCredits").c_str(), total_credits)); + text.append(llformat( "%-24s %6d\n", LLTrans::getString("GroupMoneyDebits").c_str(), total_debits)); + text.append(llformat( "%-24s %6d\n", LLTrans::getString("GroupMoneyTotal").c_str(), total_credits + total_debits)); if ( mImplementationp->mTextEditorp ) { diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp index 045b722a4..3483f2577 100644 --- a/indra/newview/llpanellogin.cpp +++ b/indra/newview/llpanellogin.cpp @@ -49,7 +49,6 @@ #include "llcheckboxctrl.h" #include "llcommandhandler.h" // for secondlife:///app/login/ #include "llcombobox.h" -#include "llcurl.h" #include "llviewercontrol.h" #include "llfloaterabout.h" #include "llfloatertest.h" diff --git a/indra/newview/llpanelobject.cpp b/indra/newview/llpanelobject.cpp index 796a535d7..ef6c5f138 100644 --- a/indra/newview/llpanelobject.cpp +++ b/indra/newview/llpanelobject.cpp @@ -514,8 +514,11 @@ void LLPanelObject::getState( ) mCtrlPosY->setEnabled(enable_move); mCtrlPosZ->setEnabled(enable_move); mBtnLinkObj->setEnabled(LLSelectMgr::getInstance()->enableLinkObjects()); - mBtnUnlinkObj->setEnabled((LLSelectMgr::getInstance()->enableUnlinkObjects() - && (selected_count > 1) && LLSelectMgr::getInstance()->getSelection()->getRootObjectCount()<=1)); + LLViewerObject* linkset_parent = objectp->getSubParent()? objectp->getSubParent() : objectp; + mBtnUnlinkObj->setEnabled( + LLSelectMgr::getInstance()->enableUnlinkObjects() + && (linkset_parent->numChildren() >= 1) + && LLSelectMgr::getInstance()->getSelection()->getRootObjectCount()<=1); mBtnCopyPos->setEnabled(enable_move); mBtnPastePos->setEnabled(enable_move); mBtnPastePosClip->setEnabled(enable_move); diff --git a/indra/newview/llpanelobjectinventory.cpp b/indra/newview/llpanelobjectinventory.cpp index c78f3ce21..8929780d5 100644 --- a/indra/newview/llpanelobjectinventory.cpp +++ b/indra/newview/llpanelobjectinventory.cpp @@ -94,8 +94,8 @@ public: LLTaskInvFVBridge(LLPanelObjectInventory* panel, const LLUUID& uuid, const std::string& name, - U32 flags=0); - virtual ~LLTaskInvFVBridge( ) {} + U32 flags=0); + virtual ~LLTaskInvFVBridge() {} virtual LLFontGL::StyleFlags getLabelStyle() const { return LLFontGL::NORMAL; } virtual std::string getLabelSuffix() const { return LLStringUtil::null; } @@ -243,9 +243,9 @@ void LLTaskInvFVBridge::buyItem() if (sale_info.getSaleType() != LLSaleInfo::FS_CONTENTS) { U32 next_owner_mask = perm.getMaskNextOwner(); - args["MODIFYPERM"] = LLNotifications::instance().getGlobalString((next_owner_mask & PERM_MODIFY) ? "PermYes" : "PermNo"); - args["COPYPERM"] = LLNotifications::instance().getGlobalString((next_owner_mask & PERM_COPY) ? "PermYes" : "PermNo"); - args["RESELLPERM"] = LLNotifications::instance().getGlobalString((next_owner_mask & PERM_TRANSFER) ? "PermYes" : "PermNo"); + args["MODIFYPERM"] = LLTrans::getString((next_owner_mask & PERM_MODIFY) ? "PermYes" : "PermNo"); + args["COPYPERM"] = LLTrans::getString((next_owner_mask & PERM_COPY) ? "PermYes" : "PermNo"); + args["RESELLPERM"] = LLTrans::getString((next_owner_mask & PERM_TRANSFER) ? "PermYes" : "PermNo"); } std::string alertdesc; @@ -318,10 +318,18 @@ const std::string& LLTaskInvFVBridge::getName() const const std::string& LLTaskInvFVBridge::getDisplayName() const { LLInventoryItem* item = findItem(); + if(item) { mDisplayName.assign(item->getName()); + // Localize "New Script", "New Script 1", "New Script 2", etc. + if (item->getType() == LLAssetType::AT_LSL_TEXT && + LLStringUtil::startsWith(item->getName(), "New Script")) + { + LLStringUtil::replaceString(mDisplayName, "New Script", LLTrans::getString("PanelContentsNewScript")); + } + const LLPermissions& perm(item->getPermissions()); BOOL copy = gAgent.allowOperation(PERM_COPY, perm, GP_OBJECT_MANIPULATE); BOOL mod = gAgent.allowOperation(PERM_MODIFY, perm, GP_OBJECT_MANIPULATE); @@ -677,7 +685,9 @@ void LLTaskInvFVBridge::performAction(LLInventoryModel* model, std::string actio { if (price > 0 && price > gStatusBar->getBalance()) { - LLFloaterBuyCurrency::buyCurrency("This costs", price); + LLStringUtil::format_map_t args; + args["CURRENCY"] = gHippoGridManager->getConnectedGrid()->getCurrencySymbol(); + LLFloaterBuyCurrency::buyCurrency( LLTrans::getString("this_costs", args), price ); } else { @@ -723,7 +733,7 @@ void LLTaskInvFVBridge::buildContextMenu(LLMenuGL& menu, U32 flags) else { std::ostringstream info; - info << "Buy for " << gHippoGridManager->getConnectedGrid()->getCurrencySymbol() << price; + info << LLTrans::getString("Buyfor") << gHippoGridManager->getConnectedGrid()->getCurrencySymbol() << price; label.assign(info.str()); } @@ -1013,8 +1023,8 @@ class LLTaskSoundBridge : public LLTaskInvFVBridge { public: LLTaskSoundBridge(LLPanelObjectInventory* panel, - const LLUUID& uuid, - const std::string& name) : + const LLUUID& uuid, + const std::string& name) : LLTaskInvFVBridge(panel, uuid, name) {} virtual BOOL canOpenItem() const { return TRUE; } @@ -1086,7 +1096,7 @@ void LLTaskSoundBridge::buildContextMenu(LLMenuGL& menu, U32 flags) else { std::ostringstream info; - info << "Buy for " << gHippoGridManager->getConnectedGrid()->getCurrencySymbol() << price; + info << LLTrans::getString("Buyfor") << gHippoGridManager->getConnectedGrid()->getCurrencySymbol() << price; label.assign(info.str()); } @@ -1121,6 +1131,7 @@ void LLTaskSoundBridge::buildContextMenu(LLMenuGL& menu, U32 flags) } items.push_back(std::string("Task Play")); + /*menu.addSeparator(); menu.append(new LLMenuItemCallGL("Play", &LLTaskSoundBridge::playSound, @@ -1138,8 +1149,8 @@ class LLTaskLandmarkBridge : public LLTaskInvFVBridge { public: LLTaskLandmarkBridge(LLPanelObjectInventory* panel, - const LLUUID& uuid, - const std::string& name) : + const LLUUID& uuid, + const std::string& name) : LLTaskInvFVBridge(panel, uuid, name) {} }; diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp index 513e92f3a..81c6bcb87 100644 --- a/indra/newview/llselectmgr.cpp +++ b/indra/newview/llselectmgr.cpp @@ -5484,6 +5484,8 @@ void LLSelectMgr::renderSilhouettes(BOOL for_hud) gGL.matrixMode(LLRender::MM_MODELVIEW); gGL.pushMatrix(); + gGL.pushUIMatrix(); + gGL.loadUIIdentity(); gGL.loadIdentity(); gGL.loadMatrix(OGL_TO_CFR_ROTATION); // Load Cory's favorite reference frame gGL.translatef(-hud_bbox.getCenterLocal().mV[VX] + (depth *0.5f), 0.f, 0.f); @@ -5576,6 +5578,7 @@ void LLSelectMgr::renderSilhouettes(BOOL for_hud) gGL.matrixMode(LLRender::MM_MODELVIEW); gGL.popMatrix(); + gGL.popUIMatrix(); stop_glerror(); } @@ -5898,8 +5901,8 @@ void pushWireframe(LLDrawable* drawable) if (drawable->isState(LLDrawable::RIGGED)) { - vobj->updateRiggedVolume(); - volume = vobj->getRiggedVolume(); + vobj->updateRiggedVolume(); + volume = vobj->getRiggedVolume(); } else { @@ -5941,6 +5944,8 @@ void LLSelectNode::renderOneWireframe(const LLColor4& color) gDebugProgram.bind(); } + static LLCachedControl mode("OutlineMode",0); + gGL.matrixMode(LLRender::MM_MODELVIEW); gGL.pushMatrix(); @@ -5961,10 +5966,12 @@ void LLSelectNode::renderOneWireframe(const LLColor4& color) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + //Singu Note: Diverges from v3. If sRenderHiddenSelections set, draw non-z-culled wireframe, else draw occluded 'thick' wireframe to create an outline. if (LLSelectMgr::sRenderHiddenSelections) // && gFloaterTools && gFloaterTools->getVisible()) { gGL.blendFunc(LLRender::BF_SOURCE_COLOR, LLRender::BF_ONE); LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE, GL_GEQUAL); + if (shader) { gGL.diffuseColor4f(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE], 0.4f); @@ -5986,18 +5993,21 @@ void LLSelectNode::renderOneWireframe(const LLColor4& color) pushWireframe(drawable); } } + gGL.setSceneBlendType(LLRender::BT_ALPHA); + } + else + { + LLGLEnable cull_face(GL_CULL_FACE); + LLGLEnable offset(GL_POLYGON_OFFSET_LINE); + + gGL.setSceneBlendType(LLRender::BT_ALPHA); + gGL.diffuseColor4f(color.mV[VRED]*2, color.mV[VGREEN]*2, color.mV[VBLUE]*2, LLSelectMgr::sHighlightAlpha*2); + glPolygonOffset(3.f, 3.f); + glLineWidth(3.f); + pushWireframe(drawable); + glLineWidth(1.f); } - gGL.flush(); - gGL.setSceneBlendType(LLRender::BT_ALPHA); - - gGL.diffuseColor4f(color.mV[VRED]*2, color.mV[VGREEN]*2, color.mV[VBLUE]*2, LLSelectMgr::sHighlightAlpha*2); - - LLGLEnable offset(GL_POLYGON_OFFSET_LINE); - glPolygonOffset(3.f, 3.f); - glLineWidth(3.f); - pushWireframe(drawable); - glLineWidth(1.f); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); gGL.popMatrix(); @@ -6053,6 +6063,9 @@ void LLSelectNode::renderOneSilhouette(const LLColor4 &color) gGL.matrixMode(LLRender::MM_MODELVIEW); gGL.pushMatrix(); + gGL.pushUIMatrix(); + gGL.loadUIIdentity(); + if (!is_hud_object) { gGL.loadIdentity(); @@ -6169,6 +6182,7 @@ void LLSelectNode::renderOneSilhouette(const LLColor4 &color) gGL.flush(); } gGL.popMatrix(); + gGL.popUIMatrix(); if (shader) { diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp index 20c390931..d081f51de 100644 --- a/indra/newview/llspatialpartition.cpp +++ b/indra/newview/llspatialpartition.cpp @@ -1998,7 +1998,7 @@ public: virtual void processGroup(LLSpatialGroup* group) { - llassert(!group->isState(LLSpatialGroup::DIRTY) && !group->isEmpty()) + llassert(!group->isState(LLSpatialGroup::DIRTY) && !group->isEmpty()); if (mRes < 2) { diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 596d8d154..d0a5a6364 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -218,6 +218,7 @@ #include "llfloaterblacklist.h" #include "scriptcounter.h" #include "shfloatermediaticker.h" +#include "llpacketring.h" // #include "llpathfindingmanager.h" @@ -256,7 +257,9 @@ extern S32 gStartImageHeight; // local globals // - +#if defined(CWDEBUG) || defined(DEBUG_CURLIO) +static bool gCurlIo; +#endif static LLHost gAgentSimHost; static BOOL gSkipOptionalUpdate = FALSE; @@ -628,21 +631,21 @@ bool idle_startup() F32 dropPercent = gSavedSettings.getF32("PacketDropPercentage"); - msg->mPacketRing.setDropPercentage(dropPercent); + msg->mPacketRing->setDropPercentage(dropPercent); F32 inBandwidth = gSavedSettings.getF32("InBandwidth"); F32 outBandwidth = gSavedSettings.getF32("OutBandwidth"); if (inBandwidth != 0.f) { LL_DEBUGS("AppInit") << "Setting packetring incoming bandwidth to " << inBandwidth << LL_ENDL; - msg->mPacketRing.setUseInThrottle(TRUE); - msg->mPacketRing.setInBandwidth(inBandwidth); + msg->mPacketRing->setUseInThrottle(TRUE); + msg->mPacketRing->setInBandwidth(inBandwidth); } if (outBandwidth != 0.f) { LL_DEBUGS("AppInit") << "Setting packetring outgoing bandwidth to " << outBandwidth << LL_ENDL; - msg->mPacketRing.setUseOutThrottle(TRUE); - msg->mPacketRing.setOutBandwidth(outBandwidth); + msg->mPacketRing->setUseOutThrottle(TRUE); + msg->mPacketRing->setOutBandwidth(outBandwidth); } } @@ -1348,6 +1351,9 @@ bool idle_startup() llinfos << "Authenticating with " << grid_uri << llendl; + // Always write curl I/O debug info for the login attempt. + Debug(gCurlIo = dc::curl.is_on() && !dc::curlio.is_on(); if (gCurlIo) dc::curlio.on()); + // TODO if statement here to use web_login_key // OGPX : which routine would this end up in? the LLSD or XMLRPC, or ....? LLUserAuth::getInstance()->authenticate( @@ -1422,6 +1428,7 @@ bool idle_startup() LL_DEBUGS("AppInit") << "downloading..." << LL_ENDL; return FALSE; } + Debug(if (gCurlIo) dc::curlio.off()); // Login succeeded: restore dc::curlio to original state. LLStartUp::setStartupState( STATE_LOGIN_PROCESS_RESPONSE ); progress += 0.01f; set_startup_status(progress, LLTrans::getString("LoginProcessingResponse"), auth_message); @@ -3607,6 +3614,7 @@ std::string LLStartUp::startupStateToString(EStartupState state) #define RTNENUM(E) case E: return #E switch(state){ RTNENUM( STATE_FIRST ); + RTNENUM( STATE_BROWSER_INIT ); RTNENUM( STATE_LOGIN_SHOW ); RTNENUM( STATE_LOGIN_WAIT ); RTNENUM( STATE_LOGIN_CLEANUP ); @@ -3614,10 +3622,13 @@ std::string LLStartUp::startupStateToString(EStartupState state) RTNENUM( STATE_UPDATE_CHECK ); RTNENUM( STATE_LOGIN_AUTH_INIT ); RTNENUM( STATE_LOGIN_AUTHENTICATE ); + RTNENUM( STATE_WAIT_LEGACY_LOGIN ); + RTNENUM( STATE_XMLRPC_LEGACY_LOGIN ); RTNENUM( STATE_LOGIN_NO_DATA_YET ); RTNENUM( STATE_LOGIN_DOWNLOADING ); RTNENUM( STATE_LOGIN_PROCESS_RESPONSE ); RTNENUM( STATE_WORLD_INIT ); + RTNENUM( STATE_MULTIMEDIA_INIT ); RTNENUM( STATE_FONT_INIT ); RTNENUM( STATE_SEED_GRANTED_WAIT ); RTNENUM( STATE_SEED_CAP_GRANTED ); @@ -3630,10 +3641,10 @@ std::string LLStartUp::startupStateToString(EStartupState state) RTNENUM( STATE_WEARABLES_WAIT ); RTNENUM( STATE_CLEANUP ); RTNENUM( STATE_STARTED ); - default: - return llformat("(state #%d)", state); } #undef RTNENUM + // Never reached. + return llformat("(state #%d)", state); } diff --git a/indra/newview/lltexturectrl.cpp b/indra/newview/lltexturectrl.cpp index 990a7228a..18493bd7c 100644 --- a/indra/newview/lltexturectrl.cpp +++ b/indra/newview/lltexturectrl.cpp @@ -1103,6 +1103,7 @@ LLTextureCtrl::LLTextureCtrl( LLFontGL::getFontSansSerifSmall() ); mTentativeLabel->setHAlign( LLFontGL::HCENTER ); mTentativeLabel->setFollowsAll(); + mTentativeLabel->setMouseOpaque(FALSE); addChild( mTentativeLabel ); LLRect border_rect(0, getRect().getHeight(), getRect().getWidth(), 0); diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index f507d22ba..2a4abcf1b 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -779,7 +779,7 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher, calcWorkPriority(); mType = host.isOk() ? LLImageBase::TYPE_AVATAR_BAKE : LLImageBase::TYPE_NORMAL; -// llinfos << "Create: " << mID << " mHost:" << host << " Discard=" << discard << llendl; + llinfos << "Create: " << mID << " mHost:" << host << " Discard=" << discard << " URL:"<< mUrl << llendl; if (!mFetcher->mDebugPause) { U32 work_priority = mWorkPriority | LLWorkerThread::PRIORITY_HIGH; @@ -1320,8 +1320,15 @@ bool LLTextureFetchWorker::doWork(S32 param) // Will call callbackHttpGet when curl request completes std::vector headers; headers.push_back("Accept: image/x-j2c"); - res = mFetcher->mCurlGetRequest->getByteRange(mUrl, headers, offset, mRequestedSize, - new HTTPGetResponder(mFetcher, mID, LLTimer::getTotalTime(), mRequestedSize, offset, true)); + try + { + res = mFetcher->mCurlGetRequest->getByteRange(mUrl, headers, offset, mRequestedSize, + new HTTPGetResponder(mFetcher, mID, LLTimer::getTotalTime(), mRequestedSize, offset, true)); + } + catch(AICurlNoEasyHandle const& error) + { + llwarns << error.what() << llendl; + } } if (!res) { @@ -2100,7 +2107,7 @@ bool LLTextureFetch::createRequest(const std::string& url, const LLUUID& id, con worker->unlockWorkMutex(); } -// llinfos << "REQUESTED: " << id << " Discard: " << desired_discard << llendl; + llinfos << "REQUESTED: " << id << " Discard: " << desired_discard << llendl; return true; } @@ -2357,14 +2364,6 @@ void LLTextureFetch::commonUpdate() // Run a cross-thread command, if any. cmdDoWork(); #endif - - // Update Curl on same thread as mCurlGetRequest was constructed - llassert_always(mCurlGetRequest); - S32 processed = mCurlGetRequest->process(); - if (processed > 0) - { - lldebugs << "processed: " << processed << " messages." << llendl; - } } @@ -2429,7 +2428,7 @@ void LLTextureFetch::shutDownImageDecodeThread() void LLTextureFetch::startThread() { // Construct mCurlGetRequest from Worker Thread - mCurlGetRequest = new LLCurlRequest(); + mCurlGetRequest = new AICurlInterface::Request; } // WORKER THREAD diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h index ee3cd2c75..03073d906 100644 --- a/indra/newview/lltexturefetch.h +++ b/indra/newview/lltexturefetch.h @@ -37,7 +37,7 @@ #include "llimage.h" #include "lluuid.h" #include "llworkerthread.h" -#include "llcurl.h" +#include "llcurlrequest.h" #include "lltextureinfo.h" #include "llapr.h" @@ -107,7 +107,7 @@ public: LLViewerAssetStats * main_stats); void commandDataBreak(); - LLCurlRequest & getCurlRequest() { return *mCurlGetRequest; } + AICurlInterface::Request& getCurlRequest() { return *mCurlGetRequest; } bool isQAMode() const { return mQAMode; } @@ -178,7 +178,7 @@ private: LLTextureCache* mTextureCache; LLImageDecodeThread* mImageDecodeThread; - LLCurlRequest* mCurlGetRequest; + AICurlInterface::Request* mCurlGetRequest; // Map of all requests by UUID typedef std::map map_t; diff --git a/indra/newview/lltexturestatsuploader.cpp b/indra/newview/lltexturestatsuploader.cpp index e0358e1fc..7f14a4254 100644 --- a/indra/newview/lltexturestatsuploader.cpp +++ b/indra/newview/lltexturestatsuploader.cpp @@ -33,6 +33,7 @@ #include "llviewerprecompiledheaders.h" #include "lltexturestatsuploader.h" +#include "llhttpclient.h" LLTextureStatsUploader::LLTextureStatsUploader() { diff --git a/indra/newview/llurlsimstring.cpp b/indra/newview/llurlsimstring.cpp index a28988db5..200345cae 100644 --- a/indra/newview/llurlsimstring.cpp +++ b/indra/newview/llurlsimstring.cpp @@ -38,7 +38,10 @@ #include "llpanellogin.h" #include "llviewercontrol.h" -#include "curl/curl.h" +#include // curl_unescape, curl_free +#ifdef DEBUG_CURLIO +#include "debug_libcurl.h" +#endif //static LLURLSimString LLURLSimString::sInstance; diff --git a/indra/newview/lluserauth.cpp b/indra/newview/lluserauth.cpp index f558a3170..a10c66fca 100644 --- a/indra/newview/lluserauth.cpp +++ b/indra/newview/lluserauth.cpp @@ -47,9 +47,12 @@ #include "stringize.h" // NOTE: MUST include these after otherincludes since queue gets redefined!?!! -#include #include +#include +#ifdef DEBUG_CURLIO +#include "debug_libcurl.h" +#endif // Don't define PLATFORM_STRING for unknown platforms - they need // to get added to the login cgi script, so we want this to cause an @@ -270,7 +273,7 @@ LLUserAuth::UserAuthcode LLUserAuth::authResponse() return mAuthResponse; } - bool done = mTransaction->process(); + bool done = mTransaction->is_finished(); if (!done) { if (LLXMLRPCTransaction::StatusDownloading == mTransaction->status(0)) diff --git a/indra/newview/llviewerassetstorage.h b/indra/newview/llviewerassetstorage.h index 15d9a748e..7b85f55d7 100644 --- a/indra/newview/llviewerassetstorage.h +++ b/indra/newview/llviewerassetstorage.h @@ -34,7 +34,6 @@ #define LLVIEWERASSETSTORAGE_H #include "llassetstorage.h" -//#include "curl/curl.h" class LLVFile; diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index d17067d96..0c0b12075 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -250,6 +250,7 @@ #include "llfloatervfs.h" #include "llfloatervfsexplorer.h" #include "shfloatermediaticker.h" +#include "llpacketring.h" // #include "scriptcounter.h" @@ -4048,7 +4049,7 @@ void print_packets_lost(void*) void drop_packet(void*) { - gMessageSystem->mPacketRing.dropPackets(1); + gMessageSystem->mPacketRing->dropPackets(1); } diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index a48ac85d7..1f540a01c 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -31,8 +31,8 @@ */ #include "llviewerprecompiledheaders.h" - #include "llviewermessage.h" +#include #include @@ -78,7 +78,6 @@ #include "llviewercontrol.h" #include "lldrawpool.h" #include "llfirstuse.h" -#include "llfloateractivespeakers.h" #include "llfloateranimpreview.h" #include "llfloaterbuycurrency.h" #include "llfloaterbuyland.h" @@ -115,6 +114,7 @@ #include "llstatenums.h" #include "llstatusbar.h" #include "llimview.h" +#include "llfloateractivespeakers.h" #include "lltexturestats.h" #include "lltool.h" #include "lltoolbar.h" @@ -127,14 +127,13 @@ #include "llviewerdisplay.h" #include "llviewerfoldertype.h" #include "llviewergenericmessage.h" +#include "llviewermenu.h" #include "llviewerinventory.h" #include "llviewerjoystick.h" -#include "llviewermenu.h" #include "llviewerobject.h" #include "llviewerobjectlist.h" #include "llviewerparcelmgr.h" #include "llviewerpartsource.h" -#include "llviewerregion.h" #include "llviewerstats.h" #include "llviewertexteditor.h" #include "llviewerthrottle.h" @@ -148,6 +147,7 @@ #include "llfloaterworldmap.h" #include "llviewerdisplay.h" #include "llkeythrottle.h" +#include "llviewerregion.h" // #include "llviewernetwork.h" // @@ -336,7 +336,9 @@ void give_money(const LLUUID& uuid, LLViewerRegion* region, S32 amount, BOOL is_ } else { - LLFloaterBuyCurrency::buyCurrency("Giving", amount); + LLStringUtil::format_map_t args; + args["CURRENCY"] = gHippoGridManager->getConnectedGrid()->getCurrencySymbol(); + LLFloaterBuyCurrency::buyCurrency( LLTrans::getString("giving", args)+" ", amount ); } } @@ -399,12 +401,10 @@ void process_layer_data(LLMessageSystem *mesgsys, void **user_data) { LLViewerRegion *regionp = LLWorld::getInstance()->getRegion(mesgsys->getSender()); - if (!regionp || gNoRender) + if(!regionp || gNoRender) { return; } - - S32 size; S8 type; @@ -796,6 +796,7 @@ static void highlight_inventory_objects_in_panel(const std::vector& item } } } + static LLNotificationFunctorRegistration jgr_1("JoinGroup", join_group_response); static LLNotificationFunctorRegistration jgr_2("JoinedTooManyGroupsMember", join_group_response); static LLNotificationFunctorRegistration jgr_3("JoinGroupCanAfford", join_group_response); @@ -1160,21 +1161,29 @@ bool check_offer_throttle(const std::string& from_name, bool check_only) { // Use the name of the last item giver, who is probably the person // spamming you. - std::ostringstream message; - message << LLAppViewer::instance()->getSecondLifeTitle(); + + LLStringUtil::format_map_t arg; + std::string log_msg; + std::ostringstream time ; + time<getSecondLifeTitle(); + arg["TIME"] = time.str(); + if (!from_name.empty()) { - message << ": Items coming in too fast from " << from_name; + arg["FROM_NAME"] = from_name; + log_msg = LLTrans::getString("ItemsComingInTooFastFrom", arg); } else { - message << ": Items coming in too fast"; + log_msg = LLTrans::getString("ItemsComingInTooFast", arg); } - message << ", automatic preview disabled for " - << OFFER_THROTTLE_TIME << " seconds."; - chat.mText = message.str(); + + chat.mText = log_msg; //this is kinda important, so actually put it on screen LLFloaterChat::addChat(chat, FALSE, FALSE); + throttle_logged=true; } return false; @@ -1186,7 +1195,7 @@ bool check_offer_throttle(const std::string& from_name, bool check_only) } } } - + void open_inventory_offer(const uuid_vec_t& items, const std::string& from_name) { uuid_vec_t::const_iterator it = items.begin(); @@ -1321,7 +1330,7 @@ void inventory_offer_mute_callback(const LLUUID& blocked_id, { public: OfferMatcher(const LLUUID& to_block) : blocked_id(to_block) {} - BOOL matches(const LLNotificationPtr notification) const + bool matches(const LLNotificationPtr notification) const { if(notification->getName() == "ObjectGiveItem" || notification->getName() == "ObjectGiveItemUnknownUser" @@ -1701,7 +1710,7 @@ void inventory_offer_handler(LLOfferInfo* info, BOOL from_task) } // [/RLVa:KB] } - + LLSD args; args["[OBJECTNAME]"] = msg; @@ -1711,7 +1720,9 @@ void inventory_offer_handler(LLOfferInfo* info, BOOL from_task) std::string typestr = ll_safe_string(LLAssetType::lookupHumanReadable(info->mType)); if (!typestr.empty()) { - args["OBJECTTYPE"] = typestr; + // human readable matches string name from strings.xml + // lets get asset type localized name + args["OBJECTTYPE"] = LLTrans::getString(typestr); } else { @@ -1730,7 +1741,6 @@ void inventory_offer_handler(LLOfferInfo* info, BOOL from_task) payload["from_id"] = info->mFromID; args["OBJECTFROMNAME"] = info->mFromName; args["NAME"] = info->mFromName; - if (info->mFromGroup) { std::string group_name; @@ -1934,7 +1944,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) LLChat chat; std::string buffer; - // *TODO:translate - need to fix the full name to first/last (maybe) + // *TODO: Translate - need to fix the full name to first/last (maybe) msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, from_id); msg->getBOOLFast(_PREHASH_MessageBlock, _PREHASH_FromGroup, from_group); msg->getUUIDFast(_PREHASH_MessageBlock, _PREHASH_ToAgentID, to_id); @@ -1958,11 +1968,13 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) if((U32)std::abs(std::distance(iter, boost::sregex_iterator())) > SpamNewlines) { NACLAntiSpamRegistry::blockOnQueue((U32)NACLAntiSpamRegistry::QUEUE_IM,from_id); + llinfos << "[antispam] blocked owner due to too many newlines: " << from_id << llendl; if(gSavedSettings.getBOOL("AntiSpamNotify")) { LLSD args; - args["MESSAGE"] = "Message: Blocked newline flood from "+from_id.asString(); - LLNotificationsUtil::add("SystemMessageTip", args); + args["SOURCE"] = from_id.asString(); + args["AMOUNT"] = boost::lexical_cast(SpamNewlines); + LLNotificationsUtil::add("AntiSpamNewlineFlood", args); } return; } @@ -1999,7 +2011,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) BOOL is_muted = LLMuteList::getInstance()->isMuted(from_id, name, LLMute::flagTextChat); BOOL is_linden = LLMuteList::getInstance()->isLinden(name); BOOL is_owned_by_me = FALSE; - + LLUUID computed_session_id = LLIMMgr::computeSessionID(dialog,from_id); chat.mMuted = is_muted && !is_linden; @@ -2438,7 +2450,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) case IM_MESSAGEBOX: { // This is a block, modeless dialog. - // *TODO:translate + // *TODO: Translate args["MESSAGE"] = message; LLNotificationsUtil::add("SystemMessage", args); } @@ -2598,7 +2610,6 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) return; // NaCl End LLOfferInfo* info = new LLOfferInfo; - if (IM_INVENTORY_OFFERED == dialog) { struct offer_agent_bucket_t @@ -2642,19 +2653,12 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) info->mTransactionID = session_id; info->mFolderID = gInventory.findCategoryUUIDForType(LLFolderType::assetTypeToFolderType(info->mType)); - if (dialog == IM_TASK_INVENTORY_OFFERED) - { - info->mFromObject = TRUE; - } - else - { - info->mFromObject = FALSE; - } + info->mFromObject = (dialog == IM_TASK_INVENTORY_OFFERED); info->mFromName = name; info->mDesc = message; info->mHost = msg->getSender(); //if (((is_busy && !is_owned_by_me) || is_muted)) - if ( is_muted ) + if (is_muted) { // Same as closing window info->forceResponse(IOR_DECLINE); @@ -2810,8 +2814,8 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) chat.mFromName = name; // Build a link to open the object IM info window. - std::string location = ll_safe_string((char*)binary_bucket,binary_bucket_size); - + std::string location = ll_safe_string((char*)binary_bucket, binary_bucket_size); + LLSD query_string; query_string["owner"] = from_id; query_string["slurl"] = location.c_str(); @@ -2819,7 +2823,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) if (from_group) { query_string["groupowned"] = "true"; - } + } if (session_id.notNull()) { @@ -2932,7 +2936,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) // [/RLVa:KB] LLSD args; - // *TODO:translate -> [FIRST] [LAST] (maybe) + // *TODO: Translate -> [FIRST] [LAST] (maybe) args["NAME"] = name; args["MESSAGE"] = message; LLSD payload; @@ -3085,7 +3089,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) else { args["[MESSAGE]"] = message; - LLNotificationsUtil::add("OfferFriendship", args, payload); + LLNotificationsUtil::add("OfferFriendship", args, payload); } } } @@ -3128,7 +3132,6 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) } } - void busy_message (LLMessageSystem* msg, LLUUID from_id) { if (gAgent.getBusy()) @@ -3226,7 +3229,7 @@ void process_offer_callingcard(LLMessageSystem* msg, void**) LLNameValue* nvlast = source->getNVPair("LastName"); if (nvfirst && nvlast) { - args["NAME"] = LLCacheName::buildFullName( + source_name = LLCacheName::buildFullName( nvfirst->getString(), nvlast->getString()); } } @@ -3241,6 +3244,7 @@ void process_offer_callingcard(LLMessageSystem* msg, void**) } else { + args["NAME"] = source_name; LLNotificationsUtil::add("OfferCallingCard", args, payload); } } @@ -3355,7 +3359,7 @@ class AuthHandler : public HippoRestHandlerRaw void process_chat_from_simulator(LLMessageSystem *msg, void **user_data) { - LLChat chat; + LLChat chat; std::string mesg; std::string from_name; U8 source_temp; @@ -3376,19 +3380,15 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data) msg->getUUID("ChatData", "SourceID", from_id); chat.mFromID = from_id; - chatter = gObjectList.findObject(from_id); - - if(chatter) + if(chatter && chatter->isAvatar()) { - if(chatter->isAvatar()) - { - ((LLVOAvatar*)chatter)->mIdleTimer.reset(); - } + ((LLVOAvatar*)chatter)->mIdleTimer.reset(); } + // Object owner for objects msg->getUUID("ChatData", "OwnerID", owner_id); - + msg->getU8Fast(_PREHASH_ChatData, _PREHASH_SourceType, source_temp); chat.mSourceType = (EChatSourceType)source_temp; @@ -3450,7 +3450,7 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data) LLSD args; args["NAME"] = from_name; chat.mPosAgent = chatter->getPositionAgent(); - + // Make swirly things only for talking objects. (not script debug messages, though) // if (chat.mSourceType == CHAT_SOURCE_OBJECT // && chat.mChatType != CHAT_TYPE_DEBUG_MSG @@ -3907,7 +3907,7 @@ void process_teleport_start(LLMessageSystem *msg, void**) //if (teleport_flags & TELEPORT_FLAGS_DISABLE_CANCEL) // [RLVa:KB] - Checked: 2009-07-07 (RLVa-1.0.0d) | Added: RLVa-0.2.0b - if ( (teleport_flags & TELEPORT_FLAGS_DISABLE_CANCEL) || (!gRlvHandler.getCanCancelTp()) ) + if ( (teleport_flags & TELEPORT_FLAGS_DISABLE_CANCEL) || (!gRlvHandler.getCanCancelTp()) ) // [/RLVa:KB] { gViewerWindow->setProgressCancelButtonVisible(FALSE); @@ -3927,6 +3927,7 @@ void process_teleport_start(LLMessageSystem *msg, void**) make_ui_sound("UISndTeleportOut"); LL_INFOS("Messaging") << "Teleport initiated by remote TeleportStart message with TeleportFlags: " << teleport_flags << LL_ENDL; + // Don't call LLFirstUse::useTeleport here because this could be // due to being killed, which would send you home, not to a Telehub } @@ -4160,7 +4161,7 @@ void process_teleport_finish(LLMessageSystem* msg, void**) // Make sure we're standing gAgent.standUp(); - + // now, use the circuit info to tell simulator about us! LL_INFOS("Messaging") << "process_teleport_finish() Enabling " << sim_host << " with code " << msg->mOurCircuitCode << LL_ENDL; @@ -4294,14 +4295,15 @@ void process_agent_movement_complete(LLMessageSystem* msg, void**) gAgent.sendAgentSetAppearance(); // [RLVa:KB] - Checked: 2009-07-04 (RLVa-1.0.0a) - if ( (isAgentAvatarValid()) && (!gRlvHandler.hasBehaviour(RLV_BHVR_SHOWLOC)) ) + if (isAgentAvatarValid() && !gRlvHandler.hasBehaviour(RLV_BHVR_SHOWLOC)) // [/RLVa:KB] -// if (avatarp) +// if (isAgentAvatarValid()) { // Chat the "back" SLURL. (DEV-4907) - LLChat chat("Teleport completed from " + gAgent.getTeleportSourceSLURL()); + + LLChat chat(LLTrans::getString("completed_from") + " " + gAgent.getTeleportSourceSLURL()); chat.mSourceType = CHAT_SOURCE_SYSTEM; - LLFloaterChat::addChatHistory(chat); + LLFloaterChat::addChatHistory(chat); // Set the new position gAgentAvatarp->setPositionAgent(agent_pos); @@ -4314,7 +4316,7 @@ void process_agent_movement_complete(LLMessageSystem* msg, void**) } else { - // This is likely just the initial logging in phase. + // This is initial log-in or a region crossing gAgent.setTeleportState( LLAgent::TELEPORT_NONE ); if(LLStartUp::getStartupState() < STATE_STARTED) @@ -4529,6 +4531,7 @@ void send_agent_update(BOOL force_send, BOOL send_reliable) // MASK key_mask = gKeyboard->currentMask(TRUE); + if (key_mask & MASK_ALT || key_mask & MASK_CONTROL) { control_flags &= ~( AGENT_CONTROL_LBUTTON_DOWN | @@ -4557,7 +4560,7 @@ void send_agent_update(BOOL force_send, BOOL send_reliable) control_flag_change != 0 || flag_change != 0) { -/* + /* if (head_rot_chg < THRESHOLD_HEAD_ROT_QDOT) { //LL_INFOS("Messaging") << "head rot " << head_rotation << LL_ENDL; @@ -4579,7 +4582,7 @@ void send_agent_update(BOOL force_send, BOOL send_reliable) { LL_INFOS("Messaging") << "dcf = " << control_flag_change << LL_ENDL; } -*/ + */ duplicate_count = 0; } @@ -4918,7 +4921,7 @@ void process_sound_trigger(LLMessageSystem *msg, void **) if(owner_id != gAgent.getID() || !gSavedSettings.getBOOL("EnableGestureSoundsSelf")) return; } - + // //gAudiop->triggerSound(sound_id, owner_id, gain, LLAudioEngine::AUDIO_TYPE_SFX, pos_global); gAudiop->triggerSound(sound_id, owner_id, gain, LLAudioEngine::AUDIO_TYPE_SFX, pos_global, object_id); @@ -5327,7 +5330,7 @@ void process_avatar_appearance(LLMessageSystem *mesgsys, void **user_data) mesgsys->getUUIDFast(_PREHASH_Sender, _PREHASH_ID, uuid); LLVOAvatar* avatarp = gObjectList.findAvatar(uuid); - if( avatarp ) + if (avatarp) { avatarp->processAvatarAppearance( mesgsys ); } @@ -5649,7 +5652,6 @@ void process_time_dilation(LLMessageSystem *msg, void **user_data) */ - void process_money_balance_reply( LLMessageSystem* msg, void** ) { S32 balance = 0; @@ -5926,24 +5928,33 @@ void process_alert_core(const std::string& message, BOOL modal) } else { - // *TODO:translate - args["MESSAGE"] = text; + std::string new_msg =LLNotifications::instance().getGlobalString(text); + args["MESSAGE"] = new_msg; LLNotificationsUtil::add("SystemMessage", args); } } else if (modal) { - // *TODO:translate LLSD args; - args["ERROR_MESSAGE"] = message; + std::string new_msg =LLNotifications::instance().getGlobalString(message); + args["ERROR_MESSAGE"] = new_msg; LLNotificationsUtil::add("ErrorMessage", args); } else { - // *TODO:translate - LLSD args; - args["MESSAGE"] = message; - LLNotificationsUtil::add("SystemMessageTip", args); + // Hack fix for EXP-623 (blame fix on RN :)) to avoid a sim deploy + const std::string AUTOPILOT_CANCELED_MSG("Autopilot canceled"); + if (message.find(AUTOPILOT_CANCELED_MSG) == std::string::npos ) + { + LLSD args; + std::string new_msg =LLNotifications::instance().getGlobalString(message); + + std::string localized_msg; + bool is_message_localized = LLTrans::findString(localized_msg, new_msg); + + args["MESSAGE"] = is_message_localized ? localized_msg : new_msg; + LLNotificationsUtil::add("SystemMessageTip", args); + } } } @@ -6230,7 +6241,7 @@ bool script_question_cb(const LLSD& notification, const LLSD& response) { public: OfferMatcher(const LLUUID& to_block) : blocked_id(to_block) {} - BOOL matches(const LLNotificationPtr notification) const + bool matches(const LLNotificationPtr notification) const { if (notification->getName() == "ScriptQuestionCaution" || notification->getName() == "ScriptQuestion") @@ -6416,7 +6427,7 @@ void container_inventory_arrived(LLViewerObject* object, // create a new inventory category to put this in LLUUID cat_id; cat_id = gInventory.createNewCategory(gInventory.getRootFolderID(), - LLFolderType::FT_NONE, + LLFolderType::FT_NONE, LLTrans::getString("AcquiredItems")); LLInventoryObject::object_list_t::const_iterator it = inventory->begin(); @@ -6524,6 +6535,9 @@ void process_teleport_failed(LLMessageSystem *msg, void**) std::string big_reason; LLSD args; + // Let the interested parties know that teleport failed. + LLViewerParcelMgr::getInstance()->onTeleportFailed(); + // if we have additional alert data if (msg->has(_PREHASH_AlertInfo) && msg->getSizeFast(_PREHASH_AlertInfo, _PREHASH_Message) > 0) { @@ -6582,9 +6596,6 @@ void process_teleport_failed(LLMessageSystem *msg, void**) LLNotificationsUtil::add("CouldNotTeleportReason", args); - // Let the interested parties know that teleport failed. - LLViewerParcelMgr::getInstance()->onTeleportFailed(); - if( gAgent.getTeleportState() != LLAgent::TELEPORT_NONE ) { gAgent.setTeleportState( LLAgent::TELEPORT_NONE ); @@ -6608,7 +6619,7 @@ void process_teleport_local(LLMessageSystem *msg,void**) msg->getVector3Fast(_PREHASH_Info, _PREHASH_Position, pos); msg->getVector3Fast(_PREHASH_Info, _PREHASH_LookAt, look_at); msg->getU32Fast(_PREHASH_Info, _PREHASH_TeleportFlags, teleport_flags); - + if( gAgent.getTeleportState() != LLAgent::TELEPORT_NONE ) { if( gAgent.getTeleportState() == LLAgent::TELEPORT_LOCAL ) @@ -6782,6 +6793,7 @@ void handle_lure(const uuid_vec_t& ids) if (ids.empty()) return; if (!gAgent.getRegion()) return; + LLSD edit_args; // [RLVa:KB] - Version: 1.23.4 | Checked: 2009-07-04 (RLVa-1.0.0a) edit_args["REGION"] = @@ -6909,6 +6921,7 @@ const char* SCRIPT_DIALOG_HEADER = "Script Dialog:\n"; bool callback_script_dialog(const LLSD& notification, const LLSD& response) { LLNotificationForm form(notification["form"]); + std::string button = LLNotification::getSelectedOptionName(response); S32 button_idx = LLNotification::getSelectedOption(notification, response); // Didn't click "Ignore" @@ -6954,7 +6967,7 @@ void process_script_dialog(LLMessageSystem* msg, void**) return; // NaCl End - // For compability with OS grids first check for presence of extended packet before fetching data. +// For compability with OS grids first check for presence of extended packet before fetching data. LLUUID owner_id; if (gMessageSystem->getNumberOfBlocks("OwnerData") > 0) { @@ -6974,12 +6987,12 @@ void process_script_dialog(LLMessageSystem* msg, void**) std::string message; std::string first_name; std::string last_name; - std::string title; + std::string object_name; S32 chat_channel; msg->getString("Data", "FirstName", first_name); msg->getString("Data", "LastName", last_name); - msg->getString("Data", "ObjectName", title); + msg->getString("Data", "ObjectName", object_name); msg->getString("Data", "Message", message); msg->getS32("Data", "ChatChannel", chat_channel); @@ -7017,7 +7030,7 @@ void process_script_dialog(LLMessageSystem* msg, void**) } else { - for (i = 1; i < button_count; i++) + for (i = 1; i < button_count; i++) { std::string tdesc; msg->getString("Buttons", "ButtonLabel", tdesc, i); @@ -7026,7 +7039,7 @@ void process_script_dialog(LLMessageSystem* msg, void**) } LLSD args; - args["TITLE"] = title; + args["TITLE"] = object_name; args["MESSAGE"] = message; // args["CHANNEL"] = chat_channel; @@ -7090,7 +7103,7 @@ void callback_load_url_name(const LLUUID& id, const std::string& full_name, bool std::string owner_name; if (is_group) { - owner_name = full_name + " (group)"; + owner_name = full_name + LLTrans::getString("Group"); } else { @@ -7220,7 +7233,7 @@ void process_script_teleport_request(LLMessageSystem* msg, void**) return; // NaCl End if (!gSavedSettings.getBOOL("ScriptsCanShowUI")) return; - + std::string object_name; std::string sim_name; LLVector3 pos; @@ -7292,11 +7305,11 @@ void process_covenant_reply(LLMessageSystem* msg, void**) if (estate_owner_id.isNull()) { // mainland - covenant_text = "There is no Covenant provided for this Estate."; + covenant_text = LLTrans::getString("RegionNoCovenant"); } else { - covenant_text = "There is no Covenant provided for this Estate. The land on this estate is being sold by the Estate owner, not Linden Lab. Please contact the Estate Owner for sales details."; + covenant_text = LLTrans::getString("RegionNoCovenantOtherOwner"); } LLPanelEstateCovenant::updateCovenantText(covenant_text, covenant_id); LLPanelLandCovenant::updateCovenantText(covenant_text); @@ -7432,3 +7445,4 @@ void LLOfferInfo::forceResponse(InventoryOfferResponse response) params.functor(boost::bind(&LLOfferInfo::inventory_offer_callback, this, _1, _2)); LLNotifications::instance().forceResponse(params, response); } + diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index 186e7d57b..b8833d302 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -241,6 +241,7 @@ LLViewerObject::LLViewerObject(const LLUUID &id, const LLPCode pcode, LLViewerRe mNumFaces(0), mTimeDilation(1.f), mRotTime(0.f), + mAngularVelocityRot(), mState(0), mMedia(NULL), mClickAction(0), @@ -270,6 +271,7 @@ LLViewerObject::LLViewerObject(const LLUUID &id, const LLPCode pcode, LLViewerRe { mPositionAgent = mRegionp->getOriginAgent(); } + resetRot(); LLViewerObject::sNumObjects++; } @@ -2105,14 +2107,14 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, if (new_rot != getRotation() || new_angv != old_angv) { - if (new_rot != getRotation()) + if (new_angv != old_angv) { - setRotation(new_rot); + resetRot(); } - + + // Set the rotation of the object followed by adjusting for the accumulated angular velocity (llSetTargetOmega) + setRotation(new_rot * mAngularVelocityRot); setChanged(ROTATED | SILHOUETTE); - - resetRot(); } @@ -5498,8 +5500,13 @@ void LLViewerObject::applyAngularVelocity(F32 dt) ang_vel *= 1.f/omega; + // calculate the delta increment based on the object's angular velocity dQ.setQuat(angle, ang_vel); + + // accumulate the angular velocity rotations to re-apply in the case of an object update + mAngularVelocityRot *= dQ; + // Just apply the delta increment to the current rotation setRotation(getRotation()*dQ); setChanged(MOVED | SILHOUETTE); } @@ -5508,6 +5515,9 @@ void LLViewerObject::applyAngularVelocity(F32 dt) void LLViewerObject::resetRot() { mRotTime = 0.0f; + + // Reset the accumulated angular velocity rotation + mAngularVelocityRot.loadIdentity(); } U32 LLViewerObject::getPartitionType() const diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h index 2b9597510..791393533 100644 --- a/indra/newview/llviewerobject.h +++ b/indra/newview/llviewerobject.h @@ -736,6 +736,7 @@ protected: F32 mTimeDilation; // Time dilation sent with the object. F32 mRotTime; // Amount (in seconds) that object has rotated according to angular velocity (llSetTargetOmega) + LLQuaternion mAngularVelocityRot; // accumulated rotation from the angular velocity computations U8 mState; // legacy LLViewerObjectMedia* mMedia; // NULL if no media associated diff --git a/indra/newview/llviewerparcelmedia.cpp b/indra/newview/llviewerparcelmedia.cpp index 1349429ee..49cd7a1a3 100644 --- a/indra/newview/llviewerparcelmedia.cpp +++ b/indra/newview/llviewerparcelmedia.cpp @@ -467,7 +467,7 @@ 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); + LLHTTPClient::post(region_url, body, new LLHTTPClient::ResponderIgnore); } else { diff --git a/indra/newview/llviewerparcelmgr.cpp b/indra/newview/llviewerparcelmgr.cpp index a15722cda..9ed016d71 100644 --- a/indra/newview/llviewerparcelmgr.cpp +++ b/indra/newview/llviewerparcelmgr.cpp @@ -1310,7 +1310,7 @@ void LLViewerParcelMgr::sendParcelPropertiesUpdate(LLParcel* parcel, bool use_ag parcel->packMessage(body); llinfos << "Sending parcel properties update via capability to: " << url << llendl; - LLHTTPClient::post(url, body, new LLHTTPClient::Responder()); + LLHTTPClient::post(url, body, new LLHTTPClient::ResponderIgnore); } else { diff --git a/indra/newview/llviewerprecompiledheaders.h b/indra/newview/llviewerprecompiledheaders.h index 9fb55c7ee..3b8444b54 100644 --- a/indra/newview/llviewerprecompiledheaders.h +++ b/indra/newview/llviewerprecompiledheaders.h @@ -151,7 +151,7 @@ #include "llnamevalue.h" #include "llpacketack.h" #include "llpacketbuffer.h" -#include "llpacketring.h" +//#include "llpacketring.h" #include "llpartdata.h" #include "llregionhandle.h" #include "lltaskname.h" diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp index 75b2170fb..356cc6cac 100644 --- a/indra/newview/llviewershadermgr.cpp +++ b/indra/newview/llviewershadermgr.cpp @@ -576,10 +576,10 @@ void LLViewerShaderMgr::setShaders() //Flag base shader objects for deletion //Don't worry-- they won't be deleted until no programs refrence them. - std::map::iterator it = mShaderObjects.begin(); + std::multimap::iterator it = mShaderObjects.begin(); for(; it!=mShaderObjects.end();++it) - if(it->second) - glDeleteObjectARB(it->second); + if(it->second.mHandle) + glDeleteObjectARB(it->second.mHandle); mShaderObjects.clear(); } else diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp index 90806a724..d685b951e 100644 --- a/indra/newview/llviewertexturelist.cpp +++ b/indra/newview/llviewertexturelist.cpp @@ -277,7 +277,7 @@ void LLViewerTextureList::shutdown() break; } - if (count > 0 && !gDirUtilp->getLindenUserDir(true).empty()) + if (count > 0 && !gDirUtilp->getLindenUserDir(true).empty()) { std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, get_texture_list_name()); llofstream file; diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index c2f4d51e0..4b27f6a2a 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -2099,7 +2099,9 @@ LLViewerObject* LLVOAvatar::lineSegmentIntersectRiggedAttachments(const LLVector LLVector3* normal, LLVector3* bi_normal) { - if (isSelf() && !gAgent.needsRenderAvatar()) + static const LLCachedControl allow_mesh_picking("SGAllowRiggedMeshSelection"); + + if (!allow_mesh_picking || (isSelf() && !gAgent.needsRenderAvatar())) { return NULL; } diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp index 0362adaf4..78a000a93 100644 --- a/indra/newview/llvoiceclient.cpp +++ b/indra/newview/llvoiceclient.cpp @@ -1113,7 +1113,7 @@ LLVoiceClient::LLVoiceClient() gVoiceClient = this; mWriteInProgress = false; mAreaVoiceDisabled = false; - mPTT = true; + mPTT = false; mUserPTTState = false; mMuteMic = false; mSessionTerminateRequested = false; diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index 29d6b24f3..1e1ca8bad 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -1044,9 +1044,9 @@ BOOL LLVOVolume::calcLOD() F32 radius; F32 distance; - if (mDrawable->isState(LLDrawable::RIGGED) && getAvatar()) + if (mDrawable->isState(LLDrawable::RIGGED) && getAvatar() && getAvatar()->mDrawable) { - LLVOAvatar* avatar = getAvatar(); + LLVOAvatar* avatar = getAvatar(); distance = avatar->mDrawable->mDistanceWRTCamera; radius = avatar->getBinRadius(); } @@ -1605,9 +1605,13 @@ S32 LLVOVolume::setTEColor(const U8 te, const LLColor4& color) } else if (color != tep->getColor()) { - if (color.mV[3] != tep->getColor().mV[3]) + F32 old_alpha = tep->getColor().mV[3]; + if ((color.mV[3] != old_alpha) && (color.mV[3] == 1.f || old_alpha == 1.f)) { gPipeline.markTextured(mDrawable); + //treat this alpha change as an LoD update since render batches may need to get rebuilt + mLODChanged = TRUE; + gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, FALSE); } retval = LLPrimitive::setTEColor(te, color); if (mDrawable.notNull() && retval) @@ -2827,10 +2831,11 @@ BOOL LLVOVolume::lineSegmentIntersect(const LLVector3& start, const LLVector3& e if (mDrawable->isState(LLDrawable::RIGGED)) { - if (gFloaterTools->getVisible() && getAvatar()->isSelf()) + static const LLCachedControl allow_mesh_picking("SGAllowRiggedMeshSelection"); + if (allow_mesh_picking && gFloaterTools->getVisible() && getAvatar()->isSelf()) { updateRiggedVolume(); - genBBoxes(FALSE); + //genBBoxes(FALSE); volume = mRiggedVolume; transform = false; } diff --git a/indra/newview/llwaterparammanager.cpp b/indra/newview/llwaterparammanager.cpp index ab95ee14d..307edffdd 100644 --- a/indra/newview/llwaterparammanager.cpp +++ b/indra/newview/llwaterparammanager.cpp @@ -76,8 +76,6 @@ #include "llagentcamera.h" -#include "curl/curl.h" - LLWaterParamManager::LLWaterParamManager() : mFogColor(22.f/255.f, 43.f/255.f, 54.f/255.f, 0.0f, 0.0f, "waterFogColor", "WaterFogColor"), mFogDensity(4, "waterFogDensity", 2), diff --git a/indra/newview/llwearablelist.cpp b/indra/newview/llwearablelist.cpp index ed81936f7..6f6411ce3 100644 --- a/indra/newview/llwearablelist.cpp +++ b/indra/newview/llwearablelist.cpp @@ -177,8 +177,7 @@ void LLWearableList::processGetAssetReply( const char* filename, const LLAssetID else { LLSD args; - // *TODO:translate - args["TYPE"] = LLAssetType::lookupHumanReadable(data->mAssetType); + args["TYPE"] =LLTrans::getString(LLAssetType::lookupHumanReadable(data->mAssetType)); if (isNewWearable) { LLNotificationsUtil::add("InvalidWearable"); @@ -230,7 +229,7 @@ LLWearable* LLWearableList::createNewWearable( LLWearableType::EType type ) LLWearable *wearable = generateNewWearable(); wearable->setType( type ); - std::string name = LLWearableType::getTypeDefaultNewName(wearable->getType()); + std::string name = LLTrans::getString( LLWearableType::getTypeDefaultNewName(wearable->getType()) ); wearable->setName( name ); LLPermissions perm; diff --git a/indra/newview/llwlparammanager.cpp b/indra/newview/llwlparammanager.cpp index a87487f55..284da40cd 100644 --- a/indra/newview/llwlparammanager.cpp +++ b/indra/newview/llwlparammanager.cpp @@ -77,7 +77,6 @@ #include "llviewerregion.h" #include "llassetuploadresponders.h" -#include "curl/curl.h" #include "llstreamtools.h" // [RLVa:KB] - Checked: 2011-09-04 (RLVa-1.4.1a) | Added: RLVa-1.4.1a diff --git a/indra/newview/llworld.cpp b/indra/newview/llworld.cpp index 6f92bedf9..a265b3613 100644 --- a/indra/newview/llworld.cpp +++ b/indra/newview/llworld.cpp @@ -61,6 +61,7 @@ #include "message.h" #include "pipeline.h" #include "llappviewer.h" // for do_disconnect() +#include "llpacketring.h" #include #include @@ -779,8 +780,8 @@ void LLWorld::updateNetStats() S32 packets_out = gMessageSystem->mPacketsOut - mLastPacketsOut; S32 packets_lost = gMessageSystem->mDroppedPackets - mLastPacketsLost; - S32 actual_in_bits = gMessageSystem->mPacketRing.getAndResetActualInBits(); - S32 actual_out_bits = gMessageSystem->mPacketRing.getAndResetActualOutBits(); + S32 actual_in_bits = gMessageSystem->mPacketRing->getAndResetActualInBits(); + S32 actual_out_bits = gMessageSystem->mPacketRing->getAndResetActualOutBits(); LLViewerStats::getInstance()->mActualInKBitStat.addValue(actual_in_bits/1024.f); LLViewerStats::getInstance()->mActualOutKBitStat.addValue(actual_out_bits/1024.f); LLViewerStats::getInstance()->mKBitStat.addValue(bits/1024.f); diff --git a/indra/newview/llxmlrpctransaction.cpp b/indra/newview/llxmlrpctransaction.cpp index 11dc653aa..227904f90 100644 --- a/indra/newview/llxmlrpctransaction.cpp +++ b/indra/newview/llxmlrpctransaction.cpp @@ -43,6 +43,11 @@ #include "llappviewer.h" #include "hippogridmanager.h" +#include "statemachine/aicurleasyrequeststatemachine.h" + +#ifdef CWDEBUG +#include +#endif LLXMLRPCValue LLXMLRPCValue::operator[](const char* id) const { @@ -154,7 +159,7 @@ class LLXMLRPCTransaction::Impl public: typedef LLXMLRPCTransaction::Status Status; - LLCurlEasyRequest* mCurlRequest; + AICurlEasyRequestStateMachine* mCurlEasyRequestStateMachinePtr; Status mStatus; CURLcode mCurlCode; @@ -163,8 +168,6 @@ public: LLCurl::TransferInfo mTransferInfo; std::string mURI; - char* mRequestText; - int mRequestTextSize; std::string mProxyAddress; @@ -176,7 +179,8 @@ public: const std::string& method, LLXMLRPCValue params, bool useGzip); ~Impl(); - bool process(); + bool is_finished(void) const; + void curlEasyRequestCallback(bool success); void setStatus(Status code, const std::string& message = "", const std::string& uri = ""); @@ -191,10 +195,9 @@ private: LLXMLRPCTransaction::Impl::Impl(const std::string& uri, XMLRPC_REQUEST request, bool useGzip) - : mCurlRequest(0), + : mCurlEasyRequestStateMachinePtr(NULL), mStatus(LLXMLRPCTransaction::StatusNotStarted), mURI(uri), - mRequestText(0), mResponse(0) { init(request, useGzip); @@ -203,10 +206,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), + : mCurlEasyRequestStateMachinePtr(NULL), mStatus(LLXMLRPCTransaction::StatusNotStarted), mURI(uri), - mRequestText(0), mResponse(0) { XMLRPC_REQUEST request = XMLRPC_RequestNew(); @@ -217,177 +219,170 @@ LLXMLRPCTransaction::Impl::Impl(const std::string& uri, init(request, useGzip); } - - +// Store pointer to data allocated with XMLRPC_REQUEST_ToXML and call XMLRPC_Free to free it upon destruction. +class AIXMLRPCData : public AIPostField +{ + public: + AIXMLRPCData(char const* allocated_data) : AIPostField(allocated_data) { } + /*virtual*/ ~AIXMLRPCData() { XMLRPC_Free(const_cast(mData)); mData = NULL; } +}; void LLXMLRPCTransaction::Impl::init(XMLRPC_REQUEST request, bool useGzip) { - if (!mCurlRequest) { - mCurlRequest = new LLCurlEasyRequest(); + try + { + mCurlEasyRequestStateMachinePtr = new AICurlEasyRequestStateMachine(false); + } + catch(AICurlNoEasyHandle const& error) + { + llwarns << "Failed to initialize LLXMLRPCTransaction: " << error.what() << llendl; + setStatus(StatusOtherError, "No curl easy handle"); + return; + } + AICurlEasyRequest_wat curlEasyRequest_w(*mCurlEasyRequestStateMachinePtr->mCurlEasyRequest); + + curlEasyRequest_w->setWriteCallback(&curlDownloadCallback, (void*)this); + BOOL vefifySSLCert = !gSavedSettings.getBOOL("NoVerifySSLCert"); + curlEasyRequest_w->setopt(CURLOPT_SSL_VERIFYPEER, vefifySSLCert); + curlEasyRequest_w->setopt(CURLOPT_SSL_VERIFYHOST, vefifySSLCert ? 2 : 0); + // Be a little impatient about establishing connections. + curlEasyRequest_w->setopt(CURLOPT_CONNECTTIMEOUT, 40L); + + /* Setting the DNS cache timeout to -1 disables it completely. + This might help with bug #503 */ + curlEasyRequest_w->setopt(CURLOPT_DNS_CACHE_TIMEOUT, -1L); + + curlEasyRequest_w->addHeader("Content-Type: text/xml"); + + if (useGzip) + { + curlEasyRequest_w->setoptString(CURLOPT_ENCODING, ""); + } + + int requestTextSize; + char* requestText = XMLRPC_REQUEST_ToXML(request, &requestTextSize); + if (requestText) + { + curlEasyRequest_w->setPost(new AIXMLRPCData(requestText), requestTextSize); + } + else + { + setStatus(StatusOtherError); + } + + curlEasyRequest_w->finalizeRequest(mURI); } - - LLProxy::getInstance()->applyProxySettings(mCurlRequest); - -// mCurlRequest->setopt(CURLOPT_VERBOSE, 1); // usefull for debugging - mCurlRequest->setopt(CURLOPT_NOSIGNAL, 1); - mCurlRequest->setWriteCallback(&curlDownloadCallback, (void*)this); - BOOL vefifySSLCert = !gSavedSettings.getBOOL("NoVerifySSLCert"); - 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); - - /* Setting the DNS cache timeout to -1 disables it completely. - This might help with bug #503 */ - mCurlRequest->setopt(CURLOPT_DNS_CACHE_TIMEOUT, -1); - - mCurlRequest->slist_append("Content-Type: text/xml"); - - if (useGzip) + if (mStatus == LLXMLRPCTransaction::StatusNotStarted) // It could be LLXMLRPCTransaction::StatusOtherError. { - mCurlRequest->setoptString(CURLOPT_ENCODING, ""); - } - - mRequestText = XMLRPC_REQUEST_ToXML(request, &mRequestTextSize); - if (mRequestText) - { - mCurlRequest->setoptString(CURLOPT_POSTFIELDS, mRequestText); - mCurlRequest->setopt(CURLOPT_POSTFIELDSIZE, mRequestTextSize); + mCurlEasyRequestStateMachinePtr->run(boost::bind(&LLXMLRPCTransaction::Impl::curlEasyRequestCallback, this, _1)); + setStatus(LLXMLRPCTransaction::StatusStarted); } else { - setStatus(StatusOtherError); + // This deletes the statemachine immediately. + mCurlEasyRequestStateMachinePtr->kill(); + mCurlEasyRequestStateMachinePtr = NULL; } - - mCurlRequest->sendRequest(mURI); } - LLXMLRPCTransaction::Impl::~Impl() { + if (mCurlEasyRequestStateMachinePtr && mCurlEasyRequestStateMachinePtr->running()) + { + llwarns << "Calling LLXMLRPCTransaction::Impl::~Impl while mCurlEasyRequestStateMachinePtr is still running" << llendl; + mCurlEasyRequestStateMachinePtr->abort(); + } + if (mResponse) { XMLRPC_RequestFree(mResponse, 1); } - - if (mRequestText) - { - XMLRPC_Free(mRequestText); - } - - delete mCurlRequest; - mCurlRequest = NULL ; } -bool LLXMLRPCTransaction::Impl::process() +bool LLXMLRPCTransaction::Impl::is_finished(void) const { - if(!mCurlRequest || !mCurlRequest->getEasy()) - { - llwarns << "transaction failed." << llendl ; + // Nothing to process anymore. Just wait till the statemachine finished. + return mStatus != LLXMLRPCTransaction::StatusNotStarted && + mStatus != LLXMLRPCTransaction::StatusStarted && + mStatus != LLXMLRPCTransaction::StatusDownloading; +} - delete mCurlRequest ; - mCurlRequest = NULL ; - return true ; //failed, quit. +void LLXMLRPCTransaction::Impl::curlEasyRequestCallback(bool success) +{ + llassert(mStatus == LLXMLRPCTransaction::StatusStarted || mStatus == LLXMLRPCTransaction::StatusDownloading); + + AICurlEasyRequestStateMachine* state_machine = mCurlEasyRequestStateMachinePtr; + // We're done with the statemachine, one way or another. + // Set mCurlEasyRequestStateMachinePtr to NULL so we won't call mCurlEasyRequestStateMachinePtr->running() in the destructor. + // Note that the state machine auto-cleaning: it will be deleted by the main-thread after this function returns. + mCurlEasyRequestStateMachinePtr = NULL; + + if (!success) + { + // AICurlEasyRequestStateMachine did abort. + // This currently only happens when libcurl didn't finish before the timer expired. + std::ostringstream msg; + F32 timeout_value = gSavedSettings.getF32("CurlRequestTimeOut"); + msg << "Connection to " << mURI << " timed out (" << timeout_value << " s)!"; + if (timeout_value < 40) + { + msg << "\nTry increasing CurlRequestTimeOut in Debug Settings."; + } + setStatus(LLXMLRPCTransaction::StatusOtherError, msg.str()); + return; } - switch(mStatus) + AICurlEasyRequest_wat curlEasyRequest_w(*state_machine->mCurlEasyRequest); + CURLcode result; + curlEasyRequest_w->getResult(&result, &mTransferInfo); + + if (result != CURLE_OK) { - case LLXMLRPCTransaction::StatusComplete: - case LLXMLRPCTransaction::StatusCURLError: - case LLXMLRPCTransaction::StatusXMLRPCError: - case LLXMLRPCTransaction::StatusOtherError: - { - return true; - } - - case LLXMLRPCTransaction::StatusNotStarted: - { - setStatus(LLXMLRPCTransaction::StatusStarted); - break; - } - - default: - { - // continue onward - } + setCurlStatus(result); + llwarns << "LLXMLRPCTransaction CURL error " + << mCurlCode << ": " << curlEasyRequest_w->getErrorString() << llendl; + llwarns << "LLXMLRPCTransaction request URI: " + << mURI << llendl; + + return; } - //const F32 MAX_PROCESSING_TIME = 0.05f; - //LLTimer timer; + setStatus(LLXMLRPCTransaction::StatusComplete); - mCurlRequest->perform(); + mResponse = XMLRPC_REQUEST_FromXML( + mResponseText.data(), mResponseText.size(), NULL); - /*while (mCurlRequest->perform() > 0) + bool hasError = false; + bool hasFault = false; + int faultCode = 0; + std::string faultString; + + LLXMLRPCValue error(XMLRPC_RequestGetError(mResponse)); + if (error.isValid()) { - if (timer.getElapsedTimeF32() >= MAX_PROCESSING_TIME) - { - return false; - } - }*/ - - while(1) - { - CURLcode result; - bool newmsg = mCurlRequest->getResult(&result, &mTransferInfo); - if (newmsg) - { - if (result != CURLE_OK) - { - setCurlStatus(result); - llwarns << "LLXMLRPCTransaction CURL error " - << mCurlCode << ": " << mCurlRequest->getErrorString() << llendl; - llwarns << "LLXMLRPCTransaction request URI: " - << mURI << llendl; - - return true; - } - - setStatus(LLXMLRPCTransaction::StatusComplete); - - 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); - - llwarns << "LLXMLRPCTransaction XMLRPC " - << (hasError ? "error " : "fault ") - << faultCode << ": " - << faultString << llendl; - llwarns << "LLXMLRPCTransaction request URI: " - << mURI << llendl; - } - - return true; - } - else - { - break; // done - } + 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); + + llwarns << "LLXMLRPCTransaction XMLRPC " + << (hasError ? "error " : "fault ") + << faultCode << ": " + << faultString << llendl; + llwarns << "LLXMLRPCTransaction request URI: " + << mURI << llendl; } - - return false; } void LLXMLRPCTransaction::Impl::setStatus(Status status, @@ -483,6 +478,13 @@ size_t LLXMLRPCTransaction::Impl::curlDownloadCallback( size_t n = size * nmemb; +#ifdef CWDEBUG + if (n < 80) + Dout(dc::curl, "Entering LLXMLRPCTransaction::Impl::curlDownloadCallback(\"" << buf2str(data, n) << "\", " << size << ", " << nmemb << ", " << user_data << ")"); + else + Dout(dc::curl, "Entering LLXMLRPCTransaction::Impl::curlDownloadCallback(\"" << buf2str(data, 40) << "\"...\"" << buf2str(data + n - 40, 40) << "\", " << size << ", " << nmemb << ", " << user_data << ")"); +#endif + impl.mResponseText.append(data, n); if (impl.mStatus == LLXMLRPCTransaction::StatusStarted) @@ -511,9 +513,9 @@ LLXMLRPCTransaction::~LLXMLRPCTransaction() delete &impl; } -bool LLXMLRPCTransaction::process() +bool LLXMLRPCTransaction::is_finished(void) const { - return impl.process(); + return impl.is_finished(); } LLXMLRPCTransaction::Status LLXMLRPCTransaction::status(int* curlCode) diff --git a/indra/newview/llxmlrpctransaction.h b/indra/newview/llxmlrpctransaction.h index 528451fcb..6d70f8ff1 100644 --- a/indra/newview/llxmlrpctransaction.h +++ b/indra/newview/llxmlrpctransaction.h @@ -110,8 +110,8 @@ public: StatusOtherError } Status; - bool process(); - // run the request a little, returns true when done + bool is_finished(void) const; + // Returns true when done. Status status(int* curlCode); // return status, and extended CURL code, if code isn't null @@ -127,7 +127,7 @@ public: // retains ownership of the result object, don't free it F64 transferRate(); - // only valid if StsatusComplete, otherwise 0.0 + // only valid if StatusComplete, otherwise 0.0 private: class Impl; diff --git a/indra/newview/m7wlinterface.cpp b/indra/newview/m7wlinterface.cpp index 365e6165a..7906ab81a 100644 --- a/indra/newview/m7wlinterface.cpp +++ b/indra/newview/m7wlinterface.cpp @@ -45,6 +45,22 @@ void M7WindlightInterface::receiveMessage(LLMessageSystem* msg) _PREHASH_ParamList, _PREHASH_Parameter, buf, size, i, 249); +#if 0 + std::ostringstream wldump; + char hex []= "0123456789abcdefRRRR"; + for (int i = 0; i<250; ++i){ + wldump << "\\x" << hex[((U8)buf[i]&0xF0)>>4] << hex[(U8)buf[i]&0x0F]; + } + llinfos << "Received LightShare data: " << wldump.str() << llendl; +#endif + char default_windlight[] = "\x00\x00\x80\x40\x00\x00\x18\x42\x00\x00\x80\x42\x00\x00\x80\x40\x00\x00\x80\x3e\x00\x00\x00\x40\x00\x00\x00\x40\x00\x00\x00\x40\xcd\xcc\xcc\x3e\x00\x00\x00\x3f\x8f\xc2\xf5\x3c\xcd\xcc\x4c\x3e\x0a\xd7\x23\x3d\x66\x66\x86\x3f\x3d\x0a\xd7\xbe\x7b\x14\x8e\x3f\xe1\x7a\x94\xbf\x82\x2d\xed\x49\x9a\x6c\xf6\x1c\xcb\x89\x6d\xf5\x4f\x42\xcd\xf4\x00\x00\x80\x3e\x00\x00\x80\x3e\x0a\xd7\xa3\x3e\x0a\xd7\xa3\x3e\x5c\x8f\x42\x3e\x8f\xc2\xf5\x3d\xae\x47\x61\x3e\x5c\x8f\xc2\x3e\x5c\x8f\xc2\x3e\x33\x33\x33\x3f\xec\x51\x38\x3e\xcd\xcc\x4c\x3f\x8f\xc2\x75\x3e\xb8\x1e\x85\x3e\x9a\x99\x99\x3e\x9a\x99\x99\x3e\xd3\x4d\xa2\x3e\x33\x33\xb3\x3e\x33\x33\xb3\x3e\x33\x33\xb3\x3e\x33\x33\xb3\x3e\x00\x00\x00\x00\xcd\xcc\xcc\x3d\x00\x00\xe0\x3f\x00\x00\x80\x3f\x00\x00\x00\x00\x85\xeb\xd1\x3e\x85\xeb\xd1\x3e\x85\xeb\xd1\x3e\x85\xeb\xd1\x3e\x00\x00\x80\x3f\x14\xae\x07\x3f\x00\x00\x80\x3f\x71\x3d\x8a\x3e\x3d\x0a\xd7\x3e\x00\x00\x80\x3f\x14\xae\x07\x3f\x8f\xc2\xf5\x3d\xcd\xcc\x4c\x3e\x0a\xd7\x23\x3c\x45\x06\x00"; + if(!memcmp(default_windlight, buf, sizeof(default_windlight))) + { + llinfos << "LightShare matches default" << llendl; + receiveReset(); + return; + } + LLWaterParamManager::getInstance()->getParamSet("Default", mWater); Meta7WindlightPacket* wl = (Meta7WindlightPacket*)buf; @@ -120,6 +136,7 @@ void M7WindlightInterface::receiveMessage(LLMessageSystem* msg) void M7WindlightInterface::receiveReset() { + llinfos << "Received LightShare reset" << llendl; mHasOverride = false; LLEnvManagerNew::getInstance()->usePrefs(); } diff --git a/indra/newview/meta7windlight.h b/indra/newview/meta7windlight.h index 04ce86d46..03af81165 100644 --- a/indra/newview/meta7windlight.h +++ b/indra/newview/meta7windlight.h @@ -31,6 +31,9 @@ #include "linden_common.h" +#pragma pack(push) +#pragma pack(1) + struct M7Color3{ M7Color3(){}; M7Color3(F32 pRed, F32 pGreen, F32 pBlue) @@ -124,8 +127,8 @@ struct Meta7WindlightPacket { char cloudScrollXLock; char cloudScrollYLock; char drawClassicClouds; - - }; +#pragma pack(pop) + #endif diff --git a/indra/newview/scriptcounter.cpp b/indra/newview/scriptcounter.cpp index 37acc1e37..3da258c28 100644 --- a/indra/newview/scriptcounter.cpp +++ b/indra/newview/scriptcounter.cpp @@ -43,6 +43,7 @@ #include "llviewercontrol.h" #include "llviewernetwork.h" #include "llviewerobject.h" +#include "llpacketring.h" #include ScriptCounter* ScriptCounter::sInstance; @@ -201,8 +202,8 @@ void ScriptCounter::serializeSelection(bool delScript) F32 throttle = gSavedSettings.getF32("OutBandwidth"); if((throttle == 0.f) || (throttle > 128000.f)) { - gMessageSystem->mPacketRing.setOutBandwidth(128000); - gMessageSystem->mPacketRing.setUseOutThrottle(TRUE); + gMessageSystem->mPacketRing->setOutBandwidth(128000); + gMessageSystem->mPacketRing->setUseOutThrottle(TRUE); } showResult(llformat("Counting scripts, please wait...")); if((objectCount == 1) && !(foo->isAvatar())) @@ -340,13 +341,13 @@ void ScriptCounter::completechk() F32 throttle = gSavedSettings.getF32("OutBandwidth"); if(throttle != 0.f) { - gMessageSystem->mPacketRing.setOutBandwidth(throttle); - gMessageSystem->mPacketRing.setUseOutThrottle(TRUE); + gMessageSystem->mPacketRing->setOutBandwidth(throttle); + gMessageSystem->mPacketRing->setUseOutThrottle(TRUE); } else { - gMessageSystem->mPacketRing.setOutBandwidth(0.0); - gMessageSystem->mPacketRing.setUseOutThrottle(FALSE); + gMessageSystem->mPacketRing->setOutBandwidth(0.0); + gMessageSystem->mPacketRing->setUseOutThrottle(FALSE); } llinfos << "Sending readout to chat..." << llendl; showResult(user_msg); diff --git a/indra/newview/skins/default/xui/en-us/floater_god_tools.xml b/indra/newview/skins/default/xui/en-us/floater_god_tools.xml index c4dd40f74..bb8e1c8a8 100644 --- a/indra/newview/skins/default/xui/en-us/floater_god_tools.xml +++ b/indra/newview/skins/default/xui/en-us/floater_god_tools.xml @@ -154,6 +154,10 @@ + + (no target) + - - You are the only user in this session. - - - [NAME] is offline. - - - Click the [BUTTON NAME] button to accept/connect to this voice chat. - - - You have muted this resident. Sending a message will automatically unmute them. - - - - Error making request, please try again later. - - - You do not have sufficient permissions. - - - The session no longer exists - - - You do not have that ability. - - - You are not a session moderator. - - - A group moderator disabled your text chat. - - - - - Unable to add users to chat session with [RECIPIENT]. - - - Unable to send your message to the chat session with [RECIPIENT]. - - - You have been removed from the group. - - - You no longer have the ability to be in the chat session. - diff --git a/indra/newview/skins/default/xui/en-us/floater_instant_message.xml b/indra/newview/skins/default/xui/en-us/floater_instant_message.xml index 233b7645d..c0b222733 100644 --- a/indra/newview/skins/default/xui/en-us/floater_instant_message.xml +++ b/indra/newview/skins/default/xui/en-us/floater_instant_message.xml @@ -17,7 +17,7 @@ RP Mode