Merged in lindenlab/viewer-release

master
AndreyL ProductEngine 2016-04-01 03:56:20 +03:00
commit eb413ec41e
361 changed files with 20267 additions and 31218 deletions

View File

@ -513,3 +513,4 @@ e821ef17c6edea4a59997719d8ba416d8c16e143 3.8.5-release
ae3297cdd03ab14f19f3811acbc4acd3eb600336 4.0.0-release
759710a9acef61aaf7b69f4bc4a5a913de87ad8a 4.0.1-release
e9d350764dfbf5a46229e627547ef5c1b1eeef00 4.0.2-release
86dfba7ec4332c323025ebeacd8bf343ed0d8cfd 4.0.3-release

View File

@ -4,7 +4,7 @@
# https://wiki.secondlife.com/wiki/Automated_Build_System
# Global setting for now...
# Global setting for now....
Darwin.symbolfiles = "newview/Release/secondlife-symbols-darwin.tar.bz2"
CYGWIN.symbolfiles = "newview/Release/secondlife-symbols-windows.tar.bz2"
Linux.symbolfiles = "newview/secondlife-symbols-linux.tar.bz2"

View File

@ -87,60 +87,6 @@
<key>version</key>
<string>1.4.5.297252</string>
</map>
<key>ares</key>
<map>
<key>copyright</key>
<string>Copyright 1998 by the Massachusetts Institute of Technology.</string>
<key>description</key>
<string>C-ares, an asynchronous resolver library.</string>
<key>license</key>
<string>c-ares</string>
<key>license_file</key>
<string>LICENSES/c-ares.txt</string>
<key>name</key>
<string>ares</string>
<key>platforms</key>
<map>
<key>darwin</key>
<map>
<key>archive</key>
<map>
<key>hash</key>
<string>637b4996f703f3e5bf835d847fc4cb81</string>
<key>url</key>
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/ares_3p-update-ares/rev/295506/arch/Darwin/installer/ares-1.10.0.295506-darwin-295506.tar.bz2</string>
</map>
<key>name</key>
<string>darwin</string>
</map>
<key>linux</key>
<map>
<key>archive</key>
<map>
<key>hash</key>
<string>7771d3653a0daf22d35bf96055d02d9a</string>
<key>url</key>
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/ares_3p-update-ares/rev/295506/arch/Linux/installer/ares-1.10.0.295506-linux-295506.tar.bz2</string>
</map>
<key>name</key>
<string>linux</string>
</map>
<key>windows</key>
<map>
<key>archive</key>
<map>
<key>hash</key>
<string>f044de05e704d3f3fb6934adf42447c2</string>
<key>url</key>
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/ares_3p-update-ares/rev/295506/arch/CYGWIN/installer/ares-1.10.0.295506-windows-295506.tar.bz2</string>
</map>
<key>name</key>
<string>windows</string>
</map>
</map>
<key>version</key>
<string>1.10.0.295506</string>
</map>
<key>boost</key>
<map>
<key>copyright</key>
@ -266,9 +212,9 @@
<key>archive</key>
<map>
<key>hash</key>
<string>d1c5125650a339a5209f429c70f4d395</string>
<string>ad0061db7188a1b9a974eb0512eeeb8d</string>
<key>url</key>
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/curl_3p-update-curl/rev/297172/arch/Darwin/installer/curl-7.38.0.297172-darwin-297172.tar.bz2</string>
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-curl/rev/312763/arch/Darwin/installer/curl-7.47.0.312763-darwin-312763.tar.bz2</string>
</map>
<key>name</key>
<string>darwin</string>
@ -278,9 +224,9 @@
<key>archive</key>
<map>
<key>hash</key>
<string>ee6c089ee193e551040d610befc5d1c1</string>
<string>f49d4ed203b03852a3f6b01b18319f7a</string>
<key>url</key>
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/curl_3p-update-curl/rev/297172/arch/Linux/installer/curl-7.38.0.297172-linux-297172.tar.bz2</string>
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-curl/rev/312763/arch/Linux/installer/curl-7.47.0.312763-linux-312763.tar.bz2</string>
</map>
<key>name</key>
<string>linux</string>
@ -290,16 +236,18 @@
<key>archive</key>
<map>
<key>hash</key>
<string>fdeca7cbc074a88d2701d74a31d21bd8</string>
<string>5e0d4f4a5a5bbcba610aafbb91c30b2b</string>
<key>hash_algorithm</key>
<string>md5</string>
<key>url</key>
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/curl_3p-update-curl/rev/297172/arch/CYGWIN/installer/curl-7.38.0.297172-windows-297172.tar.bz2</string>
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-curl/rev/312763/arch/CYGWIN/installer/curl-7.47.0.312763-windows-312763.tar.bz2</string>
</map>
<key>name</key>
<string>windows</string>
</map>
</map>
<key>version</key>
<string>7.38.0.297172</string>
<string>7.47.0.312763</string>
</map>
<key>db</key>
<map>
@ -696,9 +644,9 @@
<key>archive</key>
<map>
<key>hash</key>
<string>f577144536fd7c9d26d9f989acf17857</string>
<string>44c596c659d32a86972ac9c6f206cb68</string>
<key>url</key>
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/glh-linear_3p-update-glh-linear/rev/297692/arch/Linux/installer/glh_linear-0.0.0-common-297692.tar.bz2</string>
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/glh-linear_3p-glh-linear/rev/303446/arch/Linux/installer/glh_linear-0.0.0-common-303446.tar.bz2</string>
</map>
<key>name</key>
<string>common</string>
@ -1506,9 +1454,9 @@
<key>archive</key>
<map>
<key>hash</key>
<string>ce1261a54d877ab5530ae6a9134a77a3</string>
<string>faa1e5b7cf70c143caabe190fa5588ce</string>
<key>url</key>
<string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/llappearance_viewer-update-llappearance-utility/rev/294906/arch/Linux/installer/llappearance_utility-0.0.1-linux-294906.tar.bz2</string>
<string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/llappearance_viewer-update-llappearance-utility/rev/304432/arch/Linux/installer/llappearance_utility-0.0.1-linux-304432.tar.bz2</string>
</map>
<key>name</key>
<string>linux</string>
@ -2068,9 +2016,9 @@
<key>archive</key>
<map>
<key>hash</key>
<string>68a8fab5ad3a180487598d3a3e0d57b1</string>
<string>3dd9bf4185bf2df413d890ca9eeab76c</string>
<key>url</key>
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/slvoice_3p-update-slvoice/rev/298329/arch/Darwin/installer/slvoice-4.6.0017.21209.298329-darwin-298329.tar.bz2</string>
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/vivox_3p-slvoice/rev/302004/arch/Darwin/installer/slvoice-4.6.0017.22050.302004-darwin-302004.tar.bz2</string>
</map>
<key>name</key>
<string>darwin</string>
@ -2080,9 +2028,9 @@
<key>archive</key>
<map>
<key>hash</key>
<string>48ed7ddcf93fa3c751d677f5eb5f9367</string>
<string>06c3a9b1005249f0c94a8b9f3775f3d3</string>
<key>url</key>
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/slvoice_3p-update-slvoice/rev/298329/arch/Linux/installer/slvoice-3.2.0002.10426.298329-linux-298329.tar.bz2</string>
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/vivox_3p-slvoice/rev/302004/arch/Linux/installer/slvoice-3.2.0002.10426.302004-linux-302004.tar.bz2</string>
</map>
<key>name</key>
<string>linux</string>
@ -2092,16 +2040,16 @@
<key>archive</key>
<map>
<key>hash</key>
<string>399afab7047e6fa62e7b2fb1768059ea</string>
<string>47a3316dae47cc4e7c1ea7b74ba8dd1e</string>
<key>url</key>
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/slvoice_3p-update-slvoice/rev/298329/arch/CYGWIN/installer/slvoice-4.6.0017.21209.298329-windows-298329.tar.bz2</string>
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/vivox_3p-slvoice/rev/302004/arch/CYGWIN/installer/slvoice-4.6.0017.22050.302004-windows-302004.tar.bz2</string>
</map>
<key>name</key>
<string>windows</string>
</map>
</map>
<key>version</key>
<string>4.6.0017.21209.2988329</string>
<string>3.2.0002.10426.302004</string>
</map>
<key>tut</key>
<map>

View File

@ -51,7 +51,6 @@ Compilation
WINVER=0x0501 " "
_WIN32_WINNT=0x0501 " "
LL_OS_DRAGDROP_ENABLED=1 " "
CARES_STATICLIB " "
LIB_NDOF=1 " "
----------------------------------------------------------------------------
@ -109,7 +108,6 @@ Compilation
DEBUG_INFO=1
LL_DARWIN=1 " "
LL_OS_DRAGDROP_ENABLED=1 " "
CARES_STATICLIB " "
LIB_NDOF=1 " "
----------------------------------------------------------------------------

View File

@ -123,3 +123,9 @@ else (USESYSTEMLIBS)
debug boost_thread-mt-d)
endif (WINDOWS)
endif (USESYSTEMLIBS)
if (LINUX)
set(BOOST_SYSTEM_LIBRARY ${BOOST_SYSTEM_LIBRARY} rt)
set(BOOST_THREAD_LIBRARY ${BOOST_THREAD_LIBRARY} rt)
endif (LINUX)

View File

@ -1,21 +0,0 @@
# -*- cmake -*-
include(Linking)
include(Prebuilt)
set(CARES_FIND_QUIETLY ON)
set(CARES_FIND_REQUIRED ON)
if (USESYSTEMLIBS)
include(FindCARes)
else (USESYSTEMLIBS)
use_prebuilt_binary(ares)
add_definitions("-DCARES_STATICLIB")
if (WINDOWS)
set(CARES_LIBRARIES areslib)
elseif (DARWIN)
set(CARES_LIBRARIES cares)
else (WINDOWS)
set(CARES_LIBRARIES cares)
endif (WINDOWS)
set(CARES_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/include/ares)
endif (USESYSTEMLIBS)

View File

@ -13,7 +13,6 @@ set(cmake_SOURCE_FILES
BerkeleyDB.cmake
Boost.cmake
BuildVersion.cmake
CARes.cmake
CEFPlugin.cmake
CMakeCopyIfDifferent.cmake
ConfigurePkgConfig.cmake
@ -28,7 +27,6 @@ set(cmake_SOURCE_FILES
FindAPR.cmake
FindAutobuild.cmake
FindBerkeleyDB.cmake
FindCARes.cmake
FindFMODEX.cmake
FindGLH.cmake
FindGoogleBreakpad.cmake
@ -59,7 +57,6 @@ set(cmake_SOURCE_FILES
JsonCpp.cmake
LLAddBuildTest.cmake
LLAppearance.cmake
LLAppearanceUtility.cmake
LLAudio.cmake
LLCharacter.cmake
LLCommon.cmake

View File

@ -22,10 +22,8 @@ if(WINDOWS)
SLVoice.exe
ca-bundle.crt
libsndfile-1.dll
vivoxplatform.dll
vivoxsdk.dll
ortp.dll
zlib1.dll
vivoxoal.dll
)

View File

@ -1,48 +0,0 @@
# -*- cmake -*-
# - Find c-ares
# Find the c-ares includes and library
# This module defines
# CARES_INCLUDE_DIR, where to find ares.h, etc.
# CARES_LIBRARIES, the libraries needed to use c-ares.
# CARES_FOUND, If false, do not try to use c-ares.
# also defined, but not for general use are
# CARES_LIBRARY, where to find the c-ares library.
FIND_PATH(CARES_INCLUDE_DIR ares.h
/usr/local/include
/usr/include
)
SET(CARES_NAMES ${CARES_NAMES} cares)
FIND_LIBRARY(CARES_LIBRARY
NAMES ${CARES_NAMES}
PATHS /usr/lib /usr/local/lib
)
IF (CARES_LIBRARY AND CARES_INCLUDE_DIR)
SET(CARES_LIBRARIES ${CARES_LIBRARY})
SET(CARES_FOUND "YES")
ELSE (CARES_LIBRARY AND CARES_INCLUDE_DIR)
SET(CARES_FOUND "NO")
ENDIF (CARES_LIBRARY AND CARES_INCLUDE_DIR)
IF (CARES_FOUND)
IF (NOT CARES_FIND_QUIETLY)
MESSAGE(STATUS "Found c-ares: ${CARES_LIBRARIES}")
ENDIF (NOT CARES_FIND_QUIETLY)
ELSE (CARES_FOUND)
IF (CARES_FIND_REQUIRED)
MESSAGE(FATAL_ERROR "Could not find c-ares library")
ENDIF (CARES_FIND_REQUIRED)
ENDIF (CARES_FOUND)
# Deprecated declarations.
SET (NATIVE_CARES_INCLUDE_PATH ${CARES_INCLUDE_DIR} )
GET_FILENAME_COMPONENT (NATIVE_CARES_LIB_PATH ${CARES_LIBRARY} PATH)
MARK_AS_ADVANCED(
CARES_LIBRARY
CARES_INCLUDE_DIR
)

View File

@ -35,6 +35,7 @@ INCLUDE(GoogleMock)
${APRUTIL_LIBRARIES}
${APR_LIBRARIES}
llcommon
llcorehttp
)
IF(NOT "${project}" STREQUAL "llmath")
# add llmath as a dep unless the tested module *is* llmath!
@ -49,6 +50,9 @@ INCLUDE(GoogleMock)
${GOOGLEMOCK_INCLUDE_DIRS}
)
SET(alltest_LIBRARIES
${BOOST_COROUTINE_LIBRARY}
${BOOST_CONTEXT_LIBRARY}
${BOOST_SYSTEM_LIBRARY}
${GOOGLEMOCK_LIBRARIES}
${PTHREAD_LIBRARY}
${WINDOWS_LIBRARIES}
@ -191,6 +195,9 @@ FUNCTION(LL_ADD_INTEGRATION_TEST
SET(libraries
${library_dependencies}
${BOOST_COROUTINE_LIBRARY}
${BOOST_CONTEXT_LIBRARY}
${BOOST_SYSTEM_LIBRARY}
${GOOGLEMOCK_LIBRARIES}
${PTHREAD_LIBRARY}
)

View File

@ -1,6 +1,9 @@
# -*- cmake -*-
include(Variables)
include(Boost)
include(LLMessage)
include(LLCoreHttp)
set(LLAPPEARANCE_INCLUDE_DIRS
${LIBS_OPEN_DIR}/llappearance
@ -12,6 +15,13 @@ if (BUILD_HEADLESS)
)
endif (BUILD_HEADLESS)
set(LLAPPEARANCE_LIBRARIES llappearance)
set(LLAPPEARANCE_LIBRARIES llappearance
llmessage
llcorehttp
${BOOST_COROUTINE_LIBRARY}
${BOOST_CONTEXT_LIBRARY}
${BOOST_SYSTEM_LIBRARY}
)

View File

@ -1,5 +1,6 @@
# -*- cmake -*-
include(Prebuilt)
include(Boost)
# Linux proprietary build only
if (INSTALL_PROPRIETARY)
@ -10,3 +11,4 @@ if (INSTALL_PROPRIETARY)
endif (LINUX)
endif (INSTALL_PROPRIETARY)

View File

@ -19,9 +19,19 @@ if (LINUX)
# In order to support using ld.gold on linux, we need to explicitely
# specify all libraries that llcommon uses.
# llcommon uses `clock_gettime' which is provided by librt on linux.
set(LLCOMMON_LIBRARIES llcommon rt)
set(LLCOMMON_LIBRARIES llcommon
${BOOST_COROUTINE_LIBRARY}
${BOOST_CONTEXT_LIBRARY}
${BOOST_THREAD_LIBRARY}
${BOOST_SYSTEM_LIBRARY}
rt
)
else (LINUX)
set(LLCOMMON_LIBRARIES llcommon)
set(LLCOMMON_LIBRARIES llcommon
${BOOST_COROUTINE_LIBRARY}
${BOOST_CONTEXT_LIBRARY}
${BOOST_THREAD_LIBRARY}
${BOOST_SYSTEM_LIBRARY} )
endif (LINUX)
# add_definitions(${TCMALLOC_FLAG})

View File

@ -1,16 +1,17 @@
# -*- cmake -*-
include(CARes)
include(CURL)
include(OpenSSL)
include(Boost)
set(LLCOREHTTP_INCLUDE_DIRS
${LIBS_OPEN_DIR}/llcorehttp
${CARES_INCLUDE_DIRS}
${CURL_INCLUDE_DIRS}
${OPENSSL_INCLUDE_DIRS}
${BOOST_INCLUDE_DIRS}
)
set(LLCOREHTTP_LIBRARIES llcorehttp)
set(LLCOREHTTP_LIBRARIES llcorehttp
${BOOST_COROUTINE_LIBRARY}
${BOOST_CONTEXT_LIBRARY}
${BOOST_SYSTEM_LIBRARY})

View File

@ -1,13 +1,11 @@
# -*- cmake -*-
include(CARes)
include(CURL)
include(OpenSSL)
include(XmlRpcEpi)
set(LLMESSAGE_INCLUDE_DIRS
${LIBS_OPEN_DIR}/llmessage
${CARES_INCLUDE_DIRS}
${CURL_INCLUDE_DIRS}
${OPENSSL_INCLUDE_DIRS}
)

View File

@ -4,6 +4,7 @@ project(linux_crash_logger)
include(00-Common)
include(GLH)
include(LLCoreHttp)
include(LLCommon)
include(LLCrashLogger)
include(LLMath)
@ -13,8 +14,10 @@ include(LLXML)
include(Linking)
include(UI)
include(FreeType)
include(Boost)
include_directories(
${LLCOREHTTP_INCLUDE_DIRS}
${LLCOMMON_INCLUDE_DIRS}
${LLCRASHLOGGER_INCLUDE_DIRS}
${LLMATH_INCLUDE_DIRS}
@ -53,6 +56,10 @@ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--as-needed")
add_executable(linux-crash-logger ${linux_crash_logger_SOURCE_FILES})
# llcommon uses `clock_gettime' which is provided by librt on linux.
set(LIBRT_LIBRARY rt)
target_link_libraries(linux-crash-logger
${LLCRASHLOGGER_LIBRARIES}
${LLVFS_LIBRARIES}
@ -60,10 +67,14 @@ target_link_libraries(linux-crash-logger
${LLMESSAGE_LIBRARIES}
${LLVFS_LIBRARIES}
${LLMATH_LIBRARIES}
${LLCOREHTTP_LIBRARIES}
${LLCOMMON_LIBRARIES}
${BOOST_COROUTINE_LIBRARY}
${BOOST_CONTEXT_LIBRARY}
${UI_LIBRARIES}
${DB_LIBRARIES}
${FREETYPE_LIBRARIES}
${LIBRT_LIBRARY}
)
add_custom_target(linux-crash-logger-target ALL

View File

@ -9,6 +9,7 @@ include(LLImage)
include(LLInventory)
include(LLMath)
include(LLMessage)
include(LLCoreHttp)
include(LLRender)
include(LLVFS)
include(LLWindow)
@ -86,6 +87,8 @@ target_link_libraries(llappearance
${LLMATH_LIBRARIES}
${LLXML_LIBRARIES}
${LLMATH_LIBRARIES}
${LLMESSAGE_LIBRARIES}
${LLCOREHTTP_LIBRARIES}
${LLCOMMON_LIBRARIES}
)
@ -101,6 +104,8 @@ if (BUILD_HEADLESS)
${LLMATH_LIBRARIES}
${LLXML_LIBRARIES}
${LLMATH_LIBRARIES}
${LLMESSAGE_LIBRARIES}
${LLCOREHTTP_LIBRARIES}
${LLCOMMON_LIBRARIES}
)
endif (BUILD_HEADLESS)

View File

@ -8,6 +8,7 @@ include(LLCommon)
include(Linking)
include(Boost)
include(LLSharedLibs)
include(JsonCpp)
include(GoogleBreakpad)
include(GooglePerfTools)
include(Copy3rdPartyLibs)
@ -17,6 +18,7 @@ include(URIPARSER)
include_directories(
${EXPAT_INCLUDE_DIRS}
${LLCOMMON_INCLUDE_DIRS}
${JSONCPP_INCLUDE_DIR}
${ZLIB_INCLUDE_DIRS}
${BREAKPAD_INCLUDE_DIRECTORIES}
${URIPARSER_INCLUDE_DIRS}
@ -86,6 +88,7 @@ set(llcommon_SOURCE_FILES
llrefcount.cpp
llrun.cpp
llsd.cpp
llsdjson.cpp
llsdparam.cpp
llsdserialize.cpp
llsdserialize_xml.cpp
@ -195,6 +198,7 @@ set(llcommon_HEADER_FILES
llrefcount.h
llsafehandle.h
llsd.h
llsdjson.h
llsdparam.h
llsdserialize.h
llsdserialize_xml.h
@ -262,10 +266,14 @@ target_link_libraries(
${APRUTIL_LIBRARIES}
${APR_LIBRARIES}
${EXPAT_LIBRARIES}
${JSONCPP_LIBRARIES}
${ZLIB_LIBRARIES}
${WINDOWS_LIBRARIES}
${BOOST_COROUTINE_LIBRARY}
${BOOST_CONTEXT_LIBRARY}
${BOOST_PROGRAM_OPTIONS_LIBRARY}
${BOOST_REGEX_LIBRARY}
${BOOST_SYSTEM_LIBRARY}
${GOOGLE_PERFTOOLS_LIBRARIES}
${URIPARSER_LIBRARIES}
)
@ -286,7 +294,14 @@ if (LL_TESTS)
LL_ADD_PROJECT_UNIT_TESTS(llcommon "${llcommon_TEST_SOURCE_FILES}")
#set(TEST_DEBUG on)
set(test_libs llcommon ${LLCOMMON_LIBRARIES} ${WINDOWS_LIBRARIES} ${GOOGLEMOCK_LIBRARIES})
set(test_libs llcommon
${LLCOMMON_LIBRARIES}
${WINDOWS_LIBRARIES}
${GOOGLEMOCK_LIBRARIES}
${BOOST_COROUTINE_LIBRARY}
${BOOST_CONTEXT_LIBRARY}
${BOOST_THREAD_LIBRARY}
${BOOST_SYSTEM_LIBRARY})
LL_ADD_INTEGRATION_TEST(commonmisc "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(bitpack "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llbase64 "" "${test_libs}")
@ -308,7 +323,7 @@ if (LL_TESTS)
LL_ADD_INTEGRATION_TEST(llunits "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(stringize "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(lleventdispatcher "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(lleventcoro "" "${test_libs};${BOOST_CONTEXT_LIBRARY};${BOOST_THREAD_LIBRARY};${BOOST_COROUTINE_LIBRARY};${BOOST_SYSTEM_LIBRARY}")
LL_ADD_INTEGRATION_TEST(lleventcoro "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llprocess "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llleap "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llstreamqueue "" "${test_libs}")

View File

@ -16,10 +16,6 @@
// these macros all over again.
// who injects MACROS with such generic names?! Grr.
#ifdef equivalent
#undef equivalent
#endif
#ifdef check
#undef check
#endif

View File

@ -51,6 +51,7 @@
#include <cstdlib>
#include <ctime>
#include <iosfwd>
#include <memory>
// Linden only libs in alpha-order other than stdtypes.h
// *NOTE: Please keep includes here to a minimum, see above.

View File

@ -39,6 +39,62 @@
#include "llerror.h"
#include "stringize.h"
// do nothing, when we need nothing done
void LLCoros::no_cleanup(CoroData*) {}
// CoroData for the currently-running coroutine. Use a thread_specific_ptr
// because each thread potentially has its own distinct pool of coroutines.
// This thread_specific_ptr does NOT own the CoroData object! That's owned by
// LLCoros::mCoros. It merely identifies it. For this reason we instantiate
// it with a no-op cleanup function.
boost::thread_specific_ptr<LLCoros::CoroData>
LLCoros::sCurrentCoro(LLCoros::no_cleanup);
//static
LLCoros::CoroData& LLCoros::get_CoroData(const std::string& caller)
{
CoroData* current = sCurrentCoro.get();
if (! current)
{
LL_ERRS("LLCoros") << "Calling " << caller << " from non-coroutine context!" << LL_ENDL;
}
return *current;
}
//static
LLCoros::coro::self& LLCoros::get_self()
{
return *get_CoroData("get_self()").mSelf;
}
//static
void LLCoros::set_consuming(bool consuming)
{
get_CoroData("set_consuming()").mConsuming = consuming;
}
//static
bool LLCoros::get_consuming()
{
return get_CoroData("get_consuming()").mConsuming;
}
llcoro::Suspending::Suspending():
mSuspended(LLCoros::sCurrentCoro.get())
{
// Revert mCurrentCoro to the value it had at the moment we last switched
// into this coroutine.
LLCoros::sCurrentCoro.reset(mSuspended->mPrev);
}
llcoro::Suspending::~Suspending()
{
// Okay, we're back, update our mPrev
mSuspended->mPrev = LLCoros::sCurrentCoro.get();
// and reinstate our sCurrentCoro.
LLCoros::sCurrentCoro.reset(mSuspended);
}
LLCoros::LLCoros():
// MAINT-2724: default coroutine stack size too small on Windows.
// Previously we used
@ -53,14 +109,33 @@ LLCoros::LLCoros():
bool LLCoros::cleanup(const LLSD&)
{
static std::string previousName;
static int previousCount = 0;
// Walk the mCoros map, checking and removing completed coroutines.
for (CoroMap::iterator mi(mCoros.begin()), mend(mCoros.end()); mi != mend; )
{
// Has this coroutine exited (normal return, exception, exit() call)
// since last tick?
if (mi->second->exited())
if (mi->second->mCoro.exited())
{
LL_INFOS("LLCoros") << "LLCoros: cleaning up coroutine " << mi->first << LL_ENDL;
if (previousName != mi->first)
{
previousName = mi->first;
previousCount = 1;
}
else
{
++previousCount;
}
if ((previousCount < 5) || !(previousCount % 50))
{
if (previousCount < 5)
LL_INFOS("LLCoros") << "LLCoros: cleaning up coroutine " << mi->first << LL_ENDL;
else
LL_INFOS("LLCoros") << "LLCoros: cleaning up coroutine " << mi->first << "("<< previousCount << ")" << LL_ENDL;
}
// The erase() call will invalidate its passed iterator value --
// so increment mi FIRST -- but pass its original value to
// erase(). This is what postincrement is all about.
@ -78,6 +153,9 @@ bool LLCoros::cleanup(const LLSD&)
std::string LLCoros::generateDistinctName(const std::string& prefix) const
{
static std::string previousName;
static int previousCount = 0;
// Allowing empty name would make getName()'s not-found return ambiguous.
if (prefix.empty())
{
@ -94,7 +172,25 @@ std::string LLCoros::generateDistinctName(const std::string& prefix) const
{
if (mCoros.find(name) == mCoros.end())
{
LL_INFOS("LLCoros") << "LLCoros: launching coroutine " << name << LL_ENDL;
if (previousName != name)
{
previousName = name;
previousCount = 1;
}
else
{
++previousCount;
}
if ((previousCount < 5) || !(previousCount % 50))
{
if (previousCount < 5)
LL_INFOS("LLCoros") << "LLCoros: launching coroutine " << name << LL_ENDL;
else
LL_INFOS("LLCoros") << "LLCoros: launching coroutine " << name << "(" << previousCount << ")" << LL_ENDL;
}
return name;
}
}
@ -114,20 +210,15 @@ bool LLCoros::kill(const std::string& name)
return true;
}
std::string LLCoros::getNameByID(const void* self_id) const
std::string LLCoros::getName() const
{
// Walk the existing coroutines, looking for one from which the 'self_id'
// passed to us comes.
for (CoroMap::const_iterator mi(mCoros.begin()), mend(mCoros.end()); mi != mend; ++mi)
CoroData* current = sCurrentCoro.get();
if (! current)
{
namespace coro_private = boost::dcoroutines::detail;
if (static_cast<void*>(coro_private::coroutine_accessor::get_impl(const_cast<coro&>(*mi->second)).get())
== self_id)
{
return mi->first;
}
// not in a coroutine
return "";
}
return "";
return current->mName;
}
void LLCoros::setStackSize(S32 stacksize)
@ -136,10 +227,24 @@ void LLCoros::setStackSize(S32 stacksize)
mStackSize = stacksize;
}
// Top-level wrapper around caller's coroutine callable. This function accepts
// the coroutine library's implicit coro::self& parameter and sets sCurrentSelf
// but does not pass it down to the caller's callable.
void LLCoros::toplevel(coro::self& self, CoroData* data, const callable_t& callable)
{
// capture the 'self' param in CoroData
data->mSelf = &self;
// run the code the caller actually wants in the coroutine
callable();
// This cleanup isn't perfectly symmetrical with the way we initially set
// data->mPrev, but this is our last chance to reset mCurrentCoro.
sCurrentCoro.reset(data->mPrev);
}
/*****************************************************************************
* MUST BE LAST
*****************************************************************************/
// Turn off MSVC optimizations for just LLCoros::launchImpl() -- see
// Turn off MSVC optimizations for just LLCoros::launch() -- see
// DEV-32777. But MSVC doesn't support push/pop for optimization flags as it
// does for warning suppression, and we really don't want to force
// optimization ON for other code even in Debug or RelWithDebInfo builds.
@ -147,15 +252,35 @@ void LLCoros::setStackSize(S32 stacksize)
#if LL_MSVC
// work around broken optimizations
#pragma warning(disable: 4748)
#pragma warning(disable: 4355) // 'this' used in initializer list: yes, intentionally
#pragma optimize("", off)
#endif // LL_MSVC
std::string LLCoros::launchImpl(const std::string& prefix, coro* newCoro)
LLCoros::CoroData::CoroData(CoroData* prev, const std::string& name,
const callable_t& callable, S32 stacksize):
mPrev(prev),
mName(name),
// Wrap the caller's callable in our toplevel() function so we can manage
// sCurrentCoro appropriately at startup and shutdown of each coroutine.
mCoro(boost::bind(toplevel, _1, this, callable), stacksize),
// don't consume events unless specifically directed
mConsuming(false),
mSelf(0)
{
}
std::string LLCoros::launch(const std::string& prefix, const callable_t& callable)
{
std::string name(generateDistinctName(prefix));
// pass the current value of sCurrentCoro as previous context
CoroData* newCoro = new CoroData(sCurrentCoro.get(), name,
callable, mStackSize);
// Store it in our pointer map
mCoros.insert(name, newCoro);
// also set it as current
sCurrentCoro.reset(newCoro);
/* Run the coroutine until its first wait, then return here */
(*newCoro)(std::nothrow);
(newCoro->mCoro)(std::nothrow);
return name;
}

View File

@ -30,14 +30,20 @@
#define LL_LLCOROS_H
#include <boost/dcoroutine/coroutine.hpp>
#include <boost/dcoroutine/future.hpp>
#include "llsingleton.h"
#include <boost/ptr_container/ptr_map.hpp>
#include <boost/function.hpp>
#include <boost/thread/tss.hpp>
#include <string>
#include <boost/preprocessor/repetition/enum_params.hpp>
#include <boost/preprocessor/repetition/enum_binary_params.hpp>
#include <boost/preprocessor/iteration/local.hpp>
#include <stdexcept>
// forward-declare helper class
namespace llcoro
{
class Suspending;
}
/**
* Registry of named Boost.Coroutine instances
*
@ -80,8 +86,8 @@ class LL_COMMON_API LLCoros: public LLSingleton<LLCoros>
public:
/// Canonical boost::dcoroutines::coroutine signature we use
typedef boost::dcoroutines::coroutine<void()> coro;
/// Canonical 'self' type
typedef coro::self self;
/// Canonical callable type
typedef boost::function<void()> callable_t;
/**
* Create and start running a new coroutine with specified name. The name
@ -94,39 +100,33 @@ public:
* {
* public:
* ...
* // Do NOT NOT NOT accept reference params other than 'self'!
* // Do NOT NOT NOT accept reference params!
* // Pass by value only!
* void myCoroutineMethod(LLCoros::self& self, std::string, LLSD);
* void myCoroutineMethod(std::string, LLSD);
* ...
* };
* ...
* std::string name = LLCoros::instance().launch(
* "mycoro", boost::bind(&MyClass::myCoroutineMethod, this, _1,
* "mycoro", boost::bind(&MyClass::myCoroutineMethod, this,
* "somestring", LLSD(17));
* @endcode
*
* Your function/method must accept LLCoros::self& as its first parameter.
* It can accept any other parameters you want -- but ONLY BY VALUE!
* Other reference parameters are a BAD IDEA! You Have Been Warned. See
* Your function/method can accept any parameters you want -- but ONLY BY
* VALUE! Reference parameters are a BAD IDEA! You Have Been Warned. See
* DEV-32777 comments for an explanation.
*
* Pass a callable that accepts the single LLCoros::self& parameter. It
* may work to pass a free function whose only parameter is 'self'; for
* all other cases use boost::bind(). Of course, for a non-static class
* method, the first parameter must be the class instance. Use the
* placeholder _1 for the 'self' parameter. Any other parameters should be
* passed via the bind() expression.
* Pass a nullary callable. It works to directly pass a nullary free
* function (or static method); for all other cases use boost::bind(). Of
* course, for a non-static class method, the first parameter must be the
* class instance. Any other parameters should be passed via the bind()
* expression.
*
* launch() tweaks the suggested name so it won't collide with any
* existing coroutine instance, creates the coroutine instance, registers
* it with the tweaked name and runs it until its first wait. At that
* point it returns the tweaked name.
*/
template <typename CALLABLE>
std::string launch(const std::string& prefix, const CALLABLE& callable)
{
return launchImpl(prefix, new coro(callable, mStackSize));
}
std::string launch(const std::string& prefix, const callable_t& callable);
/**
* Abort a running coroutine by name. Normally, when a coroutine either
@ -138,33 +138,150 @@ public:
bool kill(const std::string& name);
/**
* From within a coroutine, pass its @c self object to look up the
* (tweaked) name string by which this coroutine is registered. Returns
* the empty string if not found (e.g. if the coroutine was launched by
* hand rather than using LLCoros::launch()).
* From within a coroutine, look up the (tweaked) name string by which
* this coroutine is registered. Returns the empty string if not found
* (e.g. if the coroutine was launched by hand rather than using
* LLCoros::launch()).
*/
template <typename COROUTINE_SELF>
std::string getName(const COROUTINE_SELF& self) const
{
return getNameByID(self.get_id());
}
/// getName() by self.get_id()
std::string getNameByID(const void* self_id) const;
std::string getName() const;
/// for delayed initialization
void setStackSize(S32 stacksize);
/// get the current coro::self& for those who really really care
static coro::self& get_self();
/**
* Most coroutines, most of the time, don't "consume" the events for which
* they're suspending. This way, an arbitrary number of listeners (whether
* coroutines or simple callbacks) can be registered on a particular
* LLEventPump, every listener responding to each of the events on that
* LLEventPump. But a particular coroutine can assert that it will consume
* each event for which it suspends. (See also llcoro::postAndSuspend(),
* llcoro::VoidListener)
*/
static void set_consuming(bool consuming);
static bool get_consuming();
/**
* Please do NOT directly use boost::dcoroutines::future! It is essential
* to maintain the "current" coroutine at every context switch. This
* Future wraps the essential boost::dcoroutines::future functionality
* with that maintenance.
*/
template <typename T>
class Future;
private:
friend class LLSingleton<LLCoros>;
LLCoros();
std::string launchImpl(const std::string& prefix, coro* newCoro);
friend class LLSingleton<LLCoros>;
friend class llcoro::Suspending;
std::string generateDistinctName(const std::string& prefix) const;
bool cleanup(const LLSD&);
struct CoroData;
static void no_cleanup(CoroData*);
static void toplevel(coro::self& self, CoroData* data, const callable_t& callable);
static CoroData& get_CoroData(const std::string& caller);
S32 mStackSize;
typedef boost::ptr_map<std::string, coro> CoroMap;
// coroutine-local storage, as it were: one per coro we track
struct CoroData
{
CoroData(CoroData* prev, const std::string& name,
const callable_t& callable, S32 stacksize);
// The boost::dcoroutines library supports asymmetric coroutines. Every
// time we context switch out of a coroutine, we pass control to the
// previously-active one (or to the non-coroutine stack owned by the
// thread). So our management of the "current" coroutine must be able to
// restore the previous value when we're about to switch away.
CoroData* mPrev;
// tweaked name of the current coroutine
const std::string mName;
// the actual coroutine instance
LLCoros::coro mCoro;
// set_consuming() state
bool mConsuming;
// When the dcoroutine library calls a top-level callable, it implicitly
// passes coro::self& as the first parameter. All our consumer code used
// to explicitly pass coro::self& down through all levels of call stack,
// because at the leaf level we need it for context-switching. But since
// coroutines are based on cooperative switching, we can cause the
// top-level entry point to stash a pointer to the currently-running
// coroutine, and manage it appropriately as we switch out and back in.
// That eliminates the need to pass it as an explicit parameter down
// through every level, which is unfortunately viral in nature. Finding it
// implicitly rather than explicitly allows minor maintenance in which a
// leaf-level function adds a new async I/O call that suspends the calling
// coroutine, WITHOUT having to propagate coro::self& through every
// function signature down to that point -- and of course through every
// other caller of every such function.
LLCoros::coro::self* mSelf;
};
typedef boost::ptr_map<std::string, CoroData> CoroMap;
CoroMap mCoros;
// identify the current coroutine's CoroData
static boost::thread_specific_ptr<LLCoros::CoroData> sCurrentCoro;
};
namespace llcoro
{
/// Instantiate one of these in a block surrounding any leaf point when
/// control literally switches away from this coroutine.
class Suspending
{
public:
Suspending();
~Suspending();
private:
LLCoros::CoroData* mSuspended;
};
} // namespace llcoro
template <typename T>
class LLCoros::Future
{
typedef boost::dcoroutines::future<T> dfuture;
public:
Future():
mFuture(get_self())
{}
typedef typename boost::dcoroutines::make_callback_result<dfuture>::type callback_t;
callback_t make_callback()
{
return boost::dcoroutines::make_callback(mFuture);
}
#ifndef LL_LINUX
explicit
#endif
operator bool() const
{
return bool(mFuture);
}
bool operator!() const
{
return ! mFuture;
}
T get()
{
// instantiate Suspending to manage the "current" coroutine
llcoro::Suspending suspended;
return *mFuture;
}
private:
dfuture mFuture;
};
#endif /* ! defined(LL_LLCOROS_H) */

View File

@ -354,6 +354,7 @@ typedef LLError::NoClassInfo _LL_CLASS_TO_LOG;
#define LL_WARNS(...) lllog(LLError::LEVEL_WARN, false, ##__VA_ARGS__)
#define LL_ERRS(...) lllog(LLError::LEVEL_ERROR, false, ##__VA_ARGS__)
// alternative to llassert_always that prints explanatory message
#define LL_WARNS_IF(exp, ...) if (exp) LL_WARNS(##__VA_ARGS__) << "(" #exp ")"
#define LL_ERRS_IF(exp, ...) if (exp) LL_ERRS(##__VA_ARGS__) << "(" #exp ")"
// Only print the log message once (good for warnings or infos that would otherwise

View File

@ -38,34 +38,60 @@
#include "llsdserialize.h"
#include "llerror.h"
#include "llcoros.h"
#include "llmake.h"
std::string LLEventDetail::listenerNameForCoroImpl(const void* self_id)
#include "lleventfilter.h"
namespace
{
// First, if this coroutine was launched by LLCoros::launch(), find that name.
std::string name(LLCoros::instance().getNameByID(self_id));
/**
* suspendUntilEventOn() permits a coroutine to temporarily listen on an
* LLEventPump any number of times. We don't really want to have to ask
* the caller to label each such call with a distinct string; the whole
* point of suspendUntilEventOn() is to present a nice sequential interface to
* the underlying LLEventPump-with-named-listeners machinery. So we'll use
* LLEventPump::inventName() to generate a distinct name for each
* temporary listener. On the other hand, because a given coroutine might
* call suspendUntilEventOn() any number of times, we don't really want to
* consume an arbitrary number of generated inventName()s: that namespace,
* though large, is nonetheless finite. So we memoize an invented name for
* each distinct coroutine instance.
*/
std::string listenerNameForCoro()
{
// If this coroutine was launched by LLCoros::launch(), find that name.
std::string name(LLCoros::instance().getName());
if (! name.empty())
{
return name;
}
// Apparently this coroutine wasn't launched by LLCoros::launch(). Check
// whether we have a memo for this self_id.
typedef std::map<const void*, std::string> MapType;
static MapType memo;
MapType::const_iterator found = memo.find(self_id);
if (found != memo.end())
{
// this coroutine instance has called us before, reuse same name
return found->second;
}
// this is the first time we've been called for this coroutine instance
name = LLEventPump::inventName("coro");
memo[self_id] = name;
LL_INFOS("LLEventCoro") << "listenerNameForCoroImpl(" << self_id << "): inventing coro name '"
LL_INFOS("LLEventCoro") << "listenerNameForCoro(): inventing coro name '"
<< name << "'" << LL_ENDL;
return name;
}
void LLEventDetail::storeToLLSDPath(LLSD& dest, const LLSD& rawPath, const LLSD& value)
/**
* Implement behavior described for postAndSuspend()'s @a replyPumpNamePath
* parameter:
*
* * If <tt>path.isUndefined()</tt>, do nothing.
* * If <tt>path.isString()</tt>, @a dest is an LLSD map: store @a value
* into <tt>dest[path.asString()]</tt>.
* * If <tt>path.isInteger()</tt>, @a dest is an LLSD array: store @a
* value into <tt>dest[path.asInteger()]</tt>.
* * If <tt>path.isArray()</tt>, iteratively apply the rules above to step
* down through the structure of @a dest. The last array entry in @a
* path specifies the entry in the lowest-level structure in @a dest
* into which to store @a value.
*
* @note
* In the degenerate case in which @a path is an empty array, @a dest will
* @em become @a value rather than @em containing it.
*/
void storeToLLSDPath(LLSD& dest, const LLSD& rawPath, const LLSD& value)
{
if (rawPath.isUndefined())
{
@ -118,6 +144,185 @@ void LLEventDetail::storeToLLSDPath(LLSD& dest, const LLSD& rawPath, const LLSD&
*pdest = value;
}
/// For LLCoros::Future<LLSD>::make_callback(), the callback has a signature
/// like void callback(LLSD), which isn't a valid LLEventPump listener: such
/// listeners must return bool.
template <typename LISTENER>
class FutureListener
{
public:
// FutureListener is instantiated on the coroutine stack: the stack, in
// other words, that wants to suspend.
FutureListener(const LISTENER& listener):
mListener(listener),
// Capture the suspending coroutine's flag as a consuming or
// non-consuming listener.
mConsume(LLCoros::get_consuming())
{}
// operator()() is called on the main stack: the stack on which the
// expected event is fired.
bool operator()(const LLSD& event)
{
mListener(event);
// tell upstream LLEventPump whether listener consumed
return mConsume;
}
protected:
LISTENER mListener;
bool mConsume;
};
} // anonymous
void llcoro::suspend()
{
// By viewer convention, we post an event on the "mainloop" LLEventPump
// each iteration of the main event-handling loop. So waiting for a single
// event on "mainloop" gives us a one-frame suspend.
suspendUntilEventOn("mainloop");
}
void llcoro::suspendUntilTimeout(float seconds)
{
LLEventTimeout timeout;
timeout.eventAfter(seconds, LLSD());
llcoro::suspendUntilEventOn(timeout);
}
LLSD llcoro::postAndSuspend(const LLSD& event, const LLEventPumpOrPumpName& requestPump,
const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath)
{
// declare the future
LLCoros::Future<LLSD> future;
// make a callback that will assign a value to the future, and listen on
// the specified LLEventPump with that callback
std::string listenerName(listenerNameForCoro());
LLTempBoundListener connection(
replyPump.getPump().listen(listenerName,
llmake<FutureListener>(future.make_callback())));
// skip the "post" part if requestPump is default-constructed
if (requestPump)
{
// If replyPumpNamePath is non-empty, store the replyPump name in the
// request event.
LLSD modevent(event);
storeToLLSDPath(modevent, replyPumpNamePath, replyPump.getPump().getName());
LL_DEBUGS("lleventcoro") << "postAndSuspend(): coroutine " << listenerName
<< " posting to " << requestPump.getPump().getName()
<< LL_ENDL;
// *NOTE:Mani - Removed because modevent could contain user's hashed passwd.
// << ": " << modevent << LL_ENDL;
requestPump.getPump().post(modevent);
}
LL_DEBUGS("lleventcoro") << "postAndSuspend(): coroutine " << listenerName
<< " about to wait on LLEventPump " << replyPump.getPump().getName()
<< LL_ENDL;
// calling get() on the future makes us wait for it
LLSD value(future.get());
LL_DEBUGS("lleventcoro") << "postAndSuspend(): coroutine " << listenerName
<< " resuming with " << value << LL_ENDL;
// returning should disconnect the connection
return value;
}
namespace
{
/**
* This helper is specifically for postAndSuspend2(). We use a single future
* object, but we want to listen on two pumps with it. Since we must still
* adapt from the callable constructed by boost::dcoroutines::make_callback()
* (void return) to provide an event listener (bool return), we've adapted
* FutureListener for the purpose. The basic idea is that we construct a
* distinct instance of FutureListener2 -- binding different instance data --
* for each of the pumps. Then, when a pump delivers an LLSD value to either
* FutureListener2, it can combine that LLSD with its discriminator to feed
* the future object.
*
* DISCRIM is a template argument so we can use llmake() rather than
* having to write our own argument-deducing helper function.
*/
template <typename LISTENER, typename DISCRIM>
class FutureListener2: public FutureListener<LISTENER>
{
typedef FutureListener<LISTENER> super;
public:
// instantiated on coroutine stack: the stack about to suspend
FutureListener2(const LISTENER& listener, DISCRIM discriminator):
super(listener),
mDiscrim(discriminator)
{}
// called on main stack: the stack on which event is fired
bool operator()(const LLSD& event)
{
// our future object is defined to accept LLEventWithID
super::mListener(LLEventWithID(event, mDiscrim));
// tell LLEventPump whether or not event was consumed
return super::mConsume;
}
private:
const DISCRIM mDiscrim;
};
} // anonymous
namespace llcoro
{
LLEventWithID postAndSuspend2(const LLSD& event,
const LLEventPumpOrPumpName& requestPump,
const LLEventPumpOrPumpName& replyPump0,
const LLEventPumpOrPumpName& replyPump1,
const LLSD& replyPump0NamePath,
const LLSD& replyPump1NamePath)
{
// declare the future
LLCoros::Future<LLEventWithID> future;
// either callback will assign a value to this future; listen on
// each specified LLEventPump with a callback
std::string name(listenerNameForCoro());
LLTempBoundListener connection0(
replyPump0.getPump().listen(
name + "a",
llmake<FutureListener2>(future.make_callback(), 0)));
LLTempBoundListener connection1(
replyPump1.getPump().listen(
name + "b",
llmake<FutureListener2>(future.make_callback(), 1)));
// skip the "post" part if requestPump is default-constructed
if (requestPump)
{
// If either replyPumpNamePath is non-empty, store the corresponding
// replyPump name in the request event.
LLSD modevent(event);
storeToLLSDPath(modevent, replyPump0NamePath,
replyPump0.getPump().getName());
storeToLLSDPath(modevent, replyPump1NamePath,
replyPump1.getPump().getName());
LL_DEBUGS("lleventcoro") << "postAndSuspend2(): coroutine " << name
<< " posting to " << requestPump.getPump().getName()
<< ": " << modevent << LL_ENDL;
requestPump.getPump().post(modevent);
}
LL_DEBUGS("lleventcoro") << "postAndSuspend2(): coroutine " << name
<< " about to wait on LLEventPumps " << replyPump0.getPump().getName()
<< ", " << replyPump1.getPump().getName() << LL_ENDL;
// calling get() on the future makes us wait for it
LLEventWithID value(future.get());
LL_DEBUGS("lleventcoro") << "postAndSuspend(): coroutine " << name
<< " resuming with (" << value.first << ", " << value.second << ")"
<< LL_ENDL;
// returning should disconnect both connections
return value;
}
LLSD errorException(const LLEventWithID& result, const std::string& desc)
{
// If the result arrived on the error pump (pump 1), instead of
@ -144,3 +349,5 @@ LLSD errorLog(const LLEventWithID& result, const std::string& desc)
// A simple return must therefore be from the reply pump (pump 0).
return result.first;
}
} // namespace llcoro

View File

@ -29,11 +29,10 @@
#if ! defined(LL_LLEVENTCORO_H)
#define LL_LLEVENTCORO_H
#include <boost/dcoroutine/coroutine.hpp>
#include <boost/dcoroutine/future.hpp>
#include <boost/optional.hpp>
#include <string>
#include <stdexcept>
#include <utility> // std::pair
#include "llevents.h"
#include "llerror.h"
@ -74,111 +73,46 @@ private:
boost::optional<LLEventPump&> mPump;
};
/// This is an adapter for a signature like void LISTENER(const LLSD&), which
/// isn't a valid LLEventPump listener: such listeners should return bool.
template <typename LISTENER>
class LLVoidListener
namespace llcoro
{
public:
LLVoidListener(const LISTENER& listener):
mListener(listener)
{}
bool operator()(const LLSD& event)
{
mListener(event);
// don't swallow the event, let other listeners see it
return false;
}
private:
LISTENER mListener;
};
/// LLVoidListener helper function to infer the type of the LISTENER
template <typename LISTENER>
LLVoidListener<LISTENER> voidlistener(const LISTENER& listener)
{
return LLVoidListener<LISTENER>(listener);
}
namespace LLEventDetail
{
/// Implementation for listenerNameForCoro(), see below
LL_COMMON_API std::string listenerNameForCoroImpl(const void* self_id);
/**
* waitForEventOn() permits a coroutine to temporarily listen on an
* LLEventPump any number of times. We don't really want to have to ask
* the caller to label each such call with a distinct string; the whole
* point of waitForEventOn() is to present a nice sequential interface to
* the underlying LLEventPump-with-named-listeners machinery. So we'll use
* LLEventPump::inventName() to generate a distinct name for each
* temporary listener. On the other hand, because a given coroutine might
* call waitForEventOn() any number of times, we don't really want to
* consume an arbitrary number of generated inventName()s: that namespace,
* though large, is nonetheless finite. So we memoize an invented name for
* each distinct coroutine instance (each different 'self' object). We
* can't know the type of 'self', because it depends on the coroutine
* body's signature. So we cast its address to void*, looking for distinct
* pointer values. Yes, that means that an early coroutine could cache a
* value here, then be destroyed, only to be supplanted by a later
* coroutine (of the same or different type), and we'll end up
* "recognizing" the second one and reusing the listener name -- but
* that's okay, since it won't collide with any listener name used by the
* earlier coroutine since that earlier coroutine no longer exists.
*/
template <typename COROUTINE_SELF>
std::string listenerNameForCoro(COROUTINE_SELF& self)
{
return listenerNameForCoroImpl(self.get_id());
}
/**
* Implement behavior described for postAndWait()'s @a replyPumpNamePath
* parameter:
*
* * If <tt>path.isUndefined()</tt>, do nothing.
* * If <tt>path.isString()</tt>, @a dest is an LLSD map: store @a value
* into <tt>dest[path.asString()]</tt>.
* * If <tt>path.isInteger()</tt>, @a dest is an LLSD array: store @a
* value into <tt>dest[path.asInteger()]</tt>.
* * If <tt>path.isArray()</tt>, iteratively apply the rules above to step
* down through the structure of @a dest. The last array entry in @a
* path specifies the entry in the lowest-level structure in @a dest
* into which to store @a value.
*
* @note
* In the degenerate case in which @a path is an empty array, @a dest will
* @em become @a value rather than @em containing it.
*/
LL_COMMON_API void storeToLLSDPath(LLSD& dest, const LLSD& path, const LLSD& value);
} // namespace LLEventDetail
/**
* Post specified LLSD event on the specified LLEventPump, then wait for a
* Yield control from a coroutine for one "mainloop" tick. If your coroutine
* runs without suspending for nontrivial time, sprinkle in calls to this
* function to avoid stalling the rest of the viewer processing.
*/
void suspend();
/**
* Yield control from a coroutine for at least the specified number of seconds
*/
void suspendUntilTimeout(float seconds);
/**
* Post specified LLSD event on the specified LLEventPump, then suspend for a
* response on specified other LLEventPump. This is more than mere
* convenience: the difference between this function and the sequence
* @code
* requestPump.post(myEvent);
* LLSD reply = waitForEventOn(self, replyPump);
* LLSD reply = suspendUntilEventOn(replyPump);
* @endcode
* is that the sequence above fails if the reply is posted immediately on
* @a replyPump, that is, before <tt>requestPump.post()</tt> returns. In the
* sequence above, the running coroutine isn't even listening on @a replyPump
* until <tt>requestPump.post()</tt> returns and @c waitForEventOn() is
* until <tt>requestPump.post()</tt> returns and @c suspendUntilEventOn() is
* entered. Therefore, the coroutine completely misses an immediate reply
* event, making it wait indefinitely.
* event, making it suspend indefinitely.
*
* By contrast, postAndWait() listens on the @a replyPump @em before posting
* By contrast, postAndSuspend() listens on the @a replyPump @em before posting
* the specified LLSD event on the specified @a requestPump.
*
* @param self The @c self object passed into a coroutine
* @param event LLSD data to be posted on @a requestPump
* @param requestPump an LLEventPump on which to post @a event. Pass either
* the LLEventPump& or its string name. However, if you pass a
* default-constructed @c LLEventPumpOrPumpName, we skip the post() call.
* @param replyPump an LLEventPump on which postAndWait() will listen for a
* @param replyPump an LLEventPump on which postAndSuspend() will listen for a
* reply. Pass either the LLEventPump& or its string name. The calling
* coroutine will wait until that reply arrives. (If you're concerned about a
* coroutine will suspend until that reply arrives. (If you're concerned about a
* reply that might not arrive, please see also LLEventTimeout.)
* @param replyPumpNamePath specifies the location within @a event in which to
* store <tt>replyPump.getName()</tt>. This is a strictly optional convenience
@ -201,101 +135,29 @@ namespace LLEventDetail
* @a replyPumpNamePath specifies the entry in the lowest-level structure in
* @a event into which to store <tt>replyPump.getName()</tt>.
*/
template <typename SELF>
LLSD postAndWait(SELF& self, const LLSD& event, const LLEventPumpOrPumpName& requestPump,
const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath=LLSD())
{
// declare the future
boost::dcoroutines::future<LLSD> future(self);
// make a callback that will assign a value to the future, and listen on
// the specified LLEventPump with that callback
std::string listenerName(LLEventDetail::listenerNameForCoro(self));
LLTempBoundListener connection(
replyPump.getPump().listen(listenerName,
voidlistener(boost::dcoroutines::make_callback(future))));
// skip the "post" part if requestPump is default-constructed
if (requestPump)
{
// If replyPumpNamePath is non-empty, store the replyPump name in the
// request event.
LLSD modevent(event);
LLEventDetail::storeToLLSDPath(modevent, replyPumpNamePath, replyPump.getPump().getName());
LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName
<< " posting to " << requestPump.getPump().getName()
<< LL_ENDL;
// *NOTE:Mani - Removed because modevent could contain user's hashed passwd.
// << ": " << modevent << LL_ENDL;
requestPump.getPump().post(modevent);
}
LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName
<< " about to wait on LLEventPump " << replyPump.getPump().getName()
<< LL_ENDL;
// trying to dereference ("resolve") the future makes us wait for it
LLSD value(*future);
LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName
<< " resuming with " << value << LL_ENDL;
// returning should disconnect the connection
return value;
}
LLSD postAndSuspend(const LLSD& event, const LLEventPumpOrPumpName& requestPump,
const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath=LLSD());
/// Wait for the next event on the specified LLEventPump. Pass either the
/// LLEventPump& or its string name.
template <typename SELF>
LLSD waitForEventOn(SELF& self, const LLEventPumpOrPumpName& pump)
inline
LLSD suspendUntilEventOn(const LLEventPumpOrPumpName& pump)
{
// This is now a convenience wrapper for postAndWait().
return postAndWait(self, LLSD(), LLEventPumpOrPumpName(), pump);
// This is now a convenience wrapper for postAndSuspend().
return postAndSuspend(LLSD(), LLEventPumpOrPumpName(), pump);
}
/// return type for two-pump variant of waitForEventOn()
} // namespace llcoro
/// return type for two-pump variant of suspendUntilEventOn()
typedef std::pair<LLSD, int> LLEventWithID;
namespace LLEventDetail
namespace llcoro
{
/**
* This helper is specifically for the two-pump version of waitForEventOn().
* We use a single future object, but we want to listen on two pumps with it.
* Since we must still adapt from (the callable constructed by)
* boost::dcoroutines::make_callback() (void return) to provide an event
* listener (bool return), we've adapted LLVoidListener for the purpose. The
* basic idea is that we construct a distinct instance of WaitForEventOnHelper
* -- binding different instance data -- for each of the pumps. Then, when a
* pump delivers an LLSD value to either WaitForEventOnHelper, it can combine
* that LLSD with its discriminator to feed the future object.
*/
template <typename LISTENER>
class WaitForEventOnHelper
{
public:
WaitForEventOnHelper(const LISTENER& listener, int discriminator):
mListener(listener),
mDiscrim(discriminator)
{}
// this signature is required for an LLEventPump listener
bool operator()(const LLSD& event)
{
// our future object is defined to accept LLEventWithID
mListener(LLEventWithID(event, mDiscrim));
// don't swallow the event, let other listeners see it
return false;
}
private:
LISTENER mListener;
const int mDiscrim;
};
/// WaitForEventOnHelper type-inference helper
template <typename LISTENER>
WaitForEventOnHelper<LISTENER> wfeoh(const LISTENER& listener, int discriminator)
{
return WaitForEventOnHelper<LISTENER>(listener, discriminator);
}
} // namespace LLEventDetail
/**
* This function waits for a reply on either of two specified LLEventPumps.
* Otherwise, it closely resembles postAndWait(); please see the documentation
* Otherwise, it closely resembles postAndSuspend(); please see the documentation
* for that function for detailed parameter info.
*
* While we could have implemented the single-pump variant in terms of this
@ -310,81 +172,41 @@ namespace LLEventDetail
* the index of the pump on which it arrived (0 or 1).
*
* @note
* I'd have preferred to overload the name postAndWait() for both signatures.
* I'd have preferred to overload the name postAndSuspend() for both signatures.
* But consider the following ambiguous call:
* @code
* postAndWait(self, LLSD(), requestPump, replyPump, "someString");
* postAndSuspend(LLSD(), requestPump, replyPump, "someString");
* @endcode
* "someString" could be converted to either LLSD (@a replyPumpNamePath for
* the single-pump function) or LLEventOrPumpName (@a replyPump1 for two-pump
* function).
*
* It seems less burdensome to write postAndWait2() than to write either
* It seems less burdensome to write postAndSuspend2() than to write either
* LLSD("someString") or LLEventOrPumpName("someString").
*/
template <typename SELF>
LLEventWithID postAndWait2(SELF& self, const LLSD& event,
LLEventWithID postAndSuspend2(const LLSD& event,
const LLEventPumpOrPumpName& requestPump,
const LLEventPumpOrPumpName& replyPump0,
const LLEventPumpOrPumpName& replyPump1,
const LLSD& replyPump0NamePath=LLSD(),
const LLSD& replyPump1NamePath=LLSD())
{
// declare the future
boost::dcoroutines::future<LLEventWithID> future(self);
// either callback will assign a value to this future; listen on
// each specified LLEventPump with a callback
std::string name(LLEventDetail::listenerNameForCoro(self));
LLTempBoundListener connection0(
replyPump0.getPump().listen(name + "a",
LLEventDetail::wfeoh(boost::dcoroutines::make_callback(future), 0)));
LLTempBoundListener connection1(
replyPump1.getPump().listen(name + "b",
LLEventDetail::wfeoh(boost::dcoroutines::make_callback(future), 1)));
// skip the "post" part if requestPump is default-constructed
if (requestPump)
{
// If either replyPumpNamePath is non-empty, store the corresponding
// replyPump name in the request event.
LLSD modevent(event);
LLEventDetail::storeToLLSDPath(modevent, replyPump0NamePath,
replyPump0.getPump().getName());
LLEventDetail::storeToLLSDPath(modevent, replyPump1NamePath,
replyPump1.getPump().getName());
LL_DEBUGS("lleventcoro") << "postAndWait2(): coroutine " << name
<< " posting to " << requestPump.getPump().getName()
<< ": " << modevent << LL_ENDL;
requestPump.getPump().post(modevent);
}
LL_DEBUGS("lleventcoro") << "postAndWait2(): coroutine " << name
<< " about to wait on LLEventPumps " << replyPump0.getPump().getName()
<< ", " << replyPump1.getPump().getName() << LL_ENDL;
// trying to dereference ("resolve") the future makes us wait for it
LLEventWithID value(*future);
LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << name
<< " resuming with (" << value.first << ", " << value.second << ")"
<< LL_ENDL;
// returning should disconnect both connections
return value;
}
const LLSD& replyPump1NamePath=LLSD());
/**
* Wait for the next event on either of two specified LLEventPumps.
*/
template <typename SELF>
inline
LLEventWithID
waitForEventOn(SELF& self,
const LLEventPumpOrPumpName& pump0, const LLEventPumpOrPumpName& pump1)
suspendUntilEventOn(const LLEventPumpOrPumpName& pump0, const LLEventPumpOrPumpName& pump1)
{
// This is now a convenience wrapper for postAndWait2().
return postAndWait2(self, LLSD(), LLEventPumpOrPumpName(), pump0, pump1);
// This is now a convenience wrapper for postAndSuspend2().
return postAndSuspend2(LLSD(), LLEventPumpOrPumpName(), pump0, pump1);
}
/**
* Helper for the two-pump variant of waitForEventOn(), e.g.:
* Helper for the two-pump variant of suspendUntilEventOn(), e.g.:
*
* @code
* LLSD reply = errorException(waitForEventOn(self, replyPump, errorPump),
* LLSD reply = errorException(suspendUntilEventOn(replyPump, errorPump),
* "error response from login.cgi");
* @endcode
*
@ -400,6 +222,8 @@ waitForEventOn(SELF& self,
*/
LLSD errorException(const LLEventWithID& result, const std::string& desc);
} // namespace llcoro
/**
* Exception thrown by errorException(). We don't call this LLEventError
* because it's not an error in event processing: rather, this exception
@ -420,12 +244,17 @@ private:
LLSD mData;
};
namespace llcoro
{
/**
* Like errorException(), save that this trips a fatal error using LL_ERRS
* rather than throwing an exception.
*/
LL_COMMON_API LLSD errorLog(const LLEventWithID& result, const std::string& desc);
} // namespace llcoro
/**
* Certain event APIs require the name of an LLEventPump on which they should
* post results. While it works to invent a distinct name and let
@ -437,7 +266,7 @@ LL_COMMON_API LLSD errorLog(const LLEventWithID& result, const std::string& desc
* 2. Provide its actual name to the event API in question as the name of the
* reply LLEventPump.
* 3. Initiate the request to the event API.
* 4. Call your LLEventTempStream's wait() method to wait for the reply.
* 4. Call your LLEventTempStream's suspend() method to suspend for the reply.
* 5. Let the LLCoroEventPump go out of scope.
*/
class LL_COMMON_API LLCoroEventPump
@ -454,26 +283,16 @@ public:
/**
* Wait for an event on this LLEventPump.
*
* @note
* The other major usage pattern we considered was to bind @c self at
* LLCoroEventPump construction time, which would avoid passing the
* parameter to each wait() call. But if we were going to bind @c self as
* a class member, we'd need to specify a class template parameter
* indicating its type. The big advantage of passing it to the wait() call
* is that the type can be implicit.
*/
template <typename SELF>
LLSD wait(SELF& self)
LLSD suspend()
{
return waitForEventOn(self, mPump);
return llcoro::suspendUntilEventOn(mPump);
}
template <typename SELF>
LLSD postAndWait(SELF& self, const LLSD& event, const LLEventPumpOrPumpName& requestPump,
LLSD postAndSuspend(const LLSD& event, const LLEventPumpOrPumpName& requestPump,
const LLSD& replyPumpNamePath=LLSD())
{
return ::postAndWait(self, event, requestPump, mPump, replyPumpNamePath);
return llcoro::postAndSuspend(event, requestPump, mPump, replyPumpNamePath);
}
private:
@ -509,57 +328,51 @@ public:
/// request pump 1
LLEventPump& getPump1() { return mPump1; }
/// waitForEventOn(self, either of our two LLEventPumps)
template <typename SELF>
LLEventWithID wait(SELF& self)
/// suspendUntilEventOn(either of our two LLEventPumps)
LLEventWithID suspend()
{
return waitForEventOn(self, mPump0, mPump1);
return llcoro::suspendUntilEventOn(mPump0, mPump1);
}
/// errorException(wait(self))
template <typename SELF>
LLSD waitWithException(SELF& self)
/// errorException(suspend())
LLSD suspendWithException()
{
return errorException(wait(self), std::string("Error event on ") + getName1());
return llcoro::errorException(suspend(), std::string("Error event on ") + getName1());
}
/// errorLog(wait(self))
template <typename SELF>
LLSD waitWithLog(SELF& self)
/// errorLog(suspend())
LLSD suspendWithLog()
{
return errorLog(wait(self), std::string("Error event on ") + getName1());
return llcoro::errorLog(suspend(), std::string("Error event on ") + getName1());
}
template <typename SELF>
LLEventWithID postAndWait(SELF& self, const LLSD& event,
LLEventWithID postAndSuspend(const LLSD& event,
const LLEventPumpOrPumpName& requestPump,
const LLSD& replyPump0NamePath=LLSD(),
const LLSD& replyPump1NamePath=LLSD())
{
return postAndWait2(self, event, requestPump, mPump0, mPump1,
replyPump0NamePath, replyPump1NamePath);
return llcoro::postAndSuspend2(event, requestPump, mPump0, mPump1,
replyPump0NamePath, replyPump1NamePath);
}
template <typename SELF>
LLSD postAndWaitWithException(SELF& self, const LLSD& event,
LLSD postAndSuspendWithException(const LLSD& event,
const LLEventPumpOrPumpName& requestPump,
const LLSD& replyPump0NamePath=LLSD(),
const LLSD& replyPump1NamePath=LLSD())
{
return errorException(postAndWait(self, event, requestPump,
replyPump0NamePath, replyPump1NamePath),
std::string("Error event on ") + getName1());
return llcoro::errorException(postAndSuspend(event, requestPump,
replyPump0NamePath, replyPump1NamePath),
std::string("Error event on ") + getName1());
}
template <typename SELF>
LLSD postAndWaitWithLog(SELF& self, const LLSD& event,
LLSD postAndSuspendWithLog(const LLSD& event,
const LLEventPumpOrPumpName& requestPump,
const LLSD& replyPump0NamePath=LLSD(),
const LLSD& replyPump1NamePath=LLSD())
{
return errorLog(postAndWait(self, event, requestPump,
replyPump0NamePath, replyPump1NamePath),
std::string("Error event on ") + getName1());
return llcoro::errorLog(postAndSuspend(event, requestPump,
replyPump0NamePath, replyPump1NamePath),
std::string("Error event on ") + getName1());
}
private:

View File

@ -132,6 +132,17 @@ LLEventPump& LLEventPumps::obtain(const std::string& name)
return *newInstance;
}
bool LLEventPumps::post(const std::string&name, const LLSD&message)
{
PumpMap::iterator found = mPumpMap.find(name);
if (found == mPumpMap.end())
return false;
return (*found).second->post(message);
}
void LLEventPumps::flush()
{
// Flush every known LLEventPump instance. Leave it up to each instance to
@ -496,6 +507,43 @@ bool LLEventStream::post(const LLSD& event)
return (*signal)(event);
}
/*****************************************************************************
* LLEventMailDrop
*****************************************************************************/
bool LLEventMailDrop::post(const LLSD& event)
{
bool posted = false;
if (!mSignal->empty())
posted = LLEventStream::post(event);
if (!posted)
{ // if the event was not handled we will save it for later so that it can
// be posted to any future listeners when they attach.
mEventHistory.push_back(event);
}
return posted;
}
LLBoundListener LLEventMailDrop::listen_impl(const std::string& name,
const LLEventListener& listener,
const NameList& after,
const NameList& before)
{
if (!mEventHistory.empty())
{
if (listener(mEventHistory.front()))
{
mEventHistory.pop_front();
}
}
return LLEventStream::listen_impl(name, listener, after, before);
}
/*****************************************************************************
* LLEventQueue
*****************************************************************************/

View File

@ -217,6 +217,18 @@ public:
* an instance without conferring @em ownership.
*/
LLEventPump& obtain(const std::string& name);
/**
* Find the named LLEventPump instance. If it exists post the message to it.
* If the pump does not exist, do nothing.
*
* returns the result of the LLEventPump::post. If no pump exists returns false.
*
* This is syntactically similar to LLEventPumps::instance().post(name, message),
* however if the pump does not already exist it will not be created.
*/
bool post(const std::string&, const LLSD&);
/**
* Flush all known LLEventPump instances
*/
@ -496,7 +508,7 @@ public:
// at the boost::bind object itself before that happens.
return LLEventDetail::visit_and_connect(name,
listener,
boost::bind(&LLEventPump::listen_impl,
boost::bind(&LLEventPump::listen_invoke,
this,
name,
_1,
@ -541,13 +553,23 @@ private:
virtual void reset();
private:
virtual LLBoundListener listen_impl(const std::string& name, const LLEventListener&,
const NameList& after,
const NameList& before);
LLBoundListener listen_invoke(const std::string& name, const LLEventListener& listener,
const NameList& after,
const NameList& before)
{
return this->listen_impl(name, listener, after, before);
}
std::string mName;
protected:
virtual LLBoundListener listen_impl(const std::string& name, const LLEventListener&,
const NameList& after,
const NameList& before);
/// implement the dispatching
boost::shared_ptr<LLStandardSignal> mSignal;
@ -585,11 +607,40 @@ public:
virtual bool post(const LLSD& event);
};
/*****************************************************************************
* LLEventMailDrop
*****************************************************************************/
/**
* LLEventMailDrop is a specialization of LLEventStream. Events are posted normally,
* however if no listeners return that they have handled the event it is placed in
* a queue. Subsequent attaching listeners will receive stored events from the queue
* until a listener indicates that the event has been handled. In order to receive
* multiple events from a mail drop the listener must disconnect and reconnect.
*/
class LL_COMMON_API LLEventMailDrop : public LLEventStream
{
public:
LLEventMailDrop(const std::string& name, bool tweak = false) : LLEventStream(name, tweak) {}
virtual ~LLEventMailDrop() {}
/// Post an event to all listeners
virtual bool post(const LLSD& event);
protected:
virtual LLBoundListener listen_impl(const std::string& name, const LLEventListener&,
const NameList& after,
const NameList& before);
private:
typedef std::list<LLSD> EventList;
EventList mEventHistory;
};
/*****************************************************************************
* LLEventQueue
*****************************************************************************/
/**
* LLEventQueue isa LLEventPump whose post() method defers calling registered
* LLEventQueue is a LLEventPump whose post() method defers calling registered
* listeners until flush() is called.
*/
class LL_COMMON_API LLEventQueue: public LLEventPump

63
indra/llcommon/llmake.h Normal file
View File

@ -0,0 +1,63 @@
/**
* @file llmake.h
* @author Nat Goodspeed
* @date 2015-12-18
* @brief Generic llmake<Template>(arg) function to instantiate
* Template<decltype(arg)>(arg).
*
* Many of our class templates have an accompanying helper function to
* make an instance with arguments of arbitrary type. llmake()
* eliminates the need to declare a new helper function for every such
* class template.
*
* also relevant:
*
* Template parameter deduction for constructors
* http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0091r0.html
*
* https://github.com/viboes/std-make
*
* but obviously we're not there yet.
*
* $LicenseInfo:firstyear=2015&license=viewerlgpl$
* Copyright (c) 2015, Linden Research, Inc.
* $/LicenseInfo$
*/
#if ! defined(LL_LLMAKE_H)
#define LL_LLMAKE_H
/*==========================================================================*|
// When we allow ourselves to compile with C++11 features enabled, this form
// should generically handle an arbitrary number of arguments.
template <template<typename...> class CLASS_TEMPLATE, typename... ARGS>
CLASS_TEMPLATE<ARGS...> llmake(ARGS && ... args)
{
return CLASS_TEMPLATE<ARGS...>(std::forward<ARGS>(args)...);
}
|*==========================================================================*/
// As of 2015-12-18, this is what we'll use instead. Add explicit overloads
// for different numbers of template parameters as use cases arise.
/**
* Usage: llmake<SomeTemplate>(arg)
*
* Deduces the type T of 'arg' and returns an instance of SomeTemplate<T>
* initialized with 'arg'. Assumes a constructor accepting T (by value,
* reference or whatever).
*/
template <template<typename> class CLASS_TEMPLATE, typename ARG1>
CLASS_TEMPLATE<ARG1> llmake(const ARG1& arg1)
{
return CLASS_TEMPLATE<ARG1>(arg1);
}
template <template<typename, typename> class CLASS_TEMPLATE, typename ARG1, typename ARG2>
CLASS_TEMPLATE<ARG1, ARG2> llmake(const ARG1& arg1, const ARG2& arg2)
{
return CLASS_TEMPLATE<ARG1, ARG2>(arg1, arg2);
}
#endif /* ! defined(LL_LLMAKE_H) */

View File

@ -710,7 +710,7 @@ LLProcess::LLProcess(const LLSDOrParams& params):
// Tie the lifespan of this child process to the lifespan of our APR
// pool: on destruction of the pool, forcibly kill the process. Tell
// APR to try SIGTERM and wait 3 seconds. If that didn't work, use
// APR to try SIGTERM and suspend 3 seconds. If that didn't work, use
// SIGKILL.
apr_pool_note_subprocess(gAPRPoolp, &mProcess, APR_KILL_AFTER_TIMEOUT);
|*==========================================================================*/
@ -989,9 +989,9 @@ void LLProcess::handle_status(int reason, int status)
// wi->rv = apr_proc_wait(wi->child, &wi->rc, &wi->why, APR_NOWAIT);
// It's just wrong to call apr_proc_wait() here. The only way APR knows to
// call us with APR_OC_REASON_DEATH is that it's already reaped this child
// process, so calling wait() will only produce "huh?" from the OS. We
// process, so calling suspend() will only produce "huh?" from the OS. We
// must rely on the status param passed in, which unfortunately comes
// straight from the OS wait() call, which means we have to decode it by
// straight from the OS suspend() call, which means we have to decode it by
// hand.
mStatus = interpret_status(status);
LL_INFOS("LLProcess") << getStatusString() << LL_ENDL;

View File

@ -143,15 +143,10 @@ private:
LLAtomic32< S32 > mRef;
};
/**
* intrusive pointer support
* this allows you to use boost::intrusive_ptr with any LLRefCount-derived type
*/
/**
* intrusive pointer support for LLThreadSafeRefCount
* this allows you to use boost::intrusive_ptr with any LLThreadSafeRefCount-derived type
*/
inline void intrusive_ptr_add_ref(LLThreadSafeRefCount* p)
{
p->ref();
@ -162,6 +157,10 @@ inline void intrusive_ptr_release(LLThreadSafeRefCount* p)
p->unref();
}
/**
* intrusive pointer support
* this allows you to use boost::intrusive_ptr with any LLRefCount-derived type
*/
inline void intrusive_ptr_add_ref(LLRefCount* p)
{
p->ref();

126
indra/llcommon/llsdjson.cpp Normal file
View File

@ -0,0 +1,126 @@
/**
* @file llsdjson.cpp
* @brief LLSD flexible data system
*
* $LicenseInfo:firstyear=2015&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2015, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
// Must turn on conditional declarations in header file so definitions end up
// with proper linkage.
#define LLSD_DEBUG_INFO
#include "linden_common.h"
#include "llsdjson.h"
#include "llerror.h"
#include "../llmath/llmath.h"
//=========================================================================
LLSD LlsdFromJson(const Json::Value &val)
{
LLSD result;
switch (val.type())
{
default:
case Json::nullValue:
break;
case Json::intValue:
result = LLSD(static_cast<LLSD::Integer>(val.asInt()));
break;
case Json::uintValue:
result = LLSD(static_cast<LLSD::Integer>(val.asUInt()));
break;
case Json::realValue:
result = LLSD(static_cast<LLSD::Real>(val.asDouble()));
break;
case Json::stringValue:
result = LLSD(static_cast<LLSD::String>(val.asString()));
break;
case Json::booleanValue:
result = LLSD(static_cast<LLSD::Boolean>(val.asBool()));
break;
case Json::arrayValue:
result = LLSD::emptyArray();
for (Json::ValueConstIterator it = val.begin(); it != val.end(); ++it)
{
result.append(LlsdFromJson((*it)));
}
break;
case Json::objectValue:
result = LLSD::emptyMap();
for (Json::ValueConstIterator it = val.begin(); it != val.end(); ++it)
{
result[it.memberName()] = LlsdFromJson((*it));
}
break;
}
return result;
}
//=========================================================================
Json::Value LlsdToJson(const LLSD &val)
{
Json::Value result;
switch (val.type())
{
case LLSD::TypeUndefined:
result = Json::Value::null;
break;
case LLSD::TypeBoolean:
result = Json::Value(static_cast<bool>(val.asBoolean()));
break;
case LLSD::TypeInteger:
result = Json::Value(static_cast<int>(val.asInteger()));
break;
case LLSD::TypeReal:
result = Json::Value(static_cast<double>(val.asReal()));
break;
case LLSD::TypeURI:
case LLSD::TypeDate:
case LLSD::TypeUUID:
case LLSD::TypeString:
result = Json::Value(val.asString());
break;
case LLSD::TypeMap:
result = Json::Value(Json::objectValue);
for (LLSD::map_const_iterator it = val.beginMap(); it != val.endMap(); ++it)
{
result[it->first] = LlsdToJson(it->second);
}
break;
case LLSD::TypeArray:
result = Json::Value(Json::arrayValue);
for (LLSD::array_const_iterator it = val.beginArray(); it != val.endArray(); ++it)
{
result.append(LlsdToJson(*it));
}
break;
case LLSD::TypeBinary:
default:
LL_ERRS("LlsdToJson") << "Unsupported conversion to JSON from LLSD type (" << val.type() << ")." << LL_ENDL;
break;
}
return result;
}

77
indra/llcommon/llsdjson.h Normal file
View File

@ -0,0 +1,77 @@
/**
* @file llsdjson.cpp
* @brief LLSD flexible data system
*
* $LicenseInfo:firstyear=2015&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2015, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LL_LLSDJSON_H
#define LL_LLSDJSON_H
#include <map>
#include <string>
#include <vector>
#include "stdtypes.h"
#include "llsd.h"
#include "value.h"
/// Convert a parsed JSON structure into LLSD maintaining member names and
/// array indexes.
/// JSON/JavaScript types are converted as follows:
///
/// JSON Type | LLSD Type
/// --------------+--------------
/// null | undefined
/// integer | LLSD::Integer
/// unsigned | LLSD::Integer
/// real/numeric | LLSD::Real
/// string | LLSD::String
/// boolean | LLSD::Boolean
/// array | LLSD::Array
/// object | LLSD::Map
///
/// For maps and arrays child entries will be converted and added to the structure.
/// Order is preserved for an array but not for objects.
LLSD LlsdFromJson(const Json::Value &val);
/// Convert an LLSD object into Parsed JSON object maintaining member names and
/// array indexs.
///
/// Types are converted as follows:
/// LLSD Type | JSON Type
/// --------------+----------------
/// TypeUndefined | null
/// TypeBoolean | boolean
/// TypeInteger | integer
/// TypeReal | real/numeric
/// TypeString | string
/// TypeURI | string
/// TypeDate | string
/// TypeUUID | string
/// TypeMap | object
/// TypeArray | array
/// TypeBinary | unsupported
Json::Value LlsdToJson(const LLSD &val);
#endif // LL_LLSDJSON_H

View File

@ -1394,6 +1394,7 @@ BOOL LLStringUtilBase<T>::containsNonprintable(const string_type& string)
return rv;
}
// *TODO: reimplement in terms of algorithm
//static
template<class T>
void LLStringUtilBase<T>::stripNonprintable(string_type& string)
@ -1427,6 +1428,7 @@ void LLStringUtilBase<T>::stripNonprintable(string_type& string)
delete []c_string;
}
// *TODO: reimplement in terms of algorithm
template<class T>
std::basic_string<T> LLStringUtilBase<T>::quote(const string_type& str,
const string_type& triggers,

File diff suppressed because it is too large Load Diff

View File

@ -5,7 +5,6 @@ project(llcorehttp)
include(00-Common)
include(GoogleMock)
include(CURL)
include(CARes)
include(OpenSSL)
include(ZLIB)
include(LLCoreHttp)
@ -26,6 +25,7 @@ set(llcorehttp_SOURCE_FILES
bufferarray.cpp
bufferstream.cpp
httpcommon.cpp
llhttpconstants.cpp
httpheaders.cpp
httpoptions.cpp
httprequest.cpp
@ -51,6 +51,7 @@ set(llcorehttp_HEADER_FILES
bufferarray.h
bufferstream.h
httpcommon.h
llhttpconstants.h
httphandler.h
httpheaders.h
httpoptions.h
@ -89,7 +90,6 @@ add_library (llcorehttp ${llcorehttp_SOURCE_FILES})
target_link_libraries(
llcorehttp
${CURL_LIBRARIES}
${CARES_LIBRARIES}
${OPENSSL_LIBRARIES}
${CRYPTO_LIBRARIES}
${BOOST_THREAD_LIBRARY}
@ -127,16 +127,19 @@ if (LL_TESTS)
${LLCOMMON_LIBRARIES}
${GOOGLEMOCK_LIBRARIES}
${CURL_LIBRARIES}
${CARES_LIBRARIES}
${OPENSSL_LIBRARIES}
${CRYPTO_LIBRARIES}
${BOOST_SYSTEM_LIBRARY}
${BOOST_THREAD_LIBRARY}
)
# If http_proxy is in the current environment (e.g. to fetch s3-proxy
# autobuild packages), suppress it for this integration test: it screws up
# the tests.
LL_ADD_INTEGRATION_TEST(llcorehttp
"${llcorehttp_TEST_SOURCE_FILES}"
"${test_libs}"
"-Dhttp_proxy"
${PYTHON_EXECUTABLE}
"${CMAKE_CURRENT_SOURCE_DIR}/tests/test_llcorehttp_peer.py"
)
@ -192,7 +195,6 @@ endif (DARWIN)
${LLCOMMON_LIBRARIES}
${GOOGLEMOCK_LIBRARIES}
${CURL_LIBRARIES}
${CARES_LIBRARIES}
${OPENSSL_LIBRARIES}
${CRYPTO_LIBRARIES}
${BOOST_SYSTEM_LIBRARY}

View File

@ -104,8 +104,9 @@ namespace LLCore
{
// Maxium number of policy classes that can be defined.
// *TODO: Currently limited to the default class + 1, extend.
const int HTTP_POLICY_CLASS_LIMIT = 8;
// *TODO: Currently limited to the default class + 1, extend.
// (TSN: should this be more dynamically sized. Is there a reason to hard limit the number of policies?)
const int HTTP_POLICY_CLASS_LIMIT = 32;
// Debug/informational tracing. Used both
// as a global option and in per-request traces.

View File

@ -71,11 +71,10 @@ void HttpLibcurl::shutdown()
{
while (! mActiveOps.empty())
{
HttpOpRequest * op(* mActiveOps.begin());
HttpOpRequest::ptr_t op(* mActiveOps.begin());
mActiveOps.erase(mActiveOps.begin());
cancelRequest(op);
op->release();
}
if (mMultiHandles)
@ -204,7 +203,7 @@ HttpService::ELoopSpeed HttpLibcurl::processTransport()
// Caller has provided us with a ref count on op.
void HttpLibcurl::addOp(HttpOpRequest * op)
void HttpLibcurl::addOp(const HttpOpRequest::ptr_t &op)
{
llassert_always(op->mReqPolicy < mPolicyCount);
llassert_always(mMultiHandles[op->mReqPolicy] != NULL);
@ -235,21 +234,21 @@ void HttpLibcurl::addOp(HttpOpRequest * op)
HttpPolicy & policy(mService->getPolicy());
LL_INFOS(LOG_CORE) << "TRACE, ToActiveQueue, Handle: "
<< static_cast<HttpHandle>(op)
<< ", Actives: " << mActiveOps.size()
<< ", Readies: " << policy.getReadyCount(op->mReqPolicy)
<< LL_ENDL;
<< op->getHandle()
<< ", Actives: " << mActiveOps.size()
<< ", Readies: " << policy.getReadyCount(op->mReqPolicy)
<< LL_ENDL;
}
}
// Implements the transport part of any cancel operation.
// See if the handle is an active operation and if so,
// use the more complicated transport-based cancelation
// use the more complicated transport-based cancellation
// method to kill the request.
bool HttpLibcurl::cancel(HttpHandle handle)
{
HttpOpRequest * op(static_cast<HttpOpRequest *>(handle));
HttpOpRequest::ptr_t op = HttpOpRequest::fromHandle<HttpOpRequest>(handle);
active_set_t::iterator it(mActiveOps.find(op));
if (mActiveOps.end() == it)
{
@ -262,7 +261,6 @@ bool HttpLibcurl::cancel(HttpHandle handle)
// Drop references
mActiveOps.erase(it);
--mActiveHandles[op->mReqPolicy];
op->release();
return true;
}
@ -273,7 +271,7 @@ bool HttpLibcurl::cancel(HttpHandle handle)
// remove the op from the active list and release the op *after*
// calling this method. It must be called first to deliver the
// op to the reply queue with refcount intact.
void HttpLibcurl::cancelRequest(HttpOpRequest * op)
void HttpLibcurl::cancelRequest(const HttpOpRequest::ptr_t &op)
{
// Deactivate request
op->mCurlActive = false;
@ -287,7 +285,7 @@ void HttpLibcurl::cancelRequest(HttpOpRequest * op)
if (op->mTracing > HTTP_TRACE_OFF)
{
LL_INFOS(LOG_CORE) << "TRACE, RequestCanceled, Handle: "
<< static_cast<HttpHandle>(op)
<< op->getHandle()
<< ", Status: " << op->mStatus.toTerseString()
<< LL_ENDL;
}
@ -301,8 +299,23 @@ void HttpLibcurl::cancelRequest(HttpOpRequest * op)
// Keep them synchronized as necessary.
bool HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode status)
{
HttpOpRequest * op(NULL);
curl_easy_getinfo(handle, CURLINFO_PRIVATE, &op);
HttpHandle ophandle(NULL);
CURLcode ccode(CURLE_OK);
ccode = curl_easy_getinfo(handle, CURLINFO_PRIVATE, &ophandle);
if (ccode)
{
LL_WARNS(LOG_CORE) << "libcurl error: " << ccode << " Unable to retrieve operation handle from CURL handle" << LL_ENDL;
return false;
}
HttpOpRequest::ptr_t op(HttpOpRequest::fromHandle<HttpOpRequest>(ophandle));
if (!op)
{
LL_WARNS() << "Unable to locate operation by handle. May have expired!" << LL_ENDL;
return false;
}
if (handle != op->mCurlHandle || ! op->mCurlActive)
{
@ -331,42 +344,72 @@ bool HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode
{
op->mStatus = HttpStatus(HttpStatus::EXT_CURL_EASY, status);
}
if (op->mStatus)
{
int http_status(HTTP_OK);
if (op->mStatus)
{
int http_status(HTTP_OK);
curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &http_status);
if (http_status >= 100 && http_status <= 999)
{
char * cont_type(NULL);
curl_easy_getinfo(handle, CURLINFO_CONTENT_TYPE, &cont_type);
if (cont_type)
{
op->mReplyConType = cont_type;
}
op->mStatus = HttpStatus(http_status);
}
else
{
LL_WARNS(LOG_CORE) << "Invalid HTTP response code ("
<< http_status << ") received from server."
<< LL_ENDL;
op->mStatus = HttpStatus(HttpStatus::LLCORE, HE_INVALID_HTTP_STATUS);
}
if (handle)
{
ccode = curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &http_status);
if (ccode == CURLE_OK)
{
if (http_status >= 100 && http_status <= 999)
{
char * cont_type(NULL);
ccode = curl_easy_getinfo(handle, CURLINFO_CONTENT_TYPE, &cont_type);
if (ccode == CURLE_OK)
{
if (cont_type)
{
op->mReplyConType = cont_type;
}
}
else
{
LL_WARNS(LOG_CORE) << "CURL error:" << ccode << " Attempting to get content type." << LL_ENDL;
}
op->mStatus = HttpStatus(http_status);
}
else
{
LL_WARNS(LOG_CORE) << "Invalid HTTP response code ("
<< http_status << ") received from server."
<< LL_ENDL;
op->mStatus = HttpStatus(HttpStatus::LLCORE, HE_INVALID_HTTP_STATUS);
}
}
else
{
op->mStatus = HttpStatus(HttpStatus::LLCORE, HE_INVALID_HTTP_STATUS);
}
}
else
{
LL_WARNS(LOG_CORE) << "Attempt to retrieve status from NULL handle!" << LL_ENDL;
}
}
// Detach from multi and recycle handle
curl_multi_remove_handle(multi_handle, handle);
mHandleCache.freeHandle(op->mCurlHandle);
op->mCurlHandle = NULL;
if (multi_handle && handle)
{
// Detach from multi and recycle handle
curl_multi_remove_handle(multi_handle, handle);
mHandleCache.freeHandle(op->mCurlHandle);
}
else
{
LL_WARNS(LOG_CORE) << "Curl multi_handle or handle is NULL on remove! multi:"
<< std::hex << multi_handle << " h:" << std::hex << handle << std::dec << LL_ENDL;
}
op->mCurlHandle = NULL;
// Tracing
if (op->mTracing > HTTP_TRACE_OFF)
{
LL_INFOS(LOG_CORE) << "TRACE, RequestComplete, Handle: "
<< static_cast<HttpHandle>(op)
<< ", Status: " << op->mStatus.toTerseString()
<< LL_ENDL;
<< op->getHandle()
<< ", Status: " << op->mStatus.toTerseString()
<< LL_ENDL;
}
// Dispatch to next stage
@ -554,7 +597,7 @@ void HttpLibcurl::HandleCache::freeHandle(CURL * handle)
// ---------------------------------------
struct curl_slist * append_headers_to_slist(const HttpHeaders * headers, struct curl_slist * slist)
struct curl_slist * append_headers_to_slist(const HttpHeaders::ptr_t &headers, struct curl_slist * slist)
{
const HttpHeaders::const_iterator end(headers->end());
for (HttpHeaders::const_iterator it(headers->begin()); end != it; ++it)

View File

@ -65,6 +65,8 @@ private:
void operator=(const HttpLibcurl &); // Not defined
public:
typedef boost::shared_ptr<HttpOpRequest> opReqPtr_t;
/// Give cycles to libcurl to run active requests. Completed
/// operations (successful or failed) will be retried or handed
/// over to the reply queue as final responses.
@ -80,7 +82,7 @@ public:
/// request. (No additional references will be added.)
///
/// Threading: called by worker thread.
void addOp(HttpOpRequest * op);
void addOp(const opReqPtr_t & op);
/// One-time call to set the number of policy classes to be
/// serviced and to create the resources for each. Value
@ -148,10 +150,10 @@ protected:
/// Invoked to cancel an active request, mainly during shutdown
/// and destroy.
void cancelRequest(HttpOpRequest * op);
void cancelRequest(const opReqPtr_t &op);
protected:
typedef std::set<HttpOpRequest *> active_set_t;
typedef std::set<opReqPtr_t> active_set_t;
/// Simple request handle cache for libcurl.
///

View File

@ -56,13 +56,8 @@ public:
/// be canceled.
HttpOpCancel(HttpHandle handle);
protected:
virtual ~HttpOpCancel(); // Use release()
private:
HttpOpCancel(const HttpOpCancel &); // Not defined
void operator=(const HttpOpCancel &); // Not defined
public:
virtual void stageFromRequest(HttpService *);

View File

@ -53,15 +53,18 @@ namespace LLCore
// ==================================
// HttpOperation
// ==================================
/*static*/
HttpOperation::handleMap_t HttpOperation::mHandleMap;
LLCoreInt::HttpMutex HttpOperation::mOpMutex;
HttpOperation::HttpOperation()
: LLCoreInt::RefCounted(true),
mReplyQueue(NULL),
mUserHandler(NULL),
mReqPolicy(HttpRequest::DEFAULT_POLICY_ID),
mReqPriority(0U),
mTracing(HTTP_TRACE_OFF)
HttpOperation::HttpOperation():
boost::enable_shared_from_this<HttpOperation>(),
mReplyQueue(),
mUserHandler(),
mReqPolicy(HttpRequest::DEFAULT_POLICY_ID),
mReqPriority(0U),
mTracing(HTTP_TRACE_OFF),
mMyHandle(LLCORE_HTTP_HANDLE_INVALID)
{
mMetricCreated = totalTime();
}
@ -69,30 +72,17 @@ HttpOperation::HttpOperation()
HttpOperation::~HttpOperation()
{
setReplyPath(NULL, NULL);
destroyHandle();
mReplyQueue.reset();
mUserHandler.reset();
}
void HttpOperation::setReplyPath(HttpReplyQueue * reply_queue,
HttpHandler * user_handler)
void HttpOperation::setReplyPath(HttpReplyQueue::ptr_t reply_queue,
HttpHandler::ptr_t user_handler)
{
if (reply_queue != mReplyQueue)
{
if (mReplyQueue)
{
mReplyQueue->release();
}
if (reply_queue)
{
reply_queue->addRef();
}
mReplyQueue = reply_queue;
}
// Not refcounted
mUserHandler = user_handler;
mReplyQueue.swap(reply_queue);
mUserHandler.swap(user_handler);
}
@ -134,7 +124,7 @@ void HttpOperation::visitNotifier(HttpRequest *)
HttpResponse * response = new HttpResponse();
response->setStatus(mStatus);
mUserHandler->onCompleted(static_cast<HttpHandle>(this), response);
mUserHandler->onCompleted(getHandle(), response);
response->release();
}
@ -148,20 +138,83 @@ HttpStatus HttpOperation::cancel()
return status;
}
// Handle methods
HttpHandle HttpOperation::getHandle()
{
if (mMyHandle == LLCORE_HTTP_HANDLE_INVALID)
return createHandle();
return mMyHandle;
}
HttpHandle HttpOperation::createHandle()
{
HttpHandle handle = static_cast<HttpHandle>(this);
{
LLCoreInt::HttpScopedLock lock(mOpMutex);
mHandleMap[handle] = shared_from_this();
mMyHandle = handle;
}
return mMyHandle;
}
void HttpOperation::destroyHandle()
{
if (mMyHandle == LLCORE_HTTP_HANDLE_INVALID)
return;
{
LLCoreInt::HttpScopedLock lock(mOpMutex);
handleMap_t::iterator it = mHandleMap.find(mMyHandle);
if (it != mHandleMap.end())
mHandleMap.erase(it);
}
}
/*static*/
HttpOperation::ptr_t HttpOperation::findByHandle(HttpHandle handle)
{
wptr_t weak;
if (!handle)
return ptr_t();
{
LLCoreInt::HttpScopedLock lock(mOpMutex);
handleMap_t::iterator it = mHandleMap.find(handle);
if (it == mHandleMap.end())
{
LL_WARNS("LLCore::HTTP") << "Could not find operation for handle " << handle << LL_ENDL;
return ptr_t();
}
weak = (*it).second;
}
if (!weak.expired())
return weak.lock();
return ptr_t();
}
void HttpOperation::addAsReply()
{
if (mTracing > HTTP_TRACE_OFF)
{
LL_INFOS(LOG_CORE) << "TRACE, ToReplyQueue, Handle: "
<< static_cast<HttpHandle>(this)
<< getHandle()
<< LL_ENDL;
}
if (mReplyQueue)
{
addRef();
mReplyQueue->addOp(this);
HttpOperation::ptr_t op = shared_from_this();
mReplyQueue->addOp(op);
}
}
@ -244,11 +297,8 @@ void HttpOpSpin::stageFromRequest(HttpService * service)
else
{
ms_sleep(1); // backoff interlock plumbing a bit
this->addRef();
if (! service->getRequestQueue().addOp(this))
{
this->release();
}
HttpOperation::ptr_t opptr = shared_from_this();
service->getRequestQueue().addOp(opptr);
}
}

View File

@ -30,8 +30,7 @@
#include "httpcommon.h"
#include "httprequest.h"
#include "_refcounted.h"
#include "_mutex.h"
namespace LLCore
{
@ -69,19 +68,20 @@ class HttpService;
/// via queue-like interfaces that are thread compatible
/// and those interfaces establish the access rules.
class HttpOperation : public LLCoreInt::RefCounted
class HttpOperation : private boost::noncopyable,
public boost::enable_shared_from_this<HttpOperation>
{
public:
typedef boost::shared_ptr<HttpOperation> ptr_t;
typedef boost::weak_ptr<HttpOperation> wptr_t;
typedef boost::shared_ptr<HttpReplyQueue> HttpReplyQueuePtr_t;
/// Threading: called by consumer thread.
HttpOperation();
protected:
/// Threading: called by any thread.
virtual ~HttpOperation(); // Use release()
private:
HttpOperation(const HttpOperation &); // Not defined
void operator=(const HttpOperation &); // Not defined
public:
/// Register a reply queue and a handler for completion notifications.
@ -110,8 +110,8 @@ public:
///
/// Threading: called by consumer thread.
///
void setReplyPath(HttpReplyQueue * reply_queue,
HttpHandler * handler);
void setReplyPath(HttpReplyQueuePtr_t reply_queue,
HttpHandler::ptr_t handler);
/// The three possible staging steps in an operation's lifecycle.
/// Asynchronous requests like HTTP operations move from the
@ -152,6 +152,18 @@ public:
/// Threading: called by worker thread.
///
virtual HttpStatus cancel();
/// Retrieves a unique handle for this operation.
HttpHandle getHandle();
template< class OPT >
static boost::shared_ptr< OPT > fromHandle(HttpHandle handle)
{
ptr_t ptr = findByHandle(handle);
if (!ptr)
return boost::shared_ptr< OPT >();
return boost::dynamic_pointer_cast< OPT >(ptr);
}
protected:
/// Delivers request to reply queue on completion. After this
@ -163,8 +175,8 @@ protected:
void addAsReply();
protected:
HttpReplyQueue * mReplyQueue; // Have refcount
HttpHandler * mUserHandler; // Naked pointer
HttpReplyQueuePtr_t mReplyQueue;
HttpHandler::ptr_t mUserHandler;
public:
// Request Data
@ -177,6 +189,21 @@ public:
// Tracing, debug and metrics
HttpTime mMetricCreated;
int mTracing;
private:
typedef std::map<HttpHandle, wptr_t> handleMap_t;
HttpHandle createHandle();
void destroyHandle();
HttpHandle mMyHandle;
static handleMap_t mHandleMap;
static LLCoreInt::HttpMutex mOpMutex;
protected:
static ptr_t findByHandle(HttpHandle handle);
}; // end class HttpOperation
@ -195,7 +222,6 @@ class HttpOpStop : public HttpOperation
public:
HttpOpStop();
protected:
virtual ~HttpOpStop();
private:
@ -218,7 +244,6 @@ class HttpOpNull : public HttpOperation
public:
HttpOpNull();
protected:
virtual ~HttpOpNull();
private:
@ -241,7 +266,6 @@ public:
// 1 does a soft spin continuously requeuing itself
HttpOpSpin(int mode);
protected:
virtual ~HttpOpSpin();
private:

View File

@ -122,8 +122,8 @@ HttpOpRequest::HttpOpRequest()
mReqBody(NULL),
mReqOffset(0),
mReqLength(0),
mReqHeaders(NULL),
mReqOptions(NULL),
mReqHeaders(),
mReqOptions(),
mCurlActive(false),
mCurlHandle(NULL),
mCurlService(NULL),
@ -135,11 +135,12 @@ HttpOpRequest::HttpOpRequest()
mReplyOffset(0),
mReplyLength(0),
mReplyFullLength(0),
mReplyHeaders(NULL),
mReplyHeaders(),
mPolicyRetries(0),
mPolicy503Retries(0),
mPolicyRetryAt(HttpTime(0)),
mPolicyRetryLimit(HTTP_RETRY_COUNT_DEFAULT)
mPolicyRetryLimit(HTTP_RETRY_COUNT_DEFAULT),
mCallbackSSLVerify(NULL)
{
// *NOTE: As members are added, retry initialization/cleanup
// may need to be extended in @see prepareRequest().
@ -155,18 +156,6 @@ HttpOpRequest::~HttpOpRequest()
mReqBody = NULL;
}
if (mReqOptions)
{
mReqOptions->release();
mReqOptions = NULL;
}
if (mReqHeaders)
{
mReqHeaders->release();
mReqHeaders = NULL;
}
if (mCurlHandle)
{
// Uncertain of thread context so free using
@ -193,25 +182,20 @@ HttpOpRequest::~HttpOpRequest()
mReplyBody = NULL;
}
if (mReplyHeaders)
{
mReplyHeaders->release();
mReplyHeaders = NULL;
}
}
void HttpOpRequest::stageFromRequest(HttpService * service)
{
addRef();
service->getPolicy().addOp(this); // transfers refcount
HttpOpRequest::ptr_t self(boost::dynamic_pointer_cast<HttpOpRequest>(shared_from_this()));
service->getPolicy().addOp(self); // transfers refcount
}
void HttpOpRequest::stageFromReady(HttpService * service)
{
addRef();
service->getTransport().addOp(this); // transfers refcount
HttpOpRequest::ptr_t self(boost::dynamic_pointer_cast<HttpOpRequest>(shared_from_this()));
service->getTransport().addOp(self); // transfers refcount
}
@ -259,7 +243,9 @@ void HttpOpRequest::visitNotifier(HttpRequest * request)
response->setStatus(mStatus);
response->setBody(mReplyBody);
response->setHeaders(mReplyHeaders);
if (mReplyOffset || mReplyLength)
response->setRequestURL(mReqURL);
if (mReplyOffset || mReplyLength)
{
// Got an explicit offset/length in response
response->setRange(mReplyOffset, mReplyLength, mReplyFullLength);
@ -267,12 +253,27 @@ void HttpOpRequest::visitNotifier(HttpRequest * request)
response->setContentType(mReplyConType);
response->setRetries(mPolicyRetries, mPolicy503Retries);
mUserHandler->onCompleted(static_cast<HttpHandle>(this), response);
HttpResponse::TransferStats::ptr_t stats = HttpResponse::TransferStats::ptr_t(new HttpResponse::TransferStats);
curl_easy_getinfo(mCurlHandle, CURLINFO_SIZE_DOWNLOAD, &stats->mSizeDownload);
curl_easy_getinfo(mCurlHandle, CURLINFO_TOTAL_TIME, &stats->mTotalTime);
curl_easy_getinfo(mCurlHandle, CURLINFO_SPEED_DOWNLOAD, &stats->mSpeedDownload);
response->setTransferStats(stats);
mUserHandler->onCompleted(this->getHandle(), response);
response->release();
}
}
// /*static*/
// HttpOpRequest::ptr_t HttpOpRequest::fromHandle(HttpHandle handle)
// {
//
// return boost::dynamic_pointer_cast<HttpOpRequest>((static_cast<HttpOpRequest *>(handle))->shared_from_this());
// }
HttpStatus HttpOpRequest::cancel()
{
@ -287,8 +288,8 @@ HttpStatus HttpOpRequest::cancel()
HttpStatus HttpOpRequest::setupGet(HttpRequest::policy_t policy_id,
HttpRequest::priority_t priority,
const std::string & url,
HttpOptions * options,
HttpHeaders * headers)
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers)
{
setupCommon(policy_id, priority, url, NULL, options, headers);
mReqMethod = HOR_GET;
@ -302,8 +303,8 @@ HttpStatus HttpOpRequest::setupGetByteRange(HttpRequest::policy_t policy_id,
const std::string & url,
size_t offset,
size_t len,
HttpOptions * options,
HttpHeaders * headers)
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers)
{
setupCommon(policy_id, priority, url, NULL, options, headers);
mReqMethod = HOR_GET;
@ -322,8 +323,8 @@ HttpStatus HttpOpRequest::setupPost(HttpRequest::policy_t policy_id,
HttpRequest::priority_t priority,
const std::string & url,
BufferArray * body,
HttpOptions * options,
HttpHeaders * headers)
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers)
{
setupCommon(policy_id, priority, url, body, options, headers);
mReqMethod = HOR_POST;
@ -336,8 +337,8 @@ HttpStatus HttpOpRequest::setupPut(HttpRequest::policy_t policy_id,
HttpRequest::priority_t priority,
const std::string & url,
BufferArray * body,
HttpOptions * options,
HttpHeaders * headers)
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers)
{
setupCommon(policy_id, priority, url, body, options, headers);
mReqMethod = HOR_PUT;
@ -346,12 +347,65 @@ HttpStatus HttpOpRequest::setupPut(HttpRequest::policy_t policy_id,
}
HttpStatus HttpOpRequest::setupDelete(HttpRequest::policy_t policy_id,
HttpRequest::priority_t priority,
const std::string & url,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers)
{
setupCommon(policy_id, priority, url, NULL, options, headers);
mReqMethod = HOR_DELETE;
return HttpStatus();
}
HttpStatus HttpOpRequest::setupPatch(HttpRequest::policy_t policy_id,
HttpRequest::priority_t priority,
const std::string & url,
BufferArray * body,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers)
{
setupCommon(policy_id, priority, url, body, options, headers);
mReqMethod = HOR_PATCH;
return HttpStatus();
}
HttpStatus HttpOpRequest::setupCopy(HttpRequest::policy_t policy_id,
HttpRequest::priority_t priority,
const std::string & url,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t &headers)
{
setupCommon(policy_id, priority, url, NULL, options, headers);
mReqMethod = HOR_COPY;
return HttpStatus();
}
HttpStatus HttpOpRequest::setupMove(HttpRequest::policy_t policy_id,
HttpRequest::priority_t priority,
const std::string & url,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t &headers)
{
setupCommon(policy_id, priority, url, NULL, options, headers);
mReqMethod = HOR_MOVE;
return HttpStatus();
}
void HttpOpRequest::setupCommon(HttpRequest::policy_t policy_id,
HttpRequest::priority_t priority,
const std::string & url,
BufferArray * body,
HttpOptions * options,
HttpHeaders * headers)
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers)
{
mProcFlags = 0U;
mReqPolicy = policy_id;
@ -364,12 +418,10 @@ void HttpOpRequest::setupCommon(HttpRequest::policy_t policy_id,
}
if (headers && ! mReqHeaders)
{
headers->addRef();
mReqHeaders = headers;
}
if (options && ! mReqOptions)
if (options && !mReqOptions)
{
options->addRef();
mReqOptions = options;
if (options->getWantHeaders())
{
@ -416,11 +468,7 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service)
mReplyOffset = 0;
mReplyLength = 0;
mReplyFullLength = 0;
if (mReplyHeaders)
{
mReplyHeaders->release();
mReplyHeaders = NULL;
}
mReplyHeaders.reset();
mReplyConType.clear();
// *FIXME: better error handling later
@ -447,37 +495,73 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service)
check_curl_easy_code(code, CURLOPT_NOPROGRESS);
code = curl_easy_setopt(mCurlHandle, CURLOPT_URL, mReqURL.c_str());
check_curl_easy_code(code, CURLOPT_URL);
code = curl_easy_setopt(mCurlHandle, CURLOPT_PRIVATE, this);
code = curl_easy_setopt(mCurlHandle, CURLOPT_PRIVATE, getHandle());
check_curl_easy_code(code, CURLOPT_PRIVATE);
code = curl_easy_setopt(mCurlHandle, CURLOPT_ENCODING, "");
check_curl_easy_code(code, CURLOPT_ENCODING);
code = curl_easy_setopt(mCurlHandle, CURLOPT_AUTOREFERER, 1);
check_curl_easy_code(code, CURLOPT_AUTOREFERER);
code = curl_easy_setopt(mCurlHandle, CURLOPT_MAXREDIRS, HTTP_REDIRECTS_DEFAULT);
check_curl_easy_code(code, CURLOPT_MAXREDIRS);
code = curl_easy_setopt(mCurlHandle, CURLOPT_WRITEFUNCTION, writeCallback);
check_curl_easy_code(code, CURLOPT_WRITEFUNCTION);
code = curl_easy_setopt(mCurlHandle, CURLOPT_WRITEDATA, getHandle());
check_curl_easy_code(code, CURLOPT_WRITEDATA);
code = curl_easy_setopt(mCurlHandle, CURLOPT_READFUNCTION, readCallback);
check_curl_easy_code(code, CURLOPT_READFUNCTION);
code = curl_easy_setopt(mCurlHandle, CURLOPT_READDATA, getHandle());
check_curl_easy_code(code, CURLOPT_READDATA);
code = curl_easy_setopt(mCurlHandle, CURLOPT_SEEKFUNCTION, seekCallback);
check_curl_easy_code(code, CURLOPT_SEEKFUNCTION);
code = curl_easy_setopt(mCurlHandle, CURLOPT_SEEKDATA, getHandle());
check_curl_easy_code(code, CURLOPT_SEEKDATA);
code = curl_easy_setopt(mCurlHandle, CURLOPT_COOKIEFILE, "");
check_curl_easy_code(code, CURLOPT_COOKIEFILE);
if (gpolicy.mSslCtxCallback)
{
code = curl_easy_setopt(mCurlHandle, CURLOPT_SSL_CTX_FUNCTION, curlSslCtxCallback);
check_curl_easy_code(code, CURLOPT_SSL_CTX_FUNCTION);
code = curl_easy_setopt(mCurlHandle, CURLOPT_SSL_CTX_DATA, getHandle());
check_curl_easy_code(code, CURLOPT_SSL_CTX_DATA);
mCallbackSSLVerify = gpolicy.mSslCtxCallback;
}
long follow_redirect(1L);
long sslPeerV(0L);
long sslHostV(0L);
long dnsCacheTimeout(-1L);
long nobody(0L);
if (mReqOptions)
{
follow_redirect = mReqOptions->getFollowRedirects() ? 1L : 0L;
sslPeerV = mReqOptions->getSSLVerifyPeer() ? 1L : 0L;
sslHostV = mReqOptions->getSSLVerifyHost() ? 2L : 0L;
dnsCacheTimeout = mReqOptions->getDNSCacheTimeout();
nobody = mReqOptions->getHeadersOnly() ? 1L : 0L;
}
code = curl_easy_setopt(mCurlHandle, CURLOPT_FOLLOWLOCATION, follow_redirect);
check_curl_easy_code(code, CURLOPT_FOLLOWLOCATION);
code = curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYPEER, sslPeerV);
check_curl_easy_code(code, CURLOPT_SSL_VERIFYPEER);
code = curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYHOST, sslHostV);
check_curl_easy_code(code, CURLOPT_SSL_VERIFYHOST);
code = curl_easy_setopt(mCurlHandle, CURLOPT_NOBODY, nobody);
check_curl_easy_code(code, CURLOPT_NOBODY);
// The Linksys WRT54G V5 router has an issue with frequent
// DNS lookups from LAN machines. If they happen too often,
// like for every HTTP request, the router gets annoyed after
// about 700 or so requests and starts issuing TCP RSTs to
// new connections. Reuse the DNS lookups for even a few
// seconds and no RSTs.
code = curl_easy_setopt(mCurlHandle, CURLOPT_DNS_CACHE_TIMEOUT, 15);
code = curl_easy_setopt(mCurlHandle, CURLOPT_DNS_CACHE_TIMEOUT, dnsCacheTimeout);
check_curl_easy_code(code, CURLOPT_DNS_CACHE_TIMEOUT);
code = curl_easy_setopt(mCurlHandle, CURLOPT_AUTOREFERER, 1);
check_curl_easy_code(code, CURLOPT_AUTOREFERER);
code = curl_easy_setopt(mCurlHandle, CURLOPT_FOLLOWLOCATION, 1);
check_curl_easy_code(code, CURLOPT_FOLLOWLOCATION);
code = curl_easy_setopt(mCurlHandle, CURLOPT_MAXREDIRS, HTTP_REDIRECTS_DEFAULT);
check_curl_easy_code(code, CURLOPT_MAXREDIRS);
code = curl_easy_setopt(mCurlHandle, CURLOPT_WRITEFUNCTION, writeCallback);
check_curl_easy_code(code, CURLOPT_WRITEFUNCTION);
code = curl_easy_setopt(mCurlHandle, CURLOPT_WRITEDATA, this);
check_curl_easy_code(code, CURLOPT_WRITEDATA);
code = curl_easy_setopt(mCurlHandle, CURLOPT_READFUNCTION, readCallback);
check_curl_easy_code(code, CURLOPT_READFUNCTION);
code = curl_easy_setopt(mCurlHandle, CURLOPT_READDATA, this);
check_curl_easy_code(code, CURLOPT_READDATA);
code = curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYPEER, 1);
check_curl_easy_code(code, CURLOPT_SSL_VERIFYPEER);
code = curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYHOST, 0);
check_curl_easy_code(code, CURLOPT_SSL_VERIFYHOST);
if (gpolicy.mUseLLProxy)
{
@ -509,10 +593,9 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service)
switch (mReqMethod)
{
case HOR_GET:
code = curl_easy_setopt(mCurlHandle, CURLOPT_HTTPGET, 1);
if (nobody == 0)
code = curl_easy_setopt(mCurlHandle, CURLOPT_HTTPGET, 1);
check_curl_easy_code(code, CURLOPT_HTTPGET);
mCurlHeaders = curl_slist_append(mCurlHeaders, "Connection: keep-alive");
mCurlHeaders = curl_slist_append(mCurlHeaders, "Keep-alive: 300");
break;
case HOR_POST:
@ -531,12 +614,14 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service)
code = curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDSIZE, data_size);
check_curl_easy_code(code, CURLOPT_POSTFIELDSIZE);
mCurlHeaders = curl_slist_append(mCurlHeaders, "Expect:");
mCurlHeaders = curl_slist_append(mCurlHeaders, "Connection: keep-alive");
mCurlHeaders = curl_slist_append(mCurlHeaders, "Keep-alive: 300");
}
break;
case HOR_PUT:
case HOR_PATCH:
code = curl_easy_setopt(mCurlHandle, CURLOPT_CUSTOMREQUEST, "PATCH");
check_curl_easy_code(code, CURLOPT_CUSTOMREQUEST);
// fall through. The rest is the same as PUT
case HOR_PUT:
{
code = curl_easy_setopt(mCurlHandle, CURLOPT_UPLOAD, 1);
check_curl_easy_code(code, CURLOPT_UPLOAD);
@ -547,15 +632,25 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service)
}
code = curl_easy_setopt(mCurlHandle, CURLOPT_INFILESIZE, data_size);
check_curl_easy_code(code, CURLOPT_INFILESIZE);
code = curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDS, (void *) NULL);
check_curl_easy_code(code, CURLOPT_POSTFIELDS);
mCurlHeaders = curl_slist_append(mCurlHeaders, "Expect:");
// *TODO: Should this be 'Keep-Alive' ?
mCurlHeaders = curl_slist_append(mCurlHeaders, "Connection: keep-alive");
mCurlHeaders = curl_slist_append(mCurlHeaders, "Keep-alive: 300");
}
break;
case HOR_DELETE:
code = curl_easy_setopt(mCurlHandle, CURLOPT_CUSTOMREQUEST, "DELETE");
check_curl_easy_code(code, CURLOPT_CUSTOMREQUEST);
break;
case HOR_COPY:
code = curl_easy_setopt(mCurlHandle, CURLOPT_CUSTOMREQUEST, "COPY");
check_curl_easy_code(code, CURLOPT_CUSTOMREQUEST);
break;
case HOR_MOVE:
code = curl_easy_setopt(mCurlHandle, CURLOPT_CUSTOMREQUEST, "MOVE");
check_curl_easy_code(code, CURLOPT_CUSTOMREQUEST);
break;
default:
LL_ERRS(LOG_CORE) << "Invalid HTTP method in request: "
<< int(mReqMethod) << ". Can't recover."
@ -563,6 +658,11 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service)
break;
}
// *TODO: Should this be 'Keep-Alive' ?
mCurlHeaders = curl_slist_append(mCurlHeaders, "Connection: keep-alive");
mCurlHeaders = curl_slist_append(mCurlHeaders, "Keep-alive: 300");
// Tracing
if (mTracing >= HTTP_TRACE_CURL_HEADERS)
{
@ -650,7 +750,11 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service)
xfer_timeout *= 2L;
}
// *DEBUG: Enable following override for timeout handling and "[curl:bugs] #1420" tests
// xfer_timeout = 1L;
//if (cpolicy.mPipelining)
//{
// xfer_timeout = 1L;
// timeout = 1L;
//}
code = curl_easy_setopt(mCurlHandle, CURLOPT_TIMEOUT, xfer_timeout);
check_curl_easy_code(code, CURLOPT_TIMEOUT);
code = curl_easy_setopt(mCurlHandle, CURLOPT_CONNECTTIMEOUT, timeout);
@ -683,7 +787,7 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service)
size_t HttpOpRequest::writeCallback(void * data, size_t size, size_t nmemb, void * userdata)
{
HttpOpRequest * op(static_cast<HttpOpRequest *>(userdata));
HttpOpRequest::ptr_t op(HttpOpRequest::fromHandle<HttpOpRequest>(userdata));
if (! op->mReplyBody)
{
@ -697,7 +801,7 @@ size_t HttpOpRequest::writeCallback(void * data, size_t size, size_t nmemb, void
size_t HttpOpRequest::readCallback(void * data, size_t size, size_t nmemb, void * userdata)
{
HttpOpRequest * op(static_cast<HttpOpRequest *>(userdata));
HttpOpRequest::ptr_t op(HttpOpRequest::fromHandle<HttpOpRequest>(userdata));
if (! op->mReqBody)
{
@ -723,6 +827,37 @@ size_t HttpOpRequest::readCallback(void * data, size_t size, size_t nmemb, void
return read_size;
}
int HttpOpRequest::seekCallback(void *userdata, curl_off_t offset, int origin)
{
HttpOpRequest::ptr_t op(HttpOpRequest::fromHandle<HttpOpRequest>(userdata));
if (!op->mReqBody)
{
return 0;
}
size_t newPos = 0;
if (origin == SEEK_SET)
newPos = offset;
else if (origin == SEEK_END)
newPos = static_cast<curl_off_t>(op->mReqBody->size()) + offset;
else if (origin == SEEK_CUR)
newPos = static_cast<curl_off_t>(op->mCurlBodyPos) + offset;
else
return 2;
if (newPos >= op->mReqBody->size())
{
LL_WARNS(LOG_CORE) << "Attempt to seek to position outside post body." << LL_ENDL;
return 2;
}
op->mCurlBodyPos = (size_t)newPos;
return 0;
}
size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, void * userdata)
{
@ -731,7 +866,7 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi
static const char con_ran_line[] = "content-range";
static const char con_retry_line[] = "retry-after";
HttpOpRequest * op(static_cast<HttpOpRequest *>(userdata));
HttpOpRequest::ptr_t op(HttpOpRequest::fromHandle<HttpOpRequest>(userdata));
const size_t hdr_size(size * nmemb);
const char * hdr_data(static_cast<const char *>(data)); // Not null terminated
@ -817,7 +952,7 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi
// Save headers in response
if (! op->mReplyHeaders)
{
op->mReplyHeaders = new HttpHeaders;
op->mReplyHeaders = HttpHeaders::ptr_t(new HttpHeaders);
}
op->mReplyHeaders->append(name, value ? value : "");
}
@ -873,9 +1008,38 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi
}
CURLcode HttpOpRequest::curlSslCtxCallback(CURL *curl, void *sslctx, void *userdata)
{
HttpOpRequest::ptr_t op(HttpOpRequest::fromHandle<HttpOpRequest>(userdata));
if (op->mCallbackSSLVerify)
{
SSL_CTX * ctx = (SSL_CTX *)sslctx;
// disable any default verification for server certs
SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL);
// set the verification callback.
SSL_CTX_set_cert_verify_callback(ctx, sslCertVerifyCallback, userdata);
// the calls are void
}
return CURLE_OK;
}
int HttpOpRequest::sslCertVerifyCallback(X509_STORE_CTX *ctx, void *param)
{
HttpOpRequest::ptr_t op(HttpOpRequest::fromHandle<HttpOpRequest>(param));
if (op->mCallbackSSLVerify)
{
op->mStatus = op->mCallbackSSLVerify(op->mReqURL, op->mUserHandler, ctx);
}
return (op->mStatus) ? 1 : 0;
}
int HttpOpRequest::debugCallback(CURL * handle, curl_infotype info, char * buffer, size_t len, void * userdata)
{
HttpOpRequest * op(static_cast<HttpOpRequest *>(userdata));
HttpOpRequest::ptr_t op(HttpOpRequest::fromHandle<HttpOpRequest>(userdata));
std::string safe_line;
std::string tag;
@ -955,7 +1119,7 @@ int HttpOpRequest::debugCallback(CURL * handle, curl_infotype info, char * buffe
if (logit)
{
LL_INFOS(LOG_CORE) << "TRACE, LibcurlDebug, Handle: "
<< static_cast<HttpHandle>(op)
<< op->getHandle()
<< ", Type: " << tag
<< ", Data: " << safe_line
<< LL_ENDL;

View File

@ -33,19 +33,22 @@
#include <string>
#include <curl/curl.h>
#include <openssl/x509_vfy.h>
#include <openssl/ssl.h>
#include "httpcommon.h"
#include "httprequest.h"
#include "_httpoperation.h"
#include "_refcounted.h"
#include "httpheaders.h"
#include "httpoptions.h"
namespace LLCore
{
class BufferArray;
class HttpHeaders;
class HttpOptions;
/// HttpOpRequest requests a supported HTTP method invocation with
@ -63,9 +66,10 @@ class HttpOptions;
class HttpOpRequest : public HttpOperation
{
public:
typedef boost::shared_ptr<HttpOpRequest> ptr_t;
HttpOpRequest();
protected:
virtual ~HttpOpRequest(); // Use release()
private:
@ -77,7 +81,11 @@ public:
{
HOR_GET,
HOR_POST,
HOR_PUT
HOR_PUT,
HOR_DELETE,
HOR_PATCH,
HOR_COPY,
HOR_MOVE
};
virtual void stageFromRequest(HttpService *);
@ -98,32 +106,57 @@ public:
HttpStatus setupGet(HttpRequest::policy_t policy_id,
HttpRequest::priority_t priority,
const std::string & url,
HttpOptions * options,
HttpHeaders * headers);
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers);
HttpStatus setupGetByteRange(HttpRequest::policy_t policy_id,
HttpRequest::priority_t priority,
const std::string & url,
size_t offset,
size_t len,
HttpOptions * options,
HttpHeaders * headers);
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers);
HttpStatus setupPost(HttpRequest::policy_t policy_id,
HttpRequest::priority_t priority,
const std::string & url,
BufferArray * body,
HttpOptions * options,
HttpHeaders * headers);
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers);
HttpStatus setupPut(HttpRequest::policy_t policy_id,
HttpRequest::priority_t priority,
const std::string & url,
BufferArray * body,
HttpOptions * options,
HttpHeaders * headers);
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers);
// Internal method used to setup the libcurl options for a request.
HttpStatus setupDelete(HttpRequest::policy_t policy_id,
HttpRequest::priority_t priority,
const std::string & url,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers);
HttpStatus setupPatch(HttpRequest::policy_t policy_id,
HttpRequest::priority_t priority,
const std::string & url,
BufferArray * body,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers);
HttpStatus setupCopy(HttpRequest::policy_t policy_id,
HttpRequest::priority_t priority,
const std::string & url,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers);
HttpStatus setupMove(HttpRequest::policy_t policy_id,
HttpRequest::priority_t priority,
const std::string & url,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers);
// Internal method used to setup the libcurl options for a request.
// Does all the libcurl handle setup in one place.
//
// Threading: called by worker thread
@ -141,8 +174,8 @@ protected:
HttpRequest::priority_t priority,
const std::string & url,
BufferArray * body,
HttpOptions * options,
HttpHeaders * headers);
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers);
// libcurl operational callbacks
//
@ -150,7 +183,11 @@ protected:
//
static size_t writeCallback(void * data, size_t size, size_t nmemb, void * userdata);
static size_t readCallback(void * data, size_t size, size_t nmemb, void * userdata);
static int seekCallback(void *data, curl_off_t offset, int origin);
static size_t headerCallback(void * data, size_t size, size_t nmemb, void * userdata);
static CURLcode curlSslCtxCallback(CURL *curl, void *ssl_ctx, void *userptr);
static int sslCertVerifyCallback(X509_STORE_CTX *ctx, void *param);
static int debugCallback(CURL *, curl_infotype info, char * buffer, size_t len, void * userdata);
protected:
@ -159,6 +196,8 @@ protected:
static const unsigned int PF_SAVE_HEADERS = 0x00000002U;
static const unsigned int PF_USE_RETRY_AFTER = 0x00000004U;
HttpRequest::policyCallback_t mCallbackSSLVerify;
public:
// Request data
EMethod mReqMethod;
@ -166,8 +205,8 @@ public:
BufferArray * mReqBody;
off_t mReqOffset;
size_t mReqLength;
HttpHeaders * mReqHeaders;
HttpOptions * mReqOptions;
HttpHeaders::ptr_t mReqHeaders;
HttpOptions::ptr_t mReqOptions;
// Transport data
bool mCurlActive;
@ -184,7 +223,7 @@ public:
off_t mReplyOffset;
size_t mReplyLength;
size_t mReplyFullLength;
HttpHeaders * mReplyHeaders;
HttpHeaders::ptr_t mReplyHeaders;
std::string mReplyConType;
int mReplyRetryAfter;
@ -215,7 +254,7 @@ public:
// Internal function to append the contents of an HttpHeaders
// instance to a curl_slist object.
curl_slist * append_headers_to_slist(const HttpHeaders *, curl_slist * slist);
curl_slist * append_headers_to_slist(const HttpHeaders::ptr_t &, curl_slist * slist);
} // end namespace LLCore

View File

@ -53,9 +53,10 @@ namespace LLCore
class HttpOpSetGet : public HttpOperation
{
public:
typedef boost::shared_ptr<HttpOpSetGet> ptr_t;
HttpOpSetGet();
protected:
virtual ~HttpOpSetGet(); // Use release()
private:

View File

@ -51,7 +51,6 @@ class HttpOpSetPriority : public HttpOperation
public:
HttpOpSetPriority(HttpHandle handle, HttpRequest::priority_t priority);
protected:
virtual ~HttpOpSetPriority();
private:

View File

@ -116,21 +116,19 @@ void HttpPolicy::shutdown()
HttpRetryQueue & retryq(state.mRetryQueue);
while (! retryq.empty())
{
HttpOpRequest * op(retryq.top());
HttpOpRequest::ptr_t op(retryq.top());
retryq.pop();
op->cancel();
op->release();
}
HttpReadyQueue & readyq(state.mReadyQueue);
while (! readyq.empty())
{
HttpOpRequest * op(readyq.top());
HttpOpRequest::ptr_t op(readyq.top());
readyq.pop();
op->cancel();
op->release();
}
}
}
@ -141,7 +139,7 @@ void HttpPolicy::start()
}
void HttpPolicy::addOp(HttpOpRequest * op)
void HttpPolicy::addOp(const HttpOpRequest::ptr_t &op)
{
const int policy_class(op->mReqPolicy);
@ -151,7 +149,7 @@ void HttpPolicy::addOp(HttpOpRequest * op)
}
void HttpPolicy::retryOp(HttpOpRequest * op)
void HttpPolicy::retryOp(const HttpOpRequest::ptr_t &op)
{
static const HttpTime retry_deltas[] =
{
@ -180,7 +178,7 @@ void HttpPolicy::retryOp(HttpOpRequest * op)
{
++op->mPolicy503Retries;
}
LL_DEBUGS(LOG_CORE) << "HTTP request " << static_cast<HttpHandle>(op)
LL_DEBUGS(LOG_CORE) << "HTTP request " << op->getHandle()
<< " retry " << op->mPolicyRetries
<< " scheduled in " << (delta / HttpTime(1000))
<< " mS (" << (external_delta ? "external" : "internal")
@ -189,10 +187,10 @@ void HttpPolicy::retryOp(HttpOpRequest * op)
if (op->mTracing > HTTP_TRACE_OFF)
{
LL_INFOS(LOG_CORE) << "TRACE, ToRetryQueue, Handle: "
<< static_cast<HttpHandle>(op)
<< ", Delta: " << (delta / HttpTime(1000))
<< ", Retries: " << op->mPolicyRetries
<< LL_ENDL;
<< op->getHandle()
<< ", Delta: " << (delta / HttpTime(1000))
<< ", Retries: " << op->mPolicyRetries
<< LL_ENDL;
}
mClasses[policy_class]->mRetryQueue.push(op);
}
@ -264,14 +262,14 @@ HttpService::ELoopSpeed HttpPolicy::processReadyQueue()
// First see if we have any retries...
while (needed > 0 && ! retryq.empty())
{
HttpOpRequest * op(retryq.top());
HttpOpRequest::ptr_t op(retryq.top());
if (op->mPolicyRetryAt > now)
break;
retryq.pop();
op->stageFromReady(mService);
op->release();
op.reset();
++state.mRequestCount;
--needed;
@ -296,11 +294,11 @@ HttpService::ELoopSpeed HttpPolicy::processReadyQueue()
// Now go on to the new requests...
while (needed > 0 && ! readyq.empty())
{
HttpOpRequest * op(readyq.top());
HttpOpRequest::ptr_t op(readyq.top());
readyq.pop();
op->stageFromReady(mService);
op->release();
op.reset();
++state.mRequestCount;
--needed;
@ -351,9 +349,9 @@ bool HttpPolicy::changePriority(HttpHandle handle, HttpRequest::priority_t prior
{
HttpReadyQueue::container_type::iterator cur(iter++);
if (static_cast<HttpHandle>(*cur) == handle)
if ((*cur)->getHandle() == handle)
{
HttpOpRequest * op(*cur);
HttpOpRequest::ptr_t op(*cur);
c.erase(cur); // All iterators are now invalidated
op->mReqPriority = priority;
state.mReadyQueue.push(op); // Re-insert using adapter class
@ -378,12 +376,11 @@ bool HttpPolicy::cancel(HttpHandle handle)
{
HttpRetryQueue::container_type::iterator cur(iter++);
if (static_cast<HttpHandle>(*cur) == handle)
if ((*cur)->getHandle() == handle)
{
HttpOpRequest * op(*cur);
HttpOpRequest::ptr_t op(*cur);
c1.erase(cur); // All iterators are now invalidated
op->cancel();
op->release();
return true;
}
}
@ -394,12 +391,11 @@ bool HttpPolicy::cancel(HttpHandle handle)
{
HttpReadyQueue::container_type::iterator cur(iter++);
if (static_cast<HttpHandle>(*cur) == handle)
if ((*cur)->getHandle() == handle)
{
HttpOpRequest * op(*cur);
HttpOpRequest::ptr_t op(*cur);
c2.erase(cur); // All iterators are now invalidated
op->cancel();
op->release();
return true;
}
}
@ -409,7 +405,7 @@ bool HttpPolicy::cancel(HttpHandle handle)
}
bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op)
bool HttpPolicy::stageAfterCompletion(const HttpOpRequest::ptr_t &op)
{
// Retry or finalize
if (! op->mStatus)
@ -420,7 +416,7 @@ bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op)
#if 0
if (op->mStatus == HttpStatus(HttpStatus::EXT_CURL_EASY, CURLE_OPERATION_TIMEDOUT))
{
LL_WARNS(LOG_CORE) << "HTTP request " << static_cast<HttpHandle>(op)
LL_WARNS(LOG_CORE) << "HTTP request " << op->getHandle()
<< " timed out."
<< LL_ENDL;
}
@ -438,7 +434,7 @@ bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op)
// This op is done, finalize it delivering it to the reply queue...
if (! op->mStatus)
{
LL_WARNS(LOG_CORE) << "HTTP request " << static_cast<HttpHandle>(op)
LL_WARNS(LOG_CORE) << "HTTP request " << op->getHandle()
<< " failed after " << op->mPolicyRetries
<< " retries. Reason: " << op->mStatus.toString()
<< " (" << op->mStatus.toTerseString() << ")"
@ -446,13 +442,12 @@ bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op)
}
else if (op->mPolicyRetries)
{
LL_DEBUGS(LOG_CORE) << "HTTP request " << static_cast<HttpHandle>(op)
LL_DEBUGS(LOG_CORE) << "HTTP request " << op->getHandle()
<< " succeeded on retry " << op->mPolicyRetries << "."
<< LL_ENDL;
}
op->stageFromActive(mService);
op->release();
return false; // not active
}

View File

@ -60,6 +60,8 @@ private:
void operator=(const HttpPolicy &); // Not defined
public:
typedef boost::shared_ptr<HttpOpRequest> opReqPtr_t;
/// Threading: called by init thread.
HttpRequest::policy_t createPolicyClass();
@ -96,7 +98,7 @@ public:
/// from queue.
///
/// Threading: called by worker thread
void addOp(HttpOpRequest *);
void addOp(const opReqPtr_t &);
/// Similar to addOp, used when a caller wants to retry a
/// request that has failed. It's placed on a special retry
@ -106,7 +108,7 @@ public:
/// order.
///
/// Threading: called by worker thread
void retryOp(HttpOpRequest *);
void retryOp(const opReqPtr_t &);
/// Attempt to change the priority of an earlier request.
/// Request that Shadows HttpService's method
@ -130,7 +132,7 @@ public:
/// sent on to the reply queue.
///
/// Threading: called by worker thread
bool stageAfterCompletion(HttpOpRequest * op);
bool stageAfterCompletion(const opReqPtr_t &op);
/// Get a reference to global policy options. Caller is expected
/// to do context checks like no setting once running. These

View File

@ -106,6 +106,20 @@ HttpStatus HttpPolicyGlobal::set(HttpRequest::EPolicyOption opt, const std::stri
return HttpStatus();
}
HttpStatus HttpPolicyGlobal::set(HttpRequest::EPolicyOption opt, HttpRequest::policyCallback_t value)
{
switch (opt)
{
case HttpRequest::PO_SSL_VERIFY_CALLBACK:
mSslCtxCallback = value;
break;
default:
return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
}
return HttpStatus();
}
HttpStatus HttpPolicyGlobal::get(HttpRequest::EPolicyOption opt, long * value) const
{
@ -154,4 +168,20 @@ HttpStatus HttpPolicyGlobal::get(HttpRequest::EPolicyOption opt, std::string * v
return HttpStatus();
}
HttpStatus HttpPolicyGlobal::get(HttpRequest::EPolicyOption opt, HttpRequest::policyCallback_t * value) const
{
switch (opt)
{
case HttpRequest::PO_SSL_VERIFY_CALLBACK:
*value = mSslCtxCallback;
break;
default:
return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
}
return HttpStatus();
}
} // end namespace LLCore

View File

@ -60,8 +60,10 @@ private:
public:
HttpStatus set(HttpRequest::EPolicyOption opt, long value);
HttpStatus set(HttpRequest::EPolicyOption opt, const std::string & value);
HttpStatus set(HttpRequest::EPolicyOption opt, HttpRequest::policyCallback_t value);
HttpStatus get(HttpRequest::EPolicyOption opt, long * value) const;
HttpStatus get(HttpRequest::EPolicyOption opt, std::string * value) const;
HttpStatus get(HttpRequest::EPolicyOption opt, HttpRequest::policyCallback_t * value) const;
public:
long mConnectionLimit;
@ -70,6 +72,7 @@ public:
std::string mHttpProxy;
long mTrace;
long mUseLLProxy;
HttpRequest::policyCallback_t mSslCtxCallback;
}; // end class HttpPolicyGlobal
} // end namespace LLCore

View File

@ -56,12 +56,12 @@ namespace LLCore
#if LLCORE_HTTP_READY_QUEUE_IGNORES_PRIORITY
typedef std::deque<HttpOpRequest *> HttpReadyQueueBase;
typedef std::deque<HttpOpRequest::ptr_t> HttpReadyQueueBase;
#else
typedef std::priority_queue<HttpOpRequest *,
std::deque<HttpOpRequest *>,
typedef std::priority_queue<HttpOpRequest::ptr_t,
std::deque<HttpOpRequest::ptr_t>,
LLCore::HttpOpRequestCompare> HttpReadyQueueBase;
#endif // LLCORE_HTTP_READY_QUEUE_IGNORES_PRIORITY

View File

@ -39,23 +39,17 @@ namespace LLCore
HttpReplyQueue::HttpReplyQueue()
: RefCounted(true)
{
}
HttpReplyQueue::~HttpReplyQueue()
{
while (! mQueue.empty())
{
HttpOperation * op = mQueue.back();
mQueue.pop_back();
op->release();
}
mQueue.clear();
}
void HttpReplyQueue::addOp(HttpOperation * op)
void HttpReplyQueue::addOp(const HttpReplyQueue::opPtr_t &op)
{
{
HttpScopedLock lock(mQueueMutex);
@ -66,15 +60,15 @@ void HttpReplyQueue::addOp(HttpOperation * op)
}
HttpOperation * HttpReplyQueue::fetchOp()
HttpReplyQueue::opPtr_t HttpReplyQueue::fetchOp()
{
HttpOperation * result(NULL);
HttpOperation::ptr_t result;
{
HttpScopedLock lock(mQueueMutex);
if (mQueue.empty())
return NULL;
return opPtr_t();
result = mQueue.front();
mQueue.erase(mQueue.begin());
@ -98,9 +92,6 @@ void HttpReplyQueue::fetchAll(OpContainer & ops)
mQueue.swap(ops);
}
}
// Caller also acquires the reference counts on each op.
return;
}

View File

@ -58,21 +58,19 @@ class HttpOperation;
/// will be coded anyway so it shouldn't be too much of a
/// burden.
class HttpReplyQueue : public LLCoreInt::RefCounted
class HttpReplyQueue : private boost::noncopyable
{
public:
/// Caller acquires a Refcount on construction
typedef boost::shared_ptr<HttpOperation> opPtr_t;
typedef boost::shared_ptr<HttpReplyQueue> ptr_t;
HttpReplyQueue();
protected:
virtual ~HttpReplyQueue(); // Use release()
private:
HttpReplyQueue(const HttpReplyQueue &); // Not defined
void operator=(const HttpReplyQueue &); // Not defined
virtual ~HttpReplyQueue();
public:
typedef std::vector<HttpOperation *> OpContainer;
typedef std::vector< opPtr_t > OpContainer;
/// Insert an object at the back of the reply queue.
///
@ -80,7 +78,7 @@ public:
/// through the queue.
///
/// Threading: callable by any thread.
void addOp(HttpOperation * op);
void addOp(const opPtr_t &op);
/// Fetch an operation from the head of the queue. Returns
/// NULL if none exists.
@ -88,7 +86,7 @@ public:
/// Caller acquires reference count on returned operation.
///
/// Threading: callable by any thread.
HttpOperation * fetchOp();
opPtr_t fetchOp();
/// Caller acquires reference count on each returned operation
///
@ -96,6 +94,7 @@ public:
void fetchAll(OpContainer & ops);
protected:
OpContainer mQueue;
LLCoreInt::HttpMutex mQueueMutex;
LLCoreInt::HttpConditionVariable mQueueCV;

View File

@ -47,12 +47,7 @@ HttpRequestQueue::HttpRequestQueue()
HttpRequestQueue::~HttpRequestQueue()
{
while (! mQueue.empty())
{
HttpOperation * op = mQueue.back();
mQueue.pop_back();
op->release();
}
mQueue.clear();
}
@ -73,7 +68,7 @@ void HttpRequestQueue::term()
}
HttpStatus HttpRequestQueue::addOp(HttpOperation * op)
HttpStatus HttpRequestQueue::addOp(const HttpRequestQueue::opPtr_t &op)
{
bool wake(false);
{
@ -95,9 +90,9 @@ HttpStatus HttpRequestQueue::addOp(HttpOperation * op)
}
HttpOperation * HttpRequestQueue::fetchOp(bool wait)
HttpRequestQueue::opPtr_t HttpRequestQueue::fetchOp(bool wait)
{
HttpOperation * result(NULL);
HttpOperation::ptr_t result;
{
HttpScopedLock lock(mQueueMutex);
@ -105,7 +100,7 @@ HttpOperation * HttpRequestQueue::fetchOp(bool wait)
while (mQueue.empty())
{
if (! wait || mQueueStopped)
return NULL;
return HttpOperation::ptr_t();
mQueueCV.wait(lock);
}

View File

@ -61,6 +61,8 @@ private:
void operator=(const HttpRequestQueue &); // Not defined
public:
typedef boost::shared_ptr<HttpOperation> opPtr_t;
static void init();
static void term();
@ -71,7 +73,7 @@ public:
}
public:
typedef std::vector<HttpOperation *> OpContainer;
typedef std::vector<opPtr_t> OpContainer;
/// Insert an object at the back of the request queue.
///
@ -83,7 +85,7 @@ public:
/// an explicit release() call.
///
/// Threading: callable by any thread.
HttpStatus addOp(HttpOperation * op);
HttpStatus addOp(const opPtr_t &op);
/// Return the operation on the front of the queue. If
/// the queue is empty and @wait is false, call returns
@ -95,7 +97,7 @@ public:
/// Caller acquires reference count any returned operation
///
/// Threading: callable by any thread.
HttpOperation * fetchOp(bool wait);
opPtr_t fetchOp(bool wait);
/// Return all queued requests to caller. The @ops argument
/// should be empty when called and will be swap()'d with

View File

@ -49,15 +49,15 @@ namespace LLCore
struct HttpOpRetryCompare
{
bool operator()(const HttpOpRequest * lhs, const HttpOpRequest * rhs)
bool operator()(const HttpOpRequest::ptr_t &lhs, const HttpOpRequest::ptr_t &rhs)
{
return lhs->mPolicyRetryAt < rhs->mPolicyRetryAt;
}
};
typedef std::priority_queue<HttpOpRequest *,
std::deque<HttpOpRequest *>,
typedef std::priority_queue<HttpOpRequest::ptr_t,
std::deque<HttpOpRequest::ptr_t>,
LLCore::HttpOpRetryCompare> HttpRetryQueueBase;
class HttpRetryQueue : public HttpRetryQueueBase

View File

@ -53,15 +53,16 @@ namespace LLCore
const HttpService::OptionDescriptor HttpService::sOptionDesc[] =
{ // isLong isDynamic isGlobal isClass
{ true, true, true, true }, // PO_CONNECTION_LIMIT
{ true, true, false, true }, // PO_PER_HOST_CONNECTION_LIMIT
{ false, false, true, false }, // PO_CA_PATH
{ false, false, true, false }, // PO_CA_FILE
{ false, true, true, false }, // PO_HTTP_PROXY
{ true, true, true, false }, // PO_LLPROXY
{ true, true, true, false }, // PO_TRACE
{ true, true, false, true }, // PO_ENABLE_PIPELINING
{ true, true, false, true } // PO_THROTTLE_RATE
{ true, true, true, true, false }, // PO_CONNECTION_LIMIT
{ true, true, false, true, false }, // PO_PER_HOST_CONNECTION_LIMIT
{ false, false, true, false, false }, // PO_CA_PATH
{ false, false, true, false, false }, // PO_CA_FILE
{ false, true, true, false, false }, // PO_HTTP_PROXY
{ true, true, true, false, false }, // PO_LLPROXY
{ true, true, true, false, false }, // PO_TRACE
{ true, true, false, true, false }, // PO_ENABLE_PIPELINING
{ true, true, false, true, false }, // PO_THROTTLE_RATE
{ false, false, true, false, true } // PO_SSL_VERIFY_CALLBACK
};
HttpService * HttpService::sInstance(NULL);
volatile HttpService::EState HttpService::sState(NOT_INITIALIZED);
@ -262,14 +263,13 @@ void HttpService::shutdown()
// Cancel requests already on the request queue
HttpRequestQueue::OpContainer ops;
mRequestQueue->fetchAll(false, ops);
while (! ops.empty())
{
HttpOperation * op(ops.front());
ops.erase(ops.begin());
op->cancel();
op->release();
}
for (HttpRequestQueue::OpContainer::iterator it = ops.begin();
it != ops.end(); ++it)
{
(*it)->cancel();
}
ops.clear();
// Shutdown transport canceling requests, freeing resources
mTransport->shutdown();
@ -323,7 +323,7 @@ HttpService::ELoopSpeed HttpService::processRequestQueue(ELoopSpeed loop)
mRequestQueue->fetchAll(wait_for_req, ops);
while (! ops.empty())
{
HttpOperation * op(ops.front());
HttpOperation::ptr_t op(ops.front());
ops.erase(ops.begin());
// Process operation
@ -337,7 +337,7 @@ HttpService::ELoopSpeed HttpService::processRequestQueue(ELoopSpeed loop)
if (op->mTracing > HTTP_TRACE_OFF)
{
LL_INFOS(LOG_CORE) << "TRACE, FromRequestQueue, Handle: "
<< static_cast<HttpHandle>(op)
<< op->getHandle()
<< LL_ENDL;
}
@ -346,7 +346,7 @@ HttpService::ELoopSpeed HttpService::processRequestQueue(ELoopSpeed loop)
}
// Done with operation
op->release();
op.reset();
}
// Queue emptied, allow polling loop to sleep
@ -413,6 +413,34 @@ HttpStatus HttpService::getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequ
return status;
}
HttpStatus HttpService::getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass,
HttpRequest::policyCallback_t * ret_value)
{
HttpStatus status(HttpStatus::LLCORE, LLCore::HE_INVALID_ARG);
if (opt < HttpRequest::PO_CONNECTION_LIMIT // option must be in range
|| opt >= HttpRequest::PO_LAST // ditto
|| (sOptionDesc[opt].mIsLong) // datatype is string
|| (pclass != HttpRequest::GLOBAL_POLICY_ID && pclass > mLastPolicy) // pclass in valid range
|| (pclass == HttpRequest::GLOBAL_POLICY_ID && !sOptionDesc[opt].mIsGlobal) // global setting permitted
|| (pclass != HttpRequest::GLOBAL_POLICY_ID && !sOptionDesc[opt].mIsClass)) // class setting permitted
// can always get, no dynamic check
{
return status;
}
// Only global has callback values
if (pclass == HttpRequest::GLOBAL_POLICY_ID)
{
HttpPolicyGlobal & opts(mPolicy->getGlobalOptions());
status = opts.get(opt, ret_value);
}
return status;
}
HttpStatus HttpService::setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass,
long value, long * ret_value)
@ -489,6 +517,37 @@ HttpStatus HttpService::setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequ
return status;
}
HttpStatus HttpService::setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass,
HttpRequest::policyCallback_t value, HttpRequest::policyCallback_t * ret_value)
{
HttpStatus status(HttpStatus::LLCORE, LLCore::HE_INVALID_ARG);
if (opt < HttpRequest::PO_CONNECTION_LIMIT // option must be in range
|| opt >= HttpRequest::PO_LAST // ditto
|| (sOptionDesc[opt].mIsLong) // datatype is string
|| (pclass != HttpRequest::GLOBAL_POLICY_ID && pclass > mLastPolicy) // pclass in valid range
|| (pclass == HttpRequest::GLOBAL_POLICY_ID && !sOptionDesc[opt].mIsGlobal) // global setting permitted
|| (pclass != HttpRequest::GLOBAL_POLICY_ID && !sOptionDesc[opt].mIsClass) // class setting permitted
|| (RUNNING == sState && !sOptionDesc[opt].mIsDynamic)) // dynamic setting permitted
{
return status;
}
// Callbacks values are always global (at this time).
if (pclass == HttpRequest::GLOBAL_POLICY_ID)
{
HttpPolicyGlobal & opts(mPolicy->getGlobalOptions());
status = opts.set(opt, value);
if (status && ret_value)
{
status = opts.get(opt, ret_value);
}
}
return status;
}
} // end namespace LLCore

View File

@ -201,17 +201,24 @@ protected:
bool mIsDynamic;
bool mIsGlobal;
bool mIsClass;
bool mIsCallback;
};
HttpStatus getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t,
long * ret_value);
HttpStatus getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t,
std::string * ret_value);
HttpStatus getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t,
HttpRequest::policyCallback_t * ret_value);
HttpStatus setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t,
long value, long * ret_value);
HttpStatus setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t,
const std::string & value, std::string * ret_value);
HttpStatus setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t,
HttpRequest::policyCallback_t value,
HttpRequest::policyCallback_t * ret_value);
protected:
static const OptionDescriptor sOptionDesc[HttpRequest::PO_LAST];
static HttpService * sInstance;

View File

@ -32,6 +32,7 @@
#include "fix_macros.h"
#include <boost/thread.hpp>
#include <boost/intrusive_ptr.hpp>
#include "llapr.h"
@ -120,7 +121,36 @@ inline void RefCounted::destroySelf()
delete this;
}
/**
* boost::intrusive_ptr may be used to manage RefCounted classes.
* Unfortunately RefCounted and boost::intrusive_ptr use different conventions
* for the initial refcount value. To avoid leaky (immortal) objects, you
* should really construct boost::intrusive_ptr<RefCounted*>(rawptr, false).
* IntrusivePtr<T> encapsulates that for you.
*/
template <typename T>
struct IntrusivePtr: public boost::intrusive_ptr<T>
{
IntrusivePtr():
boost::intrusive_ptr<T>()
{}
IntrusivePtr(T* p):
boost::intrusive_ptr<T>(p, false)
{}
};
inline void intrusive_ptr_add_ref(RefCounted* p)
{
p->addRef();
}
inline void intrusive_ptr_release(RefCounted* p)
{
p->release();
}
} // end namespace LLCoreInt
#endif // LLCOREINT__REFCOUNTED_H_

View File

@ -30,6 +30,7 @@
#include <cstdlib>
#include <vector>
#include "boost/intrusive_ptr.hpp"
#include "_refcounted.h"
@ -73,6 +74,8 @@ public:
BufferArray();
typedef LLCoreInt::IntrusivePtr<BufferArray> ptr_t;
protected:
virtual ~BufferArray(); // Use release()
@ -129,6 +132,7 @@ protected:
container_t mBlocks;
size_t mLen;
}; // end class BufferArray

View File

@ -83,7 +83,7 @@ public:
WorkingSet();
~WorkingSet();
bool reload(LLCore::HttpRequest *, LLCore::HttpOptions *);
bool reload(LLCore::HttpRequest *, LLCore::HttpOptions::ptr_t &);
virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response);
@ -121,7 +121,7 @@ public:
int mRetriesHttp503;
int mSuccesses;
long mByteCount;
LLCore::HttpHeaders * mHeaders;
LLCore::HttpHeaders::ptr_t mHeaders;
};
@ -304,7 +304,7 @@ int main(int argc, char** argv)
LLCore::HttpRequest * hr = new LLCore::HttpRequest();
// Get request options
LLCore::HttpOptions * opt = new LLCore::HttpOptions();
LLCore::HttpOptions::ptr_t opt = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions());
opt->setRetries(12);
opt->setUseRetryAfter(true);
@ -361,10 +361,9 @@ int main(int argc, char** argv)
<< std::endl;
// Clean up
hr->requestStopThread(NULL);
hr->requestStopThread(LLCore::HttpHandler::ptr_t());
ms_sleep(1000);
opt->release();
opt = NULL;
opt.reset();
delete hr;
LLCore::HttpRequest::destroyService();
term_curl();
@ -427,22 +426,22 @@ WorkingSet::WorkingSet()
{
mAssets.reserve(30000);
mHeaders = new LLCore::HttpHeaders;
mHeaders = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders);
mHeaders->append("Accept", "image/x-j2c");
}
WorkingSet::~WorkingSet()
{
if (mHeaders)
{
mHeaders->release();
mHeaders = NULL;
}
}
namespace
{
void NoOpDeletor(LLCore::HttpHandler *)
{ /*NoOp*/ }
}
bool WorkingSet::reload(LLCore::HttpRequest * hr, LLCore::HttpOptions * opt)
bool WorkingSet::reload(LLCore::HttpRequest * hr, LLCore::HttpOptions::ptr_t & opt)
{
if (mRequestLowWater <= mHandles.size())
{
@ -470,11 +469,11 @@ bool WorkingSet::reload(LLCore::HttpRequest * hr, LLCore::HttpOptions * opt)
LLCore::HttpHandle handle;
if (offset || length)
{
handle = hr->requestGetByteRange(0, 0, buffer, offset, length, opt, mHeaders, this);
handle = hr->requestGetByteRange(0, 0, buffer, offset, length, opt, mHeaders, LLCore::HttpHandler::ptr_t(this, NoOpDeletor));
}
else
{
handle = hr->requestGet(0, 0, buffer, opt, mHeaders, this);
handle = hr->requestGet(0, 0, buffer, opt, mHeaders, LLCore::HttpHandler::ptr_t(this, NoOpDeletor));
}
if (! handle)
{

View File

@ -23,12 +23,24 @@
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#if LL_WINDOWS
#define SAFE_SSL 1
#elif LL_DARWIN
#define SAFE_SSL 1
#else
#define SAFE_SSL 1
#endif
#include "linden_common.h" // Modifies curl/curl.h interfaces
#include "httpcommon.h"
#include "llmutex.h"
#include "llthread.h"
#include <curl/curl.h>
#include <string>
#include <sstream>
#if SAFE_SSL
#include <openssl/crypto.h>
#endif
namespace LLCore
@ -42,7 +54,7 @@ HttpStatus::operator unsigned long() const
{
static const int shift(sizeof(unsigned long) * 4);
unsigned long result(((unsigned long) mType) << shift | (unsigned long) (int) mStatus);
unsigned long result(((unsigned long)mDetails->mType) << shift | (unsigned long)(int)mDetails->mStatus);
return result;
}
@ -131,30 +143,34 @@ std::string HttpStatus::toString() const
{
return std::string("");
}
switch (mType)
switch (getType())
{
case EXT_CURL_EASY:
return std::string(curl_easy_strerror(CURLcode(mStatus)));
return std::string(curl_easy_strerror(CURLcode(getStatus())));
case EXT_CURL_MULTI:
return std::string(curl_multi_strerror(CURLMcode(mStatus)));
return std::string(curl_multi_strerror(CURLMcode(getStatus())));
case LLCORE:
if (mStatus >= 0 && mStatus < llcore_errors_count)
if (getStatus() >= 0 && getStatus() < llcore_errors_count)
{
return std::string(llcore_errors[mStatus]);
return std::string(llcore_errors[getStatus()]);
}
break;
default:
if (isHttpStatus())
{
// special handling for status 499 "Linden Catchall"
if ((getType() == 499) && (!getMessage().empty()))
return getMessage();
// Binary search for the error code and string
int bottom(0), top(http_errors_count);
while (true)
{
int at((bottom + top) / 2);
if (mType == http_errors[at].mCode)
if (getType() == http_errors[at].mCode)
{
return std::string(http_errors[at].mText);
}
@ -162,7 +178,7 @@ std::string HttpStatus::toString() const
{
break;
}
else if (mType < http_errors[at].mCode)
else if (getType() < http_errors[at].mCode)
{
top = at;
}
@ -182,9 +198,9 @@ std::string HttpStatus::toTerseString() const
{
std::ostringstream result;
unsigned int error_value((unsigned short) mStatus);
unsigned int error_value((unsigned short)getStatus());
switch (mType)
switch (getType())
{
case EXT_CURL_EASY:
result << "Easy_";
@ -202,7 +218,7 @@ std::string HttpStatus::toTerseString() const
if (isHttpStatus())
{
result << "Http_";
error_value = mType;
error_value = getType();
}
else
{
@ -244,7 +260,7 @@ bool HttpStatus::isRetryable() const
// Disable the '*this == inv_status' test and look for 'Core_9'
// failures in log files.
return ((isHttpStatus() && mType >= 499 && mType <= 599) || // Include special 499 in retryables
return ((isHttpStatus() && getType() >= 499 && getType() <= 599) || // Include special 499 in retryables
*this == cant_connect || // Connection reset/endpoint problems
*this == cant_res_proxy || // DNS problems
*this == cant_res_host || // DNS problems
@ -259,5 +275,168 @@ bool HttpStatus::isRetryable() const
*this == inv_cont_range); // Short data read disagrees with content-range
}
} // end namespace LLCore
namespace LLHttp
{
namespace
{
typedef boost::shared_ptr<LLMutex> LLMutex_ptr;
std::vector<LLMutex_ptr> sSSLMutex;
CURL *getCurlTemplateHandle()
{
static CURL *curlpTemplateHandle = NULL;
if (curlpTemplateHandle == NULL)
{ // Late creation of the template curl handle
curlpTemplateHandle = curl_easy_init();
if (curlpTemplateHandle == NULL)
{
LL_WARNS() << "curl error calling curl_easy_init()" << LL_ENDL;
}
else
{
CURLcode result = curl_easy_setopt(curlpTemplateHandle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
check_curl_code(result, CURLOPT_IPRESOLVE);
result = curl_easy_setopt(curlpTemplateHandle, CURLOPT_NOSIGNAL, 1);
check_curl_code(result, CURLOPT_NOSIGNAL);
result = curl_easy_setopt(curlpTemplateHandle, CURLOPT_NOPROGRESS, 1);
check_curl_code(result, CURLOPT_NOPROGRESS);
result = curl_easy_setopt(curlpTemplateHandle, CURLOPT_ENCODING, "");
check_curl_code(result, CURLOPT_ENCODING);
result = curl_easy_setopt(curlpTemplateHandle, CURLOPT_AUTOREFERER, 1);
check_curl_code(result, CURLOPT_AUTOREFERER);
result = curl_easy_setopt(curlpTemplateHandle, CURLOPT_FOLLOWLOCATION, 1);
check_curl_code(result, CURLOPT_FOLLOWLOCATION);
result = curl_easy_setopt(curlpTemplateHandle, CURLOPT_SSL_VERIFYPEER, 1);
check_curl_code(result, CURLOPT_SSL_VERIFYPEER);
result = curl_easy_setopt(curlpTemplateHandle, CURLOPT_SSL_VERIFYHOST, 0);
check_curl_code(result, CURLOPT_SSL_VERIFYHOST);
// The Linksys WRT54G V5 router has an issue with frequent
// DNS lookups from LAN machines. If they happen too often,
// like for every HTTP request, the router gets annoyed after
// about 700 or so requests and starts issuing TCP RSTs to
// new connections. Reuse the DNS lookups for even a few
// seconds and no RSTs.
result = curl_easy_setopt(curlpTemplateHandle, CURLOPT_DNS_CACHE_TIMEOUT, 15);
check_curl_code(result, CURLOPT_DNS_CACHE_TIMEOUT);
}
}
return curlpTemplateHandle;
}
LLMutex *getCurlMutex()
{
static LLMutex* sHandleMutexp = NULL;
if (!sHandleMutexp)
{
sHandleMutexp = new LLMutex(NULL);
}
return sHandleMutexp;
}
void deallocateEasyCurl(CURL *curlp)
{
LLMutexLock lock(getCurlMutex());
curl_easy_cleanup(curlp);
}
#if SAFE_SSL
//static
void ssl_locking_callback(int mode, int type, const char *file, int line)
{
if (type >= sSSLMutex.size())
{
LL_WARNS() << "Attempt to get unknown MUTEX in SSL Lock." << LL_ENDL;
}
if (mode & CRYPTO_LOCK)
{
sSSLMutex[type]->lock();
}
else
{
sSSLMutex[type]->unlock();
}
}
//static
unsigned long ssl_thread_id(void)
{
return LLThread::currentID();
}
#endif
}
void initialize()
{
// Do not change this "unless you are familiar with and mean to control
// internal operations of libcurl"
// - http://curl.haxx.se/libcurl/c/curl_global_init.html
CURLcode code = curl_global_init(CURL_GLOBAL_ALL);
check_curl_code(code, CURL_GLOBAL_ALL);
#if SAFE_SSL
S32 mutex_count = CRYPTO_num_locks();
for (S32 i = 0; i < mutex_count; i++)
{
sSSLMutex.push_back(LLMutex_ptr(new LLMutex(NULL)));
}
CRYPTO_set_id_callback(&ssl_thread_id);
CRYPTO_set_locking_callback(&ssl_locking_callback);
#endif
}
void cleanup()
{
#if SAFE_SSL
CRYPTO_set_id_callback(NULL);
CRYPTO_set_locking_callback(NULL);
sSSLMutex.clear();
#endif
curl_global_cleanup();
}
CURL_ptr createEasyHandle()
{
LLMutexLock lock(getCurlMutex());
CURL* handle = curl_easy_duphandle(getCurlTemplateHandle());
return CURL_ptr(handle, &deallocateEasyCurl);
}
std::string getCURLVersion()
{
return std::string(curl_version());
}
void check_curl_code(CURLcode code, int curl_setopt_option)
{
if (CURLE_OK != code)
{
// Comment from old llcurl code which may no longer apply:
//
// linux appears to throw a curl error once per session for a bad initialization
// at a pretty random time (when enabling cookies).
LL_WARNS() << "libcurl error detected: " << curl_easy_strerror(code)
<< ", curl_easy_setopt option: " << curl_setopt_option
<< LL_ENDL;
}
}
}
} // end namespace LLCore

View File

@ -188,9 +188,12 @@
///
#include "linden_common.h" // Modifies curl/curl.h interfaces
#include "boost/intrusive_ptr.hpp"
#include "boost/shared_ptr.hpp"
#include "boost/weak_ptr.hpp"
#include "boost/function.hpp"
#include <string>
#include <curl/curl.h>
namespace LLCore
{
@ -206,6 +209,7 @@ namespace LLCore
/// becomes invalid and may be recycled for other queued requests.
typedef void * HttpHandle;
#define LLCORE_HTTP_HANDLE_INVALID (NULL)
/// For internal scheduling and metrics, we use a microsecond
@ -286,52 +290,63 @@ enum HttpError
/// 5. Construct an HTTP 301 status code to be treated as success:
/// HttpStatus(301, HE_SUCCESS);
///
/// 6. Construct a failed status of HTTP Status 499 with a custom error message
/// HttpStatus(499, "Failed LLSD Response");
struct HttpStatus
{
typedef unsigned short type_enum_t;
HttpStatus()
: mType(LLCORE),
mStatus(HE_SUCCESS)
{}
{
mDetails = boost::shared_ptr<Details>(new Details(LLCORE, HE_SUCCESS));
}
HttpStatus(type_enum_t type, short status)
: mType(type),
mStatus(status)
{}
{
mDetails = boost::shared_ptr<Details>(new Details(type, status));
}
HttpStatus(int http_status)
: mType(http_status),
mStatus(http_status >= 200 && http_status <= 299
? HE_SUCCESS
: HE_REPLY_ERROR)
{
llassert(http_status >= 100 && http_status <= 999);
}
{
mDetails = boost::shared_ptr<Details>(new Details(http_status,
(http_status >= 200 && http_status <= 299) ? HE_SUCCESS : HE_REPLY_ERROR));
llassert(http_status >= 100 && http_status <= 999);
}
HttpStatus(int http_status, const std::string &message)
{
mDetails = boost::shared_ptr<Details>(new Details(http_status,
(http_status >= 200 && http_status <= 299) ? HE_SUCCESS : HE_REPLY_ERROR));
llassert(http_status >= 100 && http_status <= 999);
mDetails->mMessage = message;
}
HttpStatus(const HttpStatus & rhs)
: mType(rhs.mType),
mStatus(rhs.mStatus)
{}
{
mDetails = rhs.mDetails;
}
~HttpStatus()
{
}
HttpStatus & operator=(const HttpStatus & rhs)
{
// Don't care if lhs & rhs are the same object
{
mDetails = rhs.mDetails;
return *this;
}
mType = rhs.mType;
mStatus = rhs.mStatus;
return *this;
}
HttpStatus & clone(const HttpStatus &rhs)
{
mDetails = boost::shared_ptr<Details>(new Details(*rhs.mDetails));
return *this;
}
static const type_enum_t EXT_CURL_EASY = 0; ///< mStatus is an error from a curl_easy_*() call
static const type_enum_t EXT_CURL_MULTI = 1; ///< mStatus is an error from a curl_multi_*() call
static const type_enum_t LLCORE = 2; ///< mStatus is an HE_* error code
///< 100-999 directly represent HTTP status codes
type_enum_t mType;
short mStatus;
/// Test for successful status in the code regardless
/// of error source (internal, libcurl).
///
@ -339,7 +354,7 @@ struct HttpStatus
///
operator bool() const
{
return 0 == mStatus;
return 0 == mDetails->mStatus;
}
/// Inverse of previous operator.
@ -347,14 +362,14 @@ struct HttpStatus
/// @return 'true' on any error condition
bool operator !() const
{
return 0 != mStatus;
return 0 != mDetails->mStatus;
}
/// Equality and inequality tests to bypass bool conversion
/// which will do the wrong thing in conditional expressions.
bool operator==(const HttpStatus & rhs) const
{
return mType == rhs.mType && mStatus == rhs.mStatus;
return (*mDetails == *rhs.mDetails);
}
bool operator!=(const HttpStatus & rhs) const
@ -395,7 +410,7 @@ struct HttpStatus
/// HTTP response status (100 - 999).
bool isHttpStatus() const
{
return mType >= type_enum_t(100) && mType <= type_enum_t(999);
return mDetails->mType >= type_enum_t(100) && mDetails->mType <= type_enum_t(999);
}
/// Returns true if the status is one that will be retried
@ -403,9 +418,94 @@ struct HttpStatus
/// where that logic needs to be replicated. Only applies
/// to failed statuses, successful statuses will return false.
bool isRetryable() const;
/// Returns the currently set status code as a raw number
///
short getStatus() const
{
return mDetails->mStatus;
}
/// Returns the currently set status type
///
type_enum_t getType() const
{
return mDetails->mType;
}
/// Returns an optional error message if one has been set.
///
std::string getMessage() const
{
return mDetails->mMessage;
}
/// Sets an optional error message
///
void setMessage(const std::string &message)
{
mDetails->mMessage = message;
}
/// Retrieves an optionally recorded SSL certificate.
void * getErrorData() const
{
return mDetails->mErrorData;
}
/// Optionally sets an SSL certificate on this status.
void setErrorData(void *data)
{
mDetails->mErrorData = data;
}
private:
struct Details
{
Details(type_enum_t type, short status):
mType(type),
mStatus(status),
mMessage(),
mErrorData(NULL)
{}
Details(const Details &rhs) :
mType(rhs.mType),
mStatus(rhs.mStatus),
mMessage(rhs.mMessage),
mErrorData(rhs.mErrorData)
{}
bool operator == (const Details &rhs) const
{
return (mType == rhs.mType) && (mStatus == rhs.mStatus);
}
type_enum_t mType;
short mStatus;
std::string mMessage;
void * mErrorData;
};
boost::shared_ptr<Details> mDetails;
}; // end struct HttpStatus
/// A namespace for several free methods and low level utilities.
namespace LLHttp
{
typedef boost::shared_ptr<CURL> CURL_ptr;
void initialize();
void cleanup();
CURL_ptr createEasyHandle();
std::string getCURLVersion();
void check_curl_code(CURLcode code, int curl_setopt_option);
}
} // end namespace LLCore
#endif // _LLCORE_HTTP_COMMON_H_

View File

@ -45,7 +45,7 @@ class HttpResponse;
/// be shared by any number of requests and across instances
/// of HttpRequest running in the same thread.
///
/// Threading: HttpHandler itself is pure interface and is
/// Threading: HttpHandler itself is interface and is
/// tread-compatible. Most derivations, however, will have
/// different constraints.
///
@ -53,12 +53,16 @@ class HttpResponse;
/// that is rarely a good idea. Queued requests and replies keep
/// a naked pointer to the handler and this can result in a
/// dangling pointer if lifetimes aren't managed correctly.
class HttpHandler
///
/// *TODO: public std::enable_shared_from_this<HttpHandler>
class HttpHandler
{
public:
typedef boost::shared_ptr<HttpHandler> ptr_t;
typedef boost::weak_ptr<HttpHandler> wptr_t;
virtual ~HttpHandler()
{}
{ }
/// Method invoked during calls to @see update(). Each invocation
/// represents the completion of some requested operation. Caller

View File

@ -34,7 +34,6 @@ namespace LLCore
HttpHeaders::HttpHeaders()
: RefCounted(true)
{}
@ -105,7 +104,7 @@ void HttpHeaders::appendNormal(const char * header, size_t size)
// Find from end to simulate a tradition of using single-valued
// std::map for this in the past.
const std::string * HttpHeaders::find(const char * name) const
const std::string * HttpHeaders::find(const std::string &name) const
{
const_reverse_iterator iend(rend());
for (const_reverse_iterator iter(rbegin()); iend != iter; ++iter)
@ -118,6 +117,24 @@ const std::string * HttpHeaders::find(const char * name) const
return NULL;
}
void HttpHeaders::remove(const char *name)
{
remove(std::string(name));
}
void HttpHeaders::remove(const std::string &name)
{
iterator iend(end());
for (iterator iter(begin()); iend != iter; ++iter)
{
if ((*iter).first == name)
{
mHeaders.erase(iter);
return;
}
}
}
// Standard Iterators
HttpHeaders::iterator HttpHeaders::begin()

View File

@ -28,8 +28,8 @@
#define _LLCORE_HTTP_HEADERS_H_
#include "httpcommon.h"
#include <string>
#include "_refcounted.h"
@ -74,7 +74,7 @@ namespace LLCore
/// constructor is given a refcount.
///
class HttpHeaders : public LLCoreInt::RefCounted
class HttpHeaders: private boost::noncopyable
{
public:
typedef std::pair<std::string, std::string> header_t;
@ -85,15 +85,17 @@ public:
typedef container_t::const_reverse_iterator const_reverse_iterator;
typedef container_t::value_type value_type;
typedef container_t::size_type size_type;
typedef boost::shared_ptr<HttpHeaders> ptr_t;
public:
/// @post In addition to the instance, caller has a refcount
/// to the instance. A call to @see release() will destroy
/// the instance.
HttpHeaders();
virtual ~HttpHeaders(); // Use release()
//typedef LLCoreInt::IntrusivePtr<HttpHeaders> ptr_t;
protected:
virtual ~HttpHeaders(); // Use release()
HttpHeaders(const HttpHeaders &); // Not defined
void operator=(const HttpHeaders &); // Not defined
@ -145,8 +147,16 @@ public:
// a pointer to a std::string in the container.
// Pointer is valid only for the lifetime of
// the container or until container is modifed.
//
const std::string * find(const char * name) const;
const std::string * find(const std::string &name) const;
const std::string * find(const char * name) const
{
return find(std::string(name));
}
// Remove the header from the list if found.
//
void remove(const std::string &name);
void remove(const char *name);
// Count of headers currently in the list.
size_type size() const

View File

@ -25,7 +25,7 @@
*/
#include "httpoptions.h"
#include "lldefs.h"
#include "_httpinternal.h"
@ -33,14 +33,18 @@ namespace LLCore
{
HttpOptions::HttpOptions()
: RefCounted(true),
mWantHeaders(false),
mTracing(HTTP_TRACE_OFF),
mTimeout(HTTP_REQUEST_TIMEOUT_DEFAULT),
mTransferTimeout(HTTP_REQUEST_XFER_TIMEOUT_DEFAULT),
mRetries(HTTP_RETRY_COUNT_DEFAULT),
mUseRetryAfter(HTTP_USE_RETRY_AFTER_DEFAULT)
HttpOptions::HttpOptions() :
mWantHeaders(false),
mTracing(HTTP_TRACE_OFF),
mTimeout(HTTP_REQUEST_TIMEOUT_DEFAULT),
mTransferTimeout(HTTP_REQUEST_XFER_TIMEOUT_DEFAULT),
mRetries(HTTP_RETRY_COUNT_DEFAULT),
mUseRetryAfter(HTTP_USE_RETRY_AFTER_DEFAULT),
mFollowRedirects(true),
mVerifyPeer(false),
mVerifyHost(false),
mDNSCacheTimeout(-1L),
mNoBody(false)
{}
@ -82,5 +86,31 @@ void HttpOptions::setUseRetryAfter(bool use_retry)
mUseRetryAfter = use_retry;
}
void HttpOptions::setFollowRedirects(bool follow_redirect)
{
mFollowRedirects = follow_redirect;
}
void HttpOptions::setSSLVerifyPeer(bool verify)
{
mVerifyPeer = verify;
}
void HttpOptions::setSSLVerifyHost(bool verify)
{
mVerifyHost = verify;
}
void HttpOptions::setDNSCacheTimeout(int timeout)
{
mDNSCacheTimeout = timeout;
}
void HttpOptions::setHeadersOnly(bool nobody)
{
mNoBody = nobody;
if (mNoBody)
setWantHeaders(true);
}
} // end namespace LLCore

View File

@ -29,7 +29,6 @@
#include "httpcommon.h"
#include "_refcounted.h"
@ -56,59 +55,110 @@ namespace LLCore
/// Allocation: Refcounted, heap only. Caller of the constructor
/// is given a refcount.
///
class HttpOptions : public LLCoreInt::RefCounted
class HttpOptions : private boost::noncopyable
{
public:
HttpOptions();
typedef boost::shared_ptr<HttpOptions> ptr_t;
virtual ~HttpOptions(); // Use release()
protected:
virtual ~HttpOptions(); // Use release()
HttpOptions(const HttpOptions &); // Not defined
void operator=(const HttpOptions &); // Not defined
public:
// Default: false
void setWantHeaders(bool wanted);
bool getWantHeaders() const
{
return mWantHeaders;
}
{
return mWantHeaders;
}
// Default: 0
void setTrace(int long);
int getTrace() const
{
return mTracing;
}
{
return mTracing;
}
// Default: 30
void setTimeout(unsigned int timeout);
unsigned int getTimeout() const
{
return mTimeout;
}
{
return mTimeout;
}
// Default: 0
void setTransferTimeout(unsigned int timeout);
unsigned int getTransferTimeout() const
{
return mTransferTimeout;
}
{
return mTransferTimeout;
}
/// Sets the number of retries on an LLCore::HTTPRequest before the
/// request fails.
// Default: 8
void setRetries(unsigned int retries);
unsigned int getRetries() const
{
return mRetries;
}
{
return mRetries;
}
// Default: true
void setUseRetryAfter(bool use_retry);
bool getUseRetryAfter() const
{
return mUseRetryAfter;
}
{
return mUseRetryAfter;
}
/// Instructs the LLCore::HTTPRequest to follow redirects
/// Default: false
void setFollowRedirects(bool follow_redirect);
bool getFollowRedirects() const
{
return mFollowRedirects;
}
/// Instructs the LLCore::HTTPRequest to verify that the exchanged security
/// certificate is authentic.
/// Default: false
void setSSLVerifyPeer(bool verify);
bool getSSLVerifyPeer() const
{
return mVerifyPeer;
}
/// Instructs the LLCore::HTTPRequest to verify that the name in the
/// security certificate matches the name of the host contacted.
/// Default: false
void setSSLVerifyHost(bool verify);
bool getSSLVerifyHost() const
{
return mVerifyHost;
}
/// Sets the time for DNS name caching in seconds. Setting this value
/// to 0 will disable name caching. Setting this value to -1 causes the
/// name cache to never time out.
/// Default: -1
void setDNSCacheTimeout(int timeout);
int getDNSCacheTimeout() const
{
return mDNSCacheTimeout;
}
/// Retrieve only the headers and status from the request. Setting this
/// to true implies setWantHeaders(true) as well.
/// Default: false
void setHeadersOnly(bool nobody);
bool getHeadersOnly() const
{
return mNoBody;
}
protected:
bool mWantHeaders;
@ -117,6 +167,11 @@ protected:
unsigned int mTransferTimeout;
unsigned int mRetries;
bool mUseRetryAfter;
bool mFollowRedirects;
bool mVerifyPeer;
bool mVerifyHost;
int mDNSCacheTimeout;
bool mNoBody;
}; // end class HttpOptions

View File

@ -55,13 +55,13 @@ namespace LLCore
HttpRequest::HttpRequest()
: mReplyQueue(NULL),
: mReplyQueue(),
mRequestQueue(NULL)
{
mRequestQueue = HttpRequestQueue::instanceOf();
mRequestQueue->addRef();
mReplyQueue = new HttpReplyQueue();
mReplyQueue.reset( new HttpReplyQueue() );
}
@ -73,11 +73,7 @@ HttpRequest::~HttpRequest()
mRequestQueue = NULL;
}
if (mReplyQueue)
{
mReplyQueue->release();
mReplyQueue = NULL;
}
mReplyQueue.reset();
}
@ -117,60 +113,59 @@ HttpStatus HttpRequest::setStaticPolicyOption(EPolicyOption opt, policy_t pclass
return HttpService::instanceOf()->setPolicyOption(opt, pclass, value, ret_value);
}
HttpStatus HttpRequest::setStaticPolicyOption(EPolicyOption opt, policy_t pclass, policyCallback_t value, policyCallback_t * ret_value)
{
if (HttpService::RUNNING == HttpService::instanceOf()->getState())
{
return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC);
}
return HttpService::instanceOf()->setPolicyOption(opt, pclass, value, ret_value);
}
HttpHandle HttpRequest::setPolicyOption(EPolicyOption opt, policy_t pclass,
long value, HttpHandler * handler)
long value, HttpHandler::ptr_t handler)
{
HttpStatus status;
HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID);
HttpOpSetGet * op = new HttpOpSetGet();
HttpOpSetGet::ptr_t op(new HttpOpSetGet());
if (! (status = op->setupSet(opt, pclass, value)))
{
op->release();
mLastReqStatus = status;
return handle;
return LLCORE_HTTP_HANDLE_INVALID;
}
op->setReplyPath(mReplyQueue, handler);
if (! (status = mRequestQueue->addOp(op))) // transfers refcount
{
op->release();
mLastReqStatus = status;
return handle;
return LLCORE_HTTP_HANDLE_INVALID;
}
mLastReqStatus = status;
handle = static_cast<HttpHandle>(op);
return handle;
return op->getHandle();
}
HttpHandle HttpRequest::setPolicyOption(EPolicyOption opt, policy_t pclass,
const std::string & value, HttpHandler * handler)
const std::string & value, HttpHandler::ptr_t handler)
{
HttpStatus status;
HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID);
HttpOpSetGet * op = new HttpOpSetGet();
HttpOpSetGet::ptr_t op (new HttpOpSetGet());
if (! (status = op->setupSet(opt, pclass, value)))
{
op->release();
mLastReqStatus = status;
return handle;
return LLCORE_HTTP_HANDLE_INVALID;
}
op->setReplyPath(mReplyQueue, handler);
if (! (status = mRequestQueue->addOp(op))) // transfers refcount
{
op->release();
mLastReqStatus = status;
return handle;
return LLCORE_HTTP_HANDLE_INVALID;
}
mLastReqStatus = status;
handle = static_cast<HttpHandle>(op);
return handle;
return op->getHandle();
}
@ -188,32 +183,27 @@ HttpStatus HttpRequest::getStatus() const
HttpHandle HttpRequest::requestGet(policy_t policy_id,
priority_t priority,
const std::string & url,
HttpOptions * options,
HttpHeaders * headers,
HttpHandler * user_handler)
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers,
HttpHandler::ptr_t user_handler)
{
HttpStatus status;
HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID);
HttpOpRequest * op = new HttpOpRequest();
HttpOpRequest::ptr_t op(new HttpOpRequest());
if (! (status = op->setupGet(policy_id, priority, url, options, headers)))
{
op->release();
mLastReqStatus = status;
return handle;
return LLCORE_HTTP_HANDLE_INVALID;
}
op->setReplyPath(mReplyQueue, user_handler);
if (! (status = mRequestQueue->addOp(op))) // transfers refcount
{
op->release();
mLastReqStatus = status;
return handle;
return LLCORE_HTTP_HANDLE_INVALID;
}
mLastReqStatus = status;
handle = static_cast<HttpHandle>(op);
return handle;
return op->getHandle();
}
@ -222,32 +212,27 @@ HttpHandle HttpRequest::requestGetByteRange(policy_t policy_id,
const std::string & url,
size_t offset,
size_t len,
HttpOptions * options,
HttpHeaders * headers,
HttpHandler * user_handler)
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers,
HttpHandler::ptr_t user_handler)
{
HttpStatus status;
HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID);
HttpOpRequest * op = new HttpOpRequest();
HttpOpRequest::ptr_t op(new HttpOpRequest());
if (! (status = op->setupGetByteRange(policy_id, priority, url, offset, len, options, headers)))
{
op->release();
mLastReqStatus = status;
return handle;
return LLCORE_HTTP_HANDLE_INVALID;
}
op->setReplyPath(mReplyQueue, user_handler);
if (! (status = mRequestQueue->addOp(op))) // transfers refcount
{
op->release();
mLastReqStatus = status;
return handle;
return LLCORE_HTTP_HANDLE_INVALID;
}
mLastReqStatus = status;
handle = static_cast<HttpHandle>(op);
return handle;
return op->getHandle();
}
@ -255,32 +240,27 @@ HttpHandle HttpRequest::requestPost(policy_t policy_id,
priority_t priority,
const std::string & url,
BufferArray * body,
HttpOptions * options,
HttpHeaders * headers,
HttpHandler * user_handler)
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers,
HttpHandler::ptr_t user_handler)
{
HttpStatus status;
HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID);
HttpOpRequest * op = new HttpOpRequest();
HttpOpRequest::ptr_t op(new HttpOpRequest());
if (! (status = op->setupPost(policy_id, priority, url, body, options, headers)))
{
op->release();
mLastReqStatus = status;
return handle;
return LLCORE_HTTP_HANDLE_INVALID;
}
op->setReplyPath(mReplyQueue, user_handler);
if (! (status = mRequestQueue->addOp(op))) // transfers refcount
{
op->release();
mLastReqStatus = status;
return handle;
return LLCORE_HTTP_HANDLE_INVALID;
}
mLastReqStatus = status;
handle = static_cast<HttpHandle>(op);
return handle;
return op->getHandle();
}
@ -288,59 +268,156 @@ HttpHandle HttpRequest::requestPut(policy_t policy_id,
priority_t priority,
const std::string & url,
BufferArray * body,
HttpOptions * options,
HttpHeaders * headers,
HttpHandler * user_handler)
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers,
HttpHandler::ptr_t user_handler)
{
HttpStatus status;
HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID);
HttpOpRequest * op = new HttpOpRequest();
HttpOpRequest::ptr_t op (new HttpOpRequest());
if (! (status = op->setupPut(policy_id, priority, url, body, options, headers)))
{
op->release();
mLastReqStatus = status;
return handle;
return LLCORE_HTTP_HANDLE_INVALID;
}
op->setReplyPath(mReplyQueue, user_handler);
if (! (status = mRequestQueue->addOp(op))) // transfers refcount
{
op->release();
mLastReqStatus = status;
return handle;
return LLCORE_HTTP_HANDLE_INVALID;
}
mLastReqStatus = status;
handle = static_cast<HttpHandle>(op);
return handle;
return op->getHandle();
}
HttpHandle HttpRequest::requestDelete(policy_t policy_id,
priority_t priority,
const std::string & url,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers,
HttpHandler::ptr_t user_handler)
{
HttpStatus status;
HttpOpRequest::ptr_t op(new HttpOpRequest());
if (!(status = op->setupDelete(policy_id, priority, url, options, headers)))
{
mLastReqStatus = status;
return LLCORE_HTTP_HANDLE_INVALID;
}
op->setReplyPath(mReplyQueue, user_handler);
if (!(status = mRequestQueue->addOp(op))) // transfers refcount
{
mLastReqStatus = status;
return LLCORE_HTTP_HANDLE_INVALID;
}
mLastReqStatus = status;
return op->getHandle();
}
HttpHandle HttpRequest::requestPatch(policy_t policy_id,
priority_t priority,
const std::string & url,
BufferArray * body,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers,
HttpHandler::ptr_t user_handler)
{
HttpStatus status;
HttpOpRequest::ptr_t op (new HttpOpRequest());
if (!(status = op->setupPatch(policy_id, priority, url, body, options, headers)))
{
mLastReqStatus = status;
return LLCORE_HTTP_HANDLE_INVALID;
}
op->setReplyPath(mReplyQueue, user_handler);
if (!(status = mRequestQueue->addOp(op))) // transfers refcount
{
mLastReqStatus = status;
return LLCORE_HTTP_HANDLE_INVALID;
}
mLastReqStatus = status;
return op->getHandle();
}
HttpHandle HttpRequest::requestCopy(policy_t policy_id,
priority_t priority,
const std::string & url,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers,
HttpHandler::ptr_t user_handler)
{
HttpStatus status;
HttpOpRequest::ptr_t op(new HttpOpRequest());
if (!(status = op->setupCopy(policy_id, priority, url, options, headers)))
{
mLastReqStatus = status;
return LLCORE_HTTP_HANDLE_INVALID;
}
op->setReplyPath(mReplyQueue, user_handler);
if (!(status = mRequestQueue->addOp(op))) // transfers refcount
{
mLastReqStatus = status;
return LLCORE_HTTP_HANDLE_INVALID;
}
mLastReqStatus = status;
return op->getHandle();
}
HttpHandle HttpRequest::requestMove(policy_t policy_id,
priority_t priority,
const std::string & url,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers,
HttpHandler::ptr_t user_handler)
{
HttpStatus status;
HttpOpRequest::ptr_t op (new HttpOpRequest());
if (!(status = op->setupMove(policy_id, priority, url, options, headers)))
{
mLastReqStatus = status;
return LLCORE_HTTP_HANDLE_INVALID;
}
op->setReplyPath(mReplyQueue, user_handler);
if (!(status = mRequestQueue->addOp(op))) // transfers refcount
{
mLastReqStatus = status;
return LLCORE_HTTP_HANDLE_INVALID;
}
mLastReqStatus = status;
return op->getHandle();
}
HttpHandle HttpRequest::requestNoOp(HttpHandler * user_handler)
HttpHandle HttpRequest::requestNoOp(HttpHandler::ptr_t user_handler)
{
HttpStatus status;
HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID);
HttpOpNull * op = new HttpOpNull();
HttpOperation::ptr_t op (new HttpOpNull());
op->setReplyPath(mReplyQueue, user_handler);
if (! (status = mRequestQueue->addOp(op))) // transfers refcount
{
op->release();
mLastReqStatus = status;
return handle;
return LLCORE_HTTP_HANDLE_INVALID;
}
mLastReqStatus = status;
handle = static_cast<HttpHandle>(op);
return handle;
return op->getHandle();
}
HttpStatus HttpRequest::update(long usecs)
{
HttpOperation * op(NULL);
HttpOperation::ptr_t op;
if (usecs)
{
@ -351,7 +428,7 @@ HttpStatus HttpRequest::update(long usecs)
op->visitNotifier(this);
// We're done with the operation
op->release();
op.reset();
}
}
else
@ -366,13 +443,13 @@ HttpStatus HttpRequest::update(long usecs)
++iter)
{
// Swap op pointer for NULL;
op = *iter; *iter = NULL;
op.reset();
op.swap(*iter);
// Process operation
op->visitNotifier(this);
// We're done with the operation
op->release();
}
}
}
@ -387,46 +464,38 @@ HttpStatus HttpRequest::update(long usecs)
// Request Management Methods
// ====================================
HttpHandle HttpRequest::requestCancel(HttpHandle request, HttpHandler * user_handler)
HttpHandle HttpRequest::requestCancel(HttpHandle request, HttpHandler::ptr_t user_handler)
{
HttpStatus status;
HttpHandle ret_handle(LLCORE_HTTP_HANDLE_INVALID);
HttpOpCancel * op = new HttpOpCancel(request);
HttpOperation::ptr_t op(new HttpOpCancel(request));
op->setReplyPath(mReplyQueue, user_handler);
if (! (status = mRequestQueue->addOp(op))) // transfers refcount
{
op->release();
mLastReqStatus = status;
return ret_handle;
return LLCORE_HTTP_HANDLE_INVALID;
}
mLastReqStatus = status;
ret_handle = static_cast<HttpHandle>(op);
return ret_handle;
return op->getHandle();
}
HttpHandle HttpRequest::requestSetPriority(HttpHandle request, priority_t priority,
HttpHandler * handler)
HttpHandler::ptr_t handler)
{
HttpStatus status;
HttpHandle ret_handle(LLCORE_HTTP_HANDLE_INVALID);
HttpOpSetPriority * op = new HttpOpSetPriority(request, priority);
HttpOperation::ptr_t op (new HttpOpSetPriority(request, priority));
op->setReplyPath(mReplyQueue, handler);
if (! (status = mRequestQueue->addOp(op))) // transfers refcount
{
op->release();
mLastReqStatus = status;
return ret_handle;
return LLCORE_HTTP_HANDLE_INVALID;
}
mLastReqStatus = status;
ret_handle = static_cast<HttpHandle>(op);
return ret_handle;
return op->getHandle();
}
@ -475,22 +544,21 @@ HttpStatus HttpRequest::startThread()
}
HttpHandle HttpRequest::requestStopThread(HttpHandler * user_handler)
HttpHandle HttpRequest::requestStopThread(HttpHandler::ptr_t user_handler)
{
HttpStatus status;
HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID);
HttpOpStop * op = new HttpOpStop();
HttpOperation::ptr_t op(new HttpOpStop());
op->setReplyPath(mReplyQueue, user_handler);
if (! (status = mRequestQueue->addOp(op))) // transfers refcount
{
op->release();
mLastReqStatus = status;
return handle;
}
mLastReqStatus = status;
handle = static_cast<HttpHandle>(op);
handle = op->getHandle();
return handle;
}
@ -501,17 +569,16 @@ HttpHandle HttpRequest::requestSpin(int mode)
HttpStatus status;
HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID);
HttpOpSpin * op = new HttpOpSpin(mode);
op->setReplyPath(mReplyQueue, NULL);
HttpOperation::ptr_t op(new HttpOpSpin(mode));
op->setReplyPath(mReplyQueue, HttpHandler::ptr_t());
if (! (status = mRequestQueue->addOp(op))) // transfers refcount
{
op->release();
mLastReqStatus = status;
return handle;
}
mLastReqStatus = status;
handle = static_cast<HttpHandle>(op);
handle = op->getHandle();
return handle;
}

View File

@ -31,6 +31,8 @@
#include "httpcommon.h"
#include "httphandler.h"
#include "httpheaders.h"
#include "httpoptions.h"
namespace LLCore
{
@ -38,8 +40,6 @@ namespace LLCore
class HttpRequestQueue;
class HttpReplyQueue;
class HttpService;
class HttpOptions;
class HttpHeaders;
class HttpOperation;
class BufferArray;
@ -97,6 +97,8 @@ public:
typedef unsigned int policy_t;
typedef unsigned int priority_t;
typedef boost::shared_ptr<HttpRequest> ptr_t;
typedef boost::weak_ptr<HttpRequest> wptr_t;
public:
/// @name PolicyMethods
/// @{
@ -163,7 +165,7 @@ public:
/// Long value that if non-zero enables the use of the
/// traditional LLProxy code for http/socks5 support. If
// enabled, has priority over GP_HTTP_PROXY.
/// enabled, has priority over GP_HTTP_PROXY.
///
/// Global only
PO_LLPROXY,
@ -219,15 +221,25 @@ public:
/// Controls whether client-side throttling should be
/// performed on this policy class. Positive values
/// enable throttling and specify the request rate
/// (requests per second) that should be targetted.
/// (requests per second) that should be targeted.
/// A value of zero, the default, specifies no throttling.
///
/// Per-class only
PO_THROTTLE_RATE,
/// Controls the callback function used to control SSL CTX
/// certificate verification.
///
/// Global only
PO_SSL_VERIFY_CALLBACK,
PO_LAST // Always at end
};
/// Prototype for policy based callbacks. The callback methods will be executed
/// on the worker thread so no modifications should be made to the HttpHandler object.
typedef boost::function<HttpStatus(const std::string &, const HttpHandler::ptr_t &, void *)> policyCallback_t;
/// Set a policy option for a global or class parameter at
/// startup time (prior to thread start).
///
@ -243,6 +255,8 @@ public:
long value, long * ret_value);
static HttpStatus setStaticPolicyOption(EPolicyOption opt, policy_t pclass,
const std::string & value, std::string * ret_value);
static HttpStatus setStaticPolicyOption(EPolicyOption opt, policy_t pclass,
policyCallback_t value, policyCallback_t * ret_value);;
/// Set a parameter on a class-based policy option. Calls
/// made after the start of the servicing thread are
@ -256,9 +270,9 @@ public:
/// @return Handle of dynamic request. Use @see getStatus() if
/// the returned handle is invalid.
HttpHandle setPolicyOption(EPolicyOption opt, policy_t pclass, long value,
HttpHandler * handler);
HttpHandler::ptr_t handler);
HttpHandle setPolicyOption(EPolicyOption opt, policy_t pclass, const std::string & value,
HttpHandler * handler);
HttpHandler::ptr_t handler);
/// @}
@ -334,9 +348,9 @@ public:
HttpHandle requestGet(policy_t policy_id,
priority_t priority,
const std::string & url,
HttpOptions * options,
HttpHeaders * headers,
HttpHandler * handler);
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers,
HttpHandler::ptr_t handler);
/// Queue a full HTTP GET request to be issued with a 'Range' header.
@ -377,9 +391,9 @@ public:
const std::string & url,
size_t offset,
size_t len,
HttpOptions * options,
HttpHeaders * headers,
HttpHandler * handler);
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers,
HttpHandler::ptr_t handler);
/// Queue a full HTTP POST. Query arguments and body may
@ -418,9 +432,9 @@ public:
priority_t priority,
const std::string & url,
BufferArray * body,
HttpOptions * options,
HttpHeaders * headers,
HttpHandler * handler);
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers,
HttpHandler::ptr_t handler);
/// Queue a full HTTP PUT. Query arguments and body may
@ -459,12 +473,92 @@ public:
priority_t priority,
const std::string & url,
BufferArray * body,
HttpOptions * options,
HttpHeaders * headers,
HttpHandler * handler);
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers,
HttpHandler::ptr_t handler);
/// Queue a NoOp request.
/// Queue a full HTTP DELETE. Query arguments and body may
/// be provided. Caller is responsible for escaping and
/// encoding and communicating the content types.
///
/// @param policy_id @see requestGet()
/// @param priority "
/// @param url "
/// @param options @see requestGet()K(optional)
/// @param headers "
/// @param handler "
/// @return "
///
HttpHandle requestDelete(policy_t policy_id,
priority_t priority,
const std::string & url,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers,
HttpHandler::ptr_t user_handler);
/// Queue a full HTTP PATCH. Query arguments and body may
/// be provided. Caller is responsible for escaping and
/// encoding and communicating the content types.
///
/// @param policy_id @see requestGet()
/// @param priority "
/// @param url "
/// @param body Byte stream to be sent as the body. No
/// further encoding or escaping will be done
/// to the content.
/// @param options @see requestGet()K(optional)
/// @param headers "
/// @param handler "
/// @return "
///
HttpHandle requestPatch(policy_t policy_id,
priority_t priority,
const std::string & url,
BufferArray * body,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers,
HttpHandler::ptr_t user_handler);
/// Queue a full HTTP COPY. Query arguments and body may
/// be provided. Caller is responsible for escaping and
/// encoding and communicating the content types.
///
/// @param policy_id @see requestGet()
/// @param priority "
/// @param url "
/// @param options @see requestGet()K(optional)
/// @param headers "
/// @param handler "
/// @return "
///
HttpHandle requestCopy(policy_t policy_id,
priority_t priority,
const std::string & url,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers,
HttpHandler::ptr_t user_handler);
/// Queue a full HTTP MOVE. Query arguments and body may
/// be provided. Caller is responsible for escaping and
/// encoding and communicating the content types.
///
/// @param policy_id @see requestGet()
/// @param priority "
/// @param url "
/// @param options @see requestGet()K(optional)
/// @param headers "
/// @param handler "
/// @return "
///
HttpHandle requestMove(policy_t policy_id,
priority_t priority,
const std::string & url,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers,
HttpHandler::ptr_t user_handler);
/// Queue a NoOp request.
/// The request is queued and serviced by the working thread which
/// immediately processes it and returns the request to the reply
/// queue.
@ -472,7 +566,7 @@ public:
/// @param handler @see requestGet()
/// @return "
///
HttpHandle requestNoOp(HttpHandler * handler);
HttpHandle requestNoOp(HttpHandler::ptr_t handler);
/// While all the heavy work is done by the worker thread, notifications
/// must be performed in the context of the application thread. These
@ -497,7 +591,7 @@ public:
///
/// @{
HttpHandle requestCancel(HttpHandle request, HttpHandler *);
HttpHandle requestCancel(HttpHandle request, HttpHandler::ptr_t);
/// Request that a previously-issued request be reprioritized.
/// The status of whether the change itself succeeded arrives
@ -509,7 +603,7 @@ public:
/// @param handler @see requestGet()
/// @return "
///
HttpHandle requestSetPriority(HttpHandle request, priority_t priority, HttpHandler * handler);
HttpHandle requestSetPriority(HttpHandle request, priority_t priority, HttpHandler::ptr_t handler);
/// @}
@ -547,7 +641,7 @@ public:
/// As the request cannot be cancelled, the handle
/// is generally not useful.
///
HttpHandle requestStopThread(HttpHandler * handler);
HttpHandle requestStopThread(HttpHandler::ptr_t handler);
/// Queue a Spin request.
/// DEBUG/TESTING ONLY. This puts the worker into a CPU spin for
@ -561,14 +655,15 @@ public:
/// @}
protected:
void generateNotification(HttpOperation * op);
private:
typedef boost::shared_ptr<HttpReplyQueue> HttpReplyQueuePtr_t;
/// @name InstanceData
///
/// @{
HttpStatus mLastReqStatus;
HttpReplyQueue * mReplyQueue;
HttpReplyQueuePtr_t mReplyQueue;
HttpRequestQueue * mRequestQueue;
/// @}

View File

@ -39,16 +39,17 @@ HttpResponse::HttpResponse()
mReplyLength(0U),
mReplyFullLength(0U),
mBufferArray(NULL),
mHeaders(NULL),
mHeaders(),
mRetries(0U),
m503Retries(0U)
m503Retries(0U),
mRequestUrl()
{}
HttpResponse::~HttpResponse()
{
setBody(NULL);
setHeaders(NULL);
//setHeaders();
}
@ -71,23 +72,14 @@ void HttpResponse::setBody(BufferArray * ba)
}
void HttpResponse::setHeaders(HttpHeaders * headers)
void HttpResponse::setHeaders(HttpHeaders::ptr_t &headers)
{
if (mHeaders == headers)
return;
if (mHeaders)
{
mHeaders->release();
}
if (headers)
{
headers->addRef();
}
mHeaders = headers;
mHeaders = headers;
}
size_t HttpResponse::getBodySize() const
{
return (mBufferArray) ? mBufferArray->size() : 0;
}
} // end namespace LLCore

View File

@ -31,7 +31,7 @@
#include <string>
#include "httpcommon.h"
#include "httpheaders.h"
#include "_refcounted.h"
@ -69,6 +69,18 @@ protected:
void operator=(const HttpResponse &); // Not defined
public:
/// Statistics for the HTTP
struct TransferStats
{
typedef boost::shared_ptr<TransferStats> ptr_t;
TransferStats() : mSizeDownload(0.0), mTotalTime(0.0), mSpeedDownload(0.0) {}
F64 mSizeDownload;
F64 mTotalTime;
F64 mSpeedDownload;
};
/// Returns the final status of the requested operation.
///
HttpStatus getStatus() const
@ -92,6 +104,10 @@ public:
return mBufferArray;
}
/// Safely get the size of the body buffer. If the body buffer is missing
/// return 0 as the size.
size_t getBodySize() const;
/// Set the response data in the instance. Will drop the reference
/// count to any existing data and increment the count of that passed
/// in. It is legal to set the data to NULL.
@ -104,13 +120,13 @@ public:
///
/// Caller can hold onto the headers by incrementing the reference
/// count of the returned object.
HttpHeaders * getHeaders() const
{
HttpHeaders::ptr_t getHeaders() const
{
return mHeaders;
}
}
/// Behaves like @see setResponse() but for header data.
void setHeaders(HttpHeaders * headers);
void setHeaders(HttpHeaders::ptr_t &headers);
/// If a 'Range:' header was used, these methods are involved
/// in setting and returning data about the actual response.
@ -168,6 +184,27 @@ public:
m503Retries = retries_503;
}
void setTransferStats(TransferStats::ptr_t &stats)
{
mStats = stats;
}
TransferStats::ptr_t getTransferStats()
{
return mStats;
}
void setRequestURL(const std::string &url)
{
mRequestUrl = url;
}
const std::string &getRequestURL() const
{
return mRequestUrl;
}
protected:
// Response data here
HttpStatus mStatus;
@ -175,10 +212,13 @@ protected:
unsigned int mReplyLength;
unsigned int mReplyFullLength;
BufferArray * mBufferArray;
HttpHeaders * mHeaders;
HttpHeaders::ptr_t mHeaders;
std::string mContentType;
unsigned int mRetries;
unsigned int m503Retries;
std::string mRequestUrl;
TransferStats::ptr_t mStats;
};

View File

@ -133,94 +133,3 @@ const std::string HTTP_VERB_MOVE("MOVE");
const std::string HTTP_VERB_OPTIONS("OPTIONS");
const std::string HTTP_VERB_PATCH("PATCH");
const std::string HTTP_VERB_COPY("COPY");
const std::string& httpMethodAsVerb(EHTTPMethod method)
{
static const std::string VERBS[] =
{
HTTP_VERB_INVALID,
HTTP_VERB_HEAD,
HTTP_VERB_GET,
HTTP_VERB_PUT,
HTTP_VERB_POST,
HTTP_VERB_DELETE,
HTTP_VERB_MOVE,
HTTP_VERB_OPTIONS,
HTTP_VERB_PATCH,
HTTP_VERB_COPY
};
if(((S32)method <=0) || ((S32)method >= HTTP_METHOD_COUNT))
{
return VERBS[0];
}
return VERBS[method];
}
bool isHttpInformationalStatus(S32 status)
{
// Check for status 1xx.
return((100 <= status) && (status < 200));
}
bool isHttpGoodStatus(S32 status)
{
// Check for status 2xx.
return((200 <= status) && (status < 300));
}
bool isHttpRedirectStatus(S32 status)
{
// Check for status 3xx.
return((300 <= status) && (status < 400));
}
bool isHttpClientErrorStatus(S32 status)
{
// Status 499 is sometimes used for re-interpreted status 2xx errors
// based on body content. Treat these as potentially retryable 'server' status errors,
// since we do not have enough context to know if this will always fail.
if (HTTP_INTERNAL_ERROR == status) return false;
// Check for status 5xx.
return((400 <= status) && (status < 500));
}
bool isHttpServerErrorStatus(S32 status)
{
// Status 499 is sometimes used for re-interpreted status 2xx errors.
// Allow retry of these, since we don't have enough information in this
// context to know if this will always fail.
if (HTTP_INTERNAL_ERROR == status) return true;
// Check for status 5xx.
return((500 <= status) && (status < 600));
}
// Parses 'Retry-After' header contents and returns seconds until retry should occur.
bool getSecondsUntilRetryAfter(const std::string& retry_after, F32& seconds_to_wait)
{
// *TODO: This needs testing! Not in use yet.
// Examples of Retry-After headers:
// Retry-After: Fri, 31 Dec 1999 23:59:59 GMT
// Retry-After: 120
// Check for number of seconds version, first:
char* end = 0;
// Parse as double
double seconds = std::strtod(retry_after.c_str(), &end);
if ( end != 0 && *end == 0 )
{
// Successful parse
seconds_to_wait = (F32) seconds;
return true;
}
// Parse rfc1123 date.
time_t date = curl_getdate(retry_after.c_str(), NULL );
if (-1 == date) return false;
seconds_to_wait = (F64)date - LLTimer::getTotalSeconds();
return true;
}

View File

@ -116,13 +116,6 @@ enum EHTTPMethod
HTTP_METHOD_COUNT
};
const std::string& httpMethodAsVerb(EHTTPMethod method);
bool isHttpInformationalStatus(S32 status);
bool isHttpGoodStatus(S32 status);
bool isHttpRedirectStatus(S32 status);
bool isHttpClientErrorStatus(S32 status);
bool isHttpServerErrorStatus(S32 status);
// Parses 'Retry-After' header contents and returns seconds until retry should occur.
bool getSecondsUntilRetryAfter(const std::string& retry_after, F32& seconds_to_wait);

View File

@ -160,7 +160,7 @@ void stop_thread(LLCore::HttpRequest * req)
{
if (req)
{
req->requestStopThread(NULL);
req->requestStopThread(LLCore::HttpHandler::ptr_t());
int count = 0;
int limit = 10;

View File

@ -59,13 +59,12 @@ void HttpHeadersTestObjectType::test<1>()
mMemTotal = GetMemTotal();
// create a new ref counted object with an implicit reference
HttpHeaders * headers = new HttpHeaders();
ensure("One ref on construction of HttpHeaders", headers->getRefCount() == 1);
HttpHeaders::ptr_t headers = HttpHeaders::ptr_t(new HttpHeaders());
ensure("Memory being used", mMemTotal < GetMemTotal());
ensure("Nothing in headers", 0 == headers->size());
// release the implicit reference, causing the object to be released
headers->release();
headers.reset();
// make sure we didn't leak any memory
ensure(mMemTotal == GetMemTotal());
@ -80,7 +79,7 @@ void HttpHeadersTestObjectType::test<2>()
mMemTotal = GetMemTotal();
// create a new ref counted object with an implicit reference
HttpHeaders * headers = new HttpHeaders();
HttpHeaders::ptr_t headers = HttpHeaders::ptr_t(new HttpHeaders());
{
// Append a few strings
@ -101,7 +100,7 @@ void HttpHeadersTestObjectType::test<2>()
}
// release the implicit reference, causing the object to be released
headers->release();
headers.reset();
// make sure we didn't leak any memory
ensure(mMemTotal == GetMemTotal());
@ -116,7 +115,7 @@ void HttpHeadersTestObjectType::test<3>()
mMemTotal = GetMemTotal();
// create a new ref counted object with an implicit reference
HttpHeaders * headers = new HttpHeaders();
HttpHeaders::ptr_t headers = HttpHeaders::ptr_t(new HttpHeaders());
{
// Append a few strings
@ -151,7 +150,7 @@ void HttpHeadersTestObjectType::test<3>()
}
// release the implicit reference, causing the object to be released
headers->release();
headers.reset();
// make sure we didn't leak any memory
ensure(mMemTotal == GetMemTotal());
@ -166,8 +165,8 @@ void HttpHeadersTestObjectType::test<4>()
mMemTotal = GetMemTotal();
// create a new ref counted object with an implicit reference
HttpHeaders * headers = new HttpHeaders();
HttpHeaders::ptr_t headers = HttpHeaders::ptr_t(new HttpHeaders());
{
static char line1[] = " AcCePT : image/yourfacehere";
static char line1v[] = "image/yourfacehere";
@ -251,7 +250,7 @@ void HttpHeadersTestObjectType::test<4>()
}
// release the implicit reference, causing the object to be released
headers->release();
headers.reset();
// make sure we didn't leak any memory
ensure(mMemTotal == GetMemTotal());
@ -267,7 +266,7 @@ void HttpHeadersTestObjectType::test<5>()
mMemTotal = GetMemTotal();
// create a new ref counted object with an implicit reference
HttpHeaders * headers = new HttpHeaders();
HttpHeaders::ptr_t headers = HttpHeaders::ptr_t(new HttpHeaders());
HttpHeaders::iterator end(headers->end()), begin(headers->begin());
ensure("Empty container has equal begin/end const iterators", end == begin);
@ -337,7 +336,7 @@ void HttpHeadersTestObjectType::test<5>()
}
// release the implicit reference, causing the object to be released
headers->release();
headers.reset();
// make sure we didn't leak any memory
ensure(mMemTotal == GetMemTotal());
@ -353,7 +352,7 @@ void HttpHeadersTestObjectType::test<6>()
mMemTotal = GetMemTotal();
// create a new ref counted object with an implicit reference
HttpHeaders * headers = new HttpHeaders();
HttpHeaders::ptr_t headers = HttpHeaders::ptr_t(new HttpHeaders());
HttpHeaders::reverse_iterator rend(headers->rend()), rbegin(headers->rbegin());
ensure("Empty container has equal rbegin/rend const iterators", rend == rbegin);
@ -421,7 +420,7 @@ void HttpHeadersTestObjectType::test<6>()
}
// release the implicit reference, causing the object to be released
headers->release();
headers.reset();
// make sure we didn't leak any memory
ensure(mMemTotal == GetMemTotal());

View File

@ -76,12 +76,12 @@ namespace tut
mMemTotal = GetMemTotal();
// create a new ref counted object with an implicit reference
HttpOpNull * op = new HttpOpNull();
ensure(op->getRefCount() == 1);
HttpOperation::ptr_t op (new HttpOpNull());
ensure(op.use_count() == 1);
ensure(mMemTotal < GetMemTotal());
// release the implicit reference, causing the object to be released
op->release();
op.reset();
// make sure we didn't leak any memory
ensure(mMemTotal == GetMemTotal());
@ -96,26 +96,24 @@ namespace tut
mMemTotal = GetMemTotal();
// Get some handlers
TestHandler * h1 = new TestHandler();
LLCore::HttpHandler::ptr_t h1 (new TestHandler());
// create a new ref counted object with an implicit reference
HttpOpNull * op = new HttpOpNull();
HttpOperation::ptr_t op (new HttpOpNull());
// Add the handlers
op->setReplyPath(NULL, h1);
op->setReplyPath(LLCore::HttpOperation::HttpReplyQueuePtr_t(), h1);
// Check ref count
ensure(op->getRefCount() == 1);
ensure(op.unique() == 1);
// release the reference, releasing the operation but
// not the handlers.
op->release();
op = NULL;
op.reset();
ensure(mMemTotal != GetMemTotal());
// release the handlers
delete h1;
h1 = NULL;
h1.reset();
ensure(mMemTotal == GetMemTotal());
}

File diff suppressed because it is too large Load Diff

View File

@ -113,16 +113,16 @@ void HttpRequestqueueTestObjectType::test<3>()
HttpRequestQueue * rq = HttpRequestQueue::instanceOf();
HttpOperation * op = new HttpOpNull();
HttpOperation::ptr_t op(new HttpOpNull());
rq->addOp(op); // transfer my refcount
op = rq->fetchOp(true); // Potentially hangs the test on failure
ensure("One goes in, one comes out", NULL != op);
op->release();
ensure("One goes in, one comes out", static_cast<bool>(op));
op.reset();
op = rq->fetchOp(false);
ensure("Better not be two of them", NULL == op);
ensure("Better not be two of them", !op);
// release the singleton, hold on to the object
HttpRequestQueue::term();
@ -144,13 +144,13 @@ void HttpRequestqueueTestObjectType::test<4>()
HttpRequestQueue * rq = HttpRequestQueue::instanceOf();
HttpOperation * op = new HttpOpNull();
HttpOperation::ptr_t op (new HttpOpNull());
rq->addOp(op); // transfer my refcount
op = new HttpOpNull();
op.reset(new HttpOpNull());
rq->addOp(op); // transfer my refcount
op = new HttpOpNull();
op.reset(new HttpOpNull());
rq->addOp(op); // transfer my refcount
{
@ -159,8 +159,9 @@ void HttpRequestqueueTestObjectType::test<4>()
ensure("Three go in, three come out", 3 == ops.size());
op = rq->fetchOp(false);
ensure("Better not be any more of them", NULL == op);
ensure("Better not be any more of them", !op);
op.reset();
// release the singleton, hold on to the object
HttpRequestQueue::term();
@ -168,12 +169,13 @@ void HttpRequestqueueTestObjectType::test<4>()
ensure(mMemTotal < GetMemTotal());
// Release them
while (! ops.empty())
{
HttpOperation * op = ops.front();
ops.erase(ops.begin());
op->release();
}
ops.clear();
// while (! ops.empty())
// {
// HttpOperation * op = ops.front();
// ops.erase(ops.begin());
// op->release();
// }
}
// Should be clean

View File

@ -55,110 +55,97 @@ void HttpStatusTestObjectType::test<1>()
// auto allocation fine for this
HttpStatus status;
status.mType = HttpStatus::EXT_CURL_EASY;
status.mStatus = 0;
status = HttpStatus(HttpStatus::EXT_CURL_EASY, 0);
ensure(bool(status));
ensure(false == !(status));
status.mType = HttpStatus::EXT_CURL_MULTI;
status.mStatus = 0;
status = HttpStatus(HttpStatus::EXT_CURL_MULTI, 0);
ensure(bool(status));
ensure(false == !(status));
status.mType = HttpStatus::LLCORE;
status.mStatus = HE_SUCCESS;
status = HttpStatus(HttpStatus::LLCORE, HE_SUCCESS);
ensure(bool(status));
ensure(false == !(status));
status.mType = HttpStatus::EXT_CURL_MULTI;
status.mStatus = -1;
status = HttpStatus(HttpStatus::EXT_CURL_MULTI, -1);
ensure(false == bool(status));
ensure(!(status));
status.mType = HttpStatus::EXT_CURL_EASY;
status.mStatus = CURLE_BAD_DOWNLOAD_RESUME;
status = HttpStatus(HttpStatus::EXT_CURL_EASY, CURLE_BAD_DOWNLOAD_RESUME);
ensure(false == bool(status));
ensure(!(status));
}
// template <> template <>
// void HttpStatusTestObjectType::test<2>()
// {
// set_test_name("HttpStatus memory structure");
//
// // Require that an HttpStatus object can be trivially
// // returned as a function return value in registers.
// // One should fit in an int on all platforms.
//
// //ensure(sizeof(HttpStatus) <= sizeof(int));
// }
template <> template <>
void HttpStatusTestObjectType::test<2>()
{
set_test_name("HttpStatus memory structure");
set_test_name("HttpStatus valid status string conversion");
HttpStatus status = HttpStatus(HttpStatus::EXT_CURL_EASY, 0);
std::string msg = status.toString();
// std::cout << "Result: " << msg << std::endl;
ensure(msg.empty());
// Require that an HttpStatus object can be trivially
// returned as a function return value in registers.
// One should fit in an int on all platforms.
status = HttpStatus(HttpStatus::EXT_CURL_EASY, CURLE_BAD_FUNCTION_ARGUMENT);
msg = status.toString();
// std::cout << "Result: " << msg << std::endl;
ensure(! msg.empty());
ensure(sizeof(HttpStatus) <= sizeof(int));
status = HttpStatus(HttpStatus::EXT_CURL_MULTI, CURLM_OUT_OF_MEMORY);
msg = status.toString();
// std::cout << "Result: " << msg << std::endl;
ensure(! msg.empty());
status = HttpStatus(HttpStatus::LLCORE, HE_SHUTTING_DOWN);
msg = status.toString();
// std::cout << "Result: " << msg << std::endl;
ensure(! msg.empty());
}
template <> template <>
void HttpStatusTestObjectType::test<3>()
{
set_test_name("HttpStatus valid status string conversion");
set_test_name("HttpStatus invalid status string conversion");
HttpStatus status;
status.mType = HttpStatus::EXT_CURL_EASY;
status.mStatus = 0;
HttpStatus status = HttpStatus(HttpStatus::EXT_CURL_EASY, 32726);
std::string msg = status.toString();
// std::cout << "Result: " << msg << std::endl;
ensure(msg.empty());
status.mType = HttpStatus::EXT_CURL_EASY;
status.mStatus = CURLE_BAD_FUNCTION_ARGUMENT;
ensure(! msg.empty());
status = HttpStatus(HttpStatus::EXT_CURL_MULTI, -470);
msg = status.toString();
// std::cout << "Result: " << msg << std::endl;
ensure(! msg.empty());
status.mType = HttpStatus::EXT_CURL_MULTI;
status.mStatus = CURLM_OUT_OF_MEMORY;
msg = status.toString();
// std::cout << "Result: " << msg << std::endl;
ensure(! msg.empty());
status.mType = HttpStatus::LLCORE;
status.mStatus = HE_SHUTTING_DOWN;
status = HttpStatus(HttpStatus::LLCORE, 923);
msg = status.toString();
// std::cout << "Result: " << msg << std::endl;
ensure(! msg.empty());
}
template <> template <>
void HttpStatusTestObjectType::test<4>()
{
set_test_name("HttpStatus invalid status string conversion");
HttpStatus status;
status.mType = HttpStatus::EXT_CURL_EASY;
status.mStatus = 32726;
std::string msg = status.toString();
// std::cout << "Result: " << msg << std::endl;
ensure(! msg.empty());
status.mType = HttpStatus::EXT_CURL_MULTI;
status.mStatus = -470;
msg = status.toString();
// std::cout << "Result: " << msg << std::endl;
ensure(! msg.empty());
status.mType = HttpStatus::LLCORE;
status.mStatus = 923;
msg = status.toString();
// std::cout << "Result: " << msg << std::endl;
ensure(! msg.empty());
}
template <> template <>
void HttpStatusTestObjectType::test<5>()
{
set_test_name("HttpStatus equality/inequality testing");
@ -170,62 +157,55 @@ void HttpStatusTestObjectType::test<5>()
HttpStatus status2(HttpStatus::EXT_CURL_EASY, HE_SUCCESS);
ensure(status1 != status2);
status1.mType = HttpStatus::LLCORE;
status1.mStatus = HE_REPLY_ERROR;
status2.mType = HttpStatus::LLCORE;
status2.mStatus= HE_SHUTTING_DOWN;
status1 = HttpStatus(HttpStatus::LLCORE, HE_REPLY_ERROR);
status1 = HttpStatus(HttpStatus::LLCORE, HE_SHUTTING_DOWN);
ensure(status1 != status2);
}
template <> template <>
void HttpStatusTestObjectType::test<6>()
void HttpStatusTestObjectType::test<5>()
{
set_test_name("HttpStatus basic HTTP status encoding");
HttpStatus status;
status.mType = 200;
status.mStatus = HE_SUCCESS;
status = HttpStatus(200, HE_SUCCESS);
std::string msg = status.toString();
ensure(msg.empty());
ensure(bool(status));
// Normally a success but application says error
status.mStatus = HE_REPLY_ERROR;
status = HttpStatus(200, HE_REPLY_ERROR);
msg = status.toString();
ensure(! msg.empty());
ensure(! bool(status));
ensure(status.toULong() > 1UL); // Biggish number, not a bool-to-ulong
// Same statuses with distinct success/fail are distinct
status.mType = 200;
status.mStatus = HE_SUCCESS;
status = HttpStatus(200, HE_SUCCESS);
HttpStatus status2(200, HE_REPLY_ERROR);
ensure(status != status2);
// Normally an error but application says okay
status.mType = 406;
status.mStatus = HE_SUCCESS;
status = HttpStatus(406, HE_SUCCESS);
msg = status.toString();
ensure(msg.empty());
ensure(bool(status));
// Different statuses but both successful are distinct
status.mType = 200;
status.mStatus = HE_SUCCESS;
status2.mType = 201;
status2.mStatus = HE_SUCCESS;
status = HttpStatus(200, HE_SUCCESS);
status2 = HttpStatus(201, HE_SUCCESS);
ensure(status != status2);
// Different statuses but both failed are distinct
status.mType = 200;
status.mStatus = HE_REPLY_ERROR;
status2.mType = 201;
status2.mStatus = HE_REPLY_ERROR;
status = HttpStatus(200, HE_REPLY_ERROR);
status2 = HttpStatus(201, HE_REPLY_ERROR);
ensure(status != status2);
}
template <> template <>
void HttpStatusTestObjectType::test<7>()
void HttpStatusTestObjectType::test<6>()
{
set_test_name("HttpStatus HTTP status text strings");
@ -234,34 +214,30 @@ void HttpStatusTestObjectType::test<7>()
ensure(! msg.empty()); // Should be something
ensure(msg == "Continue");
status.mStatus = HE_SUCCESS;
status = HttpStatus(200, HE_SUCCESS);
msg = status.toString();
ensure(msg.empty()); // Success is empty
status.mType = 199;
status.mStatus = HE_REPLY_ERROR;
status = HttpStatus(199, HE_REPLY_ERROR);
msg = status.toString();
ensure(msg == "Unknown error");
status.mType = 505; // Last defined string
status.mStatus = HE_REPLY_ERROR;
status = HttpStatus(505, HE_REPLY_ERROR);
msg = status.toString();
ensure(msg == "HTTP Version not supported");
status.mType = 506; // One beyond
status.mStatus = HE_REPLY_ERROR;
status = HttpStatus(506, HE_REPLY_ERROR);
msg = status.toString();
ensure(msg == "Unknown error");
status.mType = 999; // Last HTTP status
status.mStatus = HE_REPLY_ERROR;
status = HttpStatus(999, HE_REPLY_ERROR);
msg = status.toString();
ensure(msg == "Unknown error");
}
template <> template <>
void HttpStatusTestObjectType::test<8>()
void HttpStatusTestObjectType::test<7>()
{
set_test_name("HttpStatus toHex() nominal function");
@ -273,7 +249,7 @@ void HttpStatusTestObjectType::test<8>()
template <> template <>
void HttpStatusTestObjectType::test<9>()
void HttpStatusTestObjectType::test<8>()
{
set_test_name("HttpStatus toTerseString() nominal function");

View File

@ -3,6 +3,7 @@
project(llcrashlogger)
include(00-Common)
include(LLCoreHttp)
include(LLCommon)
include(LLMath)
include(LLMessage)
@ -10,6 +11,7 @@ include(LLVFS)
include(LLXML)
include_directories(
${LLCOREHTTP_INCLUDE_DIRS}
${LLCOMMON_INCLUDE_DIRS}
${LLMATH_INCLUDE_DIRS}
${LLMESSAGE_INCLUDE_DIRS}

View File

@ -40,38 +40,45 @@
#include "lldir.h"
#include "llfile.h"
#include "llsdserialize.h"
#include "lliopipe.h"
#include "llpumpio.h"
#include "llhttpclient.h"
#include "llsdserialize.h"
#include "llproxy.h"
LLPumpIO* gServicePump = NULL;
#include "llcorehttputil.h"
#include "llhttpsdhandler.h"
#include "httpcommon.h"
#include "httpresponse.h"
#include <curl/curl.h>
#include <openssl/crypto.h>
BOOL gBreak = false;
BOOL gSent = false;
class LLCrashLoggerResponder : public LLHTTPClient::Responder
int LLCrashLogger::ssl_mutex_count = 0;
LLCoreInt::HttpMutex ** LLCrashLogger::ssl_mutex_list = NULL;
class LLCrashLoggerHandler : public LLHttpSDHandler
{
LOG_CLASS(LLCrashLoggerResponder);
LOG_CLASS(LLCrashLoggerHandler);
public:
LLCrashLoggerResponder()
{
}
LLCrashLoggerHandler() {}
protected:
virtual void httpFailure()
{
LL_WARNS() << dumpResponse() << LL_ENDL;
gBreak = true;
}
virtual void onSuccess(LLCore::HttpResponse * response, const LLSD &content);
virtual void onFailure(LLCore::HttpResponse * response, LLCore::HttpStatus status);
virtual void httpSuccess()
{
gBreak = true;
gSent = true;
}
};
void LLCrashLoggerHandler::onSuccess(LLCore::HttpResponse * response, const LLSD &content)
{
gBreak = true;
gSent = true;
}
void LLCrashLoggerHandler::onFailure(LLCore::HttpResponse * response, LLCore::HttpStatus status)
{
gBreak = true;
}
LLCrashLogger::LLCrashLogger() :
mCrashBehavior(CRASH_BEHAVIOR_ALWAYS_SEND),
mCrashInPreviousExec(false),
@ -207,11 +214,13 @@ void LLCrashLogger::gatherFiles()
mFileMap["SettingsXml"] = mDebugLog["SettingsFilename"].asString();
if(mDebugLog.has("CAFilename"))
{
LLCurl::setCAFile(mDebugLog["CAFilename"].asString());
LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_CA_FILE,
LLCore::HttpRequest::GLOBAL_POLICY_ID, mDebugLog["CAFilename"].asString(), NULL);
}
else
{
LLCurl::setCAFile(gDirUtilp->getCAFile());
LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_CA_FILE,
LLCore::HttpRequest::GLOBAL_POLICY_ID, gDirUtilp->getCAFile(), NULL);
}
LL_INFOS() << "Using log file from debug log " << mFileMap["SecondLifeLog"] << LL_ENDL;
@ -220,7 +229,8 @@ void LLCrashLogger::gatherFiles()
else
{
// Figure out the filename of the second life log
LLCurl::setCAFile(gDirUtilp->getCAFile());
LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_CA_FILE,
LLCore::HttpRequest::GLOBAL_POLICY_ID, gDirUtilp->getCAFile(), NULL);
mFileMap["SecondLifeLog"] = gDirUtilp->getExpandedFilename(LL_PATH_DUMP,"SecondLife.log");
mFileMap["SettingsXml"] = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS,"settings.xml");
@ -389,19 +399,38 @@ bool LLCrashLogger::saveCrashBehaviorSetting(S32 crash_behavior)
bool LLCrashLogger::runCrashLogPost(std::string host, LLSD data, std::string msg, int retries, int timeout)
{
LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions);
gBreak = false;
httpOpts->setTimeout(timeout);
for(int i = 0; i < retries; ++i)
{
updateApplication(llformat("%s, try %d...", msg.c_str(), i+1));
LLHTTPClient::post(host, data, new LLCrashLoggerResponder(), timeout);
while(!gBreak)
LLCore::HttpHandle handle = LLCoreHttpUtil::requestPostWithLLSD(httpRequest.get(), LLCore::HttpRequest::DEFAULT_POLICY_ID, 0,
host, data, httpOpts, LLCore::HttpHeaders::ptr_t(), LLCore::HttpHandler::ptr_t(new LLCrashLoggerHandler));
if (handle == LLCORE_HTTP_HANDLE_INVALID)
{
LLCore::HttpStatus status = httpRequest->getStatus();
LL_WARNS("CRASHREPORT") << "Request POST failed to " << host << " with status of [" <<
status.getType() << "]\"" << status.toString() << "\"" << LL_ENDL;
return false;
}
while(!gBreak)
{
updateApplication(); // No new message, just pump the IO
httpRequest->update(0L);
}
if(gSent)
{
return gSent;
}
LL_WARNS("CRASHREPORT") << "Failed to send crash report to \"" << host << "\"" << LL_ENDL;
}
return gSent;
}
@ -510,14 +539,12 @@ bool LLCrashLogger::sendCrashLogs()
void LLCrashLogger::updateApplication(const std::string& message)
{
gServicePump->pump();
gServicePump->callback();
if (!message.empty()) LL_INFOS() << message << LL_ENDL;
}
bool LLCrashLogger::init()
{
LLCurl::initClass(false);
LLCore::LLHttp::initialize();
// We assume that all the logs we're looking for reside on the current drive
gDirUtilp->initAppDirs("SecondLife");
@ -576,16 +603,74 @@ bool LLCrashLogger::init()
return false;
}
gServicePump = new LLPumpIO(gAPRPoolp);
gServicePump->prime(gAPRPoolp);
LLHTTPClient::setPump(*gServicePump);
init_curl();
LLCore::HttpRequest::createService();
LLCore::HttpRequest::startThread();
return true;
}
// For cleanup code common to all platforms.
void LLCrashLogger::commonCleanup()
{
term_curl();
LLError::logToFile(""); //close crashreport.log
LLProxy::cleanupClass();
}
void LLCrashLogger::init_curl()
{
curl_global_init(CURL_GLOBAL_ALL);
ssl_mutex_count = CRYPTO_num_locks();
if (ssl_mutex_count > 0)
{
ssl_mutex_list = new LLCoreInt::HttpMutex *[ssl_mutex_count];
for (int i(0); i < ssl_mutex_count; ++i)
{
ssl_mutex_list[i] = new LLCoreInt::HttpMutex;
}
CRYPTO_set_locking_callback(ssl_locking_callback);
CRYPTO_set_id_callback(ssl_thread_id_callback);
}
}
void LLCrashLogger::term_curl()
{
CRYPTO_set_locking_callback(NULL);
for (int i(0); i < ssl_mutex_count; ++i)
{
delete ssl_mutex_list[i];
}
delete[] ssl_mutex_list;
}
unsigned long LLCrashLogger::ssl_thread_id_callback(void)
{
#if LL_WINDOWS
return (unsigned long)GetCurrentThread();
#else
return (unsigned long)pthread_self();
#endif
}
void LLCrashLogger::ssl_locking_callback(int mode, int type, const char * /* file */, int /* line */)
{
if (type >= 0 && type < ssl_mutex_count)
{
if (mode & CRYPTO_LOCK)
{
ssl_mutex_list[type]->lock();
}
else
{
ssl_mutex_list[type]->unlock();
}
}
}

View File

@ -34,6 +34,7 @@
#include "llsd.h"
#include "llcontrol.h"
#include "llcrashlock.h"
#include "_mutex.h"
// Crash reporter behavior
const S32 CRASH_BEHAVIOR_ASK = 0;
@ -66,6 +67,11 @@ public:
bool readMinidump(std::string minidump_path);
protected:
static void init_curl();
static void term_curl();
static unsigned long ssl_thread_id_callback(void);
static void ssl_locking_callback(int mode, int type, const char * file, int line);
S32 mCrashBehavior;
BOOL mCrashInPreviousExec;
std::map<std::string, std::string> mFileMap;
@ -78,6 +84,10 @@ protected:
LLSD mDebugLog;
bool mSentCrashLogs;
LLCrashLock mKeyMaster;
static int ssl_mutex_count;
static LLCoreInt::HttpMutex ** ssl_mutex_list;
};
#endif //LLCRASHLOGGER_H

View File

@ -4,6 +4,7 @@ project(llinventory)
include(00-Common)
include(LLCommon)
include(LLCoreHttp)
include(LLMath)
include(LLMessage)
include(LLVFS)
@ -71,7 +72,7 @@ if (LL_TESTS)
LL_ADD_PROJECT_UNIT_TESTS(llinventory "${llinventory_TEST_SOURCE_FILES}")
#set(TEST_DEBUG on)
set(test_libs llinventory ${LLMESSAGE_LIBRARIES} ${LLVFS_LIBRARIES} ${LLMATH_LIBRARIES} ${LLCOMMON_LIBRARIES} ${WINDOWS_LIBRARIES})
set(test_libs llinventory ${LLMESSAGE_LIBRARIES} ${LLVFS_LIBRARIES} ${LLCOREHTTP_LIBRARIES} ${LLMATH_LIBRARIES} ${LLCOMMON_LIBRARIES} ${WINDOWS_LIBRARIES})
LL_ADD_INTEGRATION_TEST(inventorymisc "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llparcel "" "${test_libs}")
endif (LL_TESTS)

View File

@ -14,6 +14,7 @@ include(LLAddBuildTest)
include(Python)
include(Tut)
include(Python)
include(JsonCpp)
include_directories (${CMAKE_CURRENT_SOURCE_DIR})
@ -23,11 +24,10 @@ include_directories(
${LLMATH_INCLUDE_DIRS}
${LLMESSAGE_INCLUDE_DIRS}
${LLVFS_INCLUDE_DIRS}
${JSONCPP_INCLUDE_DIR}
)
set(llmessage_SOURCE_FILES
llares.cpp
llareslistener.cpp
llassetstorage.cpp
llavatarname.cpp
llavatarnamecache.cpp
@ -38,19 +38,15 @@ set(llmessage_SOURCE_FILES
llchainio.cpp
llcircuit.cpp
llclassifiedflags.cpp
llcoproceduremanager.cpp
llcorehttputil.cpp
llcurl.cpp
lldatapacker.cpp
lldispatcher.cpp
llexperiencecache.cpp
llfiltersd2xmlrpc.cpp
llhost.cpp
llhttpassetstorage.cpp
llhttpclient.cpp
llhttpclientadapter.cpp
llhttpconstants.cpp
llhttpnode.cpp
llhttpsender.cpp
llhttpsdhandler.cpp
llinstantmessage.cpp
lliobuffer.cpp
lliohttpserver.cpp
@ -74,11 +70,8 @@ set(llmessage_SOURCE_FILES
llpumpio.cpp
llsdappservices.cpp
llsdhttpserver.cpp
llsdmessage.cpp
llsdmessagebuilder.cpp
llsdmessagereader.cpp
llsdrpcclient.cpp
llsdrpcserver.cpp
llservicebuilder.cpp
llservice.cpp
llstoredmessage.cpp
@ -92,7 +85,6 @@ set(llmessage_SOURCE_FILES
lltransfertargetfile.cpp
lltransfertargetvfile.cpp
lltrustedmessageservice.cpp
llurlrequest.cpp
lluseroperation.cpp
llxfer.cpp
llxfer_file.cpp
@ -115,8 +107,6 @@ set(llmessage_SOURCE_FILES
set(llmessage_HEADER_FILES
CMakeLists.txt
llares.h
llareslistener.h
llassetstorage.h
llavatarname.h
llavatarnamecache.h
@ -128,8 +118,8 @@ set(llmessage_HEADER_FILES
llcipher.h
llcircuit.h
llclassifiedflags.h
llcoproceduremanager.h
llcorehttputil.h
llcurl.h
lldatapacker.h
lldbstrings.h
lldispatcher.h
@ -139,14 +129,9 @@ set(llmessage_HEADER_FILES
llfiltersd2xmlrpc.h
llfollowcamparams.h
llhost.h
llhttpassetstorage.h
llhttpclient.h
llhttpclientinterface.h
llhttpclientadapter.h
llhttpconstants.h
llhttpnode.h
llhttpnodeadapter.h
llhttpsender.h
llhttpsdhandler.h
llinstantmessage.h
llinvite.h
lliobuffer.h
@ -176,11 +161,8 @@ set(llmessage_HEADER_FILES
llregionhandle.h
llsdappservices.h
llsdhttpserver.h
llsdmessage.h
llsdmessagebuilder.h
llsdmessagereader.h
llsdrpcclient.h
llsdrpcserver.h
llservice.h
llservicebuilder.h
llstoredmessage.h
@ -196,7 +178,6 @@ set(llmessage_HEADER_FILES
lltransfertargetfile.h
lltransfertargetvfile.h
lltrustedmessageservice.h
llurlrequest.h
lluseroperation.h
llvehicleparams.h
llxfer.h
@ -222,17 +203,41 @@ set_source_files_properties(${llmessage_HEADER_FILES}
list(APPEND llmessage_SOURCE_FILES ${llmessage_HEADER_FILES})
add_library (llmessage ${llmessage_SOURCE_FILES})
if (LINUX)
target_link_libraries(
llmessage
${CURL_LIBRARIES}
${LLCOMMON_LIBRARIES}
${LLVFS_LIBRARES}
${LLVFS_LIBRARIES}
${LLMATH_LIBRARIES}
${CARES_LIBRARIES}
${JSONCPP_LIBRARIES}
${OPENSSL_LIBRARIES}
${CRYPTO_LIBRARIES}
${XMLRPCEPI_LIBRARIES}
${LLCOREHTTP_LIBRARIES}
${BOOST_COROUTINE_LIBRARY}
${BOOST_CONTEXT_LIBRARY}
${BOOST_SYSTEM_LIBRARY}
rt
)
else (LINUX)
target_link_libraries(
llmessage
${CURL_LIBRARIES}
${LLCOMMON_LIBRARIES}
${LLVFS_LIBRARIES}
${LLMATH_LIBRARIES}
${JSONCPP_LIBRARIES}
${OPENSSL_LIBRARIES}
${CRYPTO_LIBRARIES}
${XMLRPCEPI_LIBRARIES}
${LLCOREHTTP_LIBRARIES}
${BOOST_COROUTINE_LIBRARY}
${BOOST_CONTEXT_LIBRARY}
${BOOST_SYSTEM_LIBRARY}
)
endif(LINUX)
# tests
if (LL_TESTS)
@ -243,36 +248,42 @@ if (LL_TESTS)
)
LL_ADD_PROJECT_UNIT_TESTS(llmessage "${llmessage_TEST_SOURCE_FILES}")
# set(TEST_DEBUG on)
if (LINUX)
set(test_libs
${CURL_LIBRARIES}
${LLMESSAGE_LIBRARIES}
${WINDOWS_LIBRARIES}
${LLVFS_LIBRARIES}
${LLMATH_LIBRARIES}
${CURL_LIBRARIES}
${LLCOMMON_LIBRARIES}
${GOOGLEMOCK_LIBRARIES}
${LLMESSAGE_LIBRARIES}
${LLCOREHTTP_LIBRARIES}
${JSONCPP_LIBRARIES}
${BOOST_COROUTINE_LIBRARY}
${BOOST_CONTEXT_LIBRARY}
rt
${GOOGLEMOCK_LIBRARIES}
)
LL_ADD_INTEGRATION_TEST(
llsdmessage
"llsdmessage.cpp"
"${test_libs}"
${PYTHON_EXECUTABLE}
"${CMAKE_CURRENT_SOURCE_DIR}/tests/test_llsdmessage_peer.py"
else (LINUX)
set(test_libs
${WINDOWS_LIBRARIES}
${LLVFS_LIBRARIES}
${LLMATH_LIBRARIES}
${CURL_LIBRARIES}
${LLCOMMON_LIBRARIES}
${LLMESSAGE_LIBRARIES}
${LLCOREHTTP_LIBRARIES}
${JSONCPP_LIBRARIES}
${BOOST_COROUTINE_LIBRARY}
${BOOST_CONTEXT_LIBRARY}
${GOOGLEMOCK_LIBRARIES}
)
endif(LINUX)
LL_ADD_INTEGRATION_TEST(
llhttpclient
"llhttpclient.cpp"
"${test_libs}"
${PYTHON_EXECUTABLE}
"${CMAKE_CURRENT_SOURCE_DIR}/tests/test_llsdmessage_peer.py"
)
LL_ADD_INTEGRATION_TEST(llavatarnamecache "" "${test_libs}")
#LL_ADD_INTEGRATION_TEST(llavatarnamecache "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llhost "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llhttpclientadapter "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llpartdata "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llxfer_file "" "${test_libs}")
endif (LL_TESTS)

View File

@ -1,839 +0,0 @@
/**
* @file llares.cpp
* @author Bryan O'Sullivan
* @date 2007-08-15
* @brief Wrapper for asynchronous DNS lookups.
*
* $LicenseInfo:firstyear=2007&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "linden_common.h"
#include "llares.h"
#include <ares_dns.h>
#include <ares_version.h>
#include "apr_portable.h"
#include "apr_network_io.h"
#include "apr_poll.h"
#include "llapr.h"
#include "llareslistener.h"
#if defined(LL_WINDOWS)
#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally
# define ns_c_in 1
# define NS_HFIXEDSZ 12 /* #/bytes of fixed data in header */
# define NS_QFIXEDSZ 4 /* #/bytes of fixed data in query */
# define NS_RRFIXEDSZ 10 /* #/bytes of fixed data in r record */
#else
# include <arpa/nameser.h>
#endif
LLAres::HostResponder::~HostResponder()
{
}
void LLAres::HostResponder::hostResult(const hostent *ent)
{
LL_INFOS() << "LLAres::HostResponder::hostResult not implemented" << LL_ENDL;
}
void LLAres::HostResponder::hostError(int code)
{
LL_INFOS() << "LLAres::HostResponder::hostError " << code << ": "
<< LLAres::strerror(code) << LL_ENDL;
}
LLAres::NameInfoResponder::~NameInfoResponder()
{
}
void LLAres::NameInfoResponder::nameInfoResult(const char *node,
const char *service)
{
LL_INFOS() << "LLAres::NameInfoResponder::nameInfoResult not implemented"
<< LL_ENDL;
}
void LLAres::NameInfoResponder::nameInfoError(int code)
{
LL_INFOS() << "LLAres::NameInfoResponder::nameInfoError " << code << ": "
<< LLAres::strerror(code) << LL_ENDL;
}
LLAres::QueryResponder::~QueryResponder()
{
}
void LLAres::QueryResponder::queryResult(const char *buf, size_t len)
{
LL_INFOS() << "LLAres::QueryResponder::queryResult not implemented"
<< LL_ENDL;
}
void LLAres::QueryResponder::queryError(int code)
{
LL_INFOS() << "LLAres::QueryResponder::queryError " << code << ": "
<< LLAres::strerror(code) << LL_ENDL;
}
LLAres::LLAres() :
chan_(NULL),
mInitSuccess(false)
{
if (ares_library_init( ARES_LIB_INIT_ALL ) != ARES_SUCCESS ||
ares_init(&chan_) != ARES_SUCCESS)
{
LL_WARNS() << "Could not succesfully initialize ares!" << LL_ENDL;
return;
}
mListener = boost::shared_ptr< LLAresListener >(new LLAresListener(this));
mInitSuccess = true;
}
LLAres::~LLAres()
{
ares_destroy(chan_);
ares_library_cleanup();
}
void LLAres::cancel()
{
ares_cancel(chan_);
}
static void host_callback_1_5(void *arg, int status, int timeouts,
struct hostent *ent)
{
LLPointer<LLAres::HostResponder> *resp =
(LLPointer<LLAres::HostResponder> *) arg;
if (status == ARES_SUCCESS)
{
(*resp)->hostResult(ent);
} else {
(*resp)->hostError(status);
}
delete resp;
}
#if ARES_VERSION_MAJOR == 1 && ARES_VERSION_MINOR == 4
static void host_callback(void *arg, int status, struct hostent *ent)
{
host_callback_1_5(arg, status, 0, ent);
}
#else
# define host_callback host_callback_1_5
#endif
void LLAres::getHostByName(const char *name, HostResponder *resp,
int family)
{
ares_gethostbyname(chan_, name, family, host_callback,
new LLPointer<LLAres::HostResponder>(resp));
}
void LLAres::getSrvRecords(const std::string &name, SrvResponder *resp)
{
search(name, RES_SRV, resp);
}
void LLAres::rewriteURI(const std::string &uri, UriRewriteResponder *resp)
{
if (resp && uri.size())
{
LLURI* pURI = new LLURI(uri);
resp->mUri = *pURI;
delete pURI;
if (!resp->mUri.scheme().size() || !resp->mUri.hostName().size())
{
return;
}
//LL_INFOS() << "LLAres::rewriteURI (" << uri << ") search: '" << "_" + resp->mUri.scheme() + "._tcp." + resp->mUri.hostName() << "'" << LL_ENDL;
search("_" + resp->mUri.scheme() + "._tcp." + resp->mUri.hostName(), RES_SRV, resp);
}
}
LLQueryResponder::LLQueryResponder()
: LLAres::QueryResponder(),
mResult(ARES_ENODATA),
mType(RES_INVALID)
{
}
int LLQueryResponder::parseRR(const char *buf, size_t len, const char *&pos,
LLPointer<LLDnsRecord> &r)
{
std::string rrname;
size_t enclen;
int ret;
// RR name.
ret = LLAres::expandName(pos, buf, len, rrname, enclen);
if (ret != ARES_SUCCESS)
{
return ret;
}
pos += enclen;
if (pos + NS_RRFIXEDSZ > buf + len)
{
return ARES_EBADRESP;
}
int rrtype = DNS_RR_TYPE(pos);
int rrclass = DNS_RR_CLASS(pos);
int rrttl = DNS_RR_TTL(pos);
int rrlen = DNS_RR_LEN(pos);
if (rrclass != ns_c_in)
{
return ARES_EBADRESP;
}
pos += NS_RRFIXEDSZ;
if (pos + rrlen > buf + len)
{
return ARES_EBADRESP;
}
switch (rrtype)
{
case RES_A:
r = new LLARecord(rrname, rrttl);
break;
case RES_NS:
r = new LLNsRecord(rrname, rrttl);
break;
case RES_CNAME:
r = new LLCnameRecord(rrname, rrttl);
break;
case RES_PTR:
r = new LLPtrRecord(rrname, rrttl);
break;
case RES_AAAA:
r = new LLAaaaRecord(rrname, rrttl);
break;
case RES_SRV:
r = new LLSrvRecord(rrname, rrttl);
break;
default:
LL_INFOS() << "LLQueryResponder::parseRR got unknown RR type " << rrtype
<< LL_ENDL;
return ARES_EBADRESP;
}
ret = r->parse(buf, len, pos, rrlen);
if (ret == ARES_SUCCESS)
{
pos += rrlen;
} else {
r = NULL;
}
return ret;
}
int LLQueryResponder::parseSection(const char *buf, size_t len,
size_t count, const char *&pos,
dns_rrs_t &rrs)
{
int ret = ARES_SUCCESS;
for (size_t i = 0; i < count; i++)
{
LLPointer<LLDnsRecord> r;
ret = parseRR(buf, len, pos, r);
if (ret != ARES_SUCCESS)
{
break;
}
rrs.push_back(r);
}
return ret;
}
void LLQueryResponder::queryResult(const char *buf, size_t len)
{
const char *pos = buf;
int qdcount = DNS_HEADER_QDCOUNT(pos);
int ancount = DNS_HEADER_ANCOUNT(pos);
int nscount = DNS_HEADER_NSCOUNT(pos);
int arcount = DNS_HEADER_ARCOUNT(pos);
int ret;
if (qdcount == 0 || ancount + nscount + arcount == 0)
{
ret = ARES_ENODATA;
goto bail;
}
pos += NS_HFIXEDSZ;
for (int i = 0; i < qdcount; i++)
{
std::string ignore;
size_t enclen;
ret = LLAres::expandName(pos, buf, len, i == 0 ? mQuery : ignore,
enclen);
if (ret != ARES_SUCCESS)
{
goto bail;
}
pos += enclen;
if (i == 0)
{
int t = DNS_QUESTION_TYPE(pos);
switch (t)
{
case RES_A:
case RES_NS:
case RES_CNAME:
case RES_PTR:
case RES_AAAA:
case RES_SRV:
mType = (LLResType) t;
break;
default:
LL_INFOS() << "Cannot grok query type " << t << LL_ENDL;
ret = ARES_EBADQUERY;
goto bail;
}
}
pos += NS_QFIXEDSZ;
if (pos > buf + len)
{
ret = ARES_EBADRESP;
goto bail;
}
}
ret = parseSection(buf, len, ancount, pos, mAnswers);
if (ret != ARES_SUCCESS)
{
goto bail;
}
ret = parseSection(buf, len, nscount, pos, mAuthorities);
if (ret != ARES_SUCCESS)
{
goto bail;
}
ret = parseSection(buf, len, arcount, pos, mAdditional);
bail:
mResult = ret;
if (mResult == ARES_SUCCESS)
{
querySuccess();
} else {
queryError(mResult);
}
}
void LLQueryResponder::querySuccess()
{
LL_INFOS() << "LLQueryResponder::queryResult not implemented" << LL_ENDL;
}
void LLAres::SrvResponder::querySuccess()
{
if (mType == RES_SRV)
{
srvResult(mAnswers);
} else {
srvError(ARES_EBADRESP);
}
}
void LLAres::SrvResponder::queryError(int code)
{
srvError(code);
}
void LLAres::SrvResponder::srvResult(const dns_rrs_t &ents)
{
LL_INFOS() << "LLAres::SrvResponder::srvResult not implemented" << LL_ENDL;
for (size_t i = 0; i < ents.size(); i++)
{
const LLSrvRecord *s = (const LLSrvRecord *) ents[i].get();
LL_INFOS() << "[" << i << "] " << s->host() << ":" << s->port()
<< " priority " << s->priority()
<< " weight " << s->weight()
<< LL_ENDL;
}
}
void LLAres::SrvResponder::srvError(int code)
{
LL_INFOS() << "LLAres::SrvResponder::srvError " << code << ": "
<< LLAres::strerror(code) << LL_ENDL;
}
static void nameinfo_callback_1_5(void *arg, int status, int timeouts,
char *node, char *service)
{
LLPointer<LLAres::NameInfoResponder> *resp =
(LLPointer<LLAres::NameInfoResponder> *) arg;
if (status == ARES_SUCCESS)
{
(*resp)->nameInfoResult(node, service);
} else {
(*resp)->nameInfoError(status);
}
delete resp;
}
#if ARES_VERSION_MAJOR == 1 && ARES_VERSION_MINOR == 4
static void nameinfo_callback(void *arg, int status, char *node, char *service)
{
nameinfo_callback_1_5(arg, status, 0, node, service);
}
#else
# define nameinfo_callback nameinfo_callback_1_5
#endif
void LLAres::getNameInfo(const struct sockaddr &sa, socklen_t salen, int flags,
NameInfoResponder *resp)
{
ares_getnameinfo(chan_, &sa, salen, flags, nameinfo_callback,
new LLPointer<NameInfoResponder>(resp));
}
static void search_callback_1_5(void *arg, int status, int timeouts,
unsigned char *abuf, int alen)
{
LLPointer<LLAres::QueryResponder> *resp =
(LLPointer<LLAres::QueryResponder> *) arg;
if (status == ARES_SUCCESS)
{
(*resp)->queryResult((const char *) abuf, alen);
} else {
(*resp)->queryError(status);
}
delete resp;
}
#if ARES_VERSION_MAJOR == 1 && ARES_VERSION_MINOR == 4
static void search_callback(void *arg, int status, unsigned char *abuf,
int alen)
{
search_callback_1_5(arg, status, 0, abuf, alen);
}
#else
# define search_callback search_callback_1_5
#endif
void LLAres::search(const std::string &query, LLResType type,
QueryResponder *resp)
{
ares_search(chan_, query.c_str(), ns_c_in, type, search_callback,
new LLPointer<QueryResponder>(resp));
}
bool LLAres::process(U64 timeout)
{
if (!gAPRPoolp)
{
ll_init_apr();
}
ares_socket_t socks[ARES_GETSOCK_MAXNUM];
apr_pollfd_t aprFds[ARES_GETSOCK_MAXNUM];
apr_int32_t nsds = 0;
int nactive = 0;
int bitmask;
bitmask = ares_getsock(chan_, socks, ARES_GETSOCK_MAXNUM);
if (bitmask == 0)
{
return nsds > 0;
}
apr_status_t status;
LLAPRPool pool;
status = pool.getStatus() ;
ll_apr_assert_status(status);
for (int i = 0; i < ARES_GETSOCK_MAXNUM; i++)
{
if (ARES_GETSOCK_READABLE(bitmask, i))
{
aprFds[nactive].reqevents = APR_POLLIN | APR_POLLERR;
}
else if (ARES_GETSOCK_WRITABLE(bitmask, i))
{
aprFds[nactive].reqevents = APR_POLLOUT | APR_POLLERR;
} else {
continue;
}
apr_socket_t *aprSock = NULL;
status = apr_os_sock_put(&aprSock, (apr_os_sock_t *) &socks[i], pool.getAPRPool());
if (status != APR_SUCCESS)
{
ll_apr_warn_status(status);
return nsds > 0;
}
aprFds[nactive].desc.s = aprSock;
aprFds[nactive].desc_type = APR_POLL_SOCKET;
aprFds[nactive].p = pool.getAPRPool();
aprFds[nactive].rtnevents = 0;
aprFds[nactive].client_data = &socks[i];
nactive++;
}
if (nactive > 0)
{
status = apr_poll(aprFds, nactive, &nsds, timeout);
if (status != APR_SUCCESS && status != APR_TIMEUP)
{
ll_apr_warn_status(status);
}
for (int i = 0; i < nactive; i++)
{
int evts = aprFds[i].rtnevents;
int ifd = (evts & (APR_POLLIN | APR_POLLERR))
? *((int *) aprFds[i].client_data) : ARES_SOCKET_BAD;
int ofd = (evts & (APR_POLLOUT | APR_POLLERR))
? *((int *) aprFds[i].client_data) : ARES_SOCKET_BAD;
ares_process_fd(chan_, ifd, ofd);
}
}
return nsds > 0;
}
bool LLAres::processAll()
{
bool anyProcessed = false, ret;
do {
timeval tv;
ret = ares_timeout(chan_, NULL, &tv) != NULL;
if (ret)
{
ret = process(tv.tv_sec * 1000000LL + tv.tv_usec);
anyProcessed |= ret;
}
} while (ret);
return anyProcessed;
}
int LLAres::expandName(const char *encoded, const char *abuf, size_t alen,
std::string &s, size_t &enclen)
{
char *t;
int ret;
long e;
ret = ares_expand_name((const unsigned char *) encoded,
(const unsigned char *) abuf, alen, &t, &e);
if (ret == ARES_SUCCESS)
{
s.assign(t);
enclen = e;
ares_free_string(t);
}
return ret;
}
const char *LLAres::strerror(int code)
{
return ares_strerror(code);
}
LLAres *gAres;
LLAres *ll_init_ares()
{
if (gAres == NULL)
{
gAres = new LLAres();
}
return gAres;
}
void ll_cleanup_ares()
{
if (gAres != NULL)
{
delete gAres;
gAres = NULL;
}
}
LLDnsRecord::LLDnsRecord(LLResType type, const std::string &name,
unsigned ttl)
: LLRefCount(),
mType(type),
mName(name),
mTTL(ttl)
{
}
LLHostRecord::LLHostRecord(LLResType type, const std::string &name,
unsigned ttl)
: LLDnsRecord(type, name, ttl)
{
}
int LLHostRecord::parse(const char *buf, size_t len, const char *pos,
size_t rrlen)
{
int ret;
ret = LLAres::expandName(pos, buf, len, mHost);
if (ret != ARES_SUCCESS)
{
goto bail;
}
ret = ARES_SUCCESS;
bail:
return ret;
}
LLCnameRecord::LLCnameRecord(const std::string &name, unsigned ttl)
: LLHostRecord(RES_CNAME, name, ttl)
{
}
LLPtrRecord::LLPtrRecord(const std::string &name, unsigned ttl)
: LLHostRecord(RES_PTR, name, ttl)
{
}
LLAddrRecord::LLAddrRecord(LLResType type, const std::string &name,
unsigned ttl)
: LLDnsRecord(type, name, ttl),
mSize(0)
{
}
LLARecord::LLARecord(const std::string &name, unsigned ttl)
: LLAddrRecord(RES_A, name, ttl)
{
}
int LLARecord::parse(const char *buf, size_t len, const char *pos,
size_t rrlen)
{
int ret;
if (rrlen != sizeof(mSA.sin.sin_addr.s_addr))
{
ret = ARES_EBADRESP;
goto bail;
}
memset(&mSA, 0, sizeof(mSA));
memcpy(&mSA.sin.sin_addr.s_addr, pos, rrlen);
mSA.sin.sin_family = AF_INET6;
mSize = sizeof(mSA.sin);
ret = ARES_SUCCESS;
bail:
return ret;
}
LLAaaaRecord::LLAaaaRecord(const std::string &name, unsigned ttl)
: LLAddrRecord(RES_AAAA, name, ttl)
{
}
int LLAaaaRecord::parse(const char *buf, size_t len, const char *pos,
size_t rrlen)
{
int ret;
if (rrlen != sizeof(mSA.sin6.sin6_addr))
{
ret = ARES_EBADRESP;
goto bail;
}
memset(&mSA, 0, sizeof(mSA));
memcpy(&mSA.sin6.sin6_addr.s6_addr, pos, rrlen);
mSA.sin6.sin6_family = AF_INET6;
mSize = sizeof(mSA.sin6);
ret = ARES_SUCCESS;
bail:
return ret;
}
LLSrvRecord::LLSrvRecord(const std::string &name, unsigned ttl)
: LLHostRecord(RES_SRV, name, ttl),
mPriority(0),
mWeight(0),
mPort(0)
{
}
int LLSrvRecord::parse(const char *buf, size_t len, const char *pos,
size_t rrlen)
{
int ret;
if (rrlen < 6)
{
ret = ARES_EBADRESP;
goto bail;
}
memcpy(&mPriority, pos, 2);
memcpy(&mWeight, pos + 2, 2);
memcpy(&mPort, pos + 4, 2);
mPriority = ntohs(mPriority);
mWeight = ntohs(mWeight);
mPort = ntohs(mPort);
ret = LLHostRecord::parse(buf, len, pos + 6, rrlen - 6);
bail:
return ret;
}
LLNsRecord::LLNsRecord(const std::string &name, unsigned ttl)
: LLHostRecord(RES_NS, name, ttl)
{
}
void LLAres::UriRewriteResponder::queryError(int code)
{
std::vector<std::string> uris;
uris.push_back(mUri.asString());
rewriteResult(uris);
}
void LLAres::UriRewriteResponder::querySuccess()
{
std::vector<std::string> uris;
if (mType != RES_SRV)
{
goto bail;
}
for (size_t i = 0; i < mAnswers.size(); i++)
{
const LLSrvRecord *r = (const LLSrvRecord *) mAnswers[i].get();
if (r->type() == RES_SRV)
{
// Check the domain in the response to ensure that it's
// the same as the domain in the request, so that bad guys
// can't forge responses that point to their own login
// servers with their own certificates.
// Hard-coding the domain to check here is a bit of a
// hack. Hoist it to an outer caller if anyone ever needs
// this functionality on other domains.
static const std::string domain(".lindenlab.com");
const std::string &host = r->host();
std::string::size_type s = host.find(domain) + domain.length();
if (s != host.length() && s != host.length() - 1)
{
continue;
}
LLURI uri(mUri.scheme(),
mUri.userName(),
mUri.password(),
r->host(),
mUri.defaultPort() ? r->port() : mUri.hostPort(),
mUri.escapedPath(),
mUri.escapedQuery());
uris.push_back(uri.asString());
}
}
if (!uris.empty())
{
goto done;
}
bail:
uris.push_back(mUri.asString());
done:
rewriteResult(uris);
}
void LLAres::UriRewriteResponder::rewriteResult(
const std::vector<std::string> &uris)
{
LL_INFOS() << "LLAres::UriRewriteResponder::rewriteResult not implemented"
<< LL_ENDL;
for (size_t i = 0; i < uris.size(); i++)
{
LL_INFOS() << "[" << i << "] " << uris[i] << LL_ENDL;
}
}

View File

@ -1,583 +0,0 @@
/**
* @file llares.h
* @author Bryan O'Sullivan
* @date 2007-08-15
* @brief Wrapper for asynchronous DNS lookups.
*
* $LicenseInfo:firstyear=2007&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LL_LLARES_H
#define LL_LLARES_H
#ifdef LL_WINDOWS
// ares.h is broken on windows in that it depends on types defined in ws2tcpip.h
// we need to include them first to work around it, but the headers issue warnings
# pragma warning(push)
# pragma warning(disable:4996)
# include <winsock2.h>
# include <ws2tcpip.h>
# pragma warning(pop)
#endif
#ifdef LL_USESYSTEMLIBS
# include <ares.h>
#else
# include <ares/ares.h>
#endif
#include "llpointer.h"
#include "llrefcount.h"
#include "lluri.h"
#include <boost/shared_ptr.hpp>
class LLQueryResponder;
class LLAresListener;
/**
* @brief Supported DNS RR types.
*/
enum LLResType
{
RES_INVALID = 0, /**< Cookie. */
RES_A = 1, /**< "A" record. IPv4 address. */
RES_NS = 2, /**< "NS" record. Authoritative server. */
RES_CNAME = 5, /**< "CNAME" record. Canonical name. */
RES_PTR = 12, /**< "PTR" record. Domain name pointer. */
RES_AAAA = 28, /**< "AAAA" record. IPv6 Address. */
RES_SRV = 33, /**< "SRV" record. Server Selection. */
RES_MAX = 65536 /**< Sentinel; RR types are 16 bits wide. */
};
/**
* @class LLDnsRecord
* @brief Base class for all DNS RR types.
*/
class LLDnsRecord : public LLRefCount
{
protected:
friend class LLQueryResponder;
LLResType mType;
std::string mName;
unsigned mTTL;
virtual int parse(const char *buf, size_t len, const char *pos,
size_t rrlen) = 0;
LLDnsRecord(LLResType type, const std::string &name, unsigned ttl);
public:
/**
* @brief Record name.
*/
const std::string &name() const { return mName; }
/**
* @brief Time-to-live value, in seconds.
*/
unsigned ttl() const { return mTTL; }
/**
* @brief RR type.
*/
LLResType type() const { return mType; }
};
/**
* @class LLAddrRecord
* @brief Base class for address-related RRs.
*/
class LLAddrRecord : public LLDnsRecord
{
protected:
friend class LLQueryResponder;
LLAddrRecord(LLResType type, const std::string &name, unsigned ttl);
union
{
sockaddr sa;
sockaddr_in sin;
sockaddr_in6 sin6;
} mSA;
socklen_t mSize;
public:
/**
* @brief Generic socket address.
*/
const sockaddr &addr() const { return mSA.sa; }
/**
* @brief Size of the socket structure.
*/
socklen_t size() const { return mSize; }
};
/**
* @class LLARecord
* @brief A RR, for IPv4 addresses.
*/
class LLARecord : public LLAddrRecord
{
protected:
friend class LLQueryResponder;
LLARecord(const std::string &name, unsigned ttl);
int parse(const char *buf, size_t len, const char *pos, size_t rrlen);
public:
/**
* @brief Socket address.
*/
const sockaddr_in &addr_in() const { return mSA.sin; }
};
/**
* @class LLAaaaRecord
* @brief AAAA RR, for IPv6 addresses.
*/
class LLAaaaRecord : public LLAddrRecord
{
protected:
friend class LLQueryResponder;
LLAaaaRecord(const std::string &name, unsigned ttl);
int parse(const char *buf, size_t len, const char *pos, size_t rrlen);
public:
/**
* @brief Socket address.
*/
const sockaddr_in6 &addr_in6() const { return mSA.sin6; }
};
/**
* @class LLHostRecord
* @brief Base class for host-related RRs.
*/
class LLHostRecord : public LLDnsRecord
{
protected:
LLHostRecord(LLResType type, const std::string &name, unsigned ttl);
int parse(const char *buf, size_t len, const char *pos, size_t rrlen);
std::string mHost;
public:
/**
* @brief Host name.
*/
const std::string &host() const { return mHost; }
};
/**
* @class LLCnameRecord
* @brief CNAME RR.
*/
class LLCnameRecord : public LLHostRecord
{
protected:
friend class LLQueryResponder;
LLCnameRecord(const std::string &name, unsigned ttl);
};
/**
* @class LLPtrRecord
* @brief PTR RR.
*/
class LLPtrRecord : public LLHostRecord
{
protected:
friend class LLQueryResponder;
LLPtrRecord(const std::string &name, unsigned ttl);
};
/**
* @class LLSrvRecord
* @brief SRV RR.
*/
class LLSrvRecord : public LLHostRecord
{
protected:
U16 mPriority;
U16 mWeight;
U16 mPort;
int parse(const char *buf, size_t len, const char *pos, size_t rrlen);
public:
LLSrvRecord(const std::string &name, unsigned ttl);
/**
* @brief Service priority.
*/
U16 priority() const { return mPriority; }
/**
* @brief Service weight.
*/
U16 weight() const { return mWeight; }
/**
* @brief Port number of service.
*/
U16 port() const { return mPort; }
/**
* @brief Functor for sorting SRV records by priority.
*/
struct ComparePriorityLowest
{
bool operator()(const LLSrvRecord& lhs, const LLSrvRecord& rhs)
{
return lhs.mPriority < rhs.mPriority;
}
};
};
/**
* @class LLNsRecord
* @brief NS RR.
*/
class LLNsRecord : public LLHostRecord
{
public:
LLNsRecord(const std::string &name, unsigned ttl);
};
class LLQueryResponder;
/**
* @class LLAres
* @brief Asynchronous address resolver.
*/
class LLAres
{
public:
/**
* @class HostResponder
* @brief Base class for responding to hostname lookups.
* @see LLAres::getHostByName
*/
class HostResponder : public LLRefCount
{
public:
virtual ~HostResponder();
virtual void hostResult(const hostent *ent);
virtual void hostError(int code);
};
/**
* @class NameInfoResponder
* @brief Base class for responding to address lookups.
* @see LLAres::getNameInfo
*/
class NameInfoResponder : public LLRefCount
{
public:
virtual ~NameInfoResponder();
virtual void nameInfoResult(const char *node, const char *service);
virtual void nameInfoError(int code);
};
/**
* @class QueryResponder
* @brief Base class for responding to custom searches.
* @see LLAres::search
*/
class QueryResponder : public LLRefCount
{
public:
virtual ~QueryResponder();
virtual void queryResult(const char *buf, size_t len);
virtual void queryError(int code);
};
class SrvResponder;
class UriRewriteResponder;
LLAres();
~LLAres();
/**
* Cancel all outstanding requests. The error methods of the
* corresponding responders will be called, with ARES_ETIMEOUT.
*/
void cancel();
/**
* Look up the address of a host.
*
* @param name name of host to look up
* @param resp responder to call with result
* @param family AF_INET for IPv4 addresses, AF_INET6 for IPv6
*/
void getHostByName(const std::string &name, HostResponder *resp,
int family = AF_INET) {
getHostByName(name.c_str(), resp, family);
}
/**
* Look up the address of a host.
*
* @param name name of host to look up
* @param resp responder to call with result
* @param family AF_INET for IPv4 addresses, AF_INET6 for IPv6
*/
void getHostByName(const char *name, HostResponder *resp,
int family = PF_INET);
/**
* Look up the name associated with a socket address.
*
* @param sa socket address to look up
* @param salen size of socket address
* @param flags flags to use
* @param resp responder to call with result
*/
void getNameInfo(const struct sockaddr &sa, socklen_t salen, int flags,
NameInfoResponder *resp);
/**
* Look up SRV (service location) records for a service name.
*
* @param name service name (e.g. "_https._tcp.login.agni.lindenlab.com")
* @param resp responder to call with result
*/
void getSrvRecords(const std::string &name, SrvResponder *resp);
/**
* Rewrite a URI, using SRV (service location) records for its
* protocol if available. If no SRV records are published, the
* existing URI is handed to the responder.
*
* @param uri URI to rewrite
* @param resp responder to call with result
*/
void rewriteURI(const std::string &uri,
UriRewriteResponder *resp);
/**
* Start a custom search.
*
* @param query query to make
* @param type type of query to perform
* @param resp responder to call with result
*/
void search(const std::string &query, LLResType type,
QueryResponder *resp);
/**
* Process any outstanding queries. This method takes an optional
* timeout parameter (specified in microseconds). If provided, it
* will block the calling thread for that length of time to await
* possible responses. A timeout of zero will return immediately
* if there are no responses or timeouts to process.
*
* @param timeoutUsecs number of microseconds to block before timing out
* @return whether any responses were processed
*/
bool process(U64 timeoutUsecs = 0);
/**
* Process all outstanding queries, blocking the calling thread
* until all have either been responded to or timed out.
*
* @return whether any responses were processed
*/
bool processAll();
/**
* Expand a DNS-encoded compressed string into a normal string.
*
* @param encoded the encoded name (null-terminated)
* @param abuf the response buffer in which the string is embedded
* @param alen the length of the response buffer
* @param s the string into which to place the result
* @return ARES_SUCCESS on success, otherwise an error indicator
*/
static int expandName(const char *encoded, const char *abuf, size_t alen,
std::string &s) {
size_t ignore;
return expandName(encoded, abuf, alen, s, ignore);
}
static int expandName(const char *encoded, const char *abuf, size_t alen,
std::string &s, size_t &enclen);
/**
* Return a string describing an error code.
*/
static const char *strerror(int code);
bool isInitialized(void) { return mInitSuccess; }
protected:
ares_channel chan_;
bool mInitSuccess;
// boost::scoped_ptr would actually fit the requirement better, but it
// can't handle incomplete types as boost::shared_ptr can.
boost::shared_ptr<LLAresListener> mListener;
};
/**
* An ordered collection of DNS resource records.
*/
typedef std::vector<LLPointer<LLDnsRecord> > dns_rrs_t;
/**
* @class LLQueryResponder
* @brief Base class for friendly handling of DNS query responses.
*
* This class parses a DNS response and represents it in a friendly
* manner.
*
* @see LLDnsRecord
* @see LLARecord
* @see LLNsRecord
* @see LLCnameRecord
* @see LLPtrRecord
* @see LLAaaaRecord
* @see LLSrvRecord
*/
class LLQueryResponder : public LLAres::QueryResponder
{
protected:
int mResult;
std::string mQuery;
LLResType mType;
dns_rrs_t mAnswers;
dns_rrs_t mAuthorities;
dns_rrs_t mAdditional;
/**
* Parse a single RR.
*/
int parseRR(const char *buf, size_t len, const char *&pos,
LLPointer<LLDnsRecord> &r);
/**
* Parse one section of a response.
*/
int parseSection(const char *buf, size_t len,
size_t count, const char *& pos, dns_rrs_t &rrs);
void queryResult(const char *buf, size_t len);
virtual void querySuccess();
public:
LLQueryResponder();
/**
* Indicate whether the response could be parsed successfully.
*/
bool valid() const { return mResult == ARES_SUCCESS; }
/**
* The more detailed result of parsing the response.
*/
int result() const { return mResult; }
/**
* Return the query embedded in the response.
*/
const std::string &query() const { return mQuery; }
/**
* Return the contents of the "answers" section of the response.
*/
const dns_rrs_t &answers() const { return mAnswers; }
/**
* Return the contents of the "authorities" section of the
* response.
*/
const dns_rrs_t &authorities() const { return mAuthorities; }
/**
* Return the contents of the "additional records" section of the
* response.
*/
const dns_rrs_t &additional() const { return mAdditional; }
};
/**
* @class LLAres::SrvResponder
* @brief Class for handling SRV query responses.
*/
class LLAres::SrvResponder : public LLQueryResponder
{
public:
friend void LLAres::getSrvRecords(const std::string &name,
SrvResponder *resp);
void querySuccess();
void queryError(int code);
virtual void srvResult(const dns_rrs_t &ents);
virtual void srvError(int code);
};
/**
* @class LLAres::UriRewriteResponder
* @brief Class for handling URI rewrites based on SRV records.
*/
class LLAres::UriRewriteResponder : public LLQueryResponder
{
protected:
LLURI mUri;
public:
friend void LLAres::rewriteURI(const std::string &uri,
UriRewriteResponder *resp);
void querySuccess();
void queryError(int code);
virtual void rewriteResult(const std::vector<std::string> &uris);
};
/**
* Singleton responder.
*/
extern LLAres *gAres;
/**
* Set up the singleton responder. It's safe to call this more than
* once from within a single thread, but this function is not
* thread safe.
*/
extern LLAres *ll_init_ares();
extern void ll_cleanup_ares();
#endif // LL_LLARES_H

View File

@ -1,104 +0,0 @@
/**
* @file llareslistener.cpp
* @author Nat Goodspeed
* @date 2009-03-18
* @brief Implementation for llareslistener.
*
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
// Precompiled header
#include "linden_common.h"
// associated header
#include "llareslistener.h"
// STL headers
// std headers
// external library headers
// other Linden headers
#include "llares.h"
#include "llerror.h"
#include "llevents.h"
#include "llsdutil.h"
LLAresListener::LLAresListener(LLAres* llares):
LLEventAPI("LLAres",
"LLAres listener to request DNS operations"),
mAres(llares)
{
// add() every method we want to be able to invoke via this event API.
// Optional last parameter validates expected LLSD request structure.
add("rewriteURI",
"Given [\"uri\"], return on [\"reply\"] an array of alternative URIs.\n"
"On failure, returns an array containing only the original URI, so\n"
"failure case can be processed like success case.",
&LLAresListener::rewriteURI,
LLSD().with("uri", LLSD()).with("reply", LLSD()));
}
/// This UriRewriteResponder subclass packages returned URIs as an LLSD
/// array to send back to the requester.
class UriRewriteResponder: public LLAres::UriRewriteResponder
{
public:
/**
* Specify the request, containing the event pump name on which to send
* the reply.
*/
UriRewriteResponder(const LLSD& request):
mReqID(request),
mPumpName(request["reply"])
{}
/// Called by base class with results. This is called in both the
/// success and error cases. On error, the calling logic passes the
/// original URI.
virtual void rewriteResult(const std::vector<std::string>& uris)
{
LLSD result;
for (std::vector<std::string>::const_iterator ui(uris.begin()), uend(uris.end());
ui != uend; ++ui)
{
result.append(*ui);
}
// This call knows enough to avoid trying to insert a map key into an
// LLSD array. It's there so that if, for any reason, we ever decide
// to change the response from array to map, it will Just Start Working.
mReqID.stamp(result);
LLEventPumps::instance().obtain(mPumpName).post(result);
}
private:
LLReqID mReqID;
const std::string mPumpName;
};
void LLAresListener::rewriteURI(const LLSD& data)
{
if (mAres)
{
mAres->rewriteURI(data["uri"], new UriRewriteResponder(data));
}
else
{
LL_INFOS() << "LLAresListener::rewriteURI requested without Ares present. Ignoring: " << data << LL_ENDL;
}
}

View File

@ -1,53 +0,0 @@
/**
* @file llareslistener.h
* @author Nat Goodspeed
* @date 2009-03-18
* @brief LLEventPump API for LLAres. This header doesn't actually define the
* API; the API is defined by the pump name on which this class
* listens, and by the expected content of LLSD it receives.
*
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#if ! defined(LL_LLARESLISTENER_H)
#define LL_LLARESLISTENER_H
#include "lleventapi.h"
class LLAres;
class LLSD;
/// Listen on an LLEventPump with specified name for LLAres request events.
class LLAresListener: public LLEventAPI
{
public:
/// Bind the LLAres instance to use (e.g. gAres)
LLAresListener(LLAres* llares);
private:
/// command["op"] == "rewriteURI"
void rewriteURI(const LLSD& data);
LLAres* mAres;
};
#endif /* ! defined(LL_LLARESLISTENER_H) */

View File

@ -306,7 +306,7 @@ LLAssetStorage::LLAssetStorage(LLMessageSystem *msg, LLXferManager *xfer, LLVFS
LLAssetStorage::LLAssetStorage(LLMessageSystem *msg, LLXferManager *xfer,
LLVFS *vfs, LLVFS *static_vfs)
{
_init(msg, xfer, vfs, static_vfs, LLHost::invalid);
_init(msg, xfer, vfs, static_vfs, LLHost());
}
@ -1629,3 +1629,4 @@ void LLAssetStorage::markAssetToxic( const LLUUID& uuid )
}
}

View File

@ -27,7 +27,6 @@
#ifndef LL_LLASSETSTORAGE_H
#define LL_LLASSETSTORAGE_H
#include <string>
#include "lluuid.h"

View File

@ -30,12 +30,20 @@
#include "llcachename.h" // we wrap this system
#include "llframetimer.h"
#include "llhttpclient.h"
#include "llsd.h"
#include "llsdserialize.h"
#include "httpresponse.h"
#include "llhttpsdhandler.h"
#include <boost/tokenizer.hpp>
#include "httpcommon.h"
#include "httprequest.h"
#include "httpheaders.h"
#include "httpoptions.h"
#include "llcoros.h"
#include "lleventcoro.h"
#include "llcorehttputil.h"
#include <map>
#include <set>
@ -90,6 +98,12 @@ namespace LLAvatarNameCache
// Time-to-live for a temp cache entry.
const F64 TEMP_CACHE_ENTRY_LIFETIME = 60.0;
LLCore::HttpRequest::ptr_t sHttpRequest;
LLCore::HttpHeaders::ptr_t sHttpHeaders;
LLCore::HttpOptions::ptr_t sHttpOptions;
LLCore::HttpRequest::policy_t sHttpPolicy;
LLCore::HttpRequest::priority_t sHttpPriority;
//-----------------------------------------------------------------------
// Internal methods
//-----------------------------------------------------------------------
@ -121,7 +135,12 @@ namespace LLAvatarNameCache
// Erase expired names from cache
void eraseUnrefreshed();
bool expirationFromCacheControl(const LLSD& headers, F64 *expires);
bool expirationFromCacheControl(const LLSD& headers, F64 *expires);
// This is a coroutine.
void requestAvatarNameCache_(std::string url, std::vector<LLUUID> agentIds);
void handleAvNameCacheSuccess(const LLSD &data, const LLSD &httpResult);
}
/* Sample response:
@ -163,94 +182,117 @@ namespace LLAvatarNameCache
</llsd>
*/
class LLAvatarNameResponder : public LLHTTPClient::Responder
// Coroutine for sending and processing avatar name cache requests.
// Do not call directly. See documentation in lleventcoro.h and llcoro.h for
// further explanation.
void LLAvatarNameCache::requestAvatarNameCache_(std::string url, std::vector<LLUUID> agentIds)
{
LOG_CLASS(LLAvatarNameResponder);
private:
// need to store agent ids that are part of this request in case of
// an error, so we can flag them as unavailable
std::vector<LLUUID> mAgentIDs;
LL_DEBUGS("AvNameCache") << "Entering coroutine " << LLCoros::instance().getName()
<< " with url '" << url << "', requesting " << agentIds.size() << " Agent Ids" << LL_ENDL;
public:
LLAvatarNameResponder(const std::vector<LLUUID>& agent_ids)
: mAgentIDs(agent_ids)
{ }
protected:
/*virtual*/ void httpSuccess()
{
const LLSD& content = getContent();
if (!content.isMap())
{
failureResult(HTTP_INTERNAL_ERROR, "Malformed response contents", content);
return;
}
// Pull expiration out of headers if available
F64 expires = LLAvatarNameCache::nameExpirationFromHeaders(getResponseHeaders());
F64 now = LLFrameTimer::getTotalSeconds();
try
{
bool success = true;
const LLSD& agents = content["agents"];
LLSD::array_const_iterator it = agents.beginArray();
for ( ; it != agents.endArray(); ++it)
{
const LLSD& row = *it;
LLUUID agent_id = row["id"].asUUID();
LLCoreHttpUtil::HttpCoroutineAdapter httpAdapter("NameCache", LLAvatarNameCache::sHttpPolicy);
LLSD results = httpAdapter.getAndSuspend(sHttpRequest, url);
LLSD httpResults;
LLAvatarName av_name;
av_name.fromLLSD(row);
LL_DEBUGS() << results << LL_ENDL;
// Use expiration time from header
av_name.mExpires = expires;
LL_DEBUGS("AvNameCache") << "LLAvatarNameResponder::result for " << agent_id << LL_ENDL;
av_name.dump();
// cache it and fire signals
LLAvatarNameCache::processName(agent_id, av_name);
}
// Same logic as error response case
const LLSD& unresolved_agents = content["bad_ids"];
S32 num_unresolved = unresolved_agents.size();
if (num_unresolved > 0)
{
LL_WARNS("AvNameCache") << "LLAvatarNameResponder::result " << num_unresolved << " unresolved ids; "
<< "expires in " << expires - now << " seconds"
<< LL_ENDL;
it = unresolved_agents.beginArray();
for ( ; it != unresolved_agents.endArray(); ++it)
{
const LLUUID& agent_id = *it;
LL_WARNS("AvNameCache") << "LLAvatarNameResponder::result "
<< "failed id " << agent_id
<< LL_ENDL;
if (!results.isMap())
{
LL_WARNS("AvNameCache") << " Invalid result returned from LLCoreHttpUtil::HttpCoroHandler." << LL_ENDL;
success = false;
}
else
{
httpResults = results["http_result"];
success = httpResults["success"].asBoolean();
if (!success)
{
LL_WARNS("AvNameCache") << "Error result from LLCoreHttpUtil::HttpCoroHandler. Code "
<< httpResults["status"] << ": '" << httpResults["message"] << "'" << LL_ENDL;
}
}
if (!success)
{ // on any sort of failure add dummy records for any agent IDs
// in this request that we do not have cached already
std::vector<LLUUID>::const_iterator it = agentIds.begin();
for ( ; it != agentIds.end(); ++it)
{
const LLUUID& agent_id = *it;
LLAvatarNameCache::handleAgentError(agent_id);
}
}
LL_DEBUGS("AvNameCache") << "LLAvatarNameResponder::result "
<< LLAvatarNameCache::sCache.size() << " cached names"
<< LL_ENDL;
}
return;
}
LLAvatarNameCache::handleAvNameCacheSuccess(results, httpResults);
}
catch (std::exception e)
{
LL_WARNS() << "Caught exception '" << e.what() << "'" << LL_ENDL;
}
catch (...)
{
LL_WARNS() << "Caught unknown exception." << LL_ENDL;
}
}
void LLAvatarNameCache::handleAvNameCacheSuccess(const LLSD &data, const LLSD &httpResult)
{
LLSD headers = httpResult["headers"];
// Pull expiration out of headers if available
F64 expires = LLAvatarNameCache::nameExpirationFromHeaders(headers);
F64 now = LLFrameTimer::getTotalSeconds();
const LLSD& agents = data["agents"];
LLSD::array_const_iterator it = agents.beginArray();
for (; it != agents.endArray(); ++it)
{
const LLSD& row = *it;
LLUUID agent_id = row["id"].asUUID();
LLAvatarName av_name;
av_name.fromLLSD(row);
// Use expiration time from header
av_name.mExpires = expires;
LL_DEBUGS("AvNameCache") << "LLAvatarNameResponder::result for " << agent_id << LL_ENDL;
av_name.dump();
// cache it and fire signals
LLAvatarNameCache::processName(agent_id, av_name);
}
/*virtual*/ void httpFailure()
{
// If there's an error, it might be caused by PeopleApi,
// or when loading textures on startup and using a very slow
// network, this query may time out.
// What we should do depends on whether or not we have a cached name
LL_WARNS("AvNameCache") << dumpResponse() << LL_ENDL;
// Same logic as error response case
const LLSD& unresolved_agents = data["bad_ids"];
S32 num_unresolved = unresolved_agents.size();
if (num_unresolved > 0)
{
LL_WARNS("AvNameCache") << "LLAvatarNameResponder::result " << num_unresolved << " unresolved ids; "
<< "expires in " << expires - now << " seconds"
<< LL_ENDL;
it = unresolved_agents.beginArray();
for (; it != unresolved_agents.endArray(); ++it)
{
const LLUUID& agent_id = *it;
// Add dummy records for any agent IDs in this request that we do not have cached already
std::vector<LLUUID>::const_iterator it = mAgentIDs.begin();
for ( ; it != mAgentIDs.end(); ++it)
{
const LLUUID& agent_id = *it;
LLAvatarNameCache::handleAgentError(agent_id);
}
}
};
LL_WARNS("AvNameCache") << "LLAvatarNameResponder::result "
<< "failed id " << agent_id
<< LL_ENDL;
LLAvatarNameCache::handleAgentError(agent_id);
}
}
LL_DEBUGS("AvNameCache") << "LLAvatarNameResponder::result "
<< LLAvatarNameCache::sCache.size() << " cached names"
<< LL_ENDL;
}
// Provide some fallback for agents that return errors
void LLAvatarNameCache::handleAgentError(const LLUUID& agent_id)
@ -353,10 +395,15 @@ void LLAvatarNameCache::requestNamesViaCapability()
}
}
if (!url.empty())
{
LL_INFOS("AvNameCache") << "LLAvatarNameCache::requestNamesViaCapability getting " << ids << " ids" << LL_ENDL;
LLHTTPClient::get(url, new LLAvatarNameResponder(agent_ids));
if (!url.empty())
{
LL_DEBUGS("AvNameCache") << "LLAvatarNameCache::requestNamesViaCapability requested " << ids << " ids" << LL_ENDL;
std::string coroname =
LLCoros::instance().launch("LLAvatarNameCache::requestAvatarNameCache_",
boost::bind(&LLAvatarNameCache::requestAvatarNameCache_, url, agent_ids));
LL_DEBUGS("AvNameCache") << coroname << " with url '" << url << "', agent_ids.size()=" << agent_ids.size() << LL_ENDL;
}
}
@ -419,11 +466,20 @@ void LLAvatarNameCache::initClass(bool running, bool usePeopleAPI)
{
sRunning = running;
sUsePeopleAPI = usePeopleAPI;
sHttpRequest = LLCore::HttpRequest::ptr_t(new LLCore::HttpRequest());
sHttpHeaders = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders());
sHttpOptions = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions());
sHttpPolicy = LLCore::HttpRequest::DEFAULT_POLICY_ID;
sHttpPriority = 0;
}
void LLAvatarNameCache::cleanupClass()
{
sCache.clear();
sHttpRequest.reset();
sHttpHeaders.reset();
sHttpOptions.reset();
sCache.clear();
}
bool LLAvatarNameCache::importFile(std::istream& istr)
@ -698,6 +754,50 @@ void LLAvatarNameCache::insert(const LLUUID& agent_id, const LLAvatarName& av_na
sCache[agent_id] = av_name;
}
#if 0
F64 LLAvatarNameCache::nameExpirationFromHeaders(LLCore::HttpHeaders *headers)
{
F64 expires = 0.0;
if (expirationFromCacheControl(headers, &expires))
{
return expires;
}
else
{
// With no expiration info, default to an hour
const F64 DEFAULT_EXPIRES = 60.0 * 60.0;
F64 now = LLFrameTimer::getTotalSeconds();
return now + DEFAULT_EXPIRES;
}
}
bool LLAvatarNameCache::expirationFromCacheControl(LLCore::HttpHeaders *headers, F64 *expires)
{
bool fromCacheControl = false;
F64 now = LLFrameTimer::getTotalSeconds();
// Allow the header to override the default
const std::string *cache_control;
cache_control = headers->find(HTTP_IN_HEADER_CACHE_CONTROL);
if (cache_control && !cache_control->empty())
{
S32 max_age = 0;
if (max_age_from_cache_control(*cache_control, &max_age))
{
*expires = now + (F64)max_age;
fromCacheControl = true;
}
}
LL_DEBUGS("AvNameCache")
<< ( fromCacheControl ? "expires based on cache control " : "default expiration " )
<< "in " << *expires - now << " seconds"
<< LL_ENDL;
return fromCacheControl;
}
#else
F64 LLAvatarNameCache::nameExpirationFromHeaders(const LLSD& headers)
{
F64 expires = 0.0;
@ -742,7 +842,7 @@ bool LLAvatarNameCache::expirationFromCacheControl(const LLSD& headers, F64 *exp
return fromCacheControl;
}
#endif
void LLAvatarNameCache::addUseDisplayNamesCallback(const use_display_name_signal_t::slot_type& cb)
{

View File

@ -29,7 +29,6 @@
#define LLAVATARNAMECACHE_H
#include "llavatarname.h" // for convenience
#include <boost/signals2.hpp>
class LLSD;
@ -49,7 +48,7 @@ namespace LLAvatarNameCache
bool importFile(std::istream& istr);
void exportFile(std::ostream& ostr);
// On the viewer, usually a simulator capabilitity.
// On the viewer, usually a simulator capabilities.
// If empty, name cache will fall back to using legacy name lookup system.
void setNameLookupURL(const std::string& name_lookup_url);
@ -90,7 +89,7 @@ namespace LLAvatarNameCache
// Compute name expiration time from HTTP Cache-Control header,
// or return default value, in seconds from epoch.
F64 nameExpirationFromHeaders(const LLSD& headers);
F64 nameExpirationFromHeaders(const LLSD& headers);
void addUseDisplayNamesCallback(const use_display_name_signal_t::slot_type& cb);
}

View File

@ -259,7 +259,7 @@ LLCacheName::~LLCacheName()
}
LLCacheName::Impl::Impl(LLMessageSystem* msg)
: mMsg(msg), mUpstreamHost(LLHost::invalid)
: mMsg(msg), mUpstreamHost(LLHost())
{
mMsg->setHandlerFuncFast(
_PREHASH_UUIDNameRequest, handleUUIDNameRequest, (void**)this);

View File

@ -0,0 +1,405 @@
/**
* @file LLCoprocedurePool.cpp
* @author Rider Linden
* @brief Singleton class for managing asset uploads to the sim.
*
* $LicenseInfo:firstyear=2015&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2015, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "linden_common.h"
#include "llcoproceduremanager.h"
#include <boost/assign.hpp>
//=========================================================================
// Map of pool sizes for known pools
// *TODO$: When C++11 this can be initialized here as follows:
// = {{"AIS", 25}, {"Upload", 1}}
static std::map<std::string, U32> DefaultPoolSizes =
boost::assign::map_list_of
(std::string("Upload"), 1)
(std::string("AIS"), 25);
#define DEFAULT_POOL_SIZE 5
//=========================================================================
class LLCoprocedurePool: private boost::noncopyable
{
public:
typedef LLCoprocedureManager::CoProcedure_t CoProcedure_t;
LLCoprocedurePool(const std::string &name, size_t size);
virtual ~LLCoprocedurePool();
/// Places the coprocedure on the queue for processing.
///
/// @param name Is used for debugging and should identify this coroutine.
/// @param proc Is a bound function to be executed
///
/// @return This method returns a UUID that can be used later to cancel execution.
LLUUID enqueueCoprocedure(const std::string &name, CoProcedure_t proc);
/// Cancel a coprocedure. If the coprocedure is already being actively executed
/// this method calls cancelSuspendedOperation() on the associated HttpAdapter
/// If it has not yet been dequeued it is simply removed from the queue.
bool cancelCoprocedure(const LLUUID &id);
/// Requests a shutdown of the upload manager. Passing 'true' will perform
/// an immediate kill on the upload coroutine.
void shutdown(bool hardShutdown = false);
/// Returns the number of coprocedures in the queue awaiting processing.
///
inline size_t countPending() const
{
return mPendingCoprocs.size();
}
/// Returns the number of coprocedures actively being processed.
///
inline size_t countActive() const
{
return mActiveCoprocs.size();
}
/// Returns the total number of coprocedures either queued or in active processing.
///
inline size_t count() const
{
return countPending() + countActive();
}
private:
struct QueuedCoproc
{
typedef boost::shared_ptr<QueuedCoproc> ptr_t;
QueuedCoproc(const std::string &name, const LLUUID &id, CoProcedure_t proc) :
mName(name),
mId(id),
mProc(proc)
{}
std::string mName;
LLUUID mId;
CoProcedure_t mProc;
};
// we use a deque here rather than std::queue since we want to be able to
// iterate through the queue and potentially erase an entry from the middle.
typedef std::deque<QueuedCoproc::ptr_t> CoprocQueue_t;
typedef std::map<LLUUID, LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t> ActiveCoproc_t;
std::string mPoolName;
size_t mPoolSize;
CoprocQueue_t mPendingCoprocs;
ActiveCoproc_t mActiveCoprocs;
bool mShutdown;
LLEventStream mWakeupTrigger;
typedef std::map<std::string, LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t> CoroAdapterMap_t;
LLCore::HttpRequest::policy_t mHTTPPolicy;
CoroAdapterMap_t mCoroMapping;
void coprocedureInvokerCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter);
};
//=========================================================================
LLCoprocedureManager::LLCoprocedureManager()
{
}
LLCoprocedureManager::~LLCoprocedureManager()
{
}
LLCoprocedureManager::poolPtr_t LLCoprocedureManager::initializePool(const std::string &poolName)
{
// Attempt to look up a pool size in the configuration. If found use that
std::string keyName = "PoolSize" + poolName;
int size = 0;
if (poolName.empty())
LL_ERRS("CoprocedureManager") << "Poolname must not be empty" << LL_ENDL;
if (mPropertyQueryFn && !mPropertyQueryFn.empty())
{
size = mPropertyQueryFn(keyName);
}
if (size == 0)
{ // if not found grab the know default... if there is no known
// default use a reasonable number like 5.
std::map<std::string, U32>::iterator it = DefaultPoolSizes.find(poolName);
if (it == DefaultPoolSizes.end())
size = DEFAULT_POOL_SIZE;
else
size = (*it).second;
if (mPropertyDefineFn && !mPropertyDefineFn.empty())
mPropertyDefineFn(keyName, size, "Coroutine Pool size for " + poolName);
LL_WARNS() << "LLCoprocedureManager: No setting for \"" << keyName << "\" setting pool size to default of " << size << LL_ENDL;
}
poolPtr_t pool(new LLCoprocedurePool(poolName, size));
mPoolMap.insert(poolMap_t::value_type(poolName, pool));
if (!pool)
LL_ERRS("CoprocedureManager") << "Unable to create pool named \"" << poolName << "\" FATAL!" << LL_ENDL;
return pool;
}
//-------------------------------------------------------------------------
LLUUID LLCoprocedureManager::enqueueCoprocedure(const std::string &pool, const std::string &name, CoProcedure_t proc)
{
// Attempt to find the pool and enqueue the procedure. If the pool does
// not exist, create it.
poolPtr_t targetPool;
poolMap_t::iterator it = mPoolMap.find(pool);
if (it == mPoolMap.end())
{
targetPool = initializePool(pool);
}
else
{
targetPool = (*it).second;
}
return targetPool->enqueueCoprocedure(name, proc);
}
void LLCoprocedureManager::cancelCoprocedure(const LLUUID &id)
{
for (poolMap_t::const_iterator it = mPoolMap.begin(); it != mPoolMap.end(); ++it)
{
if ((*it).second->cancelCoprocedure(id))
return;
}
LL_INFOS() << "Coprocedure not found." << LL_ENDL;
}
void LLCoprocedureManager::shutdown(bool hardShutdown)
{
for (poolMap_t::const_iterator it = mPoolMap.begin(); it != mPoolMap.end(); ++it)
{
(*it).second->shutdown(hardShutdown);
}
mPoolMap.clear();
}
void LLCoprocedureManager::setPropertyMethods(SettingQuery_t queryfn, SettingUpdate_t updatefn)
{
mPropertyQueryFn = queryfn;
mPropertyDefineFn = updatefn;
}
//-------------------------------------------------------------------------
size_t LLCoprocedureManager::countPending() const
{
size_t count = 0;
for (poolMap_t::const_iterator it = mPoolMap.begin(); it != mPoolMap.end(); ++it)
{
count += (*it).second->countPending();
}
return count;
}
size_t LLCoprocedureManager::countPending(const std::string &pool) const
{
poolMap_t::const_iterator it = mPoolMap.find(pool);
if (it == mPoolMap.end())
return 0;
return (*it).second->countPending();
}
size_t LLCoprocedureManager::countActive() const
{
size_t count = 0;
for (poolMap_t::const_iterator it = mPoolMap.begin(); it != mPoolMap.end(); ++it)
{
count += (*it).second->countActive();
}
return count;
}
size_t LLCoprocedureManager::countActive(const std::string &pool) const
{
poolMap_t::const_iterator it = mPoolMap.find(pool);
if (it == mPoolMap.end())
return 0;
return (*it).second->countActive();
}
size_t LLCoprocedureManager::count() const
{
size_t count = 0;
for (poolMap_t::const_iterator it = mPoolMap.begin(); it != mPoolMap.end(); ++it)
{
count += (*it).second->count();
}
return count;
}
size_t LLCoprocedureManager::count(const std::string &pool) const
{
poolMap_t::const_iterator it = mPoolMap.find(pool);
if (it == mPoolMap.end())
return 0;
return (*it).second->count();
}
//=========================================================================
LLCoprocedurePool::LLCoprocedurePool(const std::string &poolName, size_t size):
mPoolName(poolName),
mPoolSize(size),
mPendingCoprocs(),
mShutdown(false),
mWakeupTrigger("CoprocedurePool" + poolName, true),
mCoroMapping(),
mHTTPPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID)
{
for (size_t count = 0; count < mPoolSize; ++count)
{
LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter( mPoolName + "Adapter", mHTTPPolicy));
std::string pooledCoro = LLCoros::instance().launch("LLCoprocedurePool("+mPoolName+")::coprocedureInvokerCoro",
boost::bind(&LLCoprocedurePool::coprocedureInvokerCoro, this, httpAdapter));
mCoroMapping.insert(CoroAdapterMap_t::value_type(pooledCoro, httpAdapter));
}
LL_INFOS() << "Created coprocedure pool named \"" << mPoolName << "\" with " << size << " items." << LL_ENDL;
mWakeupTrigger.post(LLSD());
}
LLCoprocedurePool::~LLCoprocedurePool()
{
shutdown();
}
//-------------------------------------------------------------------------
void LLCoprocedurePool::shutdown(bool hardShutdown)
{
CoroAdapterMap_t::iterator it;
for (it = mCoroMapping.begin(); it != mCoroMapping.end(); ++it)
{
if (hardShutdown)
{
LLCoros::instance().kill((*it).first);
}
if ((*it).second)
{
(*it).second->cancelSuspendedOperation();
}
}
mShutdown = true;
mCoroMapping.clear();
mPendingCoprocs.clear();
}
//-------------------------------------------------------------------------
LLUUID LLCoprocedurePool::enqueueCoprocedure(const std::string &name, LLCoprocedurePool::CoProcedure_t proc)
{
LLUUID id(LLUUID::generateNewID());
mPendingCoprocs.push_back(QueuedCoproc::ptr_t(new QueuedCoproc(name, id, proc)));
LL_INFOS() << "Coprocedure(" << name << ") enqueued with id=" << id.asString() << " in pool \"" << mPoolName << "\"" << LL_ENDL;
mWakeupTrigger.post(LLSD());
return id;
}
bool LLCoprocedurePool::cancelCoprocedure(const LLUUID &id)
{
// first check the active coroutines. If there, remove it and return.
ActiveCoproc_t::iterator itActive = mActiveCoprocs.find(id);
if (itActive != mActiveCoprocs.end())
{
LL_INFOS() << "Found and canceling active coprocedure with id=" << id.asString() << " in pool \"" << mPoolName << "\"" << LL_ENDL;
(*itActive).second->cancelSuspendedOperation();
mActiveCoprocs.erase(itActive);
return true;
}
for (CoprocQueue_t::iterator it = mPendingCoprocs.begin(); it != mPendingCoprocs.end(); ++it)
{
if ((*it)->mId == id)
{
LL_INFOS() << "Found and removing queued coroutine(" << (*it)->mName << ") with Id=" << id.asString() << " in pool \"" << mPoolName << "\"" << LL_ENDL;
mPendingCoprocs.erase(it);
return true;
}
}
LL_INFOS() << "Coprocedure with Id=" << id.asString() << " was not found in pool \"" << mPoolName << "\"" << LL_ENDL;
return false;
}
//-------------------------------------------------------------------------
void LLCoprocedurePool::coprocedureInvokerCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter)
{
LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
while (!mShutdown)
{
llcoro::suspendUntilEventOn(mWakeupTrigger);
if (mShutdown)
break;
while (!mPendingCoprocs.empty())
{
QueuedCoproc::ptr_t coproc = mPendingCoprocs.front();
mPendingCoprocs.pop_front();
ActiveCoproc_t::iterator itActive = mActiveCoprocs.insert(ActiveCoproc_t::value_type(coproc->mId, httpAdapter)).first;
LL_INFOS() << "Dequeued and invoking coprocedure(" << coproc->mName << ") with id=" << coproc->mId.asString() << " in pool \"" << mPoolName << "\"" << LL_ENDL;
try
{
coproc->mProc(httpAdapter, coproc->mId);
}
catch (std::exception &e)
{
LL_WARNS() << "Coprocedure(" << coproc->mName << ") id=" << coproc->mId.asString() <<
" threw an exception! Message=\"" << e.what() << "\"" << LL_ENDL;
}
catch (...)
{
LL_WARNS() << "A non std::exception was thrown from " << coproc->mName << " with id=" << coproc->mId << "." << " in pool \"" << mPoolName << "\"" << LL_ENDL;
}
LL_INFOS() << "Finished coprocedure(" << coproc->mName << ")" << " in pool \"" << mPoolName << "\"" << LL_ENDL;
mActiveCoprocs.erase(itActive);
}
}
}

View File

@ -0,0 +1,98 @@
/**
* @file llcoproceduremanager.h
* @author Rider Linden
* @brief Singleton class for managing asset uploads to the sim.
*
* $LicenseInfo:firstyear=2015&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2015, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LL_COPROCEDURE_MANAGER_H
#define LL_COPROCEDURE_MANAGER_H
#include "lleventcoro.h"
#include "llcoros.h"
#include "llcorehttputil.h"
#include "lluuid.h"
class LLCoprocedurePool;
class LLCoprocedureManager : public LLSingleton < LLCoprocedureManager >
{
friend class LLSingleton < LLCoprocedureManager > ;
public:
typedef boost::function<U32(const std::string &)> SettingQuery_t;
typedef boost::function<void(const std::string &, U32, const std::string &)> SettingUpdate_t;
typedef boost::function<void(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &, const LLUUID &id)> CoProcedure_t;
LLCoprocedureManager();
virtual ~LLCoprocedureManager();
/// Places the coprocedure on the queue for processing.
///
/// @param name Is used for debugging and should identify this coroutine.
/// @param proc Is a bound function to be executed
///
/// @return This method returns a UUID that can be used later to cancel execution.
LLUUID enqueueCoprocedure(const std::string &pool, const std::string &name, CoProcedure_t proc);
/// Cancel a coprocedure. If the coprocedure is already being actively executed
/// this method calls cancelYieldingOperation() on the associated HttpAdapter
/// If it has not yet been dequeued it is simply removed from the queue.
void cancelCoprocedure(const LLUUID &id);
/// Requests a shutdown of the upload manager. Passing 'true' will perform
/// an immediate kill on the upload coroutine.
void shutdown(bool hardShutdown = false);
void setPropertyMethods(SettingQuery_t queryfn, SettingUpdate_t updatefn);
/// Returns the number of coprocedures in the queue awaiting processing.
///
size_t countPending() const;
size_t countPending(const std::string &pool) const;
/// Returns the number of coprocedures actively being processed.
///
size_t countActive() const;
size_t countActive(const std::string &pool) const;
/// Returns the total number of coprocedures either queued or in active processing.
///
size_t count() const;
size_t count(const std::string &pool) const;
private:
typedef boost::shared_ptr<LLCoprocedurePool> poolPtr_t;
typedef std::map<std::string, poolPtr_t> poolMap_t;
poolMap_t mPoolMap;
poolPtr_t initializePool(const std::string &poolName);
SettingQuery_t mPropertyQueryFn;
SettingUpdate_t mPropertyDefineFn;
};
#endif

File diff suppressed because it is too large Load Diff

View File

@ -36,9 +36,15 @@
#include "httpheaders.h"
#include "httpoptions.h"
#include "httphandler.h"
#include "llhttpconstants.h" // *TODO: move to llcorehttp
#include "bufferarray.h"
#include "bufferstream.h"
#include "llsd.h"
#include "llevents.h"
#include "llcoros.h"
#include "lleventcoro.h"
#include "llassettype.h"
#include "lluuid.h"
///
/// The base llcorehttp library implements many HTTP idioms
@ -51,6 +57,7 @@
///
namespace LLCoreHttpUtil
{
extern const F32 HTTP_REQUEST_EXPIRY_SECS;
/// Attempt to convert a response object's contents to LLSD.
/// It is expected that the response body will be of non-zero
@ -101,15 +108,573 @@ std::string responseToString(LLCore::HttpResponse * response);
/// a now-useless HttpHandler object.
///
LLCore::HttpHandle requestPostWithLLSD(LLCore::HttpRequest * request,
LLCore::HttpRequest::policy_t policy_id,
LLCore::HttpRequest::priority_t priority,
const std::string & url,
const LLSD & body,
LLCore::HttpOptions * options,
LLCore::HttpHeaders * headers,
LLCore::HttpHandler * handler);
LLCore::HttpRequest::policy_t policy_id,
LLCore::HttpRequest::priority_t priority,
const std::string & url,
const LLSD & body,
const LLCore::HttpOptions::ptr_t &options,
const LLCore::HttpHeaders::ptr_t &headers,
const LLCore::HttpHandler::ptr_t &handler);
inline LLCore::HttpHandle requestPostWithLLSD(LLCore::HttpRequest::ptr_t & request,
LLCore::HttpRequest::policy_t policy_id,
LLCore::HttpRequest::priority_t priority,
const std::string & url,
const LLSD & body,
const LLCore::HttpOptions::ptr_t & options,
const LLCore::HttpHeaders::ptr_t & headers,
const LLCore::HttpHandler::ptr_t & handler)
{
return requestPostWithLLSD(request.get(), policy_id, priority,
url, body, options, headers, handler);
}
inline LLCore::HttpHandle requestPostWithLLSD(LLCore::HttpRequest::ptr_t & request,
LLCore::HttpRequest::policy_t policy_id,
LLCore::HttpRequest::priority_t priority,
const std::string & url,
const LLSD & body,
const LLCore::HttpHandler::ptr_t &handler)
{
LLCore::HttpOptions::ptr_t options;
LLCore::HttpHeaders::ptr_t headers;
return requestPostWithLLSD(request.get(), policy_id, priority,
url, body, options, headers, handler);
}
/// Issue a standard HttpRequest::requestPut() call but using
/// and LLSD object as the request body. Conventions are the
/// same as with that method. Caller is expected to provide
/// an HttpHeaders object with a correct 'Content-Type:' header.
/// One will not be provided by this call.
///
/// @return If request is successfully issued, the
/// HttpHandle representing the request.
/// On error, LLCORE_HTTP_HANDLE_INVALID
/// is returned and caller can fetch detailed
/// status with the getStatus() method on the
/// request object. In case of error, no
/// request is queued and caller may need to
/// perform additional cleanup such as freeing
/// a now-useless HttpHandler object.
///
LLCore::HttpHandle requestPutWithLLSD(LLCore::HttpRequest * request,
LLCore::HttpRequest::policy_t policy_id,
LLCore::HttpRequest::priority_t priority,
const std::string & url,
const LLSD & body,
const LLCore::HttpOptions::ptr_t &options,
const LLCore::HttpHeaders::ptr_t &headers,
const LLCore::HttpHandler::ptr_t &handler);
inline LLCore::HttpHandle requestPutWithLLSD(LLCore::HttpRequest::ptr_t & request,
LLCore::HttpRequest::policy_t policy_id,
LLCore::HttpRequest::priority_t priority,
const std::string & url,
const LLSD & body,
const LLCore::HttpOptions::ptr_t & options,
const LLCore::HttpHeaders::ptr_t & headers,
LLCore::HttpHandler::ptr_t handler)
{
return requestPutWithLLSD(request.get(), policy_id, priority,
url, body, options, headers, handler);
}
inline LLCore::HttpHandle requestPutWithLLSD(LLCore::HttpRequest::ptr_t & request,
LLCore::HttpRequest::policy_t policy_id,
LLCore::HttpRequest::priority_t priority,
const std::string & url,
const LLSD & body,
LLCore::HttpHandler::ptr_t handler)
{
LLCore::HttpOptions::ptr_t options;
LLCore::HttpHeaders::ptr_t headers;
return requestPutWithLLSD(request.get(), policy_id, priority,
url, body, options, headers, handler);
}
/// Issue a standard HttpRequest::requestPatch() call but using
/// and LLSD object as the request body. Conventions are the
/// same as with that method. Caller is expected to provide
/// an HttpHeaders object with a correct 'Content-Type:' header.
/// One will not be provided by this call.
///
/// @return If request is successfully issued, the
/// HttpHandle representing the request.
/// On error, LLCORE_HTTP_HANDLE_INVALID
/// is returned and caller can fetch detailed
/// status with the getStatus() method on the
/// request object. In case of error, no
/// request is queued and caller may need to
/// perform additional cleanup such as freeing
/// a now-useless HttpHandler object.
///
LLCore::HttpHandle requestPatchWithLLSD(LLCore::HttpRequest * request,
LLCore::HttpRequest::policy_t policy_id,
LLCore::HttpRequest::priority_t priority,
const std::string & url,
const LLSD & body,
const LLCore::HttpOptions::ptr_t &options,
const LLCore::HttpHeaders::ptr_t &headers,
const LLCore::HttpHandler::ptr_t &handler);
inline LLCore::HttpHandle requestPatchWithLLSD(LLCore::HttpRequest::ptr_t & request,
LLCore::HttpRequest::policy_t policy_id,
LLCore::HttpRequest::priority_t priority,
const std::string & url,
const LLSD & body,
const LLCore::HttpOptions::ptr_t & options,
const LLCore::HttpHeaders::ptr_t & headers,
const LLCore::HttpHandler::ptr_t & handler)
{
return requestPatchWithLLSD(request.get(), policy_id, priority,
url, body, options, headers, handler);
}
inline LLCore::HttpHandle requestPatchWithLLSD(LLCore::HttpRequest::ptr_t & request,
LLCore::HttpRequest::policy_t policy_id,
LLCore::HttpRequest::priority_t priority,
const std::string & url,
const LLSD & body,
const LLCore::HttpHandler::ptr_t &handler)
{
LLCore::HttpOptions::ptr_t options;
LLCore::HttpHeaders::ptr_t headers;
return requestPatchWithLLSD(request.get(), policy_id, priority,
url, body, options, headers, handler);
}
//=========================================================================
/// The HttpCoroHandler is a specialization of the LLCore::HttpHandler for
/// interacting with coroutines. When the request is completed the response
/// will be posted onto the supplied Event Pump.
///
/// The LLSD posted back to the coroutine will have the following additions:
/// llsd["http_result"] -+- ["message"] - An error message returned from the HTTP status
/// +- ["status"] - The status code associated with the HTTP call
/// +- ["success"] - Success of failure of the HTTP call and LLSD parsing.
/// +- ["type"] - The LLCore::HttpStatus type associted with the HTTP call
/// +- ["url"] - The URL used to make the call.
/// +- ["headers"] - A map of name name value pairs with the HTTP headers.
///
class HttpCoroHandler : public LLCore::HttpHandler
{
public:
typedef boost::shared_ptr<HttpCoroHandler> ptr_t;
typedef boost::weak_ptr<HttpCoroHandler> wptr_t;
HttpCoroHandler(LLEventStream &reply);
static void writeStatusCodes(LLCore::HttpStatus status, const std::string &url, LLSD &result);
virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response);
inline LLEventStream &getReplyPump()
{
return mReplyPump;
}
protected:
/// this method may modify the status value
virtual LLSD handleSuccess(LLCore::HttpResponse * response, LLCore::HttpStatus &status) = 0;
virtual LLSD parseBody(LLCore::HttpResponse *response, bool &success) = 0;
private:
void buildStatusEntry(LLCore::HttpResponse *response, LLCore::HttpStatus status, LLSD &result);
LLEventStream &mReplyPump;
};
//=========================================================================
/// An adapter to handle some of the boilerplate code surrounding HTTP and coroutine
/// interaction.
///
/// Construct an HttpCoroutineAdapter giving it a name and policy Id. After
/// any application specific setup call the post, put or get method. The request
/// will be automatically pumped and the method will return with an LLSD describing
/// the result of the operation. See HttpCoroHandler for a description of the
/// decoration done to the returned LLSD.
///
/// Posting through the adapter will automatically add the following headers to
/// the request if they have not been previously specified in a supplied
/// HttpHeaders object:
/// "Accept=application/llsd+xml"
/// "X-SecondLife-UDP-Listen-Port=###"
///
class HttpCoroutineAdapter
{
public:
static const std::string HTTP_RESULTS;
static const std::string HTTP_RESULTS_SUCCESS;
static const std::string HTTP_RESULTS_TYPE;
static const std::string HTTP_RESULTS_STATUS;
static const std::string HTTP_RESULTS_MESSAGE;
static const std::string HTTP_RESULTS_URL;
static const std::string HTTP_RESULTS_HEADERS;
static const std::string HTTP_RESULTS_CONTENT;
static const std::string HTTP_RESULTS_RAW;
typedef boost::shared_ptr<HttpCoroutineAdapter> ptr_t;
typedef boost::weak_ptr<HttpCoroutineAdapter> wptr_t;
HttpCoroutineAdapter(const std::string &name, LLCore::HttpRequest::policy_t policyId,
LLCore::HttpRequest::priority_t priority = 0L);
~HttpCoroutineAdapter();
/// Execute a Post transaction on the supplied URL and yield execution of
/// the coroutine until a result is available.
///
/// @Note: the request's smart pointer is passed by value so that it will
/// not be deallocated during the yield.
LLSD postAndSuspend(LLCore::HttpRequest::ptr_t request,
const std::string & url, const LLSD & body,
LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()),
LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders()));
LLSD postAndSuspend(LLCore::HttpRequest::ptr_t request,
const std::string & url, LLCore::BufferArray::ptr_t rawbody,
LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()),
LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders()));
LLSD postAndSuspend(LLCore::HttpRequest::ptr_t &request,
const std::string & url, const LLSD & body,
LLCore::HttpHeaders::ptr_t &headers)
{
return postAndSuspend(request, url, body,
LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), headers);
}
LLSD postAndSuspend(LLCore::HttpRequest::ptr_t &request,
const std::string & url, LLCore::BufferArray::ptr_t &rawbody,
LLCore::HttpHeaders::ptr_t &headers)
{
return postAndSuspend(request, url, rawbody,
LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), headers);
}
LLSD postRawAndSuspend(LLCore::HttpRequest::ptr_t request,
const std::string & url, LLCore::BufferArray::ptr_t rawbody,
LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()),
LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders()));
LLSD postRawAndSuspend(LLCore::HttpRequest::ptr_t &request,
const std::string & url, LLCore::BufferArray::ptr_t &rawbody,
LLCore::HttpHeaders::ptr_t &headers)
{
return postRawAndSuspend(request, url, rawbody,
LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), headers);
}
LLSD postFileAndSuspend(LLCore::HttpRequest::ptr_t request,
const std::string & url, std::string fileName,
LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()),
LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders()));
LLSD postFileAndSuspend(LLCore::HttpRequest::ptr_t &request,
const std::string & url, std::string fileName,
LLCore::HttpHeaders::ptr_t &headers)
{
return postFileAndSuspend(request, url, fileName,
LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), headers);
}
LLSD postFileAndSuspend(LLCore::HttpRequest::ptr_t request,
const std::string & url, LLUUID assetId, LLAssetType::EType assetType,
LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()),
LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders()));
LLSD postFileAndSuspend(LLCore::HttpRequest::ptr_t request,
const std::string & url, LLUUID assetId, LLAssetType::EType assetType,
LLCore::HttpHeaders::ptr_t &headers)
{
return postFileAndSuspend(request, url, assetId, assetType,
LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), headers);
}
LLSD postJsonAndSuspend(LLCore::HttpRequest::ptr_t request,
const std::string & url, const LLSD & body,
LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()),
LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders()));
LLSD postJsonAndSuspend(LLCore::HttpRequest::ptr_t &request,
const std::string & url, const LLSD & body,
LLCore::HttpHeaders::ptr_t &headers)
{
return postJsonAndSuspend(request, url, body,
LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), headers);
}
/// Execute a Put transaction on the supplied URL and yield execution of
/// the coroutine until a result is available.
///
/// @Note: the request's smart pointer is passed by value so that it will
/// not be deallocated during the yield.
LLSD putAndSuspend(LLCore::HttpRequest::ptr_t request,
const std::string & url, const LLSD & body,
LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()),
LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders()));
LLSD putAndSuspend(LLCore::HttpRequest::ptr_t request,
const std::string & url, const LLSD & body,
LLCore::HttpHeaders::ptr_t headers)
{
return putAndSuspend(request, url, body,
LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), headers);
}
LLSD putJsonAndSuspend(LLCore::HttpRequest::ptr_t request,
const std::string & url, const LLSD & body,
LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()),
LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders()));
LLSD putJsonAndSuspend(LLCore::HttpRequest::ptr_t &request,
const std::string & url, const LLSD & body,
LLCore::HttpHeaders::ptr_t &headers)
{
return putJsonAndSuspend(request, url, body,
LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), headers);
}
/// Execute a Get transaction on the supplied URL and yield execution of
/// the coroutine until a result is available.
///
/// @Note: the request's smart pointer is passed by value so that it will
/// not be deallocated during the yield.
///
LLSD getAndSuspend(LLCore::HttpRequest::ptr_t request,
const std::string & url,
LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()),
LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders()));
LLSD getAndSuspend(LLCore::HttpRequest::ptr_t &request,
const std::string & url, LLCore::HttpHeaders::ptr_t &headers)
{
return getAndSuspend(request, url,
LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()),
headers);
}
LLSD getRawAndSuspend(LLCore::HttpRequest::ptr_t request,
const std::string & url,
LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()),
LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders()));
LLSD getRawAndSuspend(LLCore::HttpRequest::ptr_t &request,
const std::string & url, LLCore::HttpHeaders::ptr_t &headers)
{
return getRawAndSuspend(request, url,
LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()),
headers);
}
/// These methods have the same behavior as @getAndSuspend() however they are
/// expecting the server to return the results formatted in a JSON string.
/// On a successful GET call the JSON results will be converted into LLSD
/// before being returned to the caller.
LLSD getJsonAndSuspend(LLCore::HttpRequest::ptr_t request,
const std::string & url,
LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()),
LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders()));
LLSD getJsonAndSuspend(LLCore::HttpRequest::ptr_t &request,
const std::string & url, LLCore::HttpHeaders::ptr_t &headers)
{
return getJsonAndSuspend(request, url,
LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()),
headers);
}
/// Execute a DELETE transaction on the supplied URL and yield execution of
/// the coroutine until a result is available.
///
/// @Note: the request's smart pointer is passed by value so that it will
/// not be deallocated during the yield.
LLSD deleteAndSuspend(LLCore::HttpRequest::ptr_t request,
const std::string & url,
LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()),
LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders()));
LLSD deleteAndSuspend(LLCore::HttpRequest::ptr_t request,
const std::string & url, LLCore::HttpHeaders::ptr_t headers)
{
return deleteAndSuspend(request, url,
LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()),
headers);
}
/// These methods have the same behavior as @deleteAndSuspend() however they are
/// expecting the server to return any results formatted in a JSON string.
/// On a successful DELETE call the JSON results will be converted into LLSD
/// before being returned to the caller.
LLSD deleteJsonAndSuspend(LLCore::HttpRequest::ptr_t request,
const std::string & url,
LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()),
LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders()));
LLSD deleteJsonAndSuspend(LLCore::HttpRequest::ptr_t request,
const std::string & url, LLCore::HttpHeaders::ptr_t headers)
{
return deleteJsonAndSuspend(request, url,
LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()),
headers);
}
/// Execute a PATCH transaction on the supplied URL and yield execution of
/// the coroutine until a result is available.
///
/// @Note: the request's smart pointer is passed by value so that it will
/// not be deallocated during the yield.
LLSD patchAndSuspend(LLCore::HttpRequest::ptr_t request,
const std::string & url, const LLSD & body,
LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()),
LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders()));
LLSD patchAndSuspend(LLCore::HttpRequest::ptr_t &request,
const std::string & url, const LLSD & body,
LLCore::HttpHeaders::ptr_t &headers)
{
return patchAndSuspend(request, url, body,
LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), headers);
}
/// Execute a COPY transaction on the supplied URL and yield execution of
/// the coroutine until a result is available.
///
/// @Note: The destination is passed through the HTTP pipe as a header
/// The header used is defined as: HTTP_OUT_HEADER_DESTINATION("Destination");
///
/// @Note: the request's smart pointer is passed by value so that it will
/// not be deallocated during the yield.
LLSD copyAndSuspend(LLCore::HttpRequest::ptr_t request,
const std::string & url, const std::string dest,
LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()),
LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders()));
LLSD copyAndSuspend(LLCore::HttpRequest::ptr_t &request,
const std::string & url, const std::string & dest,
LLCore::HttpHeaders::ptr_t &headers)
{
return copyAndSuspend(request, url, dest,
LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), headers);
}
/// Execute a MOVE transaction on the supplied URL and yield execution of
/// the coroutine until a result is available.
///
/// @Note: The destination is passed through the HTTP pipe in the headers.
/// The header used is defined as: HTTP_OUT_HEADER_DESTINATION("Destination");
///
/// @Note: the request's smart pointer is passed by value so that it will
/// not be deallocated during the yield.
LLSD moveAndSuspend(LLCore::HttpRequest::ptr_t request,
const std::string & url, const std::string dest,
LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()),
LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders()));
LLSD moveAndSuspend(LLCore::HttpRequest::ptr_t &request,
const std::string & url, const std::string & dest,
LLCore::HttpHeaders::ptr_t &headers)
{
return moveAndSuspend(request, url, dest,
LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), headers);
}
///
void cancelSuspendedOperation();
static LLCore::HttpStatus getStatusFromLLSD(const LLSD &httpResults);
/// The convenience routines below can be provided with callback functors
/// which will be invoked in the case of success or failure. These callbacks
/// should match this form.
/// @sa callbackHttpGet
/// @sa callbackHttpPost
typedef boost::function<void(const LLSD &)> completionCallback_t;
static void callbackHttpGet(const std::string &url, LLCore::HttpRequest::policy_t policyId, completionCallback_t success = NULL, completionCallback_t failure = NULL);
static void callbackHttpGet(const std::string &url, completionCallback_t success = NULL, completionCallback_t failure = NULL)
{
callbackHttpGet(url, LLCore::HttpRequest::DEFAULT_POLICY_ID, success, failure);
}
static void callbackHttpPost(const std::string &url, LLCore::HttpRequest::policy_t policyId, const LLSD &postData, completionCallback_t success = NULL, completionCallback_t failure = NULL);
static void callbackHttpPost(const std::string &url, const LLSD &postData, completionCallback_t success = NULL, completionCallback_t failure = NULL)
{
callbackHttpPost(url, LLCore::HttpRequest::DEFAULT_POLICY_ID, postData, success, failure);
}
/// Generic Get and post routines for HTTP via coroutines.
/// These static methods do all required setup for the GET or POST operation.
/// When the operation completes successfully they will put the success message in the log at INFO level,
/// If the operation fails the failure message is written to the log at WARN level.
///
static void messageHttpGet(const std::string &url, const std::string &success = std::string(), const std::string &failure = std::string());
static void messageHttpPost(const std::string &url, const LLSD &postData, const std::string &success, const std::string &failure);
private:
static LLSD buildImmediateErrorResult(const LLCore::HttpRequest::ptr_t &request, const std::string &url);
void saveState(LLCore::HttpHandle yieldingHandle, LLCore::HttpRequest::ptr_t &request,
HttpCoroHandler::ptr_t &handler);
void cleanState();
LLSD postAndSuspend_(LLCore::HttpRequest::ptr_t &request,
const std::string & url, const LLSD & body,
LLCore::HttpOptions::ptr_t &options, LLCore::HttpHeaders::ptr_t &headers,
HttpCoroHandler::ptr_t &handler);
LLSD postAndSuspend_(LLCore::HttpRequest::ptr_t &request,
const std::string & url, LLCore::BufferArray::ptr_t &rawbody,
LLCore::HttpOptions::ptr_t &options, LLCore::HttpHeaders::ptr_t &headers,
HttpCoroHandler::ptr_t &handler);
LLSD putAndSuspend_(LLCore::HttpRequest::ptr_t &request,
const std::string & url, const LLSD & body,
LLCore::HttpOptions::ptr_t &options, LLCore::HttpHeaders::ptr_t &headers,
HttpCoroHandler::ptr_t &handler);
LLSD putAndSuspend_(LLCore::HttpRequest::ptr_t &request,
const std::string & url, const LLCore::BufferArray::ptr_t & rawbody,
LLCore::HttpOptions::ptr_t &options, LLCore::HttpHeaders::ptr_t &headers,
HttpCoroHandler::ptr_t &handler);
LLSD getAndSuspend_(LLCore::HttpRequest::ptr_t &request,
const std::string & url, LLCore::HttpOptions::ptr_t &options,
LLCore::HttpHeaders::ptr_t &headers, HttpCoroHandler::ptr_t &handler);
LLSD deleteAndSuspend_(LLCore::HttpRequest::ptr_t &request,
const std::string & url, LLCore::HttpOptions::ptr_t &options,
LLCore::HttpHeaders::ptr_t &headers, HttpCoroHandler::ptr_t &handler);
LLSD patchAndSuspend_(LLCore::HttpRequest::ptr_t &request,
const std::string & url, const LLSD & body,
LLCore::HttpOptions::ptr_t &options, LLCore::HttpHeaders::ptr_t &headers,
HttpCoroHandler::ptr_t &handler);
LLSD copyAndSuspend_(LLCore::HttpRequest::ptr_t &request,
const std::string & url,
LLCore::HttpOptions::ptr_t &options, LLCore::HttpHeaders::ptr_t &headers,
HttpCoroHandler::ptr_t &handler);
LLSD moveAndSuspend_(LLCore::HttpRequest::ptr_t &request,
const std::string & url,
LLCore::HttpOptions::ptr_t &options, LLCore::HttpHeaders::ptr_t &headers,
HttpCoroHandler::ptr_t &handler);
static void trivialGetCoro(std::string url, LLCore::HttpRequest::policy_t policyId, completionCallback_t success, completionCallback_t failure);
static void trivialPostCoro(std::string url, LLCore::HttpRequest::policy_t policyId, LLSD postData, completionCallback_t success, completionCallback_t failure);
void checkDefaultHeaders(LLCore::HttpHeaders::ptr_t &headers);
std::string mAdapterName;
LLCore::HttpRequest::priority_t mPriority;
LLCore::HttpRequest::policy_t mPolicyId;
LLCore::HttpHandle mYieldingHandle;
LLCore::HttpRequest::wptr_t mWeakRequest;
HttpCoroHandler::wptr_t mWeakHandler;
};
} // end namespace LLCoreHttpUtil
#endif // LL_LLCOREHTTPUTIL_H

File diff suppressed because it is too large Load Diff

View File

@ -1,557 +0,0 @@
/**
* @file llcurl.h
* @author Zero / Donovan
* @date 2006-10-15
* @brief A wrapper around libcurl.
*
* $LicenseInfo:firstyear=2006&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LL_LLCURL_H
#define LL_LLCURL_H
#include "linden_common.h"
#include <sstream>
#include <string>
#include <vector>
#include <boost/intrusive_ptr.hpp>
#include <curl/curl.h> // TODO: remove dependency
#include "llbuffer.h"
#include "llhttpconstants.h"
#include "lliopipe.h"
#include "llsd.h"
#include "llqueuedthread.h"
#include "llframetimer.h"
#include "llpointer.h"
#include "llsingleton.h"
class LLMutex;
class LLCurlThread;
// For whatever reason, this is not typedef'd in curl.h
typedef size_t (*curl_header_callback)(void *ptr, size_t size, size_t nmemb, void *stream);
class LLCurl
{
LOG_CLASS(LLCurl);
public:
class Easy;
class Multi;
struct TransferInfo
{
TransferInfo() : mSizeDownload(0.0), mTotalTime(0.0), mSpeedDownload(0.0) {}
F64 mSizeDownload;
F64 mTotalTime;
F64 mSpeedDownload;
};
class Responder : public LLThreadSafeRefCount
{
//LOG_CLASS(Responder);
public:
Responder();
virtual ~Responder();
virtual bool followRedir()
{
return false;
}
/**
* @brief return true if the status code indicates success.
*/
bool isGoodStatus() const { return isHttpGoodStatus(mStatus); }
S32 getStatus() const { return mStatus; }
const std::string& getReason() const { return mReason; }
const LLSD& getContent() const { return mContent; }
bool hasResponseHeader(const std::string& header) const;
const std::string& getResponseHeader(const std::string& header) const;
const LLSD& getResponseHeaders() const { return mResponseHeaders; }
const std::string& getURL() const { return mURL; }
EHTTPMethod getHTTPMethod() const { return mHTTPMethod; }
// This formats response information for use in log spam. Includes content spam.
std::string dumpResponse() const;
// Allows direct triggering of success/error with different results.
void completeResult(S32 status, const std::string& reason, const LLSD& content = LLSD());
void successResult(const LLSD& content);
void failureResult(S32 status, const std::string& reason, const LLSD& content = LLSD());
// The default implementation will try to parse body content as an LLSD, however
// it should not spam about parsing failures unless the server sent a
// Content-Type: application/llsd+xml header.
virtual void completedRaw(
const LLChannelDescriptors& channels,
const LLIOPipe::buffer_ptr_t& buffer);
/**< Override point for clients that may want to use this
class when the response is some other format besides LLSD
*/
// The http* methods are not public since these should be triggered internally
// after status, reason, content, etc have been set.
// If you need to trigger a completion method, use the *Result methods, above.
protected:
// These methods are the preferred way to process final results.
// By default, when one of these is called the following information will be resolved:
// * HTTP status code - getStatus()
// * Reason string - getReason()
// * Content - getContent()
// * Response Headers - getResponseHeaders()
// By default, httpSuccess is triggered whenever httpCompleted is called with a 2xx status code.
virtual void httpSuccess();
//< called by completed for good status codes.
// By default, httpFailure is triggered whenever httpCompleted is called with a non-2xx status code.
virtual void httpFailure();
//< called by httpCompleted() on bad status
// httpCompleted does not generally need to be overridden, unless
// you don't care about the status code (which determine httpFailure or httpSuccess)
// or if you want to re-interpret what a 'good' vs' bad' status code is.
virtual void httpCompleted();
/**< The default implementation calls
either:
* httpSuccess(), or
* httpFailure()
*/
public:
void setHTTPMethod(EHTTPMethod method);
void setURL(const std::string& url);
const std::string& getURL();
void setResult(S32 status, const std::string& reason, const LLSD& content = LLSD());
void setResponseHeader(const std::string& header, const std::string& value);
private:
// These can be accessed by the get* methods. Treated as 'read-only' during completion handlers.
EHTTPMethod mHTTPMethod;
std::string mURL;
LLSD mResponseHeaders;
protected:
// These should also generally be treated as 'read-only' during completion handlers
// and should be accessed by the get* methods. The exception to this rule would
// be when overriding the completedRaw method in preparation for calling httpCompleted().
S32 mStatus;
std::string mReason;
LLSD mContent;
};
typedef LLPointer<Responder> ResponderPtr;
/**
* @ brief Set certificate authority file used to verify HTTPS certs.
*/
static void setCAFile(const std::string& file);
/**
* @ brief Set certificate authority path used to verify HTTPS certs.
*/
static void setCAPath(const std::string& path);
/**
* @ brief Return human-readable string describing libcurl version.
*/
static std::string getVersionString();
/**
* @ brief Get certificate authority file used to verify HTTPS certs.
*/
static const std::string& getCAFile() { return sCAFile; }
/**
* @ brief Get certificate authority path used to verify HTTPS certs.
*/
static const std::string& getCAPath() { return sCAPath; }
/**
* @ brief Initialize LLCurl class
*/
static void initClass(F32 curl_reuest_timeout = 120.f, S32 max_number_handles = 256, bool multi_threaded = false);
/**
* @ brief Cleanup LLCurl class
*/
static void cleanupClass();
/**
* @ brief curl error code -> string
*/
static std::string strerror(CURLcode errorcode);
// For OpenSSL callbacks
static std::vector<LLMutex*> sSSLMutex;
// OpenSSL callbacks
static void ssl_locking_callback(int mode, int type, const char *file, int line);
static unsigned long ssl_thread_id(void);
static LLCurlThread* getCurlThread() { return sCurlThread ;}
static CURLM* newMultiHandle() ;
static CURLMcode deleteMultiHandle(CURLM* handle) ;
static CURL* newEasyHandle() ;
static void deleteEasyHandle(CURL* handle) ;
static CURL* createStandardCurlHandle();
private:
static std::string sCAPath;
static std::string sCAFile;
static const unsigned int MAX_REDIRECTS;
static LLCurlThread* sCurlThread;
static LLMutex* sHandleMutexp ;
static S32 sTotalHandles ;
static S32 sMaxHandles;
static CURL* sCurlTemplateStandardHandle;
public:
static bool sNotQuitting;
static F32 sCurlRequestTimeOut;
};
class LLCurl::Easy
{
LOG_CLASS(Easy);
private:
Easy();
public:
static Easy* getEasy();
~Easy();
CURL* getCurlHandle() const { return mCurlEasyHandle; }
void setErrorBuffer();
void setCA();
void setopt(CURLoption option, S32 value);
// These assume the setter does not free value!
void setopt(CURLoption option, void* value);
void setopt(CURLoption option, char* value);
// Copies the string so that it is guaranteed to stick around
void setoptString(CURLoption option, const std::string& value);
void slist_append(const std::string& header, const std::string& value);
void slist_append(const char* str);
void setHeaders();
S32 report(CURLcode);
void getTransferInfo(LLCurl::TransferInfo* info);
void prepRequest(const std::string& url, const std::vector<std::string>& headers, LLCurl::ResponderPtr, S32 time_out = 0, bool post = false);
const char* getErrorBuffer();
std::stringstream& getInput() { return mInput; }
std::stringstream& getHeaderOutput() { return mHeaderOutput; }
LLIOPipe::buffer_ptr_t& getOutput() { return mOutput; }
const LLChannelDescriptors& getChannels() { return mChannels; }
void resetState();
static CURL* allocEasyHandle();
static void releaseEasyHandle(CURL* handle);
private:
friend class LLCurl;
friend class LLCurl::Multi;
CURL* mCurlEasyHandle;
struct curl_slist* mHeaders;
std::stringstream mRequest;
LLChannelDescriptors mChannels;
LLIOPipe::buffer_ptr_t mOutput;
std::stringstream mInput;
std::stringstream mHeaderOutput;
char mErrorBuffer[CURL_ERROR_SIZE];
// Note: char*'s not strings since we pass pointers to curl
std::vector<char*> mStrings;
LLCurl::ResponderPtr mResponder;
static std::set<CURL*> sFreeHandles;
static std::set<CURL*> sActiveHandles;
static LLMutex* sHandleMutexp ;
static void deleteAllActiveHandles();
static void deleteAllFreeHandles();
};
class LLCurl::Multi
{
LOG_CLASS(Multi);
friend class LLCurlThread ;
private:
~Multi();
void markDead() ;
bool doPerform();
public:
typedef enum
{
STATE_READY=0,
STATE_PERFORMING=1,
STATE_COMPLETED=2
} ePerformState;
Multi(F32 idle_time_out = 0.f);
LLCurl::Easy* allocEasy();
bool addEasy(LLCurl::Easy* easy);
void removeEasy(LLCurl::Easy* easy);
void lock() ;
void unlock() ;
void setState(ePerformState state) ;
ePerformState getState() ;
bool isCompleted() ;
bool isValid() {return mCurlMultiHandle != NULL && mValid;}
bool isDead() {return mDead;}
bool waitToComplete() ;
S32 process();
CURLMsg* info_read(S32* msgs_in_queue);
S32 mQueued;
S32 mErrorCount;
private:
void easyFree(LLCurl::Easy*);
void cleanup(bool deleted = false) ;
CURLM* mCurlMultiHandle;
typedef std::set<LLCurl::Easy*> easy_active_list_t;
easy_active_list_t mEasyActiveList;
typedef std::map<CURL*, LLCurl::Easy*> easy_active_map_t;
easy_active_map_t mEasyActiveMap;
typedef std::set<LLCurl::Easy*> easy_free_list_t;
easy_free_list_t mEasyFreeList;
LLQueuedThread::handle_t mHandle ;
ePerformState mState;
BOOL mDead ;
BOOL mValid ;
LLMutex* mMutexp ;
LLMutex* mDeletionMutexp ;
LLMutex* mEasyMutexp ;
LLFrameTimer mIdleTimer ;
F32 mIdleTimeOut;
};
class LLCurlThread : public LLQueuedThread
{
public:
class CurlRequest : public LLQueuedThread::QueuedRequest
{
protected:
virtual ~CurlRequest(); // use deleteRequest()
public:
CurlRequest(handle_t handle, LLCurl::Multi* multi, LLCurlThread* curl_thread);
/*virtual*/ bool processRequest();
/*virtual*/ void finishRequest(bool completed);
private:
// input
LLCurl::Multi* mMulti;
LLCurlThread* mCurlThread;
};
friend class CurlRequest;
public:
LLCurlThread(bool threaded = true) ;
virtual ~LLCurlThread() ;
S32 update(F32 max_time_ms);
void addMulti(LLCurl::Multi* multi) ;
void killMulti(LLCurl::Multi* multi) ;
private:
bool doMultiPerform(LLCurl::Multi* multi) ;
void deleteMulti(LLCurl::Multi* multi) ;
void cleanupMulti(LLCurl::Multi* multi) ;
} ;
class LLCurlRequest
{
public:
typedef std::vector<std::string> headers_t;
LLCurlRequest();
~LLCurlRequest();
void get(const std::string& url, LLCurl::ResponderPtr responder);
bool getByteRange(const std::string& url, const headers_t& headers, S32 offset, S32 length, LLCurl::ResponderPtr responder);
bool post(const std::string& url, const headers_t& headers, const LLSD& data, LLCurl::ResponderPtr responder, S32 time_out = 0);
bool post(const std::string& url, const headers_t& headers, const std::string& data, LLCurl::ResponderPtr responder, S32 time_out = 0);
S32 process();
S32 getQueued();
private:
void addMulti();
LLCurl::Easy* allocEasy();
bool addEasy(LLCurl::Easy* easy);
private:
typedef std::set<LLCurl::Multi*> curlmulti_set_t;
curlmulti_set_t mMultiSet;
LLCurl::Multi* mActiveMulti;
S32 mActiveRequestCount;
BOOL mProcessing;
};
//for texture fetch only
class LLCurlTextureRequest : public LLCurlRequest
{
public:
LLCurlTextureRequest(S32 concurrency);
~LLCurlTextureRequest();
U32 getByteRange(const std::string& url, const headers_t& headers, S32 offset, S32 length, U32 pri, LLCurl::ResponderPtr responder, F32 delay_time = -1.f);
void nextRequests();
void completeRequest(S32 received_bytes);
void updatePriority(U32 handle, U32 pri);
void removeRequest(U32 handle);
U32 getTotalReceivedBits();
U32 getTotalIssuedRequests();
S32 getNumRequests();
bool isWaiting(U32 handle);
private:
LLMutex mMutex;
S32 mConcurrency;
S32 mInQueue; //request currently in queue.
U32 mHandleCounter;
U32 mTotalIssuedRequests;
U32 mTotalReceivedBits;
typedef struct _request_t
{
_request_t(U32 handle, const std::string& url, const headers_t& headers, S32 offset, S32 length, U32 pri, LLCurl::ResponderPtr responder) :
mHandle(handle), mUrl(url), mHeaders(headers), mOffset(offset), mLength(length), mPriority(pri), mResponder(responder), mStartTime(0.f)
{}
U32 mHandle;
std::string mUrl;
LLCurlRequest::headers_t mHeaders;
S32 mOffset;
S32 mLength;
LLCurl::ResponderPtr mResponder;
U32 mPriority;
F32 mStartTime; //start time to issue this request
} request_t;
struct request_compare
{
bool operator()(const request_t* lhs, const request_t* rhs) const
{
if(lhs->mPriority != rhs->mPriority)
{
return lhs->mPriority > rhs->mPriority; // higher priority in front of queue (set)
}
else
{
return (U32)lhs < (U32)rhs;
}
}
};
typedef std::set<request_t*, request_compare> req_queue_t;
req_queue_t mCachedRequests;
std::map<S32, request_t*> mRequestMap;
LLFrameTimer mGlobalTimer;
};
class LLCurlEasyRequest
{
public:
LLCurlEasyRequest();
~LLCurlEasyRequest();
void setopt(CURLoption option, S32 value);
void setoptString(CURLoption option, const std::string& value);
void setPost(char* postdata, S32 size);
void setHeaderCallback(curl_header_callback callback, void* userdata);
void setWriteCallback(curl_write_callback callback, void* userdata);
void setReadCallback(curl_read_callback callback, void* userdata);
void setSSLCtxCallback(curl_ssl_ctx_callback callback, void* userdata);
void slist_append(const std::string& header, const std::string& value);
void slist_append(const char* str);
void sendRequest(const std::string& url);
void requestComplete();
bool getResult(CURLcode* result, LLCurl::TransferInfo* info = NULL);
std::string getErrorString();
bool isCompleted() {return mMulti->isCompleted() ;}
bool wait() { return mMulti->waitToComplete(); }
bool isValid() {return mMulti && mMulti->isValid(); }
LLCurl::Easy* getEasy() const { return mEasy; }
private:
CURLMsg* info_read(S32* queue, LLCurl::TransferInfo* info);
private:
LLCurl::Multi* mMulti;
LLCurl::Easy* mEasy;
bool mRequestSent;
bool mResultReturned;
};
// Provide access to LLCurl free functions outside of llcurl.cpp without polluting the global namespace.
namespace LLCurlFF
{
void check_easy_code(CURLcode code);
void check_multi_code(CURLMcode code);
}
#endif // LL_LLCURL_H

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More