Merge Firestorm LGPL
commit
2797c5938a
1
.hgtags
1
.hgtags
|
|
@ -547,3 +547,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
|
||||
|
|
|
|||
|
|
@ -276,60 +276,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>d0c95e3e704da4bf228479bccf0a9888</string>
|
||||
<key>url</key>
|
||||
<string>http://downloads.phoenixviewer.com/ares-1.10.0.201505181308-r65-darwin-201505181308-r65.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>
|
||||
|
|
@ -467,9 +413,9 @@
|
|||
<key>archive</key>
|
||||
<map>
|
||||
<key>hash</key>
|
||||
<string>de9e0c855ff6ee30c9e027a70bbef032</string>
|
||||
<string>f49d4ed203b03852a3f6b01b18319f7a</string>
|
||||
<key>url</key>
|
||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/curl_3p-update-curl/rev/301717/arch/Linux/installer/curl-7.42.1.301717-linux-301717.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>
|
||||
|
|
@ -479,16 +425,18 @@
|
|||
<key>archive</key>
|
||||
<map>
|
||||
<key>hash</key>
|
||||
<string>98d15713de8c439b7f54cc14f2df07ac</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/301717/arch/CYGWIN/installer/curl-7.42.1.301717-windows-301717.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.42.1.301717</string>
|
||||
<string>7.47.0.312763</string>
|
||||
</map>
|
||||
<key>db</key>
|
||||
<map>
|
||||
|
|
@ -885,9 +833,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>
|
||||
|
|
@ -1695,9 +1643,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>
|
||||
|
|
@ -1764,7 +1712,7 @@
|
|||
</map>
|
||||
</map>
|
||||
<key>version</key>
|
||||
<string>1.5.1.309177</string>
|
||||
<string>1.5.3.311349</string>
|
||||
</map>
|
||||
<key>llphysicsextensions_source</key>
|
||||
<map>
|
||||
|
|
@ -2271,9 +2219,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>
|
||||
|
|
@ -2283,9 +2231,9 @@
|
|||
<key>archive</key>
|
||||
<map>
|
||||
<key>hash</key>
|
||||
<string>8bada9cac4d5367dc26fff331a2bdd69</string>
|
||||
<string>06c3a9b1005249f0c94a8b9f3775f3d3</string>
|
||||
<key>url</key>
|
||||
<string>http://downloads.phoenixviewer.com/slvoice-3.2.0002.10426.298329-linux-20150809.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>
|
||||
|
|
@ -2295,16 +2243,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>
|
||||
|
|
|
|||
|
|
@ -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 " "
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -139,3 +139,9 @@ else (USESYSTEMLIBS)
|
|||
debug boost_wave-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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -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
|
||||
|
|
@ -60,7 +58,6 @@ set(cmake_SOURCE_FILES
|
|||
JsonCpp.cmake
|
||||
LLAddBuildTest.cmake
|
||||
LLAppearance.cmake
|
||||
LLAppearanceUtility.cmake
|
||||
LLAudio.cmake
|
||||
LLCharacter.cmake
|
||||
LLCommon.cmake
|
||||
|
|
|
|||
|
|
@ -22,10 +22,8 @@ if(WINDOWS)
|
|||
SLVoice.exe
|
||||
ca-bundle.crt
|
||||
libsndfile-1.dll
|
||||
vivoxplatform.dll
|
||||
vivoxsdk.dll
|
||||
ortp.dll
|
||||
zlib1.dll
|
||||
vivoxoal.dll
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
|
|
@ -40,6 +40,7 @@ MACRO(LL_ADD_PROJECT_UNIT_TESTS project sources)
|
|||
${APRUTIL_LIBRARIES}
|
||||
${APR_LIBRARIES}
|
||||
llcommon
|
||||
llcorehttp
|
||||
)
|
||||
IF(NOT "${project}" STREQUAL "llmath")
|
||||
# add llmath as a dep unless the tested module *is* llmath!
|
||||
|
|
@ -54,6 +55,9 @@ MACRO(LL_ADD_PROJECT_UNIT_TESTS project sources)
|
|||
${GOOGLEMOCK_INCLUDE_DIRS}
|
||||
)
|
||||
SET(alltest_LIBRARIES
|
||||
${BOOST_COROUTINE_LIBRARY}
|
||||
${BOOST_CONTEXT_LIBRARY}
|
||||
${BOOST_SYSTEM_LIBRARY}
|
||||
${GOOGLEMOCK_LIBRARIES}
|
||||
${PTHREAD_LIBRARY}
|
||||
${WINDOWS_LIBRARIES}
|
||||
|
|
@ -196,6 +200,9 @@ FUNCTION(LL_ADD_INTEGRATION_TEST
|
|||
|
||||
SET(libraries
|
||||
${library_dependencies}
|
||||
${BOOST_COROUTINE_LIBRARY}
|
||||
${BOOST_CONTEXT_LIBRARY}
|
||||
${BOOST_SYSTEM_LIBRARY}
|
||||
${GOOGLEMOCK_LIBRARIES}
|
||||
${PTHREAD_LIBRARY}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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})
|
||||
|
|
|
|||
|
|
@ -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})
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -297,10 +301,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}
|
||||
)
|
||||
|
|
@ -321,7 +329,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}")
|
||||
|
|
@ -343,7 +358,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}")
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,22 +29,21 @@
|
|||
#if ! defined(LL_LLCOROS_H)
|
||||
#define LL_LLCOROS_H
|
||||
|
||||
// <FS:TS> This silliness is needed because LL renamed the Boost coroutines
|
||||
// functions to dcoroutines.
|
||||
#if LL_USESYSTEMLIBS
|
||||
#include <boost/coroutine/coroutine.hpp>
|
||||
#define dcoroutines coroutines
|
||||
#else
|
||||
#include <boost/dcoroutine/coroutine.hpp>
|
||||
#endif
|
||||
#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
|
||||
*
|
||||
|
|
@ -87,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
|
||||
|
|
@ -101,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
|
||||
|
|
@ -145,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) */
|
||||
|
|
|
|||
|
|
@ -367,6 +367,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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -29,19 +29,10 @@
|
|||
#if ! defined(LL_LLEVENTCORO_H)
|
||||
#define LL_LLEVENTCORO_H
|
||||
|
||||
// <FS:TS> This silliness is needed because LL renamed the Boost coroutines
|
||||
// functions to dcoroutines.
|
||||
#if LL_USESYSTEMLIBS
|
||||
#include <boost/coroutine/coroutine.hpp>
|
||||
#include <boost/coroutine/future.hpp>
|
||||
#define dcoroutines coroutines
|
||||
#else
|
||||
#include <boost/dcoroutine/coroutine.hpp>
|
||||
#include <boost/dcoroutine/future.hpp>
|
||||
#endif
|
||||
#include <boost/optional.hpp>
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
#include <utility> // std::pair
|
||||
#include "llevents.h"
|
||||
#include "llerror.h"
|
||||
|
||||
|
|
@ -82,116 +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.
|
||||
*/
|
||||
// <FS:Zi> Compiler fix for Linux gcc 4.7+
|
||||
/// Implementation for listenerNameForCoro()
|
||||
LL_COMMON_API std::string listenerNameForCoroImpl(const void* self_id);
|
||||
// </FS:Zi>
|
||||
|
||||
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
|
||||
|
|
@ -214,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
|
||||
|
|
@ -323,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
|
||||
*
|
||||
|
|
@ -413,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
|
||||
|
|
@ -433,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
|
||||
|
|
@ -450,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
|
||||
|
|
@ -467,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:
|
||||
|
|
@ -522,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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*****************************************************************************/
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) */
|
||||
|
|
@ -749,7 +749,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);
|
||||
|*==========================================================================*/
|
||||
|
|
@ -1028,9 +1028,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;
|
||||
|
|
|
|||
|
|
@ -161,15 +161,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();
|
||||
|
|
@ -180,6 +175,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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -1395,6 +1395,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)
|
||||
|
|
@ -1428,6 +1429,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
|
|
@ -9,7 +9,6 @@ include(00-Common)
|
|||
# </FS:ND>
|
||||
|
||||
include(CURL)
|
||||
include(CARes)
|
||||
include(OpenSSL)
|
||||
include(ZLIB)
|
||||
include(LLCoreHttp)
|
||||
|
|
@ -30,6 +29,7 @@ set(llcorehttp_SOURCE_FILES
|
|||
bufferarray.cpp
|
||||
bufferstream.cpp
|
||||
httpcommon.cpp
|
||||
llhttpconstants.cpp
|
||||
httpheaders.cpp
|
||||
httpoptions.cpp
|
||||
httprequest.cpp
|
||||
|
|
@ -55,6 +55,7 @@ set(llcorehttp_HEADER_FILES
|
|||
bufferarray.h
|
||||
bufferstream.h
|
||||
httpcommon.h
|
||||
llhttpconstants.h
|
||||
httphandler.h
|
||||
httpheaders.h
|
||||
httpoptions.h
|
||||
|
|
@ -93,7 +94,6 @@ add_library (llcorehttp ${llcorehttp_SOURCE_FILES})
|
|||
target_link_libraries(
|
||||
llcorehttp
|
||||
${CURL_LIBRARIES}
|
||||
${CARES_LIBRARIES}
|
||||
${OPENSSL_LIBRARIES}
|
||||
${CRYPTO_LIBRARIES}
|
||||
${BOOST_THREAD_LIBRARY}
|
||||
|
|
@ -131,7 +131,6 @@ if (LL_TESTS)
|
|||
${LLCOMMON_LIBRARIES}
|
||||
${GOOGLEMOCK_LIBRARIES}
|
||||
${CURL_LIBRARIES}
|
||||
${CARES_LIBRARIES}
|
||||
${OPENSSL_LIBRARIES}
|
||||
${CRYPTO_LIBRARIES}
|
||||
${BOOST_SYSTEM_LIBRARY}
|
||||
|
|
@ -200,7 +199,6 @@ endif (DARWIN)
|
|||
${LLCOMMON_LIBRARIES}
|
||||
${GOOGLEMOCK_LIBRARIES}
|
||||
${CURL_LIBRARIES}
|
||||
${CARES_LIBRARIES}
|
||||
${OPENSSL_LIBRARIES}
|
||||
${CRYPTO_LIBRARIES}
|
||||
${BOOST_SYSTEM_LIBRARY}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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,31 +344,52 @@ bool HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode
|
|||
{
|
||||
op->mStatus = HttpStatus(HttpStatus::EXT_CURL_EASY, status);
|
||||
}
|
||||
if (op->mStatus)
|
||||
{
|
||||
if (op->mStatus)
|
||||
{
|
||||
//<FS:TS> The cURL library doc specifies that you need to pass
|
||||
// a pointer to a long, not a pointer to an int, here.
|
||||
//int http_status(HTTP_OK);
|
||||
long int http_status(HTTP_OK);
|
||||
long 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;
|
||||
}
|
||||
}
|
||||
|
||||
// <FS:ND> See if the requested URL matches a X-LL-URL header (if present) and the requested range.
|
||||
|
|
@ -412,18 +446,27 @@ bool HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode
|
|||
}
|
||||
// /</FS:ND>
|
||||
|
||||
// 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
|
||||
|
|
@ -611,7 +654,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)
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -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 *);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -824,7 +959,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 : "");
|
||||
}
|
||||
|
|
@ -880,9 +1015,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;
|
||||
|
|
@ -962,7 +1126,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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
std::string mXLLURL; // <FS:ND/> If we get a x-ll-url header, save it here, even if mReplyHeaders is not filled.
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -51,7 +51,6 @@ class HttpOpSetPriority : public HttpOperation
|
|||
public:
|
||||
HttpOpSetPriority(HttpHandle handle, HttpRequest::priority_t priority);
|
||||
|
||||
protected:
|
||||
virtual ~HttpOpSetPriority();
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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_
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -40,7 +52,12 @@ HttpStatus::type_enum_t LLCORE;
|
|||
|
||||
HttpStatus::operator unsigned long() const
|
||||
{
|
||||
unsigned long result(((unsigned long) mType) << 16 | (unsigned long) (int) mStatus);
|
||||
// <FS:ND> 64 bit compatibility
|
||||
// static const int shift(sizeof(unsigned long) * 4);
|
||||
static const int shift(sizeof(4 * 4));
|
||||
// </FS:ND>
|
||||
|
||||
unsigned long result(((unsigned long)mDetails->mType) << shift | (unsigned long)(int)mDetails->mStatus);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -129,30 +146,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);
|
||||
}
|
||||
|
|
@ -160,7 +181,7 @@ std::string HttpStatus::toString() const
|
|||
{
|
||||
break;
|
||||
}
|
||||
else if (mType < http_errors[at].mCode)
|
||||
else if (getType() < http_errors[at].mCode)
|
||||
{
|
||||
top = at;
|
||||
}
|
||||
|
|
@ -180,9 +201,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_";
|
||||
|
|
@ -200,7 +221,7 @@ std::string HttpStatus::toTerseString() const
|
|||
if (isHttpStatus())
|
||||
{
|
||||
result << "Http_";
|
||||
error_value = mType;
|
||||
error_value = getType();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -242,7 +263,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
|
||||
|
|
@ -257,5 +278,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
|
||||
|
|
|
|||
|
|
@ -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_
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
/// @}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
91
indra/llmessage/llhttpconstants.cpp → indra/llcorehttp/llhttpconstants.cpp
Normal file → Executable file
91
indra/llmessage/llhttpconstants.cpp → indra/llcorehttp/llhttpconstants.cpp
Normal file → Executable 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;
|
||||
}
|
||||
|
||||
7
indra/llmessage/llhttpconstants.h → indra/llcorehttp/llhttpconstants.h
Normal file → Executable file
7
indra/llmessage/llhttpconstants.h → indra/llcorehttp/llhttpconstants.h
Normal file → Executable 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);
|
||||
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -40,19 +40,24 @@
|
|||
#include "lldir.h"
|
||||
#include "llfile.h"
|
||||
#include "llsdserialize.h"
|
||||
#include "lliopipe.h"
|
||||
#include "llpumpio.h"
|
||||
#include "llhttpclient.h"
|
||||
#include "llsdserialize.h"
|
||||
#include "llproxy.h"
|
||||
|
||||
#include "llcorehttputil.h"
|
||||
#include "llhttpsdhandler.h"
|
||||
#include "httpcommon.h"
|
||||
#include "httpresponse.h"
|
||||
|
||||
#include <curl/curl.h>
|
||||
#include <openssl/crypto.h>
|
||||
|
||||
|
||||
// [SL:KB] - Patch: Viewer-CrashLookup | Checked: 2011-03-24 (Catznip-2.6.0a) | Added: Catznip-2.6.0a
|
||||
#ifdef LL_WINDOWS
|
||||
#include <shellapi.h>
|
||||
#endif // LL_WINDOWS
|
||||
// [/SL:KB]
|
||||
|
||||
LLPumpIO* gServicePump = NULL;
|
||||
|
||||
BOOL gBreak = false;
|
||||
BOOL gSent = false;
|
||||
|
||||
|
|
@ -62,28 +67,32 @@ std::string getFormDataField(const std::string& strFieldName, const std::string&
|
|||
std::string getStartupStateFromLog(std::string& sllog);
|
||||
// </FS:CR>
|
||||
|
||||
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() :
|
||||
// [SL:KB] - Patch: Viewer-CrashLookup | Checked: 2011-03-24 (Catznip-2.6.0a) | Added: Catznip-2.6.0a
|
||||
mCrashLookup(NULL),
|
||||
|
|
@ -233,11 +242,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;
|
||||
|
|
@ -246,10 +257,11 @@ 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");
|
||||
// mFileMap["SettingsXml"] = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS,"settings.xml");
|
||||
// }
|
||||
|
||||
// if (!gDirUtilp->fileExists(mFileMap["SecondLifeLog"]) ) //We would prefer to get this from the per-run but here's our fallback.
|
||||
|
|
@ -465,107 +477,38 @@ std::string getFormDataField(const std::string& strFieldName, const std::string&
|
|||
|
||||
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)
|
||||
{
|
||||
//status_message = llformat("%s, try %d...", msg.c_str(), i+1);
|
||||
// LLHTTPClient::post(host, data, new LLCrashLoggerResponder(), timeout);
|
||||
// [SL:KB] - Patch: Viewer-CrashReporting | Checked: 2011-03-24 (Catznip-2.6.0a) | Modified: Catznip-2.6.0a
|
||||
static const std::string BOUNDARY("------------abcdef012345xyZ");
|
||||
updateApplication(llformat("%s, try %d...", msg.c_str(), i+1));
|
||||
|
||||
LLSD headers = LLSD::emptyMap();
|
||||
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));
|
||||
|
||||
headers["Accept"] = "*/*";
|
||||
headers["Content-Type"] = "multipart/form-data; boundary=" + BOUNDARY;
|
||||
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;
|
||||
}
|
||||
|
||||
std::ostringstream body;
|
||||
|
||||
/*
|
||||
* Send viewer information for the upload handler's benefit
|
||||
*/
|
||||
if (mDebugLog.has("ClientInfo"))
|
||||
{
|
||||
body << getFormDataField("viewer_channel", mDebugLog["ClientInfo"]["Name"], BOUNDARY);
|
||||
body << getFormDataField("viewer_version", mDebugLog["ClientInfo"]["Version"], BOUNDARY);
|
||||
body << getFormDataField("viewer_platform", mDebugLog["ClientInfo"]["Platform"], BOUNDARY);
|
||||
|
||||
// <FS:ND> Add which flavor of FS generated an error
|
||||
body << getFormDataField("viewer_flavor", mDebugLog["ClientInfo"]["Flavor"], BOUNDARY );
|
||||
// </FS:ND>
|
||||
}
|
||||
|
||||
/*
|
||||
* Include crash analysis pony
|
||||
*/
|
||||
if (mCrashLookup)
|
||||
{
|
||||
body << getFormDataField("crash_module_name", mCrashLookup->getModuleName(), BOUNDARY);
|
||||
body << getFormDataField("crash_module_version", llformat("%I64d", mCrashLookup->getModuleVersion()), BOUNDARY);
|
||||
body << getFormDataField("crash_module_versionstring", mCrashLookup->getModuleVersionString(), BOUNDARY);
|
||||
body << getFormDataField("crash_module_displacement", llformat("%I64d", mCrashLookup->getModuleDisplacement()), BOUNDARY);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add the actual crash logs
|
||||
*/
|
||||
for (std::map<std::string, std::string>::const_iterator itFile = mFileMap.begin(), endFile = mFileMap.end();
|
||||
itFile != endFile; ++itFile)
|
||||
{
|
||||
if (itFile->second.empty())
|
||||
continue;
|
||||
|
||||
body << "--" << BOUNDARY << "\r\n"
|
||||
<< "Content-Disposition: form-data; name=\"crash_report[]\"; "
|
||||
<< "filename=\"" << gDirUtilp->getBaseFileName(itFile->second) << "\"\r\n"
|
||||
<< "Content-Type: application/octet-stream"
|
||||
<< "\r\n\r\n";
|
||||
|
||||
// <FS:ND> Linux wants a char const * here
|
||||
// llifstream fstream(itFile->second, std::iostream::binary | std::iostream::out);
|
||||
llifstream fstream(itFile->second.c_str(), std::iostream::binary | std::iostream::out);
|
||||
// </FS:ND>
|
||||
|
||||
if (fstream.is_open())
|
||||
{
|
||||
fstream.seekg(0, std::ios::end);
|
||||
|
||||
U32 fileSize = fstream.tellg();
|
||||
fstream.seekg(0, std::ios::beg);
|
||||
std::vector<char> fileBuffer(fileSize);
|
||||
|
||||
fstream.read(&fileBuffer[0], fileSize);
|
||||
body.write(&fileBuffer[0], fileSize);
|
||||
|
||||
fstream.close();
|
||||
}
|
||||
|
||||
body << "\r\n";
|
||||
}
|
||||
|
||||
/*
|
||||
* Close the post
|
||||
*/
|
||||
body << "--" << BOUNDARY << "--\r\n";
|
||||
|
||||
// postRaw() takes ownership of the buffer and releases it later.
|
||||
size_t size = body.str().size();
|
||||
U8 *data = new U8[size];
|
||||
memcpy(data, body.str().data(), size);
|
||||
|
||||
// Send request
|
||||
LLHTTPClient::postRaw(host, data, size, new LLCrashLoggerResponder(), headers);
|
||||
// [/SL:KB]
|
||||
//updateApplication(llformat("%s, try %d...", msg.c_str(), i+1));
|
||||
//LLHTTPClient::post(host, data, new LLCrashLoggerResponder(), timeout);
|
||||
|
||||
while(!gBreak)
|
||||
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;
|
||||
}
|
||||
|
|
@ -692,14 +635,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
|
||||
#ifdef ND_BUILD64BIT_ARCH
|
||||
|
|
@ -766,16 +707,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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
#include "llsd.h"
|
||||
#include "llcontrol.h"
|
||||
#include "llcrashlock.h"
|
||||
#include "_mutex.h"
|
||||
// [SL:KB] - Patch: Viewer-CrashLookup | Checked: 2011-03-24 (Catznip-2.6.0a) | Added: Catznip-2.6.0a
|
||||
#include "llcrashlookup.h"
|
||||
// [/SL:KB]
|
||||
|
|
@ -69,6 +70,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;
|
||||
|
|
@ -84,6 +90,10 @@ protected:
|
|||
LLSD mDebugLog;
|
||||
bool mSentCrashLogs;
|
||||
LLCrashLock mKeyMaster;
|
||||
|
||||
static int ssl_mutex_count;
|
||||
static LLCoreInt::HttpMutex ** ssl_mutex_list;
|
||||
|
||||
};
|
||||
|
||||
#endif //LLCRASHLOGGER_H
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ project(llinventory)
|
|||
|
||||
include(00-Common)
|
||||
include(LLCommon)
|
||||
include(LLCoreHttp)
|
||||
include(LLMath)
|
||||
include(LLMessage)
|
||||
include(LLVFS)
|
||||
|
|
@ -75,7 +76,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)
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ include(LLAddBuildTest)
|
|||
include(Python)
|
||||
include(Tut)
|
||||
include(Python)
|
||||
include(JsonCpp)
|
||||
|
||||
include_directories (${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
|
|
@ -27,11 +28,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
|
||||
|
|
@ -42,19 +42,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
|
||||
|
|
@ -78,11 +74,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
|
||||
|
|
@ -96,7 +89,6 @@ set(llmessage_SOURCE_FILES
|
|||
lltransfertargetfile.cpp
|
||||
lltransfertargetvfile.cpp
|
||||
lltrustedmessageservice.cpp
|
||||
llurlrequest.cpp
|
||||
lluseroperation.cpp
|
||||
llxfer.cpp
|
||||
llxfer_file.cpp
|
||||
|
|
@ -119,8 +111,6 @@ set(llmessage_SOURCE_FILES
|
|||
set(llmessage_HEADER_FILES
|
||||
CMakeLists.txt
|
||||
|
||||
llares.h
|
||||
llareslistener.h
|
||||
llassetstorage.h
|
||||
llavatarname.h
|
||||
llavatarnamecache.h
|
||||
|
|
@ -132,8 +122,8 @@ set(llmessage_HEADER_FILES
|
|||
llcipher.h
|
||||
llcircuit.h
|
||||
llclassifiedflags.h
|
||||
llcoproceduremanager.h
|
||||
llcorehttputil.h
|
||||
llcurl.h
|
||||
lldatapacker.h
|
||||
lldbstrings.h
|
||||
lldispatcher.h
|
||||
|
|
@ -143,14 +133,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
|
||||
|
|
@ -180,11 +165,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
|
||||
|
|
@ -200,7 +182,6 @@ set(llmessage_HEADER_FILES
|
|||
lltransfertargetfile.h
|
||||
lltransfertargetvfile.h
|
||||
lltrustedmessageservice.h
|
||||
llurlrequest.h
|
||||
lluseroperation.h
|
||||
llvehicleparams.h
|
||||
llxfer.h
|
||||
|
|
@ -226,17 +207,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)
|
||||
|
|
@ -247,36 +252,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)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,93 @@
|
|||
namespace FSCoreHttpUtil
|
||||
{
|
||||
void trivialPostCoroRaw(std::string url, LLCore::HttpRequest::policy_t policyId, LLCore::BufferArray::ptr_t postData, LLCore::HttpHeaders::ptr_t aHeader, completionCallback_t success, completionCallback_t failure)
|
||||
{
|
||||
LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("genericPostCoroRaw", policyId));
|
||||
LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
|
||||
LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions);
|
||||
|
||||
httpOpts->setWantHeaders(true);
|
||||
|
||||
LL_INFOS("HttpCoroutineAdapter", "genericPostCoroRaw") << "Generic POST for " << url << LL_ENDL;
|
||||
|
||||
LLSD result = httpAdapter->postRawAndSuspend(httpRequest, url, postData, httpOpts, aHeader );
|
||||
|
||||
LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
|
||||
LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
|
||||
|
||||
if (!status)
|
||||
{
|
||||
// If a failure routine is provided do it.
|
||||
if (failure)
|
||||
{
|
||||
failure(httpResults);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If a success routine is provided do it.
|
||||
if (success)
|
||||
{
|
||||
success(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void callbackHttpPostRaw(const std::string &url, std::string postData, completionCallback_t success, completionCallback_t failure, LLCore::HttpHeaders::ptr_t aHeader )
|
||||
{
|
||||
|
||||
LLCore::BufferArray::ptr_t postDataBuffer( new LLCore::BufferArray() );
|
||||
postDataBuffer->append( postData.c_str(), postData.size() );
|
||||
|
||||
LLCoros::instance().launch("HttpCoroutineAdapter::genericPostCoroRaw",
|
||||
boost::bind(trivialPostCoroRaw, url, LLCore::HttpRequest::DEFAULT_POLICY_ID, postDataBuffer, aHeader, success, failure));
|
||||
}
|
||||
|
||||
void trivialGetCoroRaw(std::string url, LLCore::HttpRequest::policy_t policyId, LLCore::HttpHeaders::ptr_t aHeader, completionCallback_t success, completionCallback_t failure)
|
||||
{
|
||||
LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t
|
||||
httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("genericGetCoro", policyId));
|
||||
LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
|
||||
LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions);
|
||||
|
||||
httpOpts->setWantHeaders(true);
|
||||
|
||||
LL_INFOS("HttpCoroutineAdapter", "genericGetCoroRaw") << "Generic GET for " << url << LL_ENDL;
|
||||
|
||||
LLSD result = httpAdapter->getRawAndSuspend(httpRequest, url, httpOpts, aHeader );
|
||||
|
||||
LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
|
||||
LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
|
||||
|
||||
if (!status)
|
||||
{
|
||||
if (failure)
|
||||
{
|
||||
failure(httpResults);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (success)
|
||||
{
|
||||
success(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void callbackHttpGetRaw(const std::string &url, completionCallback_t success, completionCallback_t failure, LLCore::HttpHeaders::ptr_t aHeader )
|
||||
{
|
||||
LLCoros::instance().launch("HttpCoroutineAdapter::genericGetCoroRaw",
|
||||
boost::bind(trivialGetCoroRaw, url, LLCore::HttpRequest::DEFAULT_POLICY_ID, aHeader, success, failure));
|
||||
}
|
||||
|
||||
LLCore::HttpHeaders::ptr_t createModifiedSinceHeader( time_t aTime )
|
||||
{
|
||||
std::string strDate = LLDate::toHTTPDateString( gmtime( &aTime ), "%A, %d %b %Y %H:%M:%S GMT" );
|
||||
|
||||
LLCore::HttpHeaders::ptr_t pHeader( new LLCore::HttpHeaders() );
|
||||
pHeader->append( "If-Modified-Since", strDate );
|
||||
|
||||
return pHeader;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
|
||||
namespace FSCoreHttpUtil
|
||||
{
|
||||
typedef boost::function<void(const LLSD &)> completionCallback_t;
|
||||
|
||||
void callbackHttpPostRaw(const std::string &url, std::string postData, completionCallback_t success = NULL, completionCallback_t failure = NULL, LLCore::HttpHeaders::ptr_t aHeader = NULL );
|
||||
void callbackHttpGetRaw(const std::string &url, completionCallback_t success = NULL, completionCallback_t failure = NULL, LLCore::HttpHeaders::ptr_t aHeader = NULL);
|
||||
|
||||
void trivialGetCoroRaw(std::string url, LLCore::HttpRequest::policy_t policyId, LLCore::HttpHeaders::ptr_t aHeader, completionCallback_t success, completionCallback_t failure);
|
||||
void trivialPostCoroRaw(std::string url, LLCore::HttpRequest::policy_t policyId, LLCore::BufferArray::ptr_t postData, LLCore::HttpHeaders::ptr_t aHeader, completionCallback_t success, completionCallback_t failure);
|
||||
}
|
||||
|
||||
|
|
@ -1,876 +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;
|
||||
}
|
||||
|
||||
// <FS:ND> Write nameserver info to logs. Need at least ares 1.10.o for ares_inet_ntop
|
||||
#if (ARES_VERSION >= 0x010A00)
|
||||
ares_addr_node *pServer(0);
|
||||
if( ARES_SUCCESS == ares_get_servers( chan_, &pServer ) && pServer )
|
||||
{
|
||||
ares_addr_node *pCur( pServer );
|
||||
while( pCur )
|
||||
{
|
||||
std::stringstream strm;
|
||||
char aBuffer[ INET6_ADDRSTRLEN +1 ] = {0};
|
||||
|
||||
switch( pCur->family )
|
||||
{
|
||||
case( AF_INET ):
|
||||
{
|
||||
ares_inet_ntop( pCur->family, &pCur->addr.addr4, aBuffer, INET6_ADDRSTRLEN );
|
||||
strm << "IPv4 nameserver " << aBuffer;
|
||||
break;
|
||||
}
|
||||
case( AF_INET6 ):
|
||||
{
|
||||
ares_inet_ntop( pCur->family, &pCur->addr.addr6, aBuffer, INET6_ADDRSTRLEN );
|
||||
strm << "IPv6 nameserver " << aBuffer;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
strm << "Unknown protocol " << pCur->family;
|
||||
}
|
||||
|
||||
LL_INFOS() << strm.str() << LL_ENDL;
|
||||
pCur = pCur->next;
|
||||
}
|
||||
ares_free_data( pServer );
|
||||
}
|
||||
#endif
|
||||
// </FS:ND>
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,585 +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
|
||||
{
|
||||
// <FS:Ansariel> Fix member accessibility
|
||||
friend class LLAres;
|
||||
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
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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) */
|
||||
|
|
@ -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 )
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@
|
|||
|
||||
#ifndef LL_LLASSETSTORAGE_H
|
||||
#define LL_LLASSETSTORAGE_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "lluuid.h"
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
#include "../newview/lggcontactsets.h"
|
||||
|
|
@ -97,6 +105,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
|
||||
//-----------------------------------------------------------------------
|
||||
|
|
@ -128,7 +142,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:
|
||||
|
|
@ -170,94 +189,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)
|
||||
|
|
@ -369,10 +411,15 @@ void LLAvatarNameCache::requestNamesViaCapability()
|
|||
}
|
||||
}
|
||||
|
||||
if (!url.empty())
|
||||
{
|
||||
LL_DEBUGS("AvNameCache") << " getting " << ids << " ids" << LL_ENDL;
|
||||
LLHTTPClient::get(url, new LLAvatarNameResponder(agent_ids));
|
||||
if (!url.empty())
|
||||
{
|
||||
LL_DEBUGS("AvNameCache") << "requested " << ids << " ids" << LL_ENDL;
|
||||
|
||||
std::string coroname =
|
||||
LLCoros::instance().launch("LLAvatarNameCache::requestAvatarNameCache_",
|
||||
boost::bind(&LLAvatarNameCache::requestAvatarNameCache_, url, agent_ids));
|
||||
LL_DEBUGS("AvNameCache") << coroname << " with url '" << url << "', agent_ids.size()=" << agent_ids.size() << LL_ENDL;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -435,11 +482,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)
|
||||
|
|
@ -761,6 +817,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)
|
||||
{
|
||||
// <FS:Ansariel> Optional legacy name cache expiration in case SL can't handle us... ;)
|
||||
|
|
@ -840,7 +940,7 @@ bool LLAvatarNameCache::expirationFromCacheControl(const LLSD& headers, F64 *exp
|
|||
|
||||
return fromCacheControl;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void LLAvatarNameCache::addUseDisplayNamesCallback(const use_display_name_signal_t::slot_type& cb)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
@ -100,7 +99,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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -261,7 +261,7 @@ LLCacheName::~LLCacheName()
|
|||
}
|
||||
|
||||
LLCacheName::Impl::Impl(LLMessageSystem* msg)
|
||||
: mMsg(msg), mUpstreamHost(LLHost::invalid)
|
||||
: mMsg(msg), mUpstreamHost(LLHost())
|
||||
{
|
||||
mMsg->setHandlerFuncFast(
|
||||
_PREHASH_UUIDNameRequest, handleUUIDNameRequest, (void**)this);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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,575 @@ 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
|
||||
|
||||
#include "fscorehttputil.h"
|
||||
|
||||
#endif // LL_LLCOREHTTPUTIL_H
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,569 +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);
|
||||
|
||||
// <Techwolf Lupindo> pass parse error down code path
|
||||
bool mDeserializeError;
|
||||
// </Techwolf Lupindo>
|
||||
|
||||
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);
|
||||
// <FS:Ansariel> Expose time out param
|
||||
//bool getByteRange(const std::string& url, const headers_t& headers, S32 offset, S32 length, LLCurl::ResponderPtr responder);
|
||||
bool getByteRange(const std::string& url, const headers_t& headers, S32 offset, S32 length, LLCurl::ResponderPtr responder, S32 time_out = 0);
|
||||
// </FS:Ansariel>
|
||||
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
|
||||
{
|
||||
// <FS:ND> 64 bit fix. do not truncate a pointer down to 32 bit
|
||||
|
||||
// return (U32)lhs < (U32)rhs;
|
||||
return lhs < rhs;
|
||||
|
||||
// </FS:ND>
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
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
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue