Merged in lindenlab/viewer-release
commit
eb413ec41e
1
.hgtags
1
.hgtags
|
|
@ -513,3 +513,4 @@ e821ef17c6edea4a59997719d8ba416d8c16e143 3.8.5-release
|
|||
ae3297cdd03ab14f19f3811acbc4acd3eb600336 4.0.0-release
|
||||
759710a9acef61aaf7b69f4bc4a5a913de87ad8a 4.0.1-release
|
||||
e9d350764dfbf5a46229e627547ef5c1b1eeef00 4.0.2-release
|
||||
86dfba7ec4332c323025ebeacd8bf343ed0d8cfd 4.0.3-release
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
# https://wiki.secondlife.com/wiki/Automated_Build_System
|
||||
|
||||
|
||||
# Global setting for now...
|
||||
# Global setting for now....
|
||||
Darwin.symbolfiles = "newview/Release/secondlife-symbols-darwin.tar.bz2"
|
||||
CYGWIN.symbolfiles = "newview/Release/secondlife-symbols-windows.tar.bz2"
|
||||
Linux.symbolfiles = "newview/secondlife-symbols-linux.tar.bz2"
|
||||
|
|
|
|||
|
|
@ -87,60 +87,6 @@
|
|||
<key>version</key>
|
||||
<string>1.4.5.297252</string>
|
||||
</map>
|
||||
<key>ares</key>
|
||||
<map>
|
||||
<key>copyright</key>
|
||||
<string>Copyright 1998 by the Massachusetts Institute of Technology.</string>
|
||||
<key>description</key>
|
||||
<string>C-ares, an asynchronous resolver library.</string>
|
||||
<key>license</key>
|
||||
<string>c-ares</string>
|
||||
<key>license_file</key>
|
||||
<string>LICENSES/c-ares.txt</string>
|
||||
<key>name</key>
|
||||
<string>ares</string>
|
||||
<key>platforms</key>
|
||||
<map>
|
||||
<key>darwin</key>
|
||||
<map>
|
||||
<key>archive</key>
|
||||
<map>
|
||||
<key>hash</key>
|
||||
<string>637b4996f703f3e5bf835d847fc4cb81</string>
|
||||
<key>url</key>
|
||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/ares_3p-update-ares/rev/295506/arch/Darwin/installer/ares-1.10.0.295506-darwin-295506.tar.bz2</string>
|
||||
</map>
|
||||
<key>name</key>
|
||||
<string>darwin</string>
|
||||
</map>
|
||||
<key>linux</key>
|
||||
<map>
|
||||
<key>archive</key>
|
||||
<map>
|
||||
<key>hash</key>
|
||||
<string>7771d3653a0daf22d35bf96055d02d9a</string>
|
||||
<key>url</key>
|
||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/ares_3p-update-ares/rev/295506/arch/Linux/installer/ares-1.10.0.295506-linux-295506.tar.bz2</string>
|
||||
</map>
|
||||
<key>name</key>
|
||||
<string>linux</string>
|
||||
</map>
|
||||
<key>windows</key>
|
||||
<map>
|
||||
<key>archive</key>
|
||||
<map>
|
||||
<key>hash</key>
|
||||
<string>f044de05e704d3f3fb6934adf42447c2</string>
|
||||
<key>url</key>
|
||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/ares_3p-update-ares/rev/295506/arch/CYGWIN/installer/ares-1.10.0.295506-windows-295506.tar.bz2</string>
|
||||
</map>
|
||||
<key>name</key>
|
||||
<string>windows</string>
|
||||
</map>
|
||||
</map>
|
||||
<key>version</key>
|
||||
<string>1.10.0.295506</string>
|
||||
</map>
|
||||
<key>boost</key>
|
||||
<map>
|
||||
<key>copyright</key>
|
||||
|
|
@ -266,9 +212,9 @@
|
|||
<key>archive</key>
|
||||
<map>
|
||||
<key>hash</key>
|
||||
<string>d1c5125650a339a5209f429c70f4d395</string>
|
||||
<string>ad0061db7188a1b9a974eb0512eeeb8d</string>
|
||||
<key>url</key>
|
||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/curl_3p-update-curl/rev/297172/arch/Darwin/installer/curl-7.38.0.297172-darwin-297172.tar.bz2</string>
|
||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-curl/rev/312763/arch/Darwin/installer/curl-7.47.0.312763-darwin-312763.tar.bz2</string>
|
||||
</map>
|
||||
<key>name</key>
|
||||
<string>darwin</string>
|
||||
|
|
@ -278,9 +224,9 @@
|
|||
<key>archive</key>
|
||||
<map>
|
||||
<key>hash</key>
|
||||
<string>ee6c089ee193e551040d610befc5d1c1</string>
|
||||
<string>f49d4ed203b03852a3f6b01b18319f7a</string>
|
||||
<key>url</key>
|
||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/curl_3p-update-curl/rev/297172/arch/Linux/installer/curl-7.38.0.297172-linux-297172.tar.bz2</string>
|
||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-curl/rev/312763/arch/Linux/installer/curl-7.47.0.312763-linux-312763.tar.bz2</string>
|
||||
</map>
|
||||
<key>name</key>
|
||||
<string>linux</string>
|
||||
|
|
@ -290,16 +236,18 @@
|
|||
<key>archive</key>
|
||||
<map>
|
||||
<key>hash</key>
|
||||
<string>fdeca7cbc074a88d2701d74a31d21bd8</string>
|
||||
<string>5e0d4f4a5a5bbcba610aafbb91c30b2b</string>
|
||||
<key>hash_algorithm</key>
|
||||
<string>md5</string>
|
||||
<key>url</key>
|
||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/curl_3p-update-curl/rev/297172/arch/CYGWIN/installer/curl-7.38.0.297172-windows-297172.tar.bz2</string>
|
||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-curl/rev/312763/arch/CYGWIN/installer/curl-7.47.0.312763-windows-312763.tar.bz2</string>
|
||||
</map>
|
||||
<key>name</key>
|
||||
<string>windows</string>
|
||||
</map>
|
||||
</map>
|
||||
<key>version</key>
|
||||
<string>7.38.0.297172</string>
|
||||
<string>7.47.0.312763</string>
|
||||
</map>
|
||||
<key>db</key>
|
||||
<map>
|
||||
|
|
@ -696,9 +644,9 @@
|
|||
<key>archive</key>
|
||||
<map>
|
||||
<key>hash</key>
|
||||
<string>f577144536fd7c9d26d9f989acf17857</string>
|
||||
<string>44c596c659d32a86972ac9c6f206cb68</string>
|
||||
<key>url</key>
|
||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/glh-linear_3p-update-glh-linear/rev/297692/arch/Linux/installer/glh_linear-0.0.0-common-297692.tar.bz2</string>
|
||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/glh-linear_3p-glh-linear/rev/303446/arch/Linux/installer/glh_linear-0.0.0-common-303446.tar.bz2</string>
|
||||
</map>
|
||||
<key>name</key>
|
||||
<string>common</string>
|
||||
|
|
@ -1506,9 +1454,9 @@
|
|||
<key>archive</key>
|
||||
<map>
|
||||
<key>hash</key>
|
||||
<string>ce1261a54d877ab5530ae6a9134a77a3</string>
|
||||
<string>faa1e5b7cf70c143caabe190fa5588ce</string>
|
||||
<key>url</key>
|
||||
<string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/llappearance_viewer-update-llappearance-utility/rev/294906/arch/Linux/installer/llappearance_utility-0.0.1-linux-294906.tar.bz2</string>
|
||||
<string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/llappearance_viewer-update-llappearance-utility/rev/304432/arch/Linux/installer/llappearance_utility-0.0.1-linux-304432.tar.bz2</string>
|
||||
</map>
|
||||
<key>name</key>
|
||||
<string>linux</string>
|
||||
|
|
@ -2068,9 +2016,9 @@
|
|||
<key>archive</key>
|
||||
<map>
|
||||
<key>hash</key>
|
||||
<string>68a8fab5ad3a180487598d3a3e0d57b1</string>
|
||||
<string>3dd9bf4185bf2df413d890ca9eeab76c</string>
|
||||
<key>url</key>
|
||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/slvoice_3p-update-slvoice/rev/298329/arch/Darwin/installer/slvoice-4.6.0017.21209.298329-darwin-298329.tar.bz2</string>
|
||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/vivox_3p-slvoice/rev/302004/arch/Darwin/installer/slvoice-4.6.0017.22050.302004-darwin-302004.tar.bz2</string>
|
||||
</map>
|
||||
<key>name</key>
|
||||
<string>darwin</string>
|
||||
|
|
@ -2080,9 +2028,9 @@
|
|||
<key>archive</key>
|
||||
<map>
|
||||
<key>hash</key>
|
||||
<string>48ed7ddcf93fa3c751d677f5eb5f9367</string>
|
||||
<string>06c3a9b1005249f0c94a8b9f3775f3d3</string>
|
||||
<key>url</key>
|
||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/slvoice_3p-update-slvoice/rev/298329/arch/Linux/installer/slvoice-3.2.0002.10426.298329-linux-298329.tar.bz2</string>
|
||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/vivox_3p-slvoice/rev/302004/arch/Linux/installer/slvoice-3.2.0002.10426.302004-linux-302004.tar.bz2</string>
|
||||
</map>
|
||||
<key>name</key>
|
||||
<string>linux</string>
|
||||
|
|
@ -2092,16 +2040,16 @@
|
|||
<key>archive</key>
|
||||
<map>
|
||||
<key>hash</key>
|
||||
<string>399afab7047e6fa62e7b2fb1768059ea</string>
|
||||
<string>47a3316dae47cc4e7c1ea7b74ba8dd1e</string>
|
||||
<key>url</key>
|
||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/slvoice_3p-update-slvoice/rev/298329/arch/CYGWIN/installer/slvoice-4.6.0017.21209.298329-windows-298329.tar.bz2</string>
|
||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/vivox_3p-slvoice/rev/302004/arch/CYGWIN/installer/slvoice-4.6.0017.22050.302004-windows-302004.tar.bz2</string>
|
||||
</map>
|
||||
<key>name</key>
|
||||
<string>windows</string>
|
||||
</map>
|
||||
</map>
|
||||
<key>version</key>
|
||||
<string>4.6.0017.21209.2988329</string>
|
||||
<string>3.2.0002.10426.302004</string>
|
||||
</map>
|
||||
<key>tut</key>
|
||||
<map>
|
||||
|
|
|
|||
|
|
@ -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 " "
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -123,3 +123,9 @@ else (USESYSTEMLIBS)
|
|||
debug boost_thread-mt-d)
|
||||
endif (WINDOWS)
|
||||
endif (USESYSTEMLIBS)
|
||||
|
||||
if (LINUX)
|
||||
set(BOOST_SYSTEM_LIBRARY ${BOOST_SYSTEM_LIBRARY} rt)
|
||||
set(BOOST_THREAD_LIBRARY ${BOOST_THREAD_LIBRARY} rt)
|
||||
endif (LINUX)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -59,7 +57,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
|
||||
)
|
||||
|
|
@ -35,6 +35,7 @@ INCLUDE(GoogleMock)
|
|||
${APRUTIL_LIBRARIES}
|
||||
${APR_LIBRARIES}
|
||||
llcommon
|
||||
llcorehttp
|
||||
)
|
||||
IF(NOT "${project}" STREQUAL "llmath")
|
||||
# add llmath as a dep unless the tested module *is* llmath!
|
||||
|
|
@ -49,6 +50,9 @@ INCLUDE(GoogleMock)
|
|||
${GOOGLEMOCK_INCLUDE_DIRS}
|
||||
)
|
||||
SET(alltest_LIBRARIES
|
||||
${BOOST_COROUTINE_LIBRARY}
|
||||
${BOOST_CONTEXT_LIBRARY}
|
||||
${BOOST_SYSTEM_LIBRARY}
|
||||
${GOOGLEMOCK_LIBRARIES}
|
||||
${PTHREAD_LIBRARY}
|
||||
${WINDOWS_LIBRARIES}
|
||||
|
|
@ -191,6 +195,9 @@ FUNCTION(LL_ADD_INTEGRATION_TEST
|
|||
|
||||
SET(libraries
|
||||
${library_dependencies}
|
||||
${BOOST_COROUTINE_LIBRARY}
|
||||
${BOOST_CONTEXT_LIBRARY}
|
||||
${BOOST_SYSTEM_LIBRARY}
|
||||
${GOOGLEMOCK_LIBRARIES}
|
||||
${PTHREAD_LIBRARY}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -262,10 +266,14 @@ target_link_libraries(
|
|||
${APRUTIL_LIBRARIES}
|
||||
${APR_LIBRARIES}
|
||||
${EXPAT_LIBRARIES}
|
||||
${JSONCPP_LIBRARIES}
|
||||
${ZLIB_LIBRARIES}
|
||||
${WINDOWS_LIBRARIES}
|
||||
${BOOST_COROUTINE_LIBRARY}
|
||||
${BOOST_CONTEXT_LIBRARY}
|
||||
${BOOST_PROGRAM_OPTIONS_LIBRARY}
|
||||
${BOOST_REGEX_LIBRARY}
|
||||
${BOOST_SYSTEM_LIBRARY}
|
||||
${GOOGLE_PERFTOOLS_LIBRARIES}
|
||||
${URIPARSER_LIBRARIES}
|
||||
)
|
||||
|
|
@ -286,7 +294,14 @@ if (LL_TESTS)
|
|||
LL_ADD_PROJECT_UNIT_TESTS(llcommon "${llcommon_TEST_SOURCE_FILES}")
|
||||
|
||||
#set(TEST_DEBUG on)
|
||||
set(test_libs llcommon ${LLCOMMON_LIBRARIES} ${WINDOWS_LIBRARIES} ${GOOGLEMOCK_LIBRARIES})
|
||||
set(test_libs llcommon
|
||||
${LLCOMMON_LIBRARIES}
|
||||
${WINDOWS_LIBRARIES}
|
||||
${GOOGLEMOCK_LIBRARIES}
|
||||
${BOOST_COROUTINE_LIBRARY}
|
||||
${BOOST_CONTEXT_LIBRARY}
|
||||
${BOOST_THREAD_LIBRARY}
|
||||
${BOOST_SYSTEM_LIBRARY})
|
||||
LL_ADD_INTEGRATION_TEST(commonmisc "" "${test_libs}")
|
||||
LL_ADD_INTEGRATION_TEST(bitpack "" "${test_libs}")
|
||||
LL_ADD_INTEGRATION_TEST(llbase64 "" "${test_libs}")
|
||||
|
|
@ -308,7 +323,7 @@ if (LL_TESTS)
|
|||
LL_ADD_INTEGRATION_TEST(llunits "" "${test_libs}")
|
||||
LL_ADD_INTEGRATION_TEST(stringize "" "${test_libs}")
|
||||
LL_ADD_INTEGRATION_TEST(lleventdispatcher "" "${test_libs}")
|
||||
LL_ADD_INTEGRATION_TEST(lleventcoro "" "${test_libs};${BOOST_CONTEXT_LIBRARY};${BOOST_THREAD_LIBRARY};${BOOST_COROUTINE_LIBRARY};${BOOST_SYSTEM_LIBRARY}")
|
||||
LL_ADD_INTEGRATION_TEST(lleventcoro "" "${test_libs}")
|
||||
LL_ADD_INTEGRATION_TEST(llprocess "" "${test_libs}")
|
||||
LL_ADD_INTEGRATION_TEST(llleap "" "${test_libs}")
|
||||
LL_ADD_INTEGRATION_TEST(llstreamqueue "" "${test_libs}")
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,14 +30,20 @@
|
|||
#define LL_LLCOROS_H
|
||||
|
||||
#include <boost/dcoroutine/coroutine.hpp>
|
||||
#include <boost/dcoroutine/future.hpp>
|
||||
#include "llsingleton.h"
|
||||
#include <boost/ptr_container/ptr_map.hpp>
|
||||
#include <boost/function.hpp>
|
||||
#include <boost/thread/tss.hpp>
|
||||
#include <string>
|
||||
#include <boost/preprocessor/repetition/enum_params.hpp>
|
||||
#include <boost/preprocessor/repetition/enum_binary_params.hpp>
|
||||
#include <boost/preprocessor/iteration/local.hpp>
|
||||
#include <stdexcept>
|
||||
|
||||
// forward-declare helper class
|
||||
namespace llcoro
|
||||
{
|
||||
class Suspending;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registry of named Boost.Coroutine instances
|
||||
*
|
||||
|
|
@ -80,8 +86,8 @@ class LL_COMMON_API LLCoros: public LLSingleton<LLCoros>
|
|||
public:
|
||||
/// Canonical boost::dcoroutines::coroutine signature we use
|
||||
typedef boost::dcoroutines::coroutine<void()> coro;
|
||||
/// Canonical 'self' type
|
||||
typedef coro::self self;
|
||||
/// Canonical callable type
|
||||
typedef boost::function<void()> callable_t;
|
||||
|
||||
/**
|
||||
* Create and start running a new coroutine with specified name. The name
|
||||
|
|
@ -94,39 +100,33 @@ public:
|
|||
* {
|
||||
* public:
|
||||
* ...
|
||||
* // Do NOT NOT NOT accept reference params other than 'self'!
|
||||
* // Do NOT NOT NOT accept reference params!
|
||||
* // Pass by value only!
|
||||
* void myCoroutineMethod(LLCoros::self& self, std::string, LLSD);
|
||||
* void myCoroutineMethod(std::string, LLSD);
|
||||
* ...
|
||||
* };
|
||||
* ...
|
||||
* std::string name = LLCoros::instance().launch(
|
||||
* "mycoro", boost::bind(&MyClass::myCoroutineMethod, this, _1,
|
||||
* "mycoro", boost::bind(&MyClass::myCoroutineMethod, this,
|
||||
* "somestring", LLSD(17));
|
||||
* @endcode
|
||||
*
|
||||
* Your function/method must accept LLCoros::self& as its first parameter.
|
||||
* It can accept any other parameters you want -- but ONLY BY VALUE!
|
||||
* Other reference parameters are a BAD IDEA! You Have Been Warned. See
|
||||
* Your function/method can accept any parameters you want -- but ONLY BY
|
||||
* VALUE! Reference parameters are a BAD IDEA! You Have Been Warned. See
|
||||
* DEV-32777 comments for an explanation.
|
||||
*
|
||||
* Pass a callable that accepts the single LLCoros::self& parameter. It
|
||||
* may work to pass a free function whose only parameter is 'self'; for
|
||||
* all other cases use boost::bind(). Of course, for a non-static class
|
||||
* method, the first parameter must be the class instance. Use the
|
||||
* placeholder _1 for the 'self' parameter. Any other parameters should be
|
||||
* passed via the bind() expression.
|
||||
* Pass a nullary callable. It works to directly pass a nullary free
|
||||
* function (or static method); for all other cases use boost::bind(). Of
|
||||
* course, for a non-static class method, the first parameter must be the
|
||||
* class instance. Any other parameters should be passed via the bind()
|
||||
* expression.
|
||||
*
|
||||
* launch() tweaks the suggested name so it won't collide with any
|
||||
* existing coroutine instance, creates the coroutine instance, registers
|
||||
* it with the tweaked name and runs it until its first wait. At that
|
||||
* point it returns the tweaked name.
|
||||
*/
|
||||
template <typename CALLABLE>
|
||||
std::string launch(const std::string& prefix, const CALLABLE& callable)
|
||||
{
|
||||
return launchImpl(prefix, new coro(callable, mStackSize));
|
||||
}
|
||||
std::string launch(const std::string& prefix, const callable_t& callable);
|
||||
|
||||
/**
|
||||
* Abort a running coroutine by name. Normally, when a coroutine either
|
||||
|
|
@ -138,33 +138,150 @@ public:
|
|||
bool kill(const std::string& name);
|
||||
|
||||
/**
|
||||
* From within a coroutine, pass its @c self object to look up the
|
||||
* (tweaked) name string by which this coroutine is registered. Returns
|
||||
* the empty string if not found (e.g. if the coroutine was launched by
|
||||
* hand rather than using LLCoros::launch()).
|
||||
* From within a coroutine, look up the (tweaked) name string by which
|
||||
* this coroutine is registered. Returns the empty string if not found
|
||||
* (e.g. if the coroutine was launched by hand rather than using
|
||||
* LLCoros::launch()).
|
||||
*/
|
||||
template <typename COROUTINE_SELF>
|
||||
std::string getName(const COROUTINE_SELF& self) const
|
||||
{
|
||||
return getNameByID(self.get_id());
|
||||
}
|
||||
|
||||
/// getName() by self.get_id()
|
||||
std::string getNameByID(const void* self_id) const;
|
||||
std::string getName() const;
|
||||
|
||||
/// for delayed initialization
|
||||
void setStackSize(S32 stacksize);
|
||||
|
||||
/// get the current coro::self& for those who really really care
|
||||
static coro::self& get_self();
|
||||
|
||||
/**
|
||||
* Most coroutines, most of the time, don't "consume" the events for which
|
||||
* they're suspending. This way, an arbitrary number of listeners (whether
|
||||
* coroutines or simple callbacks) can be registered on a particular
|
||||
* LLEventPump, every listener responding to each of the events on that
|
||||
* LLEventPump. But a particular coroutine can assert that it will consume
|
||||
* each event for which it suspends. (See also llcoro::postAndSuspend(),
|
||||
* llcoro::VoidListener)
|
||||
*/
|
||||
static void set_consuming(bool consuming);
|
||||
static bool get_consuming();
|
||||
|
||||
/**
|
||||
* Please do NOT directly use boost::dcoroutines::future! It is essential
|
||||
* to maintain the "current" coroutine at every context switch. This
|
||||
* Future wraps the essential boost::dcoroutines::future functionality
|
||||
* with that maintenance.
|
||||
*/
|
||||
template <typename T>
|
||||
class Future;
|
||||
|
||||
private:
|
||||
friend class LLSingleton<LLCoros>;
|
||||
LLCoros();
|
||||
std::string launchImpl(const std::string& prefix, coro* newCoro);
|
||||
friend class LLSingleton<LLCoros>;
|
||||
friend class llcoro::Suspending;
|
||||
std::string generateDistinctName(const std::string& prefix) const;
|
||||
bool cleanup(const LLSD&);
|
||||
struct CoroData;
|
||||
static void no_cleanup(CoroData*);
|
||||
static void toplevel(coro::self& self, CoroData* data, const callable_t& callable);
|
||||
static CoroData& get_CoroData(const std::string& caller);
|
||||
|
||||
S32 mStackSize;
|
||||
typedef boost::ptr_map<std::string, coro> CoroMap;
|
||||
|
||||
// coroutine-local storage, as it were: one per coro we track
|
||||
struct CoroData
|
||||
{
|
||||
CoroData(CoroData* prev, const std::string& name,
|
||||
const callable_t& callable, S32 stacksize);
|
||||
|
||||
// The boost::dcoroutines library supports asymmetric coroutines. Every
|
||||
// time we context switch out of a coroutine, we pass control to the
|
||||
// previously-active one (or to the non-coroutine stack owned by the
|
||||
// thread). So our management of the "current" coroutine must be able to
|
||||
// restore the previous value when we're about to switch away.
|
||||
CoroData* mPrev;
|
||||
// tweaked name of the current coroutine
|
||||
const std::string mName;
|
||||
// the actual coroutine instance
|
||||
LLCoros::coro mCoro;
|
||||
// set_consuming() state
|
||||
bool mConsuming;
|
||||
// When the dcoroutine library calls a top-level callable, it implicitly
|
||||
// passes coro::self& as the first parameter. All our consumer code used
|
||||
// to explicitly pass coro::self& down through all levels of call stack,
|
||||
// because at the leaf level we need it for context-switching. But since
|
||||
// coroutines are based on cooperative switching, we can cause the
|
||||
// top-level entry point to stash a pointer to the currently-running
|
||||
// coroutine, and manage it appropriately as we switch out and back in.
|
||||
// That eliminates the need to pass it as an explicit parameter down
|
||||
// through every level, which is unfortunately viral in nature. Finding it
|
||||
// implicitly rather than explicitly allows minor maintenance in which a
|
||||
// leaf-level function adds a new async I/O call that suspends the calling
|
||||
// coroutine, WITHOUT having to propagate coro::self& through every
|
||||
// function signature down to that point -- and of course through every
|
||||
// other caller of every such function.
|
||||
LLCoros::coro::self* mSelf;
|
||||
};
|
||||
typedef boost::ptr_map<std::string, CoroData> CoroMap;
|
||||
CoroMap mCoros;
|
||||
|
||||
// identify the current coroutine's CoroData
|
||||
static boost::thread_specific_ptr<LLCoros::CoroData> sCurrentCoro;
|
||||
};
|
||||
|
||||
namespace llcoro
|
||||
{
|
||||
|
||||
/// Instantiate one of these in a block surrounding any leaf point when
|
||||
/// control literally switches away from this coroutine.
|
||||
class Suspending
|
||||
{
|
||||
public:
|
||||
Suspending();
|
||||
~Suspending();
|
||||
|
||||
private:
|
||||
LLCoros::CoroData* mSuspended;
|
||||
};
|
||||
|
||||
} // namespace llcoro
|
||||
|
||||
template <typename T>
|
||||
class LLCoros::Future
|
||||
{
|
||||
typedef boost::dcoroutines::future<T> dfuture;
|
||||
|
||||
public:
|
||||
Future():
|
||||
mFuture(get_self())
|
||||
{}
|
||||
|
||||
typedef typename boost::dcoroutines::make_callback_result<dfuture>::type callback_t;
|
||||
|
||||
callback_t make_callback()
|
||||
{
|
||||
return boost::dcoroutines::make_callback(mFuture);
|
||||
}
|
||||
|
||||
#ifndef LL_LINUX
|
||||
explicit
|
||||
#endif
|
||||
operator bool() const
|
||||
{
|
||||
return bool(mFuture);
|
||||
}
|
||||
|
||||
bool operator!() const
|
||||
{
|
||||
return ! mFuture;
|
||||
}
|
||||
|
||||
T get()
|
||||
{
|
||||
// instantiate Suspending to manage the "current" coroutine
|
||||
llcoro::Suspending suspended;
|
||||
return *mFuture;
|
||||
}
|
||||
|
||||
private:
|
||||
dfuture mFuture;
|
||||
};
|
||||
|
||||
#endif /* ! defined(LL_LLCOROS_H) */
|
||||
|
|
|
|||
|
|
@ -354,6 +354,7 @@ typedef LLError::NoClassInfo _LL_CLASS_TO_LOG;
|
|||
#define LL_WARNS(...) lllog(LLError::LEVEL_WARN, false, ##__VA_ARGS__)
|
||||
#define LL_ERRS(...) lllog(LLError::LEVEL_ERROR, false, ##__VA_ARGS__)
|
||||
// alternative to llassert_always that prints explanatory message
|
||||
#define LL_WARNS_IF(exp, ...) if (exp) LL_WARNS(##__VA_ARGS__) << "(" #exp ")"
|
||||
#define LL_ERRS_IF(exp, ...) if (exp) LL_ERRS(##__VA_ARGS__) << "(" #exp ")"
|
||||
|
||||
// Only print the log message once (good for warnings or infos that would otherwise
|
||||
|
|
|
|||
|
|
@ -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,11 +29,10 @@
|
|||
#if ! defined(LL_LLEVENTCORO_H)
|
||||
#define LL_LLEVENTCORO_H
|
||||
|
||||
#include <boost/dcoroutine/coroutine.hpp>
|
||||
#include <boost/dcoroutine/future.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
#include <utility> // std::pair
|
||||
#include "llevents.h"
|
||||
#include "llerror.h"
|
||||
|
||||
|
|
@ -74,111 +73,46 @@ private:
|
|||
boost::optional<LLEventPump&> mPump;
|
||||
};
|
||||
|
||||
/// This is an adapter for a signature like void LISTENER(const LLSD&), which
|
||||
/// isn't a valid LLEventPump listener: such listeners should return bool.
|
||||
template <typename LISTENER>
|
||||
class LLVoidListener
|
||||
namespace llcoro
|
||||
{
|
||||
public:
|
||||
LLVoidListener(const LISTENER& listener):
|
||||
mListener(listener)
|
||||
{}
|
||||
bool operator()(const LLSD& event)
|
||||
{
|
||||
mListener(event);
|
||||
// don't swallow the event, let other listeners see it
|
||||
return false;
|
||||
}
|
||||
private:
|
||||
LISTENER mListener;
|
||||
};
|
||||
|
||||
/// LLVoidListener helper function to infer the type of the LISTENER
|
||||
template <typename LISTENER>
|
||||
LLVoidListener<LISTENER> voidlistener(const LISTENER& listener)
|
||||
{
|
||||
return LLVoidListener<LISTENER>(listener);
|
||||
}
|
||||
|
||||
namespace LLEventDetail
|
||||
{
|
||||
/// Implementation for listenerNameForCoro(), see below
|
||||
LL_COMMON_API std::string listenerNameForCoroImpl(const void* self_id);
|
||||
|
||||
/**
|
||||
* waitForEventOn() permits a coroutine to temporarily listen on an
|
||||
* LLEventPump any number of times. We don't really want to have to ask
|
||||
* the caller to label each such call with a distinct string; the whole
|
||||
* point of waitForEventOn() is to present a nice sequential interface to
|
||||
* the underlying LLEventPump-with-named-listeners machinery. So we'll use
|
||||
* LLEventPump::inventName() to generate a distinct name for each
|
||||
* temporary listener. On the other hand, because a given coroutine might
|
||||
* call waitForEventOn() any number of times, we don't really want to
|
||||
* consume an arbitrary number of generated inventName()s: that namespace,
|
||||
* though large, is nonetheless finite. So we memoize an invented name for
|
||||
* each distinct coroutine instance (each different 'self' object). We
|
||||
* can't know the type of 'self', because it depends on the coroutine
|
||||
* body's signature. So we cast its address to void*, looking for distinct
|
||||
* pointer values. Yes, that means that an early coroutine could cache a
|
||||
* value here, then be destroyed, only to be supplanted by a later
|
||||
* coroutine (of the same or different type), and we'll end up
|
||||
* "recognizing" the second one and reusing the listener name -- but
|
||||
* that's okay, since it won't collide with any listener name used by the
|
||||
* earlier coroutine since that earlier coroutine no longer exists.
|
||||
*/
|
||||
template <typename COROUTINE_SELF>
|
||||
std::string listenerNameForCoro(COROUTINE_SELF& self)
|
||||
{
|
||||
return listenerNameForCoroImpl(self.get_id());
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement behavior described for postAndWait()'s @a replyPumpNamePath
|
||||
* parameter:
|
||||
*
|
||||
* * If <tt>path.isUndefined()</tt>, do nothing.
|
||||
* * If <tt>path.isString()</tt>, @a dest is an LLSD map: store @a value
|
||||
* into <tt>dest[path.asString()]</tt>.
|
||||
* * If <tt>path.isInteger()</tt>, @a dest is an LLSD array: store @a
|
||||
* value into <tt>dest[path.asInteger()]</tt>.
|
||||
* * If <tt>path.isArray()</tt>, iteratively apply the rules above to step
|
||||
* down through the structure of @a dest. The last array entry in @a
|
||||
* path specifies the entry in the lowest-level structure in @a dest
|
||||
* into which to store @a value.
|
||||
*
|
||||
* @note
|
||||
* In the degenerate case in which @a path is an empty array, @a dest will
|
||||
* @em become @a value rather than @em containing it.
|
||||
*/
|
||||
LL_COMMON_API void storeToLLSDPath(LLSD& dest, const LLSD& path, const LLSD& value);
|
||||
} // namespace LLEventDetail
|
||||
|
||||
/**
|
||||
* Post specified LLSD event on the specified LLEventPump, then wait for a
|
||||
* Yield control from a coroutine for one "mainloop" tick. If your coroutine
|
||||
* runs without suspending for nontrivial time, sprinkle in calls to this
|
||||
* function to avoid stalling the rest of the viewer processing.
|
||||
*/
|
||||
void suspend();
|
||||
|
||||
/**
|
||||
* Yield control from a coroutine for at least the specified number of seconds
|
||||
*/
|
||||
void suspendUntilTimeout(float seconds);
|
||||
|
||||
/**
|
||||
* Post specified LLSD event on the specified LLEventPump, then suspend for a
|
||||
* response on specified other LLEventPump. This is more than mere
|
||||
* convenience: the difference between this function and the sequence
|
||||
* @code
|
||||
* requestPump.post(myEvent);
|
||||
* LLSD reply = waitForEventOn(self, replyPump);
|
||||
* LLSD reply = suspendUntilEventOn(replyPump);
|
||||
* @endcode
|
||||
* is that the sequence above fails if the reply is posted immediately on
|
||||
* @a replyPump, that is, before <tt>requestPump.post()</tt> returns. In the
|
||||
* sequence above, the running coroutine isn't even listening on @a replyPump
|
||||
* until <tt>requestPump.post()</tt> returns and @c waitForEventOn() is
|
||||
* until <tt>requestPump.post()</tt> returns and @c suspendUntilEventOn() is
|
||||
* entered. Therefore, the coroutine completely misses an immediate reply
|
||||
* event, making it wait indefinitely.
|
||||
* event, making it suspend indefinitely.
|
||||
*
|
||||
* By contrast, postAndWait() listens on the @a replyPump @em before posting
|
||||
* By contrast, postAndSuspend() listens on the @a replyPump @em before posting
|
||||
* the specified LLSD event on the specified @a requestPump.
|
||||
*
|
||||
* @param self The @c self object passed into a coroutine
|
||||
* @param event LLSD data to be posted on @a requestPump
|
||||
* @param requestPump an LLEventPump on which to post @a event. Pass either
|
||||
* the LLEventPump& or its string name. However, if you pass a
|
||||
* default-constructed @c LLEventPumpOrPumpName, we skip the post() call.
|
||||
* @param replyPump an LLEventPump on which postAndWait() will listen for a
|
||||
* @param replyPump an LLEventPump on which postAndSuspend() will listen for a
|
||||
* reply. Pass either the LLEventPump& or its string name. The calling
|
||||
* coroutine will wait until that reply arrives. (If you're concerned about a
|
||||
* coroutine will suspend until that reply arrives. (If you're concerned about a
|
||||
* reply that might not arrive, please see also LLEventTimeout.)
|
||||
* @param replyPumpNamePath specifies the location within @a event in which to
|
||||
* store <tt>replyPump.getName()</tt>. This is a strictly optional convenience
|
||||
|
|
@ -201,101 +135,29 @@ namespace LLEventDetail
|
|||
* @a replyPumpNamePath specifies the entry in the lowest-level structure in
|
||||
* @a event into which to store <tt>replyPump.getName()</tt>.
|
||||
*/
|
||||
template <typename SELF>
|
||||
LLSD postAndWait(SELF& self, const LLSD& event, const LLEventPumpOrPumpName& requestPump,
|
||||
const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath=LLSD())
|
||||
{
|
||||
// declare the future
|
||||
boost::dcoroutines::future<LLSD> future(self);
|
||||
// make a callback that will assign a value to the future, and listen on
|
||||
// the specified LLEventPump with that callback
|
||||
std::string listenerName(LLEventDetail::listenerNameForCoro(self));
|
||||
LLTempBoundListener connection(
|
||||
replyPump.getPump().listen(listenerName,
|
||||
voidlistener(boost::dcoroutines::make_callback(future))));
|
||||
// skip the "post" part if requestPump is default-constructed
|
||||
if (requestPump)
|
||||
{
|
||||
// If replyPumpNamePath is non-empty, store the replyPump name in the
|
||||
// request event.
|
||||
LLSD modevent(event);
|
||||
LLEventDetail::storeToLLSDPath(modevent, replyPumpNamePath, replyPump.getPump().getName());
|
||||
LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName
|
||||
<< " posting to " << requestPump.getPump().getName()
|
||||
<< LL_ENDL;
|
||||
|
||||
// *NOTE:Mani - Removed because modevent could contain user's hashed passwd.
|
||||
// << ": " << modevent << LL_ENDL;
|
||||
requestPump.getPump().post(modevent);
|
||||
}
|
||||
LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName
|
||||
<< " about to wait on LLEventPump " << replyPump.getPump().getName()
|
||||
<< LL_ENDL;
|
||||
// trying to dereference ("resolve") the future makes us wait for it
|
||||
LLSD value(*future);
|
||||
LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName
|
||||
<< " resuming with " << value << LL_ENDL;
|
||||
// returning should disconnect the connection
|
||||
return value;
|
||||
}
|
||||
LLSD postAndSuspend(const LLSD& event, const LLEventPumpOrPumpName& requestPump,
|
||||
const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath=LLSD());
|
||||
|
||||
/// Wait for the next event on the specified LLEventPump. Pass either the
|
||||
/// LLEventPump& or its string name.
|
||||
template <typename SELF>
|
||||
LLSD waitForEventOn(SELF& self, const LLEventPumpOrPumpName& pump)
|
||||
inline
|
||||
LLSD suspendUntilEventOn(const LLEventPumpOrPumpName& pump)
|
||||
{
|
||||
// This is now a convenience wrapper for postAndWait().
|
||||
return postAndWait(self, LLSD(), LLEventPumpOrPumpName(), pump);
|
||||
// This is now a convenience wrapper for postAndSuspend().
|
||||
return postAndSuspend(LLSD(), LLEventPumpOrPumpName(), pump);
|
||||
}
|
||||
|
||||
/// return type for two-pump variant of waitForEventOn()
|
||||
} // namespace llcoro
|
||||
|
||||
/// return type for two-pump variant of suspendUntilEventOn()
|
||||
typedef std::pair<LLSD, int> LLEventWithID;
|
||||
|
||||
namespace LLEventDetail
|
||||
namespace llcoro
|
||||
{
|
||||
/**
|
||||
* This helper is specifically for the two-pump version of waitForEventOn().
|
||||
* We use a single future object, but we want to listen on two pumps with it.
|
||||
* Since we must still adapt from (the callable constructed by)
|
||||
* boost::dcoroutines::make_callback() (void return) to provide an event
|
||||
* listener (bool return), we've adapted LLVoidListener for the purpose. The
|
||||
* basic idea is that we construct a distinct instance of WaitForEventOnHelper
|
||||
* -- binding different instance data -- for each of the pumps. Then, when a
|
||||
* pump delivers an LLSD value to either WaitForEventOnHelper, it can combine
|
||||
* that LLSD with its discriminator to feed the future object.
|
||||
*/
|
||||
template <typename LISTENER>
|
||||
class WaitForEventOnHelper
|
||||
{
|
||||
public:
|
||||
WaitForEventOnHelper(const LISTENER& listener, int discriminator):
|
||||
mListener(listener),
|
||||
mDiscrim(discriminator)
|
||||
{}
|
||||
// this signature is required for an LLEventPump listener
|
||||
bool operator()(const LLSD& event)
|
||||
{
|
||||
// our future object is defined to accept LLEventWithID
|
||||
mListener(LLEventWithID(event, mDiscrim));
|
||||
// don't swallow the event, let other listeners see it
|
||||
return false;
|
||||
}
|
||||
private:
|
||||
LISTENER mListener;
|
||||
const int mDiscrim;
|
||||
};
|
||||
|
||||
/// WaitForEventOnHelper type-inference helper
|
||||
template <typename LISTENER>
|
||||
WaitForEventOnHelper<LISTENER> wfeoh(const LISTENER& listener, int discriminator)
|
||||
{
|
||||
return WaitForEventOnHelper<LISTENER>(listener, discriminator);
|
||||
}
|
||||
} // namespace LLEventDetail
|
||||
|
||||
/**
|
||||
* This function waits for a reply on either of two specified LLEventPumps.
|
||||
* Otherwise, it closely resembles postAndWait(); please see the documentation
|
||||
* Otherwise, it closely resembles postAndSuspend(); please see the documentation
|
||||
* for that function for detailed parameter info.
|
||||
*
|
||||
* While we could have implemented the single-pump variant in terms of this
|
||||
|
|
@ -310,81 +172,41 @@ namespace LLEventDetail
|
|||
* the index of the pump on which it arrived (0 or 1).
|
||||
*
|
||||
* @note
|
||||
* I'd have preferred to overload the name postAndWait() for both signatures.
|
||||
* I'd have preferred to overload the name postAndSuspend() for both signatures.
|
||||
* But consider the following ambiguous call:
|
||||
* @code
|
||||
* postAndWait(self, LLSD(), requestPump, replyPump, "someString");
|
||||
* postAndSuspend(LLSD(), requestPump, replyPump, "someString");
|
||||
* @endcode
|
||||
* "someString" could be converted to either LLSD (@a replyPumpNamePath for
|
||||
* the single-pump function) or LLEventOrPumpName (@a replyPump1 for two-pump
|
||||
* function).
|
||||
*
|
||||
* It seems less burdensome to write postAndWait2() than to write either
|
||||
* It seems less burdensome to write postAndSuspend2() than to write either
|
||||
* LLSD("someString") or LLEventOrPumpName("someString").
|
||||
*/
|
||||
template <typename SELF>
|
||||
LLEventWithID postAndWait2(SELF& self, const LLSD& event,
|
||||
LLEventWithID postAndSuspend2(const LLSD& event,
|
||||
const LLEventPumpOrPumpName& requestPump,
|
||||
const LLEventPumpOrPumpName& replyPump0,
|
||||
const LLEventPumpOrPumpName& replyPump1,
|
||||
const LLSD& replyPump0NamePath=LLSD(),
|
||||
const LLSD& replyPump1NamePath=LLSD())
|
||||
{
|
||||
// declare the future
|
||||
boost::dcoroutines::future<LLEventWithID> future(self);
|
||||
// either callback will assign a value to this future; listen on
|
||||
// each specified LLEventPump with a callback
|
||||
std::string name(LLEventDetail::listenerNameForCoro(self));
|
||||
LLTempBoundListener connection0(
|
||||
replyPump0.getPump().listen(name + "a",
|
||||
LLEventDetail::wfeoh(boost::dcoroutines::make_callback(future), 0)));
|
||||
LLTempBoundListener connection1(
|
||||
replyPump1.getPump().listen(name + "b",
|
||||
LLEventDetail::wfeoh(boost::dcoroutines::make_callback(future), 1)));
|
||||
// skip the "post" part if requestPump is default-constructed
|
||||
if (requestPump)
|
||||
{
|
||||
// If either replyPumpNamePath is non-empty, store the corresponding
|
||||
// replyPump name in the request event.
|
||||
LLSD modevent(event);
|
||||
LLEventDetail::storeToLLSDPath(modevent, replyPump0NamePath,
|
||||
replyPump0.getPump().getName());
|
||||
LLEventDetail::storeToLLSDPath(modevent, replyPump1NamePath,
|
||||
replyPump1.getPump().getName());
|
||||
LL_DEBUGS("lleventcoro") << "postAndWait2(): coroutine " << name
|
||||
<< " posting to " << requestPump.getPump().getName()
|
||||
<< ": " << modevent << LL_ENDL;
|
||||
requestPump.getPump().post(modevent);
|
||||
}
|
||||
LL_DEBUGS("lleventcoro") << "postAndWait2(): coroutine " << name
|
||||
<< " about to wait on LLEventPumps " << replyPump0.getPump().getName()
|
||||
<< ", " << replyPump1.getPump().getName() << LL_ENDL;
|
||||
// trying to dereference ("resolve") the future makes us wait for it
|
||||
LLEventWithID value(*future);
|
||||
LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << name
|
||||
<< " resuming with (" << value.first << ", " << value.second << ")"
|
||||
<< LL_ENDL;
|
||||
// returning should disconnect both connections
|
||||
return value;
|
||||
}
|
||||
const LLSD& replyPump1NamePath=LLSD());
|
||||
|
||||
/**
|
||||
* Wait for the next event on either of two specified LLEventPumps.
|
||||
*/
|
||||
template <typename SELF>
|
||||
inline
|
||||
LLEventWithID
|
||||
waitForEventOn(SELF& self,
|
||||
const LLEventPumpOrPumpName& pump0, const LLEventPumpOrPumpName& pump1)
|
||||
suspendUntilEventOn(const LLEventPumpOrPumpName& pump0, const LLEventPumpOrPumpName& pump1)
|
||||
{
|
||||
// This is now a convenience wrapper for postAndWait2().
|
||||
return postAndWait2(self, LLSD(), LLEventPumpOrPumpName(), pump0, pump1);
|
||||
// This is now a convenience wrapper for postAndSuspend2().
|
||||
return postAndSuspend2(LLSD(), LLEventPumpOrPumpName(), pump0, pump1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for the two-pump variant of waitForEventOn(), e.g.:
|
||||
* Helper for the two-pump variant of suspendUntilEventOn(), e.g.:
|
||||
*
|
||||
* @code
|
||||
* LLSD reply = errorException(waitForEventOn(self, replyPump, errorPump),
|
||||
* LLSD reply = errorException(suspendUntilEventOn(replyPump, errorPump),
|
||||
* "error response from login.cgi");
|
||||
* @endcode
|
||||
*
|
||||
|
|
@ -400,6 +222,8 @@ waitForEventOn(SELF& self,
|
|||
*/
|
||||
LLSD errorException(const LLEventWithID& result, const std::string& desc);
|
||||
|
||||
} // namespace llcoro
|
||||
|
||||
/**
|
||||
* Exception thrown by errorException(). We don't call this LLEventError
|
||||
* because it's not an error in event processing: rather, this exception
|
||||
|
|
@ -420,12 +244,17 @@ private:
|
|||
LLSD mData;
|
||||
};
|
||||
|
||||
namespace llcoro
|
||||
{
|
||||
|
||||
/**
|
||||
* Like errorException(), save that this trips a fatal error using LL_ERRS
|
||||
* rather than throwing an exception.
|
||||
*/
|
||||
LL_COMMON_API LLSD errorLog(const LLEventWithID& result, const std::string& desc);
|
||||
|
||||
} // namespace llcoro
|
||||
|
||||
/**
|
||||
* Certain event APIs require the name of an LLEventPump on which they should
|
||||
* post results. While it works to invent a distinct name and let
|
||||
|
|
@ -437,7 +266,7 @@ LL_COMMON_API LLSD errorLog(const LLEventWithID& result, const std::string& desc
|
|||
* 2. Provide its actual name to the event API in question as the name of the
|
||||
* reply LLEventPump.
|
||||
* 3. Initiate the request to the event API.
|
||||
* 4. Call your LLEventTempStream's wait() method to wait for the reply.
|
||||
* 4. Call your LLEventTempStream's suspend() method to suspend for the reply.
|
||||
* 5. Let the LLCoroEventPump go out of scope.
|
||||
*/
|
||||
class LL_COMMON_API LLCoroEventPump
|
||||
|
|
@ -454,26 +283,16 @@ public:
|
|||
|
||||
/**
|
||||
* Wait for an event on this LLEventPump.
|
||||
*
|
||||
* @note
|
||||
* The other major usage pattern we considered was to bind @c self at
|
||||
* LLCoroEventPump construction time, which would avoid passing the
|
||||
* parameter to each wait() call. But if we were going to bind @c self as
|
||||
* a class member, we'd need to specify a class template parameter
|
||||
* indicating its type. The big advantage of passing it to the wait() call
|
||||
* is that the type can be implicit.
|
||||
*/
|
||||
template <typename SELF>
|
||||
LLSD wait(SELF& self)
|
||||
LLSD suspend()
|
||||
{
|
||||
return waitForEventOn(self, mPump);
|
||||
return llcoro::suspendUntilEventOn(mPump);
|
||||
}
|
||||
|
||||
template <typename SELF>
|
||||
LLSD postAndWait(SELF& self, const LLSD& event, const LLEventPumpOrPumpName& requestPump,
|
||||
LLSD postAndSuspend(const LLSD& event, const LLEventPumpOrPumpName& requestPump,
|
||||
const LLSD& replyPumpNamePath=LLSD())
|
||||
{
|
||||
return ::postAndWait(self, event, requestPump, mPump, replyPumpNamePath);
|
||||
return llcoro::postAndSuspend(event, requestPump, mPump, replyPumpNamePath);
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
@ -509,57 +328,51 @@ public:
|
|||
/// request pump 1
|
||||
LLEventPump& getPump1() { return mPump1; }
|
||||
|
||||
/// waitForEventOn(self, either of our two LLEventPumps)
|
||||
template <typename SELF>
|
||||
LLEventWithID wait(SELF& self)
|
||||
/// suspendUntilEventOn(either of our two LLEventPumps)
|
||||
LLEventWithID suspend()
|
||||
{
|
||||
return waitForEventOn(self, mPump0, mPump1);
|
||||
return llcoro::suspendUntilEventOn(mPump0, mPump1);
|
||||
}
|
||||
|
||||
/// errorException(wait(self))
|
||||
template <typename SELF>
|
||||
LLSD waitWithException(SELF& self)
|
||||
/// errorException(suspend())
|
||||
LLSD suspendWithException()
|
||||
{
|
||||
return errorException(wait(self), std::string("Error event on ") + getName1());
|
||||
return llcoro::errorException(suspend(), std::string("Error event on ") + getName1());
|
||||
}
|
||||
|
||||
/// errorLog(wait(self))
|
||||
template <typename SELF>
|
||||
LLSD waitWithLog(SELF& self)
|
||||
/// errorLog(suspend())
|
||||
LLSD suspendWithLog()
|
||||
{
|
||||
return errorLog(wait(self), std::string("Error event on ") + getName1());
|
||||
return llcoro::errorLog(suspend(), std::string("Error event on ") + getName1());
|
||||
}
|
||||
|
||||
template <typename SELF>
|
||||
LLEventWithID postAndWait(SELF& self, const LLSD& event,
|
||||
LLEventWithID postAndSuspend(const LLSD& event,
|
||||
const LLEventPumpOrPumpName& requestPump,
|
||||
const LLSD& replyPump0NamePath=LLSD(),
|
||||
const LLSD& replyPump1NamePath=LLSD())
|
||||
{
|
||||
return postAndWait2(self, event, requestPump, mPump0, mPump1,
|
||||
replyPump0NamePath, replyPump1NamePath);
|
||||
return llcoro::postAndSuspend2(event, requestPump, mPump0, mPump1,
|
||||
replyPump0NamePath, replyPump1NamePath);
|
||||
}
|
||||
|
||||
template <typename SELF>
|
||||
LLSD postAndWaitWithException(SELF& self, const LLSD& event,
|
||||
LLSD postAndSuspendWithException(const LLSD& event,
|
||||
const LLEventPumpOrPumpName& requestPump,
|
||||
const LLSD& replyPump0NamePath=LLSD(),
|
||||
const LLSD& replyPump1NamePath=LLSD())
|
||||
{
|
||||
return errorException(postAndWait(self, event, requestPump,
|
||||
replyPump0NamePath, replyPump1NamePath),
|
||||
std::string("Error event on ") + getName1());
|
||||
return llcoro::errorException(postAndSuspend(event, requestPump,
|
||||
replyPump0NamePath, replyPump1NamePath),
|
||||
std::string("Error event on ") + getName1());
|
||||
}
|
||||
|
||||
template <typename SELF>
|
||||
LLSD postAndWaitWithLog(SELF& self, const LLSD& event,
|
||||
LLSD postAndSuspendWithLog(const LLSD& event,
|
||||
const LLEventPumpOrPumpName& requestPump,
|
||||
const LLSD& replyPump0NamePath=LLSD(),
|
||||
const LLSD& replyPump1NamePath=LLSD())
|
||||
{
|
||||
return errorLog(postAndWait(self, event, requestPump,
|
||||
replyPump0NamePath, replyPump1NamePath),
|
||||
std::string("Error event on ") + getName1());
|
||||
return llcoro::errorLog(postAndSuspend(event, requestPump,
|
||||
replyPump0NamePath, replyPump1NamePath),
|
||||
std::string("Error event on ") + getName1());
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -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) */
|
||||
|
|
@ -710,7 +710,7 @@ LLProcess::LLProcess(const LLSDOrParams& params):
|
|||
|
||||
// Tie the lifespan of this child process to the lifespan of our APR
|
||||
// pool: on destruction of the pool, forcibly kill the process. Tell
|
||||
// APR to try SIGTERM and wait 3 seconds. If that didn't work, use
|
||||
// APR to try SIGTERM and suspend 3 seconds. If that didn't work, use
|
||||
// SIGKILL.
|
||||
apr_pool_note_subprocess(gAPRPoolp, &mProcess, APR_KILL_AFTER_TIMEOUT);
|
||||
|*==========================================================================*/
|
||||
|
|
@ -989,9 +989,9 @@ void LLProcess::handle_status(int reason, int status)
|
|||
// wi->rv = apr_proc_wait(wi->child, &wi->rc, &wi->why, APR_NOWAIT);
|
||||
// It's just wrong to call apr_proc_wait() here. The only way APR knows to
|
||||
// call us with APR_OC_REASON_DEATH is that it's already reaped this child
|
||||
// process, so calling wait() will only produce "huh?" from the OS. We
|
||||
// process, so calling suspend() will only produce "huh?" from the OS. We
|
||||
// must rely on the status param passed in, which unfortunately comes
|
||||
// straight from the OS wait() call, which means we have to decode it by
|
||||
// straight from the OS suspend() call, which means we have to decode it by
|
||||
// hand.
|
||||
mStatus = interpret_status(status);
|
||||
LL_INFOS("LLProcess") << getStatusString() << LL_ENDL;
|
||||
|
|
|
|||
|
|
@ -143,15 +143,10 @@ private:
|
|||
LLAtomic32< S32 > mRef;
|
||||
};
|
||||
|
||||
/**
|
||||
* intrusive pointer support
|
||||
* this allows you to use boost::intrusive_ptr with any LLRefCount-derived type
|
||||
*/
|
||||
/**
|
||||
* intrusive pointer support for LLThreadSafeRefCount
|
||||
* this allows you to use boost::intrusive_ptr with any LLThreadSafeRefCount-derived type
|
||||
*/
|
||||
|
||||
inline void intrusive_ptr_add_ref(LLThreadSafeRefCount* p)
|
||||
{
|
||||
p->ref();
|
||||
|
|
@ -162,6 +157,10 @@ inline void intrusive_ptr_release(LLThreadSafeRefCount* p)
|
|||
p->unref();
|
||||
}
|
||||
|
||||
/**
|
||||
* intrusive pointer support
|
||||
* this allows you to use boost::intrusive_ptr with any LLRefCount-derived type
|
||||
*/
|
||||
inline void intrusive_ptr_add_ref(LLRefCount* p)
|
||||
{
|
||||
p->ref();
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -1394,6 +1394,7 @@ BOOL LLStringUtilBase<T>::containsNonprintable(const string_type& string)
|
|||
return rv;
|
||||
}
|
||||
|
||||
// *TODO: reimplement in terms of algorithm
|
||||
//static
|
||||
template<class T>
|
||||
void LLStringUtilBase<T>::stripNonprintable(string_type& string)
|
||||
|
|
@ -1427,6 +1428,7 @@ void LLStringUtilBase<T>::stripNonprintable(string_type& string)
|
|||
delete []c_string;
|
||||
}
|
||||
|
||||
// *TODO: reimplement in terms of algorithm
|
||||
template<class T>
|
||||
std::basic_string<T> LLStringUtilBase<T>::quote(const string_type& str,
|
||||
const string_type& triggers,
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -5,7 +5,6 @@ project(llcorehttp)
|
|||
include(00-Common)
|
||||
include(GoogleMock)
|
||||
include(CURL)
|
||||
include(CARes)
|
||||
include(OpenSSL)
|
||||
include(ZLIB)
|
||||
include(LLCoreHttp)
|
||||
|
|
@ -26,6 +25,7 @@ set(llcorehttp_SOURCE_FILES
|
|||
bufferarray.cpp
|
||||
bufferstream.cpp
|
||||
httpcommon.cpp
|
||||
llhttpconstants.cpp
|
||||
httpheaders.cpp
|
||||
httpoptions.cpp
|
||||
httprequest.cpp
|
||||
|
|
@ -51,6 +51,7 @@ set(llcorehttp_HEADER_FILES
|
|||
bufferarray.h
|
||||
bufferstream.h
|
||||
httpcommon.h
|
||||
llhttpconstants.h
|
||||
httphandler.h
|
||||
httpheaders.h
|
||||
httpoptions.h
|
||||
|
|
@ -89,7 +90,6 @@ add_library (llcorehttp ${llcorehttp_SOURCE_FILES})
|
|||
target_link_libraries(
|
||||
llcorehttp
|
||||
${CURL_LIBRARIES}
|
||||
${CARES_LIBRARIES}
|
||||
${OPENSSL_LIBRARIES}
|
||||
${CRYPTO_LIBRARIES}
|
||||
${BOOST_THREAD_LIBRARY}
|
||||
|
|
@ -127,16 +127,19 @@ if (LL_TESTS)
|
|||
${LLCOMMON_LIBRARIES}
|
||||
${GOOGLEMOCK_LIBRARIES}
|
||||
${CURL_LIBRARIES}
|
||||
${CARES_LIBRARIES}
|
||||
${OPENSSL_LIBRARIES}
|
||||
${CRYPTO_LIBRARIES}
|
||||
${BOOST_SYSTEM_LIBRARY}
|
||||
${BOOST_THREAD_LIBRARY}
|
||||
)
|
||||
|
||||
# If http_proxy is in the current environment (e.g. to fetch s3-proxy
|
||||
# autobuild packages), suppress it for this integration test: it screws up
|
||||
# the tests.
|
||||
LL_ADD_INTEGRATION_TEST(llcorehttp
|
||||
"${llcorehttp_TEST_SOURCE_FILES}"
|
||||
"${test_libs}"
|
||||
"-Dhttp_proxy"
|
||||
${PYTHON_EXECUTABLE}
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/tests/test_llcorehttp_peer.py"
|
||||
)
|
||||
|
|
@ -192,7 +195,6 @@ endif (DARWIN)
|
|||
${LLCOMMON_LIBRARIES}
|
||||
${GOOGLEMOCK_LIBRARIES}
|
||||
${CURL_LIBRARIES}
|
||||
${CARES_LIBRARIES}
|
||||
${OPENSSL_LIBRARIES}
|
||||
${CRYPTO_LIBRARIES}
|
||||
${BOOST_SYSTEM_LIBRARY}
|
||||
|
|
|
|||
|
|
@ -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,42 +344,72 @@ bool HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode
|
|||
{
|
||||
op->mStatus = HttpStatus(HttpStatus::EXT_CURL_EASY, status);
|
||||
}
|
||||
if (op->mStatus)
|
||||
{
|
||||
int http_status(HTTP_OK);
|
||||
if (op->mStatus)
|
||||
{
|
||||
int http_status(HTTP_OK);
|
||||
|
||||
curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &http_status);
|
||||
if (http_status >= 100 && http_status <= 999)
|
||||
{
|
||||
char * cont_type(NULL);
|
||||
curl_easy_getinfo(handle, CURLINFO_CONTENT_TYPE, &cont_type);
|
||||
if (cont_type)
|
||||
{
|
||||
op->mReplyConType = cont_type;
|
||||
}
|
||||
op->mStatus = HttpStatus(http_status);
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS(LOG_CORE) << "Invalid HTTP response code ("
|
||||
<< http_status << ") received from server."
|
||||
<< LL_ENDL;
|
||||
op->mStatus = HttpStatus(HttpStatus::LLCORE, HE_INVALID_HTTP_STATUS);
|
||||
}
|
||||
if (handle)
|
||||
{
|
||||
ccode = curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &http_status);
|
||||
if (ccode == CURLE_OK)
|
||||
{
|
||||
if (http_status >= 100 && http_status <= 999)
|
||||
{
|
||||
char * cont_type(NULL);
|
||||
ccode = curl_easy_getinfo(handle, CURLINFO_CONTENT_TYPE, &cont_type);
|
||||
if (ccode == CURLE_OK)
|
||||
{
|
||||
if (cont_type)
|
||||
{
|
||||
op->mReplyConType = cont_type;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS(LOG_CORE) << "CURL error:" << ccode << " Attempting to get content type." << LL_ENDL;
|
||||
}
|
||||
op->mStatus = HttpStatus(http_status);
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS(LOG_CORE) << "Invalid HTTP response code ("
|
||||
<< http_status << ") received from server."
|
||||
<< LL_ENDL;
|
||||
op->mStatus = HttpStatus(HttpStatus::LLCORE, HE_INVALID_HTTP_STATUS);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
op->mStatus = HttpStatus(HttpStatus::LLCORE, HE_INVALID_HTTP_STATUS);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS(LOG_CORE) << "Attempt to retrieve status from NULL handle!" << LL_ENDL;
|
||||
}
|
||||
}
|
||||
|
||||
// Detach from multi and recycle handle
|
||||
curl_multi_remove_handle(multi_handle, handle);
|
||||
mHandleCache.freeHandle(op->mCurlHandle);
|
||||
op->mCurlHandle = NULL;
|
||||
if (multi_handle && handle)
|
||||
{
|
||||
// Detach from multi and recycle handle
|
||||
curl_multi_remove_handle(multi_handle, handle);
|
||||
mHandleCache.freeHandle(op->mCurlHandle);
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS(LOG_CORE) << "Curl multi_handle or handle is NULL on remove! multi:"
|
||||
<< std::hex << multi_handle << " h:" << std::hex << handle << std::dec << LL_ENDL;
|
||||
}
|
||||
|
||||
op->mCurlHandle = NULL;
|
||||
|
||||
// Tracing
|
||||
if (op->mTracing > HTTP_TRACE_OFF)
|
||||
{
|
||||
LL_INFOS(LOG_CORE) << "TRACE, RequestComplete, Handle: "
|
||||
<< static_cast<HttpHandle>(op)
|
||||
<< ", Status: " << op->mStatus.toTerseString()
|
||||
<< LL_ENDL;
|
||||
<< op->getHandle()
|
||||
<< ", Status: " << op->mStatus.toTerseString()
|
||||
<< LL_ENDL;
|
||||
}
|
||||
|
||||
// Dispatch to next stage
|
||||
|
|
@ -554,7 +597,7 @@ void HttpLibcurl::HandleCache::freeHandle(CURL * handle)
|
|||
// ---------------------------------------
|
||||
|
||||
|
||||
struct curl_slist * append_headers_to_slist(const HttpHeaders * headers, struct curl_slist * slist)
|
||||
struct curl_slist * append_headers_to_slist(const HttpHeaders::ptr_t &headers, struct curl_slist * slist)
|
||||
{
|
||||
const HttpHeaders::const_iterator end(headers->end());
|
||||
for (HttpHeaders::const_iterator it(headers->begin()); end != it; ++it)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -817,7 +952,7 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi
|
|||
// Save headers in response
|
||||
if (! op->mReplyHeaders)
|
||||
{
|
||||
op->mReplyHeaders = new HttpHeaders;
|
||||
op->mReplyHeaders = HttpHeaders::ptr_t(new HttpHeaders);
|
||||
}
|
||||
op->mReplyHeaders->append(name, value ? value : "");
|
||||
}
|
||||
|
|
@ -873,9 +1008,38 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi
|
|||
}
|
||||
|
||||
|
||||
CURLcode HttpOpRequest::curlSslCtxCallback(CURL *curl, void *sslctx, void *userdata)
|
||||
{
|
||||
HttpOpRequest::ptr_t op(HttpOpRequest::fromHandle<HttpOpRequest>(userdata));
|
||||
|
||||
if (op->mCallbackSSLVerify)
|
||||
{
|
||||
SSL_CTX * ctx = (SSL_CTX *)sslctx;
|
||||
// disable any default verification for server certs
|
||||
SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL);
|
||||
// set the verification callback.
|
||||
SSL_CTX_set_cert_verify_callback(ctx, sslCertVerifyCallback, userdata);
|
||||
// the calls are void
|
||||
}
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
int HttpOpRequest::sslCertVerifyCallback(X509_STORE_CTX *ctx, void *param)
|
||||
{
|
||||
HttpOpRequest::ptr_t op(HttpOpRequest::fromHandle<HttpOpRequest>(param));
|
||||
|
||||
if (op->mCallbackSSLVerify)
|
||||
{
|
||||
op->mStatus = op->mCallbackSSLVerify(op->mReqURL, op->mUserHandler, ctx);
|
||||
}
|
||||
|
||||
return (op->mStatus) ? 1 : 0;
|
||||
}
|
||||
|
||||
int HttpOpRequest::debugCallback(CURL * handle, curl_infotype info, char * buffer, size_t len, void * userdata)
|
||||
{
|
||||
HttpOpRequest * op(static_cast<HttpOpRequest *>(userdata));
|
||||
HttpOpRequest::ptr_t op(HttpOpRequest::fromHandle<HttpOpRequest>(userdata));
|
||||
|
||||
std::string safe_line;
|
||||
std::string tag;
|
||||
|
|
@ -955,7 +1119,7 @@ int HttpOpRequest::debugCallback(CURL * handle, curl_infotype info, char * buffe
|
|||
if (logit)
|
||||
{
|
||||
LL_INFOS(LOG_CORE) << "TRACE, LibcurlDebug, Handle: "
|
||||
<< static_cast<HttpHandle>(op)
|
||||
<< op->getHandle()
|
||||
<< ", Type: " << tag
|
||||
<< ", Data: " << safe_line
|
||||
<< LL_ENDL;
|
||||
|
|
|
|||
|
|
@ -33,19 +33,22 @@
|
|||
#include <string>
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include <openssl/x509_vfy.h>
|
||||
#include <openssl/ssl.h>
|
||||
|
||||
#include "httpcommon.h"
|
||||
#include "httprequest.h"
|
||||
#include "_httpoperation.h"
|
||||
#include "_refcounted.h"
|
||||
|
||||
#include "httpheaders.h"
|
||||
#include "httpoptions.h"
|
||||
|
||||
namespace LLCore
|
||||
{
|
||||
|
||||
|
||||
class BufferArray;
|
||||
class HttpHeaders;
|
||||
class HttpOptions;
|
||||
|
||||
|
||||
/// HttpOpRequest requests a supported HTTP method invocation with
|
||||
|
|
@ -63,9 +66,10 @@ class HttpOptions;
|
|||
class HttpOpRequest : public HttpOperation
|
||||
{
|
||||
public:
|
||||
typedef boost::shared_ptr<HttpOpRequest> ptr_t;
|
||||
|
||||
HttpOpRequest();
|
||||
|
||||
protected:
|
||||
virtual ~HttpOpRequest(); // Use release()
|
||||
|
||||
private:
|
||||
|
|
@ -77,7 +81,11 @@ public:
|
|||
{
|
||||
HOR_GET,
|
||||
HOR_POST,
|
||||
HOR_PUT
|
||||
HOR_PUT,
|
||||
HOR_DELETE,
|
||||
HOR_PATCH,
|
||||
HOR_COPY,
|
||||
HOR_MOVE
|
||||
};
|
||||
|
||||
virtual void stageFromRequest(HttpService *);
|
||||
|
|
@ -98,32 +106,57 @@ public:
|
|||
HttpStatus setupGet(HttpRequest::policy_t policy_id,
|
||||
HttpRequest::priority_t priority,
|
||||
const std::string & url,
|
||||
HttpOptions * options,
|
||||
HttpHeaders * headers);
|
||||
const HttpOptions::ptr_t & options,
|
||||
const HttpHeaders::ptr_t & headers);
|
||||
|
||||
HttpStatus setupGetByteRange(HttpRequest::policy_t policy_id,
|
||||
HttpRequest::priority_t priority,
|
||||
const std::string & url,
|
||||
size_t offset,
|
||||
size_t len,
|
||||
HttpOptions * options,
|
||||
HttpHeaders * headers);
|
||||
const HttpOptions::ptr_t & options,
|
||||
const HttpHeaders::ptr_t & headers);
|
||||
|
||||
HttpStatus setupPost(HttpRequest::policy_t policy_id,
|
||||
HttpRequest::priority_t priority,
|
||||
const std::string & url,
|
||||
BufferArray * body,
|
||||
HttpOptions * options,
|
||||
HttpHeaders * headers);
|
||||
const HttpOptions::ptr_t & options,
|
||||
const HttpHeaders::ptr_t & headers);
|
||||
|
||||
HttpStatus setupPut(HttpRequest::policy_t policy_id,
|
||||
HttpRequest::priority_t priority,
|
||||
const std::string & url,
|
||||
BufferArray * body,
|
||||
HttpOptions * options,
|
||||
HttpHeaders * headers);
|
||||
const HttpOptions::ptr_t & options,
|
||||
const HttpHeaders::ptr_t & headers);
|
||||
|
||||
// Internal method used to setup the libcurl options for a request.
|
||||
HttpStatus setupDelete(HttpRequest::policy_t policy_id,
|
||||
HttpRequest::priority_t priority,
|
||||
const std::string & url,
|
||||
const HttpOptions::ptr_t & options,
|
||||
const HttpHeaders::ptr_t & headers);
|
||||
|
||||
HttpStatus setupPatch(HttpRequest::policy_t policy_id,
|
||||
HttpRequest::priority_t priority,
|
||||
const std::string & url,
|
||||
BufferArray * body,
|
||||
const HttpOptions::ptr_t & options,
|
||||
const HttpHeaders::ptr_t & headers);
|
||||
|
||||
HttpStatus setupCopy(HttpRequest::policy_t policy_id,
|
||||
HttpRequest::priority_t priority,
|
||||
const std::string & url,
|
||||
const HttpOptions::ptr_t & options,
|
||||
const HttpHeaders::ptr_t & headers);
|
||||
|
||||
HttpStatus setupMove(HttpRequest::policy_t policy_id,
|
||||
HttpRequest::priority_t priority,
|
||||
const std::string & url,
|
||||
const HttpOptions::ptr_t & options,
|
||||
const HttpHeaders::ptr_t & headers);
|
||||
|
||||
// Internal method used to setup the libcurl options for a request.
|
||||
// Does all the libcurl handle setup in one place.
|
||||
//
|
||||
// Threading: called by worker thread
|
||||
|
|
@ -141,8 +174,8 @@ protected:
|
|||
HttpRequest::priority_t priority,
|
||||
const std::string & url,
|
||||
BufferArray * body,
|
||||
HttpOptions * options,
|
||||
HttpHeaders * headers);
|
||||
const HttpOptions::ptr_t & options,
|
||||
const HttpHeaders::ptr_t & headers);
|
||||
|
||||
// libcurl operational callbacks
|
||||
//
|
||||
|
|
@ -150,7 +183,11 @@ protected:
|
|||
//
|
||||
static size_t writeCallback(void * data, size_t size, size_t nmemb, void * userdata);
|
||||
static size_t readCallback(void * data, size_t size, size_t nmemb, void * userdata);
|
||||
static int seekCallback(void *data, curl_off_t offset, int origin);
|
||||
static size_t headerCallback(void * data, size_t size, size_t nmemb, void * userdata);
|
||||
static CURLcode curlSslCtxCallback(CURL *curl, void *ssl_ctx, void *userptr);
|
||||
static int sslCertVerifyCallback(X509_STORE_CTX *ctx, void *param);
|
||||
|
||||
static int debugCallback(CURL *, curl_infotype info, char * buffer, size_t len, void * userdata);
|
||||
|
||||
protected:
|
||||
|
|
@ -159,6 +196,8 @@ protected:
|
|||
static const unsigned int PF_SAVE_HEADERS = 0x00000002U;
|
||||
static const unsigned int PF_USE_RETRY_AFTER = 0x00000004U;
|
||||
|
||||
HttpRequest::policyCallback_t mCallbackSSLVerify;
|
||||
|
||||
public:
|
||||
// Request data
|
||||
EMethod mReqMethod;
|
||||
|
|
@ -166,8 +205,8 @@ public:
|
|||
BufferArray * mReqBody;
|
||||
off_t mReqOffset;
|
||||
size_t mReqLength;
|
||||
HttpHeaders * mReqHeaders;
|
||||
HttpOptions * mReqOptions;
|
||||
HttpHeaders::ptr_t mReqHeaders;
|
||||
HttpOptions::ptr_t mReqOptions;
|
||||
|
||||
// Transport data
|
||||
bool mCurlActive;
|
||||
|
|
@ -184,7 +223,7 @@ public:
|
|||
off_t mReplyOffset;
|
||||
size_t mReplyLength;
|
||||
size_t mReplyFullLength;
|
||||
HttpHeaders * mReplyHeaders;
|
||||
HttpHeaders::ptr_t mReplyHeaders;
|
||||
std::string mReplyConType;
|
||||
int mReplyRetryAfter;
|
||||
|
||||
|
|
@ -215,7 +254,7 @@ public:
|
|||
|
||||
// Internal function to append the contents of an HttpHeaders
|
||||
// instance to a curl_slist object.
|
||||
curl_slist * append_headers_to_slist(const HttpHeaders *, curl_slist * slist);
|
||||
curl_slist * append_headers_to_slist(const HttpHeaders::ptr_t &, curl_slist * slist);
|
||||
|
||||
} // end namespace LLCore
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -42,7 +54,7 @@ HttpStatus::operator unsigned long() const
|
|||
{
|
||||
static const int shift(sizeof(unsigned long) * 4);
|
||||
|
||||
unsigned long result(((unsigned long) mType) << shift | (unsigned long) (int) mStatus);
|
||||
unsigned long result(((unsigned long)mDetails->mType) << shift | (unsigned long)(int)mDetails->mStatus);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -131,30 +143,34 @@ std::string HttpStatus::toString() const
|
|||
{
|
||||
return std::string("");
|
||||
}
|
||||
switch (mType)
|
||||
switch (getType())
|
||||
{
|
||||
case EXT_CURL_EASY:
|
||||
return std::string(curl_easy_strerror(CURLcode(mStatus)));
|
||||
return std::string(curl_easy_strerror(CURLcode(getStatus())));
|
||||
|
||||
case EXT_CURL_MULTI:
|
||||
return std::string(curl_multi_strerror(CURLMcode(mStatus)));
|
||||
return std::string(curl_multi_strerror(CURLMcode(getStatus())));
|
||||
|
||||
case LLCORE:
|
||||
if (mStatus >= 0 && mStatus < llcore_errors_count)
|
||||
if (getStatus() >= 0 && getStatus() < llcore_errors_count)
|
||||
{
|
||||
return std::string(llcore_errors[mStatus]);
|
||||
return std::string(llcore_errors[getStatus()]);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (isHttpStatus())
|
||||
{
|
||||
// special handling for status 499 "Linden Catchall"
|
||||
if ((getType() == 499) && (!getMessage().empty()))
|
||||
return getMessage();
|
||||
|
||||
// Binary search for the error code and string
|
||||
int bottom(0), top(http_errors_count);
|
||||
while (true)
|
||||
{
|
||||
int at((bottom + top) / 2);
|
||||
if (mType == http_errors[at].mCode)
|
||||
if (getType() == http_errors[at].mCode)
|
||||
{
|
||||
return std::string(http_errors[at].mText);
|
||||
}
|
||||
|
|
@ -162,7 +178,7 @@ std::string HttpStatus::toString() const
|
|||
{
|
||||
break;
|
||||
}
|
||||
else if (mType < http_errors[at].mCode)
|
||||
else if (getType() < http_errors[at].mCode)
|
||||
{
|
||||
top = at;
|
||||
}
|
||||
|
|
@ -182,9 +198,9 @@ std::string HttpStatus::toTerseString() const
|
|||
{
|
||||
std::ostringstream result;
|
||||
|
||||
unsigned int error_value((unsigned short) mStatus);
|
||||
unsigned int error_value((unsigned short)getStatus());
|
||||
|
||||
switch (mType)
|
||||
switch (getType())
|
||||
{
|
||||
case EXT_CURL_EASY:
|
||||
result << "Easy_";
|
||||
|
|
@ -202,7 +218,7 @@ std::string HttpStatus::toTerseString() const
|
|||
if (isHttpStatus())
|
||||
{
|
||||
result << "Http_";
|
||||
error_value = mType;
|
||||
error_value = getType();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -244,7 +260,7 @@ bool HttpStatus::isRetryable() const
|
|||
// Disable the '*this == inv_status' test and look for 'Core_9'
|
||||
// failures in log files.
|
||||
|
||||
return ((isHttpStatus() && mType >= 499 && mType <= 599) || // Include special 499 in retryables
|
||||
return ((isHttpStatus() && getType() >= 499 && getType() <= 599) || // Include special 499 in retryables
|
||||
*this == cant_connect || // Connection reset/endpoint problems
|
||||
*this == cant_res_proxy || // DNS problems
|
||||
*this == cant_res_host || // DNS problems
|
||||
|
|
@ -259,5 +275,168 @@ bool HttpStatus::isRetryable() const
|
|||
*this == inv_cont_range); // Short data read disagrees with content-range
|
||||
}
|
||||
|
||||
} // end namespace LLCore
|
||||
namespace LLHttp
|
||||
{
|
||||
namespace
|
||||
{
|
||||
typedef boost::shared_ptr<LLMutex> LLMutex_ptr;
|
||||
std::vector<LLMutex_ptr> sSSLMutex;
|
||||
|
||||
CURL *getCurlTemplateHandle()
|
||||
{
|
||||
static CURL *curlpTemplateHandle = NULL;
|
||||
|
||||
if (curlpTemplateHandle == NULL)
|
||||
{ // Late creation of the template curl handle
|
||||
curlpTemplateHandle = curl_easy_init();
|
||||
if (curlpTemplateHandle == NULL)
|
||||
{
|
||||
LL_WARNS() << "curl error calling curl_easy_init()" << LL_ENDL;
|
||||
}
|
||||
else
|
||||
{
|
||||
CURLcode result = curl_easy_setopt(curlpTemplateHandle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
|
||||
check_curl_code(result, CURLOPT_IPRESOLVE);
|
||||
result = curl_easy_setopt(curlpTemplateHandle, CURLOPT_NOSIGNAL, 1);
|
||||
check_curl_code(result, CURLOPT_NOSIGNAL);
|
||||
result = curl_easy_setopt(curlpTemplateHandle, CURLOPT_NOPROGRESS, 1);
|
||||
check_curl_code(result, CURLOPT_NOPROGRESS);
|
||||
result = curl_easy_setopt(curlpTemplateHandle, CURLOPT_ENCODING, "");
|
||||
check_curl_code(result, CURLOPT_ENCODING);
|
||||
result = curl_easy_setopt(curlpTemplateHandle, CURLOPT_AUTOREFERER, 1);
|
||||
check_curl_code(result, CURLOPT_AUTOREFERER);
|
||||
result = curl_easy_setopt(curlpTemplateHandle, CURLOPT_FOLLOWLOCATION, 1);
|
||||
check_curl_code(result, CURLOPT_FOLLOWLOCATION);
|
||||
result = curl_easy_setopt(curlpTemplateHandle, CURLOPT_SSL_VERIFYPEER, 1);
|
||||
check_curl_code(result, CURLOPT_SSL_VERIFYPEER);
|
||||
result = curl_easy_setopt(curlpTemplateHandle, CURLOPT_SSL_VERIFYHOST, 0);
|
||||
check_curl_code(result, CURLOPT_SSL_VERIFYHOST);
|
||||
|
||||
// The Linksys WRT54G V5 router has an issue with frequent
|
||||
// DNS lookups from LAN machines. If they happen too often,
|
||||
// like for every HTTP request, the router gets annoyed after
|
||||
// about 700 or so requests and starts issuing TCP RSTs to
|
||||
// new connections. Reuse the DNS lookups for even a few
|
||||
// seconds and no RSTs.
|
||||
result = curl_easy_setopt(curlpTemplateHandle, CURLOPT_DNS_CACHE_TIMEOUT, 15);
|
||||
check_curl_code(result, CURLOPT_DNS_CACHE_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
return curlpTemplateHandle;
|
||||
}
|
||||
|
||||
LLMutex *getCurlMutex()
|
||||
{
|
||||
static LLMutex* sHandleMutexp = NULL;
|
||||
|
||||
if (!sHandleMutexp)
|
||||
{
|
||||
sHandleMutexp = new LLMutex(NULL);
|
||||
}
|
||||
|
||||
return sHandleMutexp;
|
||||
}
|
||||
|
||||
void deallocateEasyCurl(CURL *curlp)
|
||||
{
|
||||
LLMutexLock lock(getCurlMutex());
|
||||
|
||||
curl_easy_cleanup(curlp);
|
||||
}
|
||||
|
||||
|
||||
#if SAFE_SSL
|
||||
//static
|
||||
void ssl_locking_callback(int mode, int type, const char *file, int line)
|
||||
{
|
||||
if (type >= sSSLMutex.size())
|
||||
{
|
||||
LL_WARNS() << "Attempt to get unknown MUTEX in SSL Lock." << LL_ENDL;
|
||||
}
|
||||
|
||||
if (mode & CRYPTO_LOCK)
|
||||
{
|
||||
sSSLMutex[type]->lock();
|
||||
}
|
||||
else
|
||||
{
|
||||
sSSLMutex[type]->unlock();
|
||||
}
|
||||
}
|
||||
|
||||
//static
|
||||
unsigned long ssl_thread_id(void)
|
||||
{
|
||||
return LLThread::currentID();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
}
|
||||
|
||||
void initialize()
|
||||
{
|
||||
// Do not change this "unless you are familiar with and mean to control
|
||||
// internal operations of libcurl"
|
||||
// - http://curl.haxx.se/libcurl/c/curl_global_init.html
|
||||
CURLcode code = curl_global_init(CURL_GLOBAL_ALL);
|
||||
|
||||
check_curl_code(code, CURL_GLOBAL_ALL);
|
||||
|
||||
#if SAFE_SSL
|
||||
S32 mutex_count = CRYPTO_num_locks();
|
||||
for (S32 i = 0; i < mutex_count; i++)
|
||||
{
|
||||
sSSLMutex.push_back(LLMutex_ptr(new LLMutex(NULL)));
|
||||
}
|
||||
CRYPTO_set_id_callback(&ssl_thread_id);
|
||||
CRYPTO_set_locking_callback(&ssl_locking_callback);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
void cleanup()
|
||||
{
|
||||
#if SAFE_SSL
|
||||
CRYPTO_set_id_callback(NULL);
|
||||
CRYPTO_set_locking_callback(NULL);
|
||||
sSSLMutex.clear();
|
||||
#endif
|
||||
|
||||
curl_global_cleanup();
|
||||
}
|
||||
|
||||
|
||||
CURL_ptr createEasyHandle()
|
||||
{
|
||||
LLMutexLock lock(getCurlMutex());
|
||||
|
||||
CURL* handle = curl_easy_duphandle(getCurlTemplateHandle());
|
||||
|
||||
return CURL_ptr(handle, &deallocateEasyCurl);
|
||||
}
|
||||
|
||||
std::string getCURLVersion()
|
||||
{
|
||||
return std::string(curl_version());
|
||||
}
|
||||
|
||||
void check_curl_code(CURLcode code, int curl_setopt_option)
|
||||
{
|
||||
if (CURLE_OK != code)
|
||||
{
|
||||
// Comment from old llcurl code which may no longer apply:
|
||||
//
|
||||
// linux appears to throw a curl error once per session for a bad initialization
|
||||
// at a pretty random time (when enabling cookies).
|
||||
LL_WARNS() << "libcurl error detected: " << curl_easy_strerror(code)
|
||||
<< ", curl_easy_setopt option: " << curl_setopt_option
|
||||
<< LL_ENDL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
} // end namespace LLCore
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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,38 +40,45 @@
|
|||
#include "lldir.h"
|
||||
#include "llfile.h"
|
||||
#include "llsdserialize.h"
|
||||
#include "lliopipe.h"
|
||||
#include "llpumpio.h"
|
||||
#include "llhttpclient.h"
|
||||
#include "llsdserialize.h"
|
||||
#include "llproxy.h"
|
||||
|
||||
LLPumpIO* gServicePump = NULL;
|
||||
#include "llcorehttputil.h"
|
||||
#include "llhttpsdhandler.h"
|
||||
#include "httpcommon.h"
|
||||
#include "httpresponse.h"
|
||||
|
||||
#include <curl/curl.h>
|
||||
#include <openssl/crypto.h>
|
||||
|
||||
BOOL gBreak = false;
|
||||
BOOL gSent = false;
|
||||
|
||||
class LLCrashLoggerResponder : public LLHTTPClient::Responder
|
||||
int LLCrashLogger::ssl_mutex_count = 0;
|
||||
LLCoreInt::HttpMutex ** LLCrashLogger::ssl_mutex_list = NULL;
|
||||
|
||||
class LLCrashLoggerHandler : public LLHttpSDHandler
|
||||
{
|
||||
LOG_CLASS(LLCrashLoggerResponder);
|
||||
LOG_CLASS(LLCrashLoggerHandler);
|
||||
public:
|
||||
LLCrashLoggerResponder()
|
||||
{
|
||||
}
|
||||
LLCrashLoggerHandler() {}
|
||||
|
||||
protected:
|
||||
virtual void httpFailure()
|
||||
{
|
||||
LL_WARNS() << dumpResponse() << LL_ENDL;
|
||||
gBreak = true;
|
||||
}
|
||||
virtual void onSuccess(LLCore::HttpResponse * response, const LLSD &content);
|
||||
virtual void onFailure(LLCore::HttpResponse * response, LLCore::HttpStatus status);
|
||||
|
||||
virtual void httpSuccess()
|
||||
{
|
||||
gBreak = true;
|
||||
gSent = true;
|
||||
}
|
||||
};
|
||||
|
||||
void LLCrashLoggerHandler::onSuccess(LLCore::HttpResponse * response, const LLSD &content)
|
||||
{
|
||||
gBreak = true;
|
||||
gSent = true;
|
||||
}
|
||||
|
||||
void LLCrashLoggerHandler::onFailure(LLCore::HttpResponse * response, LLCore::HttpStatus status)
|
||||
{
|
||||
gBreak = true;
|
||||
}
|
||||
|
||||
LLCrashLogger::LLCrashLogger() :
|
||||
mCrashBehavior(CRASH_BEHAVIOR_ALWAYS_SEND),
|
||||
mCrashInPreviousExec(false),
|
||||
|
|
@ -207,11 +214,13 @@ void LLCrashLogger::gatherFiles()
|
|||
mFileMap["SettingsXml"] = mDebugLog["SettingsFilename"].asString();
|
||||
if(mDebugLog.has("CAFilename"))
|
||||
{
|
||||
LLCurl::setCAFile(mDebugLog["CAFilename"].asString());
|
||||
LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_CA_FILE,
|
||||
LLCore::HttpRequest::GLOBAL_POLICY_ID, mDebugLog["CAFilename"].asString(), NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
LLCurl::setCAFile(gDirUtilp->getCAFile());
|
||||
LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_CA_FILE,
|
||||
LLCore::HttpRequest::GLOBAL_POLICY_ID, gDirUtilp->getCAFile(), NULL);
|
||||
}
|
||||
|
||||
LL_INFOS() << "Using log file from debug log " << mFileMap["SecondLifeLog"] << LL_ENDL;
|
||||
|
|
@ -220,7 +229,8 @@ void LLCrashLogger::gatherFiles()
|
|||
else
|
||||
{
|
||||
// Figure out the filename of the second life log
|
||||
LLCurl::setCAFile(gDirUtilp->getCAFile());
|
||||
LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_CA_FILE,
|
||||
LLCore::HttpRequest::GLOBAL_POLICY_ID, gDirUtilp->getCAFile(), NULL);
|
||||
|
||||
mFileMap["SecondLifeLog"] = gDirUtilp->getExpandedFilename(LL_PATH_DUMP,"SecondLife.log");
|
||||
mFileMap["SettingsXml"] = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS,"settings.xml");
|
||||
|
|
@ -389,19 +399,38 @@ bool LLCrashLogger::saveCrashBehaviorSetting(S32 crash_behavior)
|
|||
|
||||
bool LLCrashLogger::runCrashLogPost(std::string host, LLSD data, std::string msg, int retries, int timeout)
|
||||
{
|
||||
LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
|
||||
LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions);
|
||||
|
||||
gBreak = false;
|
||||
httpOpts->setTimeout(timeout);
|
||||
|
||||
for(int i = 0; i < retries; ++i)
|
||||
{
|
||||
updateApplication(llformat("%s, try %d...", msg.c_str(), i+1));
|
||||
LLHTTPClient::post(host, data, new LLCrashLoggerResponder(), timeout);
|
||||
while(!gBreak)
|
||||
|
||||
LLCore::HttpHandle handle = LLCoreHttpUtil::requestPostWithLLSD(httpRequest.get(), LLCore::HttpRequest::DEFAULT_POLICY_ID, 0,
|
||||
host, data, httpOpts, LLCore::HttpHeaders::ptr_t(), LLCore::HttpHandler::ptr_t(new LLCrashLoggerHandler));
|
||||
|
||||
if (handle == LLCORE_HTTP_HANDLE_INVALID)
|
||||
{
|
||||
LLCore::HttpStatus status = httpRequest->getStatus();
|
||||
LL_WARNS("CRASHREPORT") << "Request POST failed to " << host << " with status of [" <<
|
||||
status.getType() << "]\"" << status.toString() << "\"" << LL_ENDL;
|
||||
return false;
|
||||
}
|
||||
|
||||
while(!gBreak)
|
||||
{
|
||||
updateApplication(); // No new message, just pump the IO
|
||||
httpRequest->update(0L);
|
||||
}
|
||||
if(gSent)
|
||||
{
|
||||
return gSent;
|
||||
}
|
||||
|
||||
LL_WARNS("CRASHREPORT") << "Failed to send crash report to \"" << host << "\"" << LL_ENDL;
|
||||
}
|
||||
return gSent;
|
||||
}
|
||||
|
|
@ -510,14 +539,12 @@ bool LLCrashLogger::sendCrashLogs()
|
|||
|
||||
void LLCrashLogger::updateApplication(const std::string& message)
|
||||
{
|
||||
gServicePump->pump();
|
||||
gServicePump->callback();
|
||||
if (!message.empty()) LL_INFOS() << message << LL_ENDL;
|
||||
}
|
||||
|
||||
bool LLCrashLogger::init()
|
||||
{
|
||||
LLCurl::initClass(false);
|
||||
LLCore::LLHttp::initialize();
|
||||
|
||||
// We assume that all the logs we're looking for reside on the current drive
|
||||
gDirUtilp->initAppDirs("SecondLife");
|
||||
|
|
@ -576,16 +603,74 @@ bool LLCrashLogger::init()
|
|||
return false;
|
||||
}
|
||||
|
||||
gServicePump = new LLPumpIO(gAPRPoolp);
|
||||
gServicePump->prime(gAPRPoolp);
|
||||
LLHTTPClient::setPump(*gServicePump);
|
||||
|
||||
init_curl();
|
||||
LLCore::HttpRequest::createService();
|
||||
LLCore::HttpRequest::startThread();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// For cleanup code common to all platforms.
|
||||
void LLCrashLogger::commonCleanup()
|
||||
{
|
||||
term_curl();
|
||||
LLError::logToFile(""); //close crashreport.log
|
||||
LLProxy::cleanupClass();
|
||||
}
|
||||
|
||||
void LLCrashLogger::init_curl()
|
||||
{
|
||||
curl_global_init(CURL_GLOBAL_ALL);
|
||||
|
||||
ssl_mutex_count = CRYPTO_num_locks();
|
||||
if (ssl_mutex_count > 0)
|
||||
{
|
||||
ssl_mutex_list = new LLCoreInt::HttpMutex *[ssl_mutex_count];
|
||||
|
||||
for (int i(0); i < ssl_mutex_count; ++i)
|
||||
{
|
||||
ssl_mutex_list[i] = new LLCoreInt::HttpMutex;
|
||||
}
|
||||
|
||||
CRYPTO_set_locking_callback(ssl_locking_callback);
|
||||
CRYPTO_set_id_callback(ssl_thread_id_callback);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void LLCrashLogger::term_curl()
|
||||
{
|
||||
CRYPTO_set_locking_callback(NULL);
|
||||
for (int i(0); i < ssl_mutex_count; ++i)
|
||||
{
|
||||
delete ssl_mutex_list[i];
|
||||
}
|
||||
delete[] ssl_mutex_list;
|
||||
}
|
||||
|
||||
|
||||
unsigned long LLCrashLogger::ssl_thread_id_callback(void)
|
||||
{
|
||||
#if LL_WINDOWS
|
||||
return (unsigned long)GetCurrentThread();
|
||||
#else
|
||||
return (unsigned long)pthread_self();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void LLCrashLogger::ssl_locking_callback(int mode, int type, const char * /* file */, int /* line */)
|
||||
{
|
||||
if (type >= 0 && type < ssl_mutex_count)
|
||||
{
|
||||
if (mode & CRYPTO_LOCK)
|
||||
{
|
||||
ssl_mutex_list[type]->lock();
|
||||
}
|
||||
else
|
||||
{
|
||||
ssl_mutex_list[type]->unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
#include "llsd.h"
|
||||
#include "llcontrol.h"
|
||||
#include "llcrashlock.h"
|
||||
#include "_mutex.h"
|
||||
|
||||
// Crash reporter behavior
|
||||
const S32 CRASH_BEHAVIOR_ASK = 0;
|
||||
|
|
@ -66,6 +67,11 @@ public:
|
|||
bool readMinidump(std::string minidump_path);
|
||||
|
||||
protected:
|
||||
static void init_curl();
|
||||
static void term_curl();
|
||||
static unsigned long ssl_thread_id_callback(void);
|
||||
static void ssl_locking_callback(int mode, int type, const char * file, int line);
|
||||
|
||||
S32 mCrashBehavior;
|
||||
BOOL mCrashInPreviousExec;
|
||||
std::map<std::string, std::string> mFileMap;
|
||||
|
|
@ -78,6 +84,10 @@ protected:
|
|||
LLSD mDebugLog;
|
||||
bool mSentCrashLogs;
|
||||
LLCrashLock mKeyMaster;
|
||||
|
||||
static int ssl_mutex_count;
|
||||
static LLCoreInt::HttpMutex ** ssl_mutex_list;
|
||||
|
||||
};
|
||||
|
||||
#endif //LLCRASHLOGGER_H
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ project(llinventory)
|
|||
|
||||
include(00-Common)
|
||||
include(LLCommon)
|
||||
include(LLCoreHttp)
|
||||
include(LLMath)
|
||||
include(LLMessage)
|
||||
include(LLVFS)
|
||||
|
|
@ -71,7 +72,7 @@ if (LL_TESTS)
|
|||
LL_ADD_PROJECT_UNIT_TESTS(llinventory "${llinventory_TEST_SOURCE_FILES}")
|
||||
|
||||
#set(TEST_DEBUG on)
|
||||
set(test_libs llinventory ${LLMESSAGE_LIBRARIES} ${LLVFS_LIBRARIES} ${LLMATH_LIBRARIES} ${LLCOMMON_LIBRARIES} ${WINDOWS_LIBRARIES})
|
||||
set(test_libs llinventory ${LLMESSAGE_LIBRARIES} ${LLVFS_LIBRARIES} ${LLCOREHTTP_LIBRARIES} ${LLMATH_LIBRARIES} ${LLCOMMON_LIBRARIES} ${WINDOWS_LIBRARIES})
|
||||
LL_ADD_INTEGRATION_TEST(inventorymisc "" "${test_libs}")
|
||||
LL_ADD_INTEGRATION_TEST(llparcel "" "${test_libs}")
|
||||
endif (LL_TESTS)
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ include(LLAddBuildTest)
|
|||
include(Python)
|
||||
include(Tut)
|
||||
include(Python)
|
||||
include(JsonCpp)
|
||||
|
||||
include_directories (${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
|
|
@ -23,11 +24,10 @@ include_directories(
|
|||
${LLMATH_INCLUDE_DIRS}
|
||||
${LLMESSAGE_INCLUDE_DIRS}
|
||||
${LLVFS_INCLUDE_DIRS}
|
||||
${JSONCPP_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
set(llmessage_SOURCE_FILES
|
||||
llares.cpp
|
||||
llareslistener.cpp
|
||||
llassetstorage.cpp
|
||||
llavatarname.cpp
|
||||
llavatarnamecache.cpp
|
||||
|
|
@ -38,19 +38,15 @@ set(llmessage_SOURCE_FILES
|
|||
llchainio.cpp
|
||||
llcircuit.cpp
|
||||
llclassifiedflags.cpp
|
||||
llcoproceduremanager.cpp
|
||||
llcorehttputil.cpp
|
||||
llcurl.cpp
|
||||
lldatapacker.cpp
|
||||
lldispatcher.cpp
|
||||
llexperiencecache.cpp
|
||||
llfiltersd2xmlrpc.cpp
|
||||
llhost.cpp
|
||||
llhttpassetstorage.cpp
|
||||
llhttpclient.cpp
|
||||
llhttpclientadapter.cpp
|
||||
llhttpconstants.cpp
|
||||
llhttpnode.cpp
|
||||
llhttpsender.cpp
|
||||
llhttpsdhandler.cpp
|
||||
llinstantmessage.cpp
|
||||
lliobuffer.cpp
|
||||
lliohttpserver.cpp
|
||||
|
|
@ -74,11 +70,8 @@ set(llmessage_SOURCE_FILES
|
|||
llpumpio.cpp
|
||||
llsdappservices.cpp
|
||||
llsdhttpserver.cpp
|
||||
llsdmessage.cpp
|
||||
llsdmessagebuilder.cpp
|
||||
llsdmessagereader.cpp
|
||||
llsdrpcclient.cpp
|
||||
llsdrpcserver.cpp
|
||||
llservicebuilder.cpp
|
||||
llservice.cpp
|
||||
llstoredmessage.cpp
|
||||
|
|
@ -92,7 +85,6 @@ set(llmessage_SOURCE_FILES
|
|||
lltransfertargetfile.cpp
|
||||
lltransfertargetvfile.cpp
|
||||
lltrustedmessageservice.cpp
|
||||
llurlrequest.cpp
|
||||
lluseroperation.cpp
|
||||
llxfer.cpp
|
||||
llxfer_file.cpp
|
||||
|
|
@ -115,8 +107,6 @@ set(llmessage_SOURCE_FILES
|
|||
set(llmessage_HEADER_FILES
|
||||
CMakeLists.txt
|
||||
|
||||
llares.h
|
||||
llareslistener.h
|
||||
llassetstorage.h
|
||||
llavatarname.h
|
||||
llavatarnamecache.h
|
||||
|
|
@ -128,8 +118,8 @@ set(llmessage_HEADER_FILES
|
|||
llcipher.h
|
||||
llcircuit.h
|
||||
llclassifiedflags.h
|
||||
llcoproceduremanager.h
|
||||
llcorehttputil.h
|
||||
llcurl.h
|
||||
lldatapacker.h
|
||||
lldbstrings.h
|
||||
lldispatcher.h
|
||||
|
|
@ -139,14 +129,9 @@ set(llmessage_HEADER_FILES
|
|||
llfiltersd2xmlrpc.h
|
||||
llfollowcamparams.h
|
||||
llhost.h
|
||||
llhttpassetstorage.h
|
||||
llhttpclient.h
|
||||
llhttpclientinterface.h
|
||||
llhttpclientadapter.h
|
||||
llhttpconstants.h
|
||||
llhttpnode.h
|
||||
llhttpnodeadapter.h
|
||||
llhttpsender.h
|
||||
llhttpsdhandler.h
|
||||
llinstantmessage.h
|
||||
llinvite.h
|
||||
lliobuffer.h
|
||||
|
|
@ -176,11 +161,8 @@ set(llmessage_HEADER_FILES
|
|||
llregionhandle.h
|
||||
llsdappservices.h
|
||||
llsdhttpserver.h
|
||||
llsdmessage.h
|
||||
llsdmessagebuilder.h
|
||||
llsdmessagereader.h
|
||||
llsdrpcclient.h
|
||||
llsdrpcserver.h
|
||||
llservice.h
|
||||
llservicebuilder.h
|
||||
llstoredmessage.h
|
||||
|
|
@ -196,7 +178,6 @@ set(llmessage_HEADER_FILES
|
|||
lltransfertargetfile.h
|
||||
lltransfertargetvfile.h
|
||||
lltrustedmessageservice.h
|
||||
llurlrequest.h
|
||||
lluseroperation.h
|
||||
llvehicleparams.h
|
||||
llxfer.h
|
||||
|
|
@ -222,17 +203,41 @@ set_source_files_properties(${llmessage_HEADER_FILES}
|
|||
list(APPEND llmessage_SOURCE_FILES ${llmessage_HEADER_FILES})
|
||||
|
||||
add_library (llmessage ${llmessage_SOURCE_FILES})
|
||||
|
||||
if (LINUX)
|
||||
target_link_libraries(
|
||||
llmessage
|
||||
${CURL_LIBRARIES}
|
||||
${LLCOMMON_LIBRARIES}
|
||||
${LLVFS_LIBRARES}
|
||||
${LLVFS_LIBRARIES}
|
||||
${LLMATH_LIBRARIES}
|
||||
${CARES_LIBRARIES}
|
||||
${JSONCPP_LIBRARIES}
|
||||
${OPENSSL_LIBRARIES}
|
||||
${CRYPTO_LIBRARIES}
|
||||
${XMLRPCEPI_LIBRARIES}
|
||||
${LLCOREHTTP_LIBRARIES}
|
||||
${BOOST_COROUTINE_LIBRARY}
|
||||
${BOOST_CONTEXT_LIBRARY}
|
||||
${BOOST_SYSTEM_LIBRARY}
|
||||
rt
|
||||
)
|
||||
else (LINUX)
|
||||
target_link_libraries(
|
||||
llmessage
|
||||
${CURL_LIBRARIES}
|
||||
${LLCOMMON_LIBRARIES}
|
||||
${LLVFS_LIBRARIES}
|
||||
${LLMATH_LIBRARIES}
|
||||
${JSONCPP_LIBRARIES}
|
||||
${OPENSSL_LIBRARIES}
|
||||
${CRYPTO_LIBRARIES}
|
||||
${XMLRPCEPI_LIBRARIES}
|
||||
${LLCOREHTTP_LIBRARIES}
|
||||
${BOOST_COROUTINE_LIBRARY}
|
||||
${BOOST_CONTEXT_LIBRARY}
|
||||
${BOOST_SYSTEM_LIBRARY}
|
||||
)
|
||||
endif(LINUX)
|
||||
|
||||
# tests
|
||||
if (LL_TESTS)
|
||||
|
|
@ -243,36 +248,42 @@ if (LL_TESTS)
|
|||
)
|
||||
LL_ADD_PROJECT_UNIT_TESTS(llmessage "${llmessage_TEST_SOURCE_FILES}")
|
||||
|
||||
|
||||
# set(TEST_DEBUG on)
|
||||
|
||||
if (LINUX)
|
||||
set(test_libs
|
||||
${CURL_LIBRARIES}
|
||||
${LLMESSAGE_LIBRARIES}
|
||||
${WINDOWS_LIBRARIES}
|
||||
${LLVFS_LIBRARIES}
|
||||
${LLMATH_LIBRARIES}
|
||||
${CURL_LIBRARIES}
|
||||
${LLCOMMON_LIBRARIES}
|
||||
${GOOGLEMOCK_LIBRARIES}
|
||||
${LLMESSAGE_LIBRARIES}
|
||||
${LLCOREHTTP_LIBRARIES}
|
||||
${JSONCPP_LIBRARIES}
|
||||
${BOOST_COROUTINE_LIBRARY}
|
||||
${BOOST_CONTEXT_LIBRARY}
|
||||
rt
|
||||
${GOOGLEMOCK_LIBRARIES}
|
||||
)
|
||||
|
||||
LL_ADD_INTEGRATION_TEST(
|
||||
llsdmessage
|
||||
"llsdmessage.cpp"
|
||||
"${test_libs}"
|
||||
${PYTHON_EXECUTABLE}
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/tests/test_llsdmessage_peer.py"
|
||||
else (LINUX)
|
||||
set(test_libs
|
||||
${WINDOWS_LIBRARIES}
|
||||
${LLVFS_LIBRARIES}
|
||||
${LLMATH_LIBRARIES}
|
||||
${CURL_LIBRARIES}
|
||||
${LLCOMMON_LIBRARIES}
|
||||
${LLMESSAGE_LIBRARIES}
|
||||
${LLCOREHTTP_LIBRARIES}
|
||||
${JSONCPP_LIBRARIES}
|
||||
${BOOST_COROUTINE_LIBRARY}
|
||||
${BOOST_CONTEXT_LIBRARY}
|
||||
${GOOGLEMOCK_LIBRARIES}
|
||||
)
|
||||
endif(LINUX)
|
||||
|
||||
LL_ADD_INTEGRATION_TEST(
|
||||
llhttpclient
|
||||
"llhttpclient.cpp"
|
||||
"${test_libs}"
|
||||
${PYTHON_EXECUTABLE}
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/tests/test_llsdmessage_peer.py"
|
||||
)
|
||||
|
||||
LL_ADD_INTEGRATION_TEST(llavatarnamecache "" "${test_libs}")
|
||||
#LL_ADD_INTEGRATION_TEST(llavatarnamecache "" "${test_libs}")
|
||||
LL_ADD_INTEGRATION_TEST(llhost "" "${test_libs}")
|
||||
LL_ADD_INTEGRATION_TEST(llhttpclientadapter "" "${test_libs}")
|
||||
LL_ADD_INTEGRATION_TEST(llpartdata "" "${test_libs}")
|
||||
LL_ADD_INTEGRATION_TEST(llxfer_file "" "${test_libs}")
|
||||
endif (LL_TESTS)
|
||||
|
|
|
|||
|
|
@ -1,839 +0,0 @@
|
|||
/**
|
||||
* @file llares.cpp
|
||||
* @author Bryan O'Sullivan
|
||||
* @date 2007-08-15
|
||||
* @brief Wrapper for asynchronous DNS lookups.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2007&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2010, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include "linden_common.h"
|
||||
#include "llares.h"
|
||||
|
||||
#include <ares_dns.h>
|
||||
#include <ares_version.h>
|
||||
|
||||
#include "apr_portable.h"
|
||||
#include "apr_network_io.h"
|
||||
#include "apr_poll.h"
|
||||
|
||||
#include "llapr.h"
|
||||
#include "llareslistener.h"
|
||||
|
||||
#if defined(LL_WINDOWS)
|
||||
#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally
|
||||
# define ns_c_in 1
|
||||
# define NS_HFIXEDSZ 12 /* #/bytes of fixed data in header */
|
||||
# define NS_QFIXEDSZ 4 /* #/bytes of fixed data in query */
|
||||
# define NS_RRFIXEDSZ 10 /* #/bytes of fixed data in r record */
|
||||
#else
|
||||
# include <arpa/nameser.h>
|
||||
#endif
|
||||
|
||||
LLAres::HostResponder::~HostResponder()
|
||||
{
|
||||
}
|
||||
|
||||
void LLAres::HostResponder::hostResult(const hostent *ent)
|
||||
{
|
||||
LL_INFOS() << "LLAres::HostResponder::hostResult not implemented" << LL_ENDL;
|
||||
}
|
||||
|
||||
void LLAres::HostResponder::hostError(int code)
|
||||
{
|
||||
LL_INFOS() << "LLAres::HostResponder::hostError " << code << ": "
|
||||
<< LLAres::strerror(code) << LL_ENDL;
|
||||
}
|
||||
|
||||
LLAres::NameInfoResponder::~NameInfoResponder()
|
||||
{
|
||||
}
|
||||
|
||||
void LLAres::NameInfoResponder::nameInfoResult(const char *node,
|
||||
const char *service)
|
||||
{
|
||||
LL_INFOS() << "LLAres::NameInfoResponder::nameInfoResult not implemented"
|
||||
<< LL_ENDL;
|
||||
}
|
||||
|
||||
void LLAres::NameInfoResponder::nameInfoError(int code)
|
||||
{
|
||||
LL_INFOS() << "LLAres::NameInfoResponder::nameInfoError " << code << ": "
|
||||
<< LLAres::strerror(code) << LL_ENDL;
|
||||
}
|
||||
|
||||
LLAres::QueryResponder::~QueryResponder()
|
||||
{
|
||||
}
|
||||
|
||||
void LLAres::QueryResponder::queryResult(const char *buf, size_t len)
|
||||
{
|
||||
LL_INFOS() << "LLAres::QueryResponder::queryResult not implemented"
|
||||
<< LL_ENDL;
|
||||
}
|
||||
|
||||
void LLAres::QueryResponder::queryError(int code)
|
||||
{
|
||||
LL_INFOS() << "LLAres::QueryResponder::queryError " << code << ": "
|
||||
<< LLAres::strerror(code) << LL_ENDL;
|
||||
}
|
||||
|
||||
LLAres::LLAres() :
|
||||
chan_(NULL),
|
||||
mInitSuccess(false)
|
||||
{
|
||||
if (ares_library_init( ARES_LIB_INIT_ALL ) != ARES_SUCCESS ||
|
||||
ares_init(&chan_) != ARES_SUCCESS)
|
||||
{
|
||||
LL_WARNS() << "Could not succesfully initialize ares!" << LL_ENDL;
|
||||
return;
|
||||
}
|
||||
|
||||
mListener = boost::shared_ptr< LLAresListener >(new LLAresListener(this));
|
||||
|
||||
mInitSuccess = true;
|
||||
}
|
||||
|
||||
LLAres::~LLAres()
|
||||
{
|
||||
ares_destroy(chan_);
|
||||
ares_library_cleanup();
|
||||
}
|
||||
|
||||
void LLAres::cancel()
|
||||
{
|
||||
ares_cancel(chan_);
|
||||
}
|
||||
|
||||
static void host_callback_1_5(void *arg, int status, int timeouts,
|
||||
struct hostent *ent)
|
||||
{
|
||||
LLPointer<LLAres::HostResponder> *resp =
|
||||
(LLPointer<LLAres::HostResponder> *) arg;
|
||||
|
||||
if (status == ARES_SUCCESS)
|
||||
{
|
||||
(*resp)->hostResult(ent);
|
||||
} else {
|
||||
(*resp)->hostError(status);
|
||||
}
|
||||
|
||||
delete resp;
|
||||
}
|
||||
|
||||
#if ARES_VERSION_MAJOR == 1 && ARES_VERSION_MINOR == 4
|
||||
static void host_callback(void *arg, int status, struct hostent *ent)
|
||||
{
|
||||
host_callback_1_5(arg, status, 0, ent);
|
||||
}
|
||||
#else
|
||||
# define host_callback host_callback_1_5
|
||||
#endif
|
||||
|
||||
void LLAres::getHostByName(const char *name, HostResponder *resp,
|
||||
int family)
|
||||
{
|
||||
ares_gethostbyname(chan_, name, family, host_callback,
|
||||
new LLPointer<LLAres::HostResponder>(resp));
|
||||
}
|
||||
|
||||
void LLAres::getSrvRecords(const std::string &name, SrvResponder *resp)
|
||||
{
|
||||
search(name, RES_SRV, resp);
|
||||
}
|
||||
|
||||
void LLAres::rewriteURI(const std::string &uri, UriRewriteResponder *resp)
|
||||
{
|
||||
if (resp && uri.size())
|
||||
{
|
||||
LLURI* pURI = new LLURI(uri);
|
||||
|
||||
resp->mUri = *pURI;
|
||||
|
||||
delete pURI;
|
||||
|
||||
if (!resp->mUri.scheme().size() || !resp->mUri.hostName().size())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//LL_INFOS() << "LLAres::rewriteURI (" << uri << ") search: '" << "_" + resp->mUri.scheme() + "._tcp." + resp->mUri.hostName() << "'" << LL_ENDL;
|
||||
|
||||
search("_" + resp->mUri.scheme() + "._tcp." + resp->mUri.hostName(), RES_SRV, resp);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
LLQueryResponder::LLQueryResponder()
|
||||
: LLAres::QueryResponder(),
|
||||
mResult(ARES_ENODATA),
|
||||
mType(RES_INVALID)
|
||||
{
|
||||
}
|
||||
|
||||
int LLQueryResponder::parseRR(const char *buf, size_t len, const char *&pos,
|
||||
LLPointer<LLDnsRecord> &r)
|
||||
{
|
||||
std::string rrname;
|
||||
size_t enclen;
|
||||
int ret;
|
||||
|
||||
// RR name.
|
||||
|
||||
ret = LLAres::expandName(pos, buf, len, rrname, enclen);
|
||||
if (ret != ARES_SUCCESS)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
pos += enclen;
|
||||
|
||||
if (pos + NS_RRFIXEDSZ > buf + len)
|
||||
{
|
||||
return ARES_EBADRESP;
|
||||
}
|
||||
|
||||
int rrtype = DNS_RR_TYPE(pos);
|
||||
int rrclass = DNS_RR_CLASS(pos);
|
||||
int rrttl = DNS_RR_TTL(pos);
|
||||
int rrlen = DNS_RR_LEN(pos);
|
||||
|
||||
if (rrclass != ns_c_in)
|
||||
{
|
||||
return ARES_EBADRESP;
|
||||
}
|
||||
|
||||
pos += NS_RRFIXEDSZ;
|
||||
|
||||
if (pos + rrlen > buf + len)
|
||||
{
|
||||
return ARES_EBADRESP;
|
||||
}
|
||||
|
||||
switch (rrtype)
|
||||
{
|
||||
case RES_A:
|
||||
r = new LLARecord(rrname, rrttl);
|
||||
break;
|
||||
case RES_NS:
|
||||
r = new LLNsRecord(rrname, rrttl);
|
||||
break;
|
||||
case RES_CNAME:
|
||||
r = new LLCnameRecord(rrname, rrttl);
|
||||
break;
|
||||
case RES_PTR:
|
||||
r = new LLPtrRecord(rrname, rrttl);
|
||||
break;
|
||||
case RES_AAAA:
|
||||
r = new LLAaaaRecord(rrname, rrttl);
|
||||
break;
|
||||
case RES_SRV:
|
||||
r = new LLSrvRecord(rrname, rrttl);
|
||||
break;
|
||||
default:
|
||||
LL_INFOS() << "LLQueryResponder::parseRR got unknown RR type " << rrtype
|
||||
<< LL_ENDL;
|
||||
return ARES_EBADRESP;
|
||||
}
|
||||
|
||||
ret = r->parse(buf, len, pos, rrlen);
|
||||
|
||||
if (ret == ARES_SUCCESS)
|
||||
{
|
||||
pos += rrlen;
|
||||
} else {
|
||||
r = NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int LLQueryResponder::parseSection(const char *buf, size_t len,
|
||||
size_t count, const char *&pos,
|
||||
dns_rrs_t &rrs)
|
||||
{
|
||||
int ret = ARES_SUCCESS;
|
||||
|
||||
for (size_t i = 0; i < count; i++)
|
||||
{
|
||||
LLPointer<LLDnsRecord> r;
|
||||
ret = parseRR(buf, len, pos, r);
|
||||
if (ret != ARES_SUCCESS)
|
||||
{
|
||||
break;
|
||||
}
|
||||
rrs.push_back(r);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void LLQueryResponder::queryResult(const char *buf, size_t len)
|
||||
{
|
||||
const char *pos = buf;
|
||||
int qdcount = DNS_HEADER_QDCOUNT(pos);
|
||||
int ancount = DNS_HEADER_ANCOUNT(pos);
|
||||
int nscount = DNS_HEADER_NSCOUNT(pos);
|
||||
int arcount = DNS_HEADER_ARCOUNT(pos);
|
||||
int ret;
|
||||
|
||||
if (qdcount == 0 || ancount + nscount + arcount == 0)
|
||||
{
|
||||
ret = ARES_ENODATA;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
pos += NS_HFIXEDSZ;
|
||||
|
||||
for (int i = 0; i < qdcount; i++)
|
||||
{
|
||||
std::string ignore;
|
||||
size_t enclen;
|
||||
|
||||
ret = LLAres::expandName(pos, buf, len, i == 0 ? mQuery : ignore,
|
||||
enclen);
|
||||
if (ret != ARES_SUCCESS)
|
||||
{
|
||||
goto bail;
|
||||
}
|
||||
|
||||
pos += enclen;
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
int t = DNS_QUESTION_TYPE(pos);
|
||||
switch (t)
|
||||
{
|
||||
case RES_A:
|
||||
case RES_NS:
|
||||
case RES_CNAME:
|
||||
case RES_PTR:
|
||||
case RES_AAAA:
|
||||
case RES_SRV:
|
||||
mType = (LLResType) t;
|
||||
break;
|
||||
default:
|
||||
LL_INFOS() << "Cannot grok query type " << t << LL_ENDL;
|
||||
ret = ARES_EBADQUERY;
|
||||
goto bail;
|
||||
}
|
||||
}
|
||||
|
||||
pos += NS_QFIXEDSZ;
|
||||
if (pos > buf + len)
|
||||
{
|
||||
ret = ARES_EBADRESP;
|
||||
goto bail;
|
||||
}
|
||||
}
|
||||
|
||||
ret = parseSection(buf, len, ancount, pos, mAnswers);
|
||||
if (ret != ARES_SUCCESS)
|
||||
{
|
||||
goto bail;
|
||||
}
|
||||
|
||||
ret = parseSection(buf, len, nscount, pos, mAuthorities);
|
||||
if (ret != ARES_SUCCESS)
|
||||
{
|
||||
goto bail;
|
||||
}
|
||||
|
||||
ret = parseSection(buf, len, arcount, pos, mAdditional);
|
||||
|
||||
bail:
|
||||
mResult = ret;
|
||||
if (mResult == ARES_SUCCESS)
|
||||
{
|
||||
querySuccess();
|
||||
} else {
|
||||
queryError(mResult);
|
||||
}
|
||||
}
|
||||
|
||||
void LLQueryResponder::querySuccess()
|
||||
{
|
||||
LL_INFOS() << "LLQueryResponder::queryResult not implemented" << LL_ENDL;
|
||||
}
|
||||
|
||||
void LLAres::SrvResponder::querySuccess()
|
||||
{
|
||||
if (mType == RES_SRV)
|
||||
{
|
||||
srvResult(mAnswers);
|
||||
} else {
|
||||
srvError(ARES_EBADRESP);
|
||||
}
|
||||
}
|
||||
|
||||
void LLAres::SrvResponder::queryError(int code)
|
||||
{
|
||||
srvError(code);
|
||||
}
|
||||
|
||||
void LLAres::SrvResponder::srvResult(const dns_rrs_t &ents)
|
||||
{
|
||||
LL_INFOS() << "LLAres::SrvResponder::srvResult not implemented" << LL_ENDL;
|
||||
|
||||
for (size_t i = 0; i < ents.size(); i++)
|
||||
{
|
||||
const LLSrvRecord *s = (const LLSrvRecord *) ents[i].get();
|
||||
|
||||
LL_INFOS() << "[" << i << "] " << s->host() << ":" << s->port()
|
||||
<< " priority " << s->priority()
|
||||
<< " weight " << s->weight()
|
||||
<< LL_ENDL;
|
||||
}
|
||||
}
|
||||
|
||||
void LLAres::SrvResponder::srvError(int code)
|
||||
{
|
||||
LL_INFOS() << "LLAres::SrvResponder::srvError " << code << ": "
|
||||
<< LLAres::strerror(code) << LL_ENDL;
|
||||
}
|
||||
|
||||
static void nameinfo_callback_1_5(void *arg, int status, int timeouts,
|
||||
char *node, char *service)
|
||||
{
|
||||
LLPointer<LLAres::NameInfoResponder> *resp =
|
||||
(LLPointer<LLAres::NameInfoResponder> *) arg;
|
||||
|
||||
if (status == ARES_SUCCESS)
|
||||
{
|
||||
(*resp)->nameInfoResult(node, service);
|
||||
} else {
|
||||
(*resp)->nameInfoError(status);
|
||||
}
|
||||
|
||||
delete resp;
|
||||
}
|
||||
|
||||
#if ARES_VERSION_MAJOR == 1 && ARES_VERSION_MINOR == 4
|
||||
static void nameinfo_callback(void *arg, int status, char *node, char *service)
|
||||
{
|
||||
nameinfo_callback_1_5(arg, status, 0, node, service);
|
||||
}
|
||||
#else
|
||||
# define nameinfo_callback nameinfo_callback_1_5
|
||||
#endif
|
||||
|
||||
void LLAres::getNameInfo(const struct sockaddr &sa, socklen_t salen, int flags,
|
||||
NameInfoResponder *resp)
|
||||
{
|
||||
ares_getnameinfo(chan_, &sa, salen, flags, nameinfo_callback,
|
||||
new LLPointer<NameInfoResponder>(resp));
|
||||
}
|
||||
|
||||
static void search_callback_1_5(void *arg, int status, int timeouts,
|
||||
unsigned char *abuf, int alen)
|
||||
{
|
||||
LLPointer<LLAres::QueryResponder> *resp =
|
||||
(LLPointer<LLAres::QueryResponder> *) arg;
|
||||
|
||||
if (status == ARES_SUCCESS)
|
||||
{
|
||||
(*resp)->queryResult((const char *) abuf, alen);
|
||||
} else {
|
||||
(*resp)->queryError(status);
|
||||
}
|
||||
|
||||
delete resp;
|
||||
}
|
||||
|
||||
#if ARES_VERSION_MAJOR == 1 && ARES_VERSION_MINOR == 4
|
||||
static void search_callback(void *arg, int status, unsigned char *abuf,
|
||||
int alen)
|
||||
{
|
||||
search_callback_1_5(arg, status, 0, abuf, alen);
|
||||
}
|
||||
#else
|
||||
# define search_callback search_callback_1_5
|
||||
#endif
|
||||
|
||||
void LLAres::search(const std::string &query, LLResType type,
|
||||
QueryResponder *resp)
|
||||
{
|
||||
ares_search(chan_, query.c_str(), ns_c_in, type, search_callback,
|
||||
new LLPointer<QueryResponder>(resp));
|
||||
}
|
||||
|
||||
bool LLAres::process(U64 timeout)
|
||||
{
|
||||
if (!gAPRPoolp)
|
||||
{
|
||||
ll_init_apr();
|
||||
}
|
||||
|
||||
ares_socket_t socks[ARES_GETSOCK_MAXNUM];
|
||||
apr_pollfd_t aprFds[ARES_GETSOCK_MAXNUM];
|
||||
apr_int32_t nsds = 0;
|
||||
int nactive = 0;
|
||||
int bitmask;
|
||||
|
||||
bitmask = ares_getsock(chan_, socks, ARES_GETSOCK_MAXNUM);
|
||||
|
||||
if (bitmask == 0)
|
||||
{
|
||||
return nsds > 0;
|
||||
}
|
||||
|
||||
apr_status_t status;
|
||||
LLAPRPool pool;
|
||||
status = pool.getStatus() ;
|
||||
ll_apr_assert_status(status);
|
||||
|
||||
for (int i = 0; i < ARES_GETSOCK_MAXNUM; i++)
|
||||
{
|
||||
if (ARES_GETSOCK_READABLE(bitmask, i))
|
||||
{
|
||||
aprFds[nactive].reqevents = APR_POLLIN | APR_POLLERR;
|
||||
}
|
||||
else if (ARES_GETSOCK_WRITABLE(bitmask, i))
|
||||
{
|
||||
aprFds[nactive].reqevents = APR_POLLOUT | APR_POLLERR;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
apr_socket_t *aprSock = NULL;
|
||||
|
||||
status = apr_os_sock_put(&aprSock, (apr_os_sock_t *) &socks[i], pool.getAPRPool());
|
||||
if (status != APR_SUCCESS)
|
||||
{
|
||||
ll_apr_warn_status(status);
|
||||
return nsds > 0;
|
||||
}
|
||||
|
||||
aprFds[nactive].desc.s = aprSock;
|
||||
aprFds[nactive].desc_type = APR_POLL_SOCKET;
|
||||
aprFds[nactive].p = pool.getAPRPool();
|
||||
aprFds[nactive].rtnevents = 0;
|
||||
aprFds[nactive].client_data = &socks[i];
|
||||
|
||||
nactive++;
|
||||
}
|
||||
|
||||
if (nactive > 0)
|
||||
{
|
||||
status = apr_poll(aprFds, nactive, &nsds, timeout);
|
||||
|
||||
if (status != APR_SUCCESS && status != APR_TIMEUP)
|
||||
{
|
||||
ll_apr_warn_status(status);
|
||||
}
|
||||
|
||||
for (int i = 0; i < nactive; i++)
|
||||
{
|
||||
int evts = aprFds[i].rtnevents;
|
||||
int ifd = (evts & (APR_POLLIN | APR_POLLERR))
|
||||
? *((int *) aprFds[i].client_data) : ARES_SOCKET_BAD;
|
||||
int ofd = (evts & (APR_POLLOUT | APR_POLLERR))
|
||||
? *((int *) aprFds[i].client_data) : ARES_SOCKET_BAD;
|
||||
|
||||
ares_process_fd(chan_, ifd, ofd);
|
||||
}
|
||||
}
|
||||
|
||||
return nsds > 0;
|
||||
}
|
||||
|
||||
bool LLAres::processAll()
|
||||
{
|
||||
bool anyProcessed = false, ret;
|
||||
|
||||
do {
|
||||
timeval tv;
|
||||
|
||||
ret = ares_timeout(chan_, NULL, &tv) != NULL;
|
||||
|
||||
if (ret)
|
||||
{
|
||||
ret = process(tv.tv_sec * 1000000LL + tv.tv_usec);
|
||||
anyProcessed |= ret;
|
||||
}
|
||||
} while (ret);
|
||||
|
||||
return anyProcessed;
|
||||
}
|
||||
|
||||
int LLAres::expandName(const char *encoded, const char *abuf, size_t alen,
|
||||
std::string &s, size_t &enclen)
|
||||
{
|
||||
char *t;
|
||||
int ret;
|
||||
long e;
|
||||
|
||||
ret = ares_expand_name((const unsigned char *) encoded,
|
||||
(const unsigned char *) abuf, alen, &t, &e);
|
||||
if (ret == ARES_SUCCESS)
|
||||
{
|
||||
s.assign(t);
|
||||
enclen = e;
|
||||
ares_free_string(t);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
const char *LLAres::strerror(int code)
|
||||
{
|
||||
return ares_strerror(code);
|
||||
}
|
||||
|
||||
LLAres *gAres;
|
||||
|
||||
LLAres *ll_init_ares()
|
||||
{
|
||||
if (gAres == NULL)
|
||||
{
|
||||
gAres = new LLAres();
|
||||
}
|
||||
return gAres;
|
||||
}
|
||||
|
||||
void ll_cleanup_ares()
|
||||
{
|
||||
if (gAres != NULL)
|
||||
{
|
||||
delete gAres;
|
||||
gAres = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
LLDnsRecord::LLDnsRecord(LLResType type, const std::string &name,
|
||||
unsigned ttl)
|
||||
: LLRefCount(),
|
||||
mType(type),
|
||||
mName(name),
|
||||
mTTL(ttl)
|
||||
{
|
||||
}
|
||||
|
||||
LLHostRecord::LLHostRecord(LLResType type, const std::string &name,
|
||||
unsigned ttl)
|
||||
: LLDnsRecord(type, name, ttl)
|
||||
{
|
||||
}
|
||||
|
||||
int LLHostRecord::parse(const char *buf, size_t len, const char *pos,
|
||||
size_t rrlen)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = LLAres::expandName(pos, buf, len, mHost);
|
||||
if (ret != ARES_SUCCESS)
|
||||
{
|
||||
goto bail;
|
||||
}
|
||||
|
||||
ret = ARES_SUCCESS;
|
||||
|
||||
bail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
LLCnameRecord::LLCnameRecord(const std::string &name, unsigned ttl)
|
||||
: LLHostRecord(RES_CNAME, name, ttl)
|
||||
{
|
||||
}
|
||||
|
||||
LLPtrRecord::LLPtrRecord(const std::string &name, unsigned ttl)
|
||||
: LLHostRecord(RES_PTR, name, ttl)
|
||||
{
|
||||
}
|
||||
|
||||
LLAddrRecord::LLAddrRecord(LLResType type, const std::string &name,
|
||||
unsigned ttl)
|
||||
: LLDnsRecord(type, name, ttl),
|
||||
|
||||
mSize(0)
|
||||
{
|
||||
}
|
||||
|
||||
LLARecord::LLARecord(const std::string &name, unsigned ttl)
|
||||
: LLAddrRecord(RES_A, name, ttl)
|
||||
{
|
||||
}
|
||||
|
||||
int LLARecord::parse(const char *buf, size_t len, const char *pos,
|
||||
size_t rrlen)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (rrlen != sizeof(mSA.sin.sin_addr.s_addr))
|
||||
{
|
||||
ret = ARES_EBADRESP;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
memset(&mSA, 0, sizeof(mSA));
|
||||
memcpy(&mSA.sin.sin_addr.s_addr, pos, rrlen);
|
||||
mSA.sin.sin_family = AF_INET6;
|
||||
mSize = sizeof(mSA.sin);
|
||||
|
||||
ret = ARES_SUCCESS;
|
||||
|
||||
bail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
LLAaaaRecord::LLAaaaRecord(const std::string &name, unsigned ttl)
|
||||
: LLAddrRecord(RES_AAAA, name, ttl)
|
||||
{
|
||||
}
|
||||
|
||||
int LLAaaaRecord::parse(const char *buf, size_t len, const char *pos,
|
||||
size_t rrlen)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (rrlen != sizeof(mSA.sin6.sin6_addr))
|
||||
{
|
||||
ret = ARES_EBADRESP;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
memset(&mSA, 0, sizeof(mSA));
|
||||
memcpy(&mSA.sin6.sin6_addr.s6_addr, pos, rrlen);
|
||||
mSA.sin6.sin6_family = AF_INET6;
|
||||
mSize = sizeof(mSA.sin6);
|
||||
|
||||
ret = ARES_SUCCESS;
|
||||
|
||||
bail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
LLSrvRecord::LLSrvRecord(const std::string &name, unsigned ttl)
|
||||
: LLHostRecord(RES_SRV, name, ttl),
|
||||
|
||||
mPriority(0),
|
||||
mWeight(0),
|
||||
mPort(0)
|
||||
{
|
||||
}
|
||||
|
||||
int LLSrvRecord::parse(const char *buf, size_t len, const char *pos,
|
||||
size_t rrlen)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (rrlen < 6)
|
||||
{
|
||||
ret = ARES_EBADRESP;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
memcpy(&mPriority, pos, 2);
|
||||
memcpy(&mWeight, pos + 2, 2);
|
||||
memcpy(&mPort, pos + 4, 2);
|
||||
|
||||
mPriority = ntohs(mPriority);
|
||||
mWeight = ntohs(mWeight);
|
||||
mPort = ntohs(mPort);
|
||||
|
||||
ret = LLHostRecord::parse(buf, len, pos + 6, rrlen - 6);
|
||||
|
||||
bail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
LLNsRecord::LLNsRecord(const std::string &name, unsigned ttl)
|
||||
: LLHostRecord(RES_NS, name, ttl)
|
||||
{
|
||||
}
|
||||
|
||||
void LLAres::UriRewriteResponder::queryError(int code)
|
||||
{
|
||||
std::vector<std::string> uris;
|
||||
uris.push_back(mUri.asString());
|
||||
rewriteResult(uris);
|
||||
}
|
||||
|
||||
void LLAres::UriRewriteResponder::querySuccess()
|
||||
{
|
||||
std::vector<std::string> uris;
|
||||
|
||||
if (mType != RES_SRV)
|
||||
{
|
||||
goto bail;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < mAnswers.size(); i++)
|
||||
{
|
||||
const LLSrvRecord *r = (const LLSrvRecord *) mAnswers[i].get();
|
||||
|
||||
if (r->type() == RES_SRV)
|
||||
{
|
||||
// Check the domain in the response to ensure that it's
|
||||
// the same as the domain in the request, so that bad guys
|
||||
// can't forge responses that point to their own login
|
||||
// servers with their own certificates.
|
||||
|
||||
// Hard-coding the domain to check here is a bit of a
|
||||
// hack. Hoist it to an outer caller if anyone ever needs
|
||||
// this functionality on other domains.
|
||||
|
||||
static const std::string domain(".lindenlab.com");
|
||||
const std::string &host = r->host();
|
||||
|
||||
std::string::size_type s = host.find(domain) + domain.length();
|
||||
|
||||
if (s != host.length() && s != host.length() - 1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
LLURI uri(mUri.scheme(),
|
||||
mUri.userName(),
|
||||
mUri.password(),
|
||||
r->host(),
|
||||
mUri.defaultPort() ? r->port() : mUri.hostPort(),
|
||||
mUri.escapedPath(),
|
||||
mUri.escapedQuery());
|
||||
uris.push_back(uri.asString());
|
||||
}
|
||||
}
|
||||
|
||||
if (!uris.empty())
|
||||
{
|
||||
goto done;
|
||||
}
|
||||
|
||||
bail:
|
||||
uris.push_back(mUri.asString());
|
||||
|
||||
done:
|
||||
rewriteResult(uris);
|
||||
}
|
||||
|
||||
void LLAres::UriRewriteResponder::rewriteResult(
|
||||
const std::vector<std::string> &uris)
|
||||
{
|
||||
LL_INFOS() << "LLAres::UriRewriteResponder::rewriteResult not implemented"
|
||||
<< LL_ENDL;
|
||||
|
||||
for (size_t i = 0; i < uris.size(); i++)
|
||||
{
|
||||
LL_INFOS() << "[" << i << "] " << uris[i] << LL_ENDL;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,583 +0,0 @@
|
|||
/**
|
||||
* @file llares.h
|
||||
* @author Bryan O'Sullivan
|
||||
* @date 2007-08-15
|
||||
* @brief Wrapper for asynchronous DNS lookups.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2007&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2010, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#ifndef LL_LLARES_H
|
||||
#define LL_LLARES_H
|
||||
|
||||
#ifdef LL_WINDOWS
|
||||
// ares.h is broken on windows in that it depends on types defined in ws2tcpip.h
|
||||
// we need to include them first to work around it, but the headers issue warnings
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable:4996)
|
||||
# include <winsock2.h>
|
||||
# include <ws2tcpip.h>
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#ifdef LL_USESYSTEMLIBS
|
||||
# include <ares.h>
|
||||
#else
|
||||
# include <ares/ares.h>
|
||||
#endif
|
||||
|
||||
#include "llpointer.h"
|
||||
#include "llrefcount.h"
|
||||
#include "lluri.h"
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
class LLQueryResponder;
|
||||
class LLAresListener;
|
||||
|
||||
/**
|
||||
* @brief Supported DNS RR types.
|
||||
*/
|
||||
enum LLResType
|
||||
{
|
||||
RES_INVALID = 0, /**< Cookie. */
|
||||
RES_A = 1, /**< "A" record. IPv4 address. */
|
||||
RES_NS = 2, /**< "NS" record. Authoritative server. */
|
||||
RES_CNAME = 5, /**< "CNAME" record. Canonical name. */
|
||||
RES_PTR = 12, /**< "PTR" record. Domain name pointer. */
|
||||
RES_AAAA = 28, /**< "AAAA" record. IPv6 Address. */
|
||||
RES_SRV = 33, /**< "SRV" record. Server Selection. */
|
||||
RES_MAX = 65536 /**< Sentinel; RR types are 16 bits wide. */
|
||||
};
|
||||
|
||||
/**
|
||||
* @class LLDnsRecord
|
||||
* @brief Base class for all DNS RR types.
|
||||
*/
|
||||
class LLDnsRecord : public LLRefCount
|
||||
{
|
||||
protected:
|
||||
friend class LLQueryResponder;
|
||||
|
||||
LLResType mType;
|
||||
std::string mName;
|
||||
unsigned mTTL;
|
||||
|
||||
virtual int parse(const char *buf, size_t len, const char *pos,
|
||||
size_t rrlen) = 0;
|
||||
|
||||
LLDnsRecord(LLResType type, const std::string &name, unsigned ttl);
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Record name.
|
||||
*/
|
||||
const std::string &name() const { return mName; }
|
||||
|
||||
/**
|
||||
* @brief Time-to-live value, in seconds.
|
||||
*/
|
||||
unsigned ttl() const { return mTTL; }
|
||||
|
||||
/**
|
||||
* @brief RR type.
|
||||
*/
|
||||
LLResType type() const { return mType; }
|
||||
};
|
||||
|
||||
/**
|
||||
* @class LLAddrRecord
|
||||
* @brief Base class for address-related RRs.
|
||||
*/
|
||||
class LLAddrRecord : public LLDnsRecord
|
||||
{
|
||||
protected:
|
||||
friend class LLQueryResponder;
|
||||
|
||||
LLAddrRecord(LLResType type, const std::string &name, unsigned ttl);
|
||||
|
||||
union
|
||||
{
|
||||
sockaddr sa;
|
||||
sockaddr_in sin;
|
||||
sockaddr_in6 sin6;
|
||||
} mSA;
|
||||
|
||||
socklen_t mSize;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Generic socket address.
|
||||
*/
|
||||
const sockaddr &addr() const { return mSA.sa; }
|
||||
|
||||
/**
|
||||
* @brief Size of the socket structure.
|
||||
*/
|
||||
socklen_t size() const { return mSize; }
|
||||
};
|
||||
|
||||
/**
|
||||
* @class LLARecord
|
||||
* @brief A RR, for IPv4 addresses.
|
||||
*/
|
||||
class LLARecord : public LLAddrRecord
|
||||
{
|
||||
protected:
|
||||
friend class LLQueryResponder;
|
||||
|
||||
LLARecord(const std::string &name, unsigned ttl);
|
||||
|
||||
int parse(const char *buf, size_t len, const char *pos, size_t rrlen);
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Socket address.
|
||||
*/
|
||||
const sockaddr_in &addr_in() const { return mSA.sin; }
|
||||
};
|
||||
|
||||
/**
|
||||
* @class LLAaaaRecord
|
||||
* @brief AAAA RR, for IPv6 addresses.
|
||||
*/
|
||||
class LLAaaaRecord : public LLAddrRecord
|
||||
{
|
||||
protected:
|
||||
friend class LLQueryResponder;
|
||||
|
||||
LLAaaaRecord(const std::string &name, unsigned ttl);
|
||||
|
||||
int parse(const char *buf, size_t len, const char *pos, size_t rrlen);
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Socket address.
|
||||
*/
|
||||
const sockaddr_in6 &addr_in6() const { return mSA.sin6; }
|
||||
};
|
||||
|
||||
/**
|
||||
* @class LLHostRecord
|
||||
* @brief Base class for host-related RRs.
|
||||
*/
|
||||
class LLHostRecord : public LLDnsRecord
|
||||
{
|
||||
protected:
|
||||
LLHostRecord(LLResType type, const std::string &name, unsigned ttl);
|
||||
|
||||
int parse(const char *buf, size_t len, const char *pos, size_t rrlen);
|
||||
|
||||
std::string mHost;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Host name.
|
||||
*/
|
||||
const std::string &host() const { return mHost; }
|
||||
};
|
||||
|
||||
/**
|
||||
* @class LLCnameRecord
|
||||
* @brief CNAME RR.
|
||||
*/
|
||||
class LLCnameRecord : public LLHostRecord
|
||||
{
|
||||
protected:
|
||||
friend class LLQueryResponder;
|
||||
|
||||
LLCnameRecord(const std::string &name, unsigned ttl);
|
||||
};
|
||||
|
||||
/**
|
||||
* @class LLPtrRecord
|
||||
* @brief PTR RR.
|
||||
*/
|
||||
class LLPtrRecord : public LLHostRecord
|
||||
{
|
||||
protected:
|
||||
friend class LLQueryResponder;
|
||||
|
||||
LLPtrRecord(const std::string &name, unsigned ttl);
|
||||
};
|
||||
|
||||
/**
|
||||
* @class LLSrvRecord
|
||||
* @brief SRV RR.
|
||||
*/
|
||||
class LLSrvRecord : public LLHostRecord
|
||||
{
|
||||
protected:
|
||||
U16 mPriority;
|
||||
U16 mWeight;
|
||||
U16 mPort;
|
||||
|
||||
int parse(const char *buf, size_t len, const char *pos, size_t rrlen);
|
||||
|
||||
public:
|
||||
LLSrvRecord(const std::string &name, unsigned ttl);
|
||||
|
||||
/**
|
||||
* @brief Service priority.
|
||||
*/
|
||||
U16 priority() const { return mPriority; }
|
||||
|
||||
/**
|
||||
* @brief Service weight.
|
||||
*/
|
||||
U16 weight() const { return mWeight; }
|
||||
|
||||
/**
|
||||
* @brief Port number of service.
|
||||
*/
|
||||
U16 port() const { return mPort; }
|
||||
|
||||
/**
|
||||
* @brief Functor for sorting SRV records by priority.
|
||||
*/
|
||||
struct ComparePriorityLowest
|
||||
{
|
||||
bool operator()(const LLSrvRecord& lhs, const LLSrvRecord& rhs)
|
||||
{
|
||||
return lhs.mPriority < rhs.mPriority;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @class LLNsRecord
|
||||
* @brief NS RR.
|
||||
*/
|
||||
class LLNsRecord : public LLHostRecord
|
||||
{
|
||||
public:
|
||||
LLNsRecord(const std::string &name, unsigned ttl);
|
||||
};
|
||||
|
||||
class LLQueryResponder;
|
||||
|
||||
/**
|
||||
* @class LLAres
|
||||
* @brief Asynchronous address resolver.
|
||||
*/
|
||||
class LLAres
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @class HostResponder
|
||||
* @brief Base class for responding to hostname lookups.
|
||||
* @see LLAres::getHostByName
|
||||
*/
|
||||
class HostResponder : public LLRefCount
|
||||
{
|
||||
public:
|
||||
virtual ~HostResponder();
|
||||
|
||||
virtual void hostResult(const hostent *ent);
|
||||
virtual void hostError(int code);
|
||||
};
|
||||
|
||||
/**
|
||||
* @class NameInfoResponder
|
||||
* @brief Base class for responding to address lookups.
|
||||
* @see LLAres::getNameInfo
|
||||
*/
|
||||
class NameInfoResponder : public LLRefCount
|
||||
{
|
||||
public:
|
||||
virtual ~NameInfoResponder();
|
||||
|
||||
virtual void nameInfoResult(const char *node, const char *service);
|
||||
virtual void nameInfoError(int code);
|
||||
};
|
||||
|
||||
/**
|
||||
* @class QueryResponder
|
||||
* @brief Base class for responding to custom searches.
|
||||
* @see LLAres::search
|
||||
*/
|
||||
class QueryResponder : public LLRefCount
|
||||
{
|
||||
public:
|
||||
virtual ~QueryResponder();
|
||||
|
||||
virtual void queryResult(const char *buf, size_t len);
|
||||
virtual void queryError(int code);
|
||||
};
|
||||
|
||||
class SrvResponder;
|
||||
class UriRewriteResponder;
|
||||
|
||||
LLAres();
|
||||
|
||||
~LLAres();
|
||||
|
||||
/**
|
||||
* Cancel all outstanding requests. The error methods of the
|
||||
* corresponding responders will be called, with ARES_ETIMEOUT.
|
||||
*/
|
||||
void cancel();
|
||||
|
||||
/**
|
||||
* Look up the address of a host.
|
||||
*
|
||||
* @param name name of host to look up
|
||||
* @param resp responder to call with result
|
||||
* @param family AF_INET for IPv4 addresses, AF_INET6 for IPv6
|
||||
*/
|
||||
void getHostByName(const std::string &name, HostResponder *resp,
|
||||
int family = AF_INET) {
|
||||
getHostByName(name.c_str(), resp, family);
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up the address of a host.
|
||||
*
|
||||
* @param name name of host to look up
|
||||
* @param resp responder to call with result
|
||||
* @param family AF_INET for IPv4 addresses, AF_INET6 for IPv6
|
||||
*/
|
||||
void getHostByName(const char *name, HostResponder *resp,
|
||||
int family = PF_INET);
|
||||
|
||||
/**
|
||||
* Look up the name associated with a socket address.
|
||||
*
|
||||
* @param sa socket address to look up
|
||||
* @param salen size of socket address
|
||||
* @param flags flags to use
|
||||
* @param resp responder to call with result
|
||||
*/
|
||||
void getNameInfo(const struct sockaddr &sa, socklen_t salen, int flags,
|
||||
NameInfoResponder *resp);
|
||||
|
||||
/**
|
||||
* Look up SRV (service location) records for a service name.
|
||||
*
|
||||
* @param name service name (e.g. "_https._tcp.login.agni.lindenlab.com")
|
||||
* @param resp responder to call with result
|
||||
*/
|
||||
void getSrvRecords(const std::string &name, SrvResponder *resp);
|
||||
|
||||
/**
|
||||
* Rewrite a URI, using SRV (service location) records for its
|
||||
* protocol if available. If no SRV records are published, the
|
||||
* existing URI is handed to the responder.
|
||||
*
|
||||
* @param uri URI to rewrite
|
||||
* @param resp responder to call with result
|
||||
*/
|
||||
void rewriteURI(const std::string &uri,
|
||||
UriRewriteResponder *resp);
|
||||
|
||||
/**
|
||||
* Start a custom search.
|
||||
*
|
||||
* @param query query to make
|
||||
* @param type type of query to perform
|
||||
* @param resp responder to call with result
|
||||
*/
|
||||
void search(const std::string &query, LLResType type,
|
||||
QueryResponder *resp);
|
||||
|
||||
/**
|
||||
* Process any outstanding queries. This method takes an optional
|
||||
* timeout parameter (specified in microseconds). If provided, it
|
||||
* will block the calling thread for that length of time to await
|
||||
* possible responses. A timeout of zero will return immediately
|
||||
* if there are no responses or timeouts to process.
|
||||
*
|
||||
* @param timeoutUsecs number of microseconds to block before timing out
|
||||
* @return whether any responses were processed
|
||||
*/
|
||||
bool process(U64 timeoutUsecs = 0);
|
||||
|
||||
/**
|
||||
* Process all outstanding queries, blocking the calling thread
|
||||
* until all have either been responded to or timed out.
|
||||
*
|
||||
* @return whether any responses were processed
|
||||
*/
|
||||
bool processAll();
|
||||
|
||||
/**
|
||||
* Expand a DNS-encoded compressed string into a normal string.
|
||||
*
|
||||
* @param encoded the encoded name (null-terminated)
|
||||
* @param abuf the response buffer in which the string is embedded
|
||||
* @param alen the length of the response buffer
|
||||
* @param s the string into which to place the result
|
||||
* @return ARES_SUCCESS on success, otherwise an error indicator
|
||||
*/
|
||||
static int expandName(const char *encoded, const char *abuf, size_t alen,
|
||||
std::string &s) {
|
||||
size_t ignore;
|
||||
return expandName(encoded, abuf, alen, s, ignore);
|
||||
}
|
||||
|
||||
static int expandName(const char *encoded, const char *abuf, size_t alen,
|
||||
std::string &s, size_t &enclen);
|
||||
|
||||
/**
|
||||
* Return a string describing an error code.
|
||||
*/
|
||||
static const char *strerror(int code);
|
||||
|
||||
bool isInitialized(void) { return mInitSuccess; }
|
||||
|
||||
protected:
|
||||
ares_channel chan_;
|
||||
bool mInitSuccess;
|
||||
// boost::scoped_ptr would actually fit the requirement better, but it
|
||||
// can't handle incomplete types as boost::shared_ptr can.
|
||||
boost::shared_ptr<LLAresListener> mListener;
|
||||
};
|
||||
|
||||
/**
|
||||
* An ordered collection of DNS resource records.
|
||||
*/
|
||||
typedef std::vector<LLPointer<LLDnsRecord> > dns_rrs_t;
|
||||
|
||||
/**
|
||||
* @class LLQueryResponder
|
||||
* @brief Base class for friendly handling of DNS query responses.
|
||||
*
|
||||
* This class parses a DNS response and represents it in a friendly
|
||||
* manner.
|
||||
*
|
||||
* @see LLDnsRecord
|
||||
* @see LLARecord
|
||||
* @see LLNsRecord
|
||||
* @see LLCnameRecord
|
||||
* @see LLPtrRecord
|
||||
* @see LLAaaaRecord
|
||||
* @see LLSrvRecord
|
||||
*/
|
||||
class LLQueryResponder : public LLAres::QueryResponder
|
||||
{
|
||||
protected:
|
||||
int mResult;
|
||||
std::string mQuery;
|
||||
LLResType mType;
|
||||
|
||||
dns_rrs_t mAnswers;
|
||||
dns_rrs_t mAuthorities;
|
||||
dns_rrs_t mAdditional;
|
||||
|
||||
/**
|
||||
* Parse a single RR.
|
||||
*/
|
||||
int parseRR(const char *buf, size_t len, const char *&pos,
|
||||
LLPointer<LLDnsRecord> &r);
|
||||
/**
|
||||
* Parse one section of a response.
|
||||
*/
|
||||
int parseSection(const char *buf, size_t len,
|
||||
size_t count, const char *& pos, dns_rrs_t &rrs);
|
||||
|
||||
void queryResult(const char *buf, size_t len);
|
||||
virtual void querySuccess();
|
||||
|
||||
public:
|
||||
LLQueryResponder();
|
||||
|
||||
/**
|
||||
* Indicate whether the response could be parsed successfully.
|
||||
*/
|
||||
bool valid() const { return mResult == ARES_SUCCESS; }
|
||||
|
||||
/**
|
||||
* The more detailed result of parsing the response.
|
||||
*/
|
||||
int result() const { return mResult; }
|
||||
|
||||
/**
|
||||
* Return the query embedded in the response.
|
||||
*/
|
||||
const std::string &query() const { return mQuery; }
|
||||
|
||||
/**
|
||||
* Return the contents of the "answers" section of the response.
|
||||
*/
|
||||
const dns_rrs_t &answers() const { return mAnswers; }
|
||||
|
||||
/**
|
||||
* Return the contents of the "authorities" section of the
|
||||
* response.
|
||||
*/
|
||||
const dns_rrs_t &authorities() const { return mAuthorities; }
|
||||
|
||||
/**
|
||||
* Return the contents of the "additional records" section of the
|
||||
* response.
|
||||
*/
|
||||
const dns_rrs_t &additional() const { return mAdditional; }
|
||||
};
|
||||
|
||||
/**
|
||||
* @class LLAres::SrvResponder
|
||||
* @brief Class for handling SRV query responses.
|
||||
*/
|
||||
class LLAres::SrvResponder : public LLQueryResponder
|
||||
{
|
||||
public:
|
||||
friend void LLAres::getSrvRecords(const std::string &name,
|
||||
SrvResponder *resp);
|
||||
void querySuccess();
|
||||
void queryError(int code);
|
||||
|
||||
virtual void srvResult(const dns_rrs_t &ents);
|
||||
virtual void srvError(int code);
|
||||
};
|
||||
|
||||
/**
|
||||
* @class LLAres::UriRewriteResponder
|
||||
* @brief Class for handling URI rewrites based on SRV records.
|
||||
*/
|
||||
class LLAres::UriRewriteResponder : public LLQueryResponder
|
||||
{
|
||||
protected:
|
||||
LLURI mUri;
|
||||
|
||||
public:
|
||||
friend void LLAres::rewriteURI(const std::string &uri,
|
||||
UriRewriteResponder *resp);
|
||||
void querySuccess();
|
||||
void queryError(int code);
|
||||
|
||||
virtual void rewriteResult(const std::vector<std::string> &uris);
|
||||
};
|
||||
|
||||
/**
|
||||
* Singleton responder.
|
||||
*/
|
||||
extern LLAres *gAres;
|
||||
|
||||
/**
|
||||
* Set up the singleton responder. It's safe to call this more than
|
||||
* once from within a single thread, but this function is not
|
||||
* thread safe.
|
||||
*/
|
||||
extern LLAres *ll_init_ares();
|
||||
extern void ll_cleanup_ares();
|
||||
|
||||
#endif // LL_LLARES_H
|
||||
|
|
@ -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>
|
||||
|
||||
|
|
@ -90,6 +98,12 @@ namespace LLAvatarNameCache
|
|||
// Time-to-live for a temp cache entry.
|
||||
const F64 TEMP_CACHE_ENTRY_LIFETIME = 60.0;
|
||||
|
||||
LLCore::HttpRequest::ptr_t sHttpRequest;
|
||||
LLCore::HttpHeaders::ptr_t sHttpHeaders;
|
||||
LLCore::HttpOptions::ptr_t sHttpOptions;
|
||||
LLCore::HttpRequest::policy_t sHttpPolicy;
|
||||
LLCore::HttpRequest::priority_t sHttpPriority;
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
// Internal methods
|
||||
//-----------------------------------------------------------------------
|
||||
|
|
@ -121,7 +135,12 @@ namespace LLAvatarNameCache
|
|||
// Erase expired names from cache
|
||||
void eraseUnrefreshed();
|
||||
|
||||
bool expirationFromCacheControl(const LLSD& headers, F64 *expires);
|
||||
bool expirationFromCacheControl(const LLSD& headers, F64 *expires);
|
||||
|
||||
// This is a coroutine.
|
||||
void requestAvatarNameCache_(std::string url, std::vector<LLUUID> agentIds);
|
||||
|
||||
void handleAvNameCacheSuccess(const LLSD &data, const LLSD &httpResult);
|
||||
}
|
||||
|
||||
/* Sample response:
|
||||
|
|
@ -163,94 +182,117 @@ namespace LLAvatarNameCache
|
|||
</llsd>
|
||||
*/
|
||||
|
||||
class LLAvatarNameResponder : public LLHTTPClient::Responder
|
||||
// Coroutine for sending and processing avatar name cache requests.
|
||||
// Do not call directly. See documentation in lleventcoro.h and llcoro.h for
|
||||
// further explanation.
|
||||
void LLAvatarNameCache::requestAvatarNameCache_(std::string url, std::vector<LLUUID> agentIds)
|
||||
{
|
||||
LOG_CLASS(LLAvatarNameResponder);
|
||||
private:
|
||||
// need to store agent ids that are part of this request in case of
|
||||
// an error, so we can flag them as unavailable
|
||||
std::vector<LLUUID> mAgentIDs;
|
||||
LL_DEBUGS("AvNameCache") << "Entering coroutine " << LLCoros::instance().getName()
|
||||
<< " with url '" << url << "', requesting " << agentIds.size() << " Agent Ids" << LL_ENDL;
|
||||
|
||||
public:
|
||||
LLAvatarNameResponder(const std::vector<LLUUID>& agent_ids)
|
||||
: mAgentIDs(agent_ids)
|
||||
{ }
|
||||
|
||||
protected:
|
||||
/*virtual*/ void httpSuccess()
|
||||
{
|
||||
const LLSD& content = getContent();
|
||||
if (!content.isMap())
|
||||
{
|
||||
failureResult(HTTP_INTERNAL_ERROR, "Malformed response contents", content);
|
||||
return;
|
||||
}
|
||||
// Pull expiration out of headers if available
|
||||
F64 expires = LLAvatarNameCache::nameExpirationFromHeaders(getResponseHeaders());
|
||||
F64 now = LLFrameTimer::getTotalSeconds();
|
||||
try
|
||||
{
|
||||
bool success = true;
|
||||
|
||||
const LLSD& agents = content["agents"];
|
||||
LLSD::array_const_iterator it = agents.beginArray();
|
||||
for ( ; it != agents.endArray(); ++it)
|
||||
{
|
||||
const LLSD& row = *it;
|
||||
LLUUID agent_id = row["id"].asUUID();
|
||||
LLCoreHttpUtil::HttpCoroutineAdapter httpAdapter("NameCache", LLAvatarNameCache::sHttpPolicy);
|
||||
LLSD results = httpAdapter.getAndSuspend(sHttpRequest, url);
|
||||
LLSD httpResults;
|
||||
|
||||
LLAvatarName av_name;
|
||||
av_name.fromLLSD(row);
|
||||
LL_DEBUGS() << results << LL_ENDL;
|
||||
|
||||
// Use expiration time from header
|
||||
av_name.mExpires = expires;
|
||||
|
||||
LL_DEBUGS("AvNameCache") << "LLAvatarNameResponder::result for " << agent_id << LL_ENDL;
|
||||
av_name.dump();
|
||||
|
||||
// cache it and fire signals
|
||||
LLAvatarNameCache::processName(agent_id, av_name);
|
||||
}
|
||||
|
||||
// Same logic as error response case
|
||||
const LLSD& unresolved_agents = content["bad_ids"];
|
||||
S32 num_unresolved = unresolved_agents.size();
|
||||
if (num_unresolved > 0)
|
||||
{
|
||||
LL_WARNS("AvNameCache") << "LLAvatarNameResponder::result " << num_unresolved << " unresolved ids; "
|
||||
<< "expires in " << expires - now << " seconds"
|
||||
<< LL_ENDL;
|
||||
it = unresolved_agents.beginArray();
|
||||
for ( ; it != unresolved_agents.endArray(); ++it)
|
||||
{
|
||||
const LLUUID& agent_id = *it;
|
||||
|
||||
LL_WARNS("AvNameCache") << "LLAvatarNameResponder::result "
|
||||
<< "failed id " << agent_id
|
||||
<< LL_ENDL;
|
||||
if (!results.isMap())
|
||||
{
|
||||
LL_WARNS("AvNameCache") << " Invalid result returned from LLCoreHttpUtil::HttpCoroHandler." << LL_ENDL;
|
||||
success = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
httpResults = results["http_result"];
|
||||
success = httpResults["success"].asBoolean();
|
||||
if (!success)
|
||||
{
|
||||
LL_WARNS("AvNameCache") << "Error result from LLCoreHttpUtil::HttpCoroHandler. Code "
|
||||
<< httpResults["status"] << ": '" << httpResults["message"] << "'" << LL_ENDL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!success)
|
||||
{ // on any sort of failure add dummy records for any agent IDs
|
||||
// in this request that we do not have cached already
|
||||
std::vector<LLUUID>::const_iterator it = agentIds.begin();
|
||||
for ( ; it != agentIds.end(); ++it)
|
||||
{
|
||||
const LLUUID& agent_id = *it;
|
||||
LLAvatarNameCache::handleAgentError(agent_id);
|
||||
}
|
||||
}
|
||||
LL_DEBUGS("AvNameCache") << "LLAvatarNameResponder::result "
|
||||
<< LLAvatarNameCache::sCache.size() << " cached names"
|
||||
<< LL_ENDL;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
LLAvatarNameCache::handleAvNameCacheSuccess(results, httpResults);
|
||||
|
||||
}
|
||||
catch (std::exception e)
|
||||
{
|
||||
LL_WARNS() << "Caught exception '" << e.what() << "'" << LL_ENDL;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LL_WARNS() << "Caught unknown exception." << LL_ENDL;
|
||||
}
|
||||
}
|
||||
|
||||
void LLAvatarNameCache::handleAvNameCacheSuccess(const LLSD &data, const LLSD &httpResult)
|
||||
{
|
||||
|
||||
LLSD headers = httpResult["headers"];
|
||||
// Pull expiration out of headers if available
|
||||
F64 expires = LLAvatarNameCache::nameExpirationFromHeaders(headers);
|
||||
F64 now = LLFrameTimer::getTotalSeconds();
|
||||
|
||||
const LLSD& agents = data["agents"];
|
||||
LLSD::array_const_iterator it = agents.beginArray();
|
||||
for (; it != agents.endArray(); ++it)
|
||||
{
|
||||
const LLSD& row = *it;
|
||||
LLUUID agent_id = row["id"].asUUID();
|
||||
|
||||
LLAvatarName av_name;
|
||||
av_name.fromLLSD(row);
|
||||
|
||||
// Use expiration time from header
|
||||
av_name.mExpires = expires;
|
||||
|
||||
LL_DEBUGS("AvNameCache") << "LLAvatarNameResponder::result for " << agent_id << LL_ENDL;
|
||||
av_name.dump();
|
||||
|
||||
// cache it and fire signals
|
||||
LLAvatarNameCache::processName(agent_id, av_name);
|
||||
}
|
||||
|
||||
/*virtual*/ void httpFailure()
|
||||
{
|
||||
// If there's an error, it might be caused by PeopleApi,
|
||||
// or when loading textures on startup and using a very slow
|
||||
// network, this query may time out.
|
||||
// What we should do depends on whether or not we have a cached name
|
||||
LL_WARNS("AvNameCache") << dumpResponse() << LL_ENDL;
|
||||
// Same logic as error response case
|
||||
const LLSD& unresolved_agents = data["bad_ids"];
|
||||
S32 num_unresolved = unresolved_agents.size();
|
||||
if (num_unresolved > 0)
|
||||
{
|
||||
LL_WARNS("AvNameCache") << "LLAvatarNameResponder::result " << num_unresolved << " unresolved ids; "
|
||||
<< "expires in " << expires - now << " seconds"
|
||||
<< LL_ENDL;
|
||||
it = unresolved_agents.beginArray();
|
||||
for (; it != unresolved_agents.endArray(); ++it)
|
||||
{
|
||||
const LLUUID& agent_id = *it;
|
||||
|
||||
// Add dummy records for any agent IDs in this request that we do not have cached already
|
||||
std::vector<LLUUID>::const_iterator it = mAgentIDs.begin();
|
||||
for ( ; it != mAgentIDs.end(); ++it)
|
||||
{
|
||||
const LLUUID& agent_id = *it;
|
||||
LLAvatarNameCache::handleAgentError(agent_id);
|
||||
}
|
||||
}
|
||||
};
|
||||
LL_WARNS("AvNameCache") << "LLAvatarNameResponder::result "
|
||||
<< "failed id " << agent_id
|
||||
<< LL_ENDL;
|
||||
|
||||
LLAvatarNameCache::handleAgentError(agent_id);
|
||||
}
|
||||
}
|
||||
LL_DEBUGS("AvNameCache") << "LLAvatarNameResponder::result "
|
||||
<< LLAvatarNameCache::sCache.size() << " cached names"
|
||||
<< LL_ENDL;
|
||||
}
|
||||
|
||||
// Provide some fallback for agents that return errors
|
||||
void LLAvatarNameCache::handleAgentError(const LLUUID& agent_id)
|
||||
|
|
@ -353,10 +395,15 @@ void LLAvatarNameCache::requestNamesViaCapability()
|
|||
}
|
||||
}
|
||||
|
||||
if (!url.empty())
|
||||
{
|
||||
LL_INFOS("AvNameCache") << "LLAvatarNameCache::requestNamesViaCapability getting " << ids << " ids" << LL_ENDL;
|
||||
LLHTTPClient::get(url, new LLAvatarNameResponder(agent_ids));
|
||||
if (!url.empty())
|
||||
{
|
||||
LL_DEBUGS("AvNameCache") << "LLAvatarNameCache::requestNamesViaCapability requested " << ids << " ids" << LL_ENDL;
|
||||
|
||||
std::string coroname =
|
||||
LLCoros::instance().launch("LLAvatarNameCache::requestAvatarNameCache_",
|
||||
boost::bind(&LLAvatarNameCache::requestAvatarNameCache_, url, agent_ids));
|
||||
LL_DEBUGS("AvNameCache") << coroname << " with url '" << url << "', agent_ids.size()=" << agent_ids.size() << LL_ENDL;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -419,11 +466,20 @@ void LLAvatarNameCache::initClass(bool running, bool usePeopleAPI)
|
|||
{
|
||||
sRunning = running;
|
||||
sUsePeopleAPI = usePeopleAPI;
|
||||
|
||||
sHttpRequest = LLCore::HttpRequest::ptr_t(new LLCore::HttpRequest());
|
||||
sHttpHeaders = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders());
|
||||
sHttpOptions = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions());
|
||||
sHttpPolicy = LLCore::HttpRequest::DEFAULT_POLICY_ID;
|
||||
sHttpPriority = 0;
|
||||
}
|
||||
|
||||
void LLAvatarNameCache::cleanupClass()
|
||||
{
|
||||
sCache.clear();
|
||||
sHttpRequest.reset();
|
||||
sHttpHeaders.reset();
|
||||
sHttpOptions.reset();
|
||||
sCache.clear();
|
||||
}
|
||||
|
||||
bool LLAvatarNameCache::importFile(std::istream& istr)
|
||||
|
|
@ -698,6 +754,50 @@ void LLAvatarNameCache::insert(const LLUUID& agent_id, const LLAvatarName& av_na
|
|||
sCache[agent_id] = av_name;
|
||||
}
|
||||
|
||||
#if 0
|
||||
F64 LLAvatarNameCache::nameExpirationFromHeaders(LLCore::HttpHeaders *headers)
|
||||
{
|
||||
F64 expires = 0.0;
|
||||
if (expirationFromCacheControl(headers, &expires))
|
||||
{
|
||||
return expires;
|
||||
}
|
||||
else
|
||||
{
|
||||
// With no expiration info, default to an hour
|
||||
const F64 DEFAULT_EXPIRES = 60.0 * 60.0;
|
||||
F64 now = LLFrameTimer::getTotalSeconds();
|
||||
return now + DEFAULT_EXPIRES;
|
||||
}
|
||||
}
|
||||
|
||||
bool LLAvatarNameCache::expirationFromCacheControl(LLCore::HttpHeaders *headers, F64 *expires)
|
||||
{
|
||||
bool fromCacheControl = false;
|
||||
F64 now = LLFrameTimer::getTotalSeconds();
|
||||
|
||||
// Allow the header to override the default
|
||||
const std::string *cache_control;
|
||||
|
||||
cache_control = headers->find(HTTP_IN_HEADER_CACHE_CONTROL);
|
||||
|
||||
if (cache_control && !cache_control->empty())
|
||||
{
|
||||
S32 max_age = 0;
|
||||
if (max_age_from_cache_control(*cache_control, &max_age))
|
||||
{
|
||||
*expires = now + (F64)max_age;
|
||||
fromCacheControl = true;
|
||||
}
|
||||
}
|
||||
LL_DEBUGS("AvNameCache")
|
||||
<< ( fromCacheControl ? "expires based on cache control " : "default expiration " )
|
||||
<< "in " << *expires - now << " seconds"
|
||||
<< LL_ENDL;
|
||||
|
||||
return fromCacheControl;
|
||||
}
|
||||
#else
|
||||
F64 LLAvatarNameCache::nameExpirationFromHeaders(const LLSD& headers)
|
||||
{
|
||||
F64 expires = 0.0;
|
||||
|
|
@ -742,7 +842,7 @@ bool LLAvatarNameCache::expirationFromCacheControl(const LLSD& headers, F64 *exp
|
|||
|
||||
return fromCacheControl;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void LLAvatarNameCache::addUseDisplayNamesCallback(const use_display_name_signal_t::slot_type& cb)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@
|
|||
#define LLAVATARNAMECACHE_H
|
||||
|
||||
#include "llavatarname.h" // for convenience
|
||||
|
||||
#include <boost/signals2.hpp>
|
||||
|
||||
class LLSD;
|
||||
|
|
@ -49,7 +48,7 @@ namespace LLAvatarNameCache
|
|||
bool importFile(std::istream& istr);
|
||||
void exportFile(std::ostream& ostr);
|
||||
|
||||
// On the viewer, usually a simulator capabilitity.
|
||||
// On the viewer, usually a simulator capabilities.
|
||||
// If empty, name cache will fall back to using legacy name lookup system.
|
||||
void setNameLookupURL(const std::string& name_lookup_url);
|
||||
|
||||
|
|
@ -90,7 +89,7 @@ namespace LLAvatarNameCache
|
|||
|
||||
// Compute name expiration time from HTTP Cache-Control header,
|
||||
// or return default value, in seconds from epoch.
|
||||
F64 nameExpirationFromHeaders(const LLSD& headers);
|
||||
F64 nameExpirationFromHeaders(const LLSD& headers);
|
||||
|
||||
void addUseDisplayNamesCallback(const use_display_name_signal_t::slot_type& cb);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -259,7 +259,7 @@ LLCacheName::~LLCacheName()
|
|||
}
|
||||
|
||||
LLCacheName::Impl::Impl(LLMessageSystem* msg)
|
||||
: mMsg(msg), mUpstreamHost(LLHost::invalid)
|
||||
: mMsg(msg), mUpstreamHost(LLHost())
|
||||
{
|
||||
mMsg->setHandlerFuncFast(
|
||||
_PREHASH_UUIDNameRequest, handleUUIDNameRequest, (void**)this);
|
||||
|
|
|
|||
|
|
@ -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,573 @@ std::string responseToString(LLCore::HttpResponse * response);
|
|||
/// a now-useless HttpHandler object.
|
||||
///
|
||||
LLCore::HttpHandle requestPostWithLLSD(LLCore::HttpRequest * request,
|
||||
LLCore::HttpRequest::policy_t policy_id,
|
||||
LLCore::HttpRequest::priority_t priority,
|
||||
const std::string & url,
|
||||
const LLSD & body,
|
||||
LLCore::HttpOptions * options,
|
||||
LLCore::HttpHeaders * headers,
|
||||
LLCore::HttpHandler * handler);
|
||||
LLCore::HttpRequest::policy_t policy_id,
|
||||
LLCore::HttpRequest::priority_t priority,
|
||||
const std::string & url,
|
||||
const LLSD & body,
|
||||
const LLCore::HttpOptions::ptr_t &options,
|
||||
const LLCore::HttpHeaders::ptr_t &headers,
|
||||
const LLCore::HttpHandler::ptr_t &handler);
|
||||
|
||||
inline LLCore::HttpHandle requestPostWithLLSD(LLCore::HttpRequest::ptr_t & request,
|
||||
LLCore::HttpRequest::policy_t policy_id,
|
||||
LLCore::HttpRequest::priority_t priority,
|
||||
const std::string & url,
|
||||
const LLSD & body,
|
||||
const LLCore::HttpOptions::ptr_t & options,
|
||||
const LLCore::HttpHeaders::ptr_t & headers,
|
||||
const LLCore::HttpHandler::ptr_t & handler)
|
||||
{
|
||||
return requestPostWithLLSD(request.get(), policy_id, priority,
|
||||
url, body, options, headers, handler);
|
||||
}
|
||||
|
||||
inline LLCore::HttpHandle requestPostWithLLSD(LLCore::HttpRequest::ptr_t & request,
|
||||
LLCore::HttpRequest::policy_t policy_id,
|
||||
LLCore::HttpRequest::priority_t priority,
|
||||
const std::string & url,
|
||||
const LLSD & body,
|
||||
const LLCore::HttpHandler::ptr_t &handler)
|
||||
{
|
||||
LLCore::HttpOptions::ptr_t options;
|
||||
LLCore::HttpHeaders::ptr_t headers;
|
||||
|
||||
return requestPostWithLLSD(request.get(), policy_id, priority,
|
||||
url, body, options, headers, handler);
|
||||
}
|
||||
|
||||
|
||||
/// Issue a standard HttpRequest::requestPut() call but using
|
||||
/// and LLSD object as the request body. Conventions are the
|
||||
/// same as with that method. Caller is expected to provide
|
||||
/// an HttpHeaders object with a correct 'Content-Type:' header.
|
||||
/// One will not be provided by this call.
|
||||
///
|
||||
/// @return If request is successfully issued, the
|
||||
/// HttpHandle representing the request.
|
||||
/// On error, LLCORE_HTTP_HANDLE_INVALID
|
||||
/// is returned and caller can fetch detailed
|
||||
/// status with the getStatus() method on the
|
||||
/// request object. In case of error, no
|
||||
/// request is queued and caller may need to
|
||||
/// perform additional cleanup such as freeing
|
||||
/// a now-useless HttpHandler object.
|
||||
///
|
||||
LLCore::HttpHandle requestPutWithLLSD(LLCore::HttpRequest * request,
|
||||
LLCore::HttpRequest::policy_t policy_id,
|
||||
LLCore::HttpRequest::priority_t priority,
|
||||
const std::string & url,
|
||||
const LLSD & body,
|
||||
const LLCore::HttpOptions::ptr_t &options,
|
||||
const LLCore::HttpHeaders::ptr_t &headers,
|
||||
const LLCore::HttpHandler::ptr_t &handler);
|
||||
|
||||
inline LLCore::HttpHandle requestPutWithLLSD(LLCore::HttpRequest::ptr_t & request,
|
||||
LLCore::HttpRequest::policy_t policy_id,
|
||||
LLCore::HttpRequest::priority_t priority,
|
||||
const std::string & url,
|
||||
const LLSD & body,
|
||||
const LLCore::HttpOptions::ptr_t & options,
|
||||
const LLCore::HttpHeaders::ptr_t & headers,
|
||||
LLCore::HttpHandler::ptr_t handler)
|
||||
{
|
||||
return requestPutWithLLSD(request.get(), policy_id, priority,
|
||||
url, body, options, headers, handler);
|
||||
}
|
||||
|
||||
inline LLCore::HttpHandle requestPutWithLLSD(LLCore::HttpRequest::ptr_t & request,
|
||||
LLCore::HttpRequest::policy_t policy_id,
|
||||
LLCore::HttpRequest::priority_t priority,
|
||||
const std::string & url,
|
||||
const LLSD & body,
|
||||
LLCore::HttpHandler::ptr_t handler)
|
||||
{
|
||||
LLCore::HttpOptions::ptr_t options;
|
||||
LLCore::HttpHeaders::ptr_t headers;
|
||||
|
||||
return requestPutWithLLSD(request.get(), policy_id, priority,
|
||||
url, body, options, headers, handler);
|
||||
}
|
||||
|
||||
/// Issue a standard HttpRequest::requestPatch() call but using
|
||||
/// and LLSD object as the request body. Conventions are the
|
||||
/// same as with that method. Caller is expected to provide
|
||||
/// an HttpHeaders object with a correct 'Content-Type:' header.
|
||||
/// One will not be provided by this call.
|
||||
///
|
||||
/// @return If request is successfully issued, the
|
||||
/// HttpHandle representing the request.
|
||||
/// On error, LLCORE_HTTP_HANDLE_INVALID
|
||||
/// is returned and caller can fetch detailed
|
||||
/// status with the getStatus() method on the
|
||||
/// request object. In case of error, no
|
||||
/// request is queued and caller may need to
|
||||
/// perform additional cleanup such as freeing
|
||||
/// a now-useless HttpHandler object.
|
||||
///
|
||||
LLCore::HttpHandle requestPatchWithLLSD(LLCore::HttpRequest * request,
|
||||
LLCore::HttpRequest::policy_t policy_id,
|
||||
LLCore::HttpRequest::priority_t priority,
|
||||
const std::string & url,
|
||||
const LLSD & body,
|
||||
const LLCore::HttpOptions::ptr_t &options,
|
||||
const LLCore::HttpHeaders::ptr_t &headers,
|
||||
const LLCore::HttpHandler::ptr_t &handler);
|
||||
|
||||
inline LLCore::HttpHandle requestPatchWithLLSD(LLCore::HttpRequest::ptr_t & request,
|
||||
LLCore::HttpRequest::policy_t policy_id,
|
||||
LLCore::HttpRequest::priority_t priority,
|
||||
const std::string & url,
|
||||
const LLSD & body,
|
||||
const LLCore::HttpOptions::ptr_t & options,
|
||||
const LLCore::HttpHeaders::ptr_t & headers,
|
||||
const LLCore::HttpHandler::ptr_t & handler)
|
||||
{
|
||||
return requestPatchWithLLSD(request.get(), policy_id, priority,
|
||||
url, body, options, headers, handler);
|
||||
}
|
||||
|
||||
inline LLCore::HttpHandle requestPatchWithLLSD(LLCore::HttpRequest::ptr_t & request,
|
||||
LLCore::HttpRequest::policy_t policy_id,
|
||||
LLCore::HttpRequest::priority_t priority,
|
||||
const std::string & url,
|
||||
const LLSD & body,
|
||||
const LLCore::HttpHandler::ptr_t &handler)
|
||||
{
|
||||
LLCore::HttpOptions::ptr_t options;
|
||||
LLCore::HttpHeaders::ptr_t headers;
|
||||
|
||||
return requestPatchWithLLSD(request.get(), policy_id, priority,
|
||||
url, body, options, headers, handler);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
/// The HttpCoroHandler is a specialization of the LLCore::HttpHandler for
|
||||
/// interacting with coroutines. When the request is completed the response
|
||||
/// will be posted onto the supplied Event Pump.
|
||||
///
|
||||
/// The LLSD posted back to the coroutine will have the following additions:
|
||||
/// llsd["http_result"] -+- ["message"] - An error message returned from the HTTP status
|
||||
/// +- ["status"] - The status code associated with the HTTP call
|
||||
/// +- ["success"] - Success of failure of the HTTP call and LLSD parsing.
|
||||
/// +- ["type"] - The LLCore::HttpStatus type associted with the HTTP call
|
||||
/// +- ["url"] - The URL used to make the call.
|
||||
/// +- ["headers"] - A map of name name value pairs with the HTTP headers.
|
||||
///
|
||||
class HttpCoroHandler : public LLCore::HttpHandler
|
||||
{
|
||||
public:
|
||||
|
||||
typedef boost::shared_ptr<HttpCoroHandler> ptr_t;
|
||||
typedef boost::weak_ptr<HttpCoroHandler> wptr_t;
|
||||
|
||||
HttpCoroHandler(LLEventStream &reply);
|
||||
|
||||
static void writeStatusCodes(LLCore::HttpStatus status, const std::string &url, LLSD &result);
|
||||
|
||||
virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response);
|
||||
|
||||
inline LLEventStream &getReplyPump()
|
||||
{
|
||||
return mReplyPump;
|
||||
}
|
||||
|
||||
protected:
|
||||
/// this method may modify the status value
|
||||
virtual LLSD handleSuccess(LLCore::HttpResponse * response, LLCore::HttpStatus &status) = 0;
|
||||
virtual LLSD parseBody(LLCore::HttpResponse *response, bool &success) = 0;
|
||||
|
||||
private:
|
||||
void buildStatusEntry(LLCore::HttpResponse *response, LLCore::HttpStatus status, LLSD &result);
|
||||
|
||||
LLEventStream &mReplyPump;
|
||||
};
|
||||
|
||||
//=========================================================================
|
||||
/// An adapter to handle some of the boilerplate code surrounding HTTP and coroutine
|
||||
/// interaction.
|
||||
///
|
||||
/// Construct an HttpCoroutineAdapter giving it a name and policy Id. After
|
||||
/// any application specific setup call the post, put or get method. The request
|
||||
/// will be automatically pumped and the method will return with an LLSD describing
|
||||
/// the result of the operation. See HttpCoroHandler for a description of the
|
||||
/// decoration done to the returned LLSD.
|
||||
///
|
||||
/// Posting through the adapter will automatically add the following headers to
|
||||
/// the request if they have not been previously specified in a supplied
|
||||
/// HttpHeaders object:
|
||||
/// "Accept=application/llsd+xml"
|
||||
/// "X-SecondLife-UDP-Listen-Port=###"
|
||||
///
|
||||
class HttpCoroutineAdapter
|
||||
{
|
||||
public:
|
||||
static const std::string HTTP_RESULTS;
|
||||
static const std::string HTTP_RESULTS_SUCCESS;
|
||||
static const std::string HTTP_RESULTS_TYPE;
|
||||
static const std::string HTTP_RESULTS_STATUS;
|
||||
static const std::string HTTP_RESULTS_MESSAGE;
|
||||
static const std::string HTTP_RESULTS_URL;
|
||||
static const std::string HTTP_RESULTS_HEADERS;
|
||||
static const std::string HTTP_RESULTS_CONTENT;
|
||||
static const std::string HTTP_RESULTS_RAW;
|
||||
|
||||
typedef boost::shared_ptr<HttpCoroutineAdapter> ptr_t;
|
||||
typedef boost::weak_ptr<HttpCoroutineAdapter> wptr_t;
|
||||
|
||||
HttpCoroutineAdapter(const std::string &name, LLCore::HttpRequest::policy_t policyId,
|
||||
LLCore::HttpRequest::priority_t priority = 0L);
|
||||
~HttpCoroutineAdapter();
|
||||
|
||||
/// Execute a Post transaction on the supplied URL and yield execution of
|
||||
/// the coroutine until a result is available.
|
||||
///
|
||||
/// @Note: the request's smart pointer is passed by value so that it will
|
||||
/// not be deallocated during the yield.
|
||||
LLSD postAndSuspend(LLCore::HttpRequest::ptr_t request,
|
||||
const std::string & url, const LLSD & body,
|
||||
LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()),
|
||||
LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders()));
|
||||
LLSD postAndSuspend(LLCore::HttpRequest::ptr_t request,
|
||||
const std::string & url, LLCore::BufferArray::ptr_t rawbody,
|
||||
LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()),
|
||||
LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders()));
|
||||
|
||||
LLSD postAndSuspend(LLCore::HttpRequest::ptr_t &request,
|
||||
const std::string & url, const LLSD & body,
|
||||
LLCore::HttpHeaders::ptr_t &headers)
|
||||
{
|
||||
return postAndSuspend(request, url, body,
|
||||
LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), headers);
|
||||
}
|
||||
|
||||
LLSD postAndSuspend(LLCore::HttpRequest::ptr_t &request,
|
||||
const std::string & url, LLCore::BufferArray::ptr_t &rawbody,
|
||||
LLCore::HttpHeaders::ptr_t &headers)
|
||||
{
|
||||
return postAndSuspend(request, url, rawbody,
|
||||
LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), headers);
|
||||
}
|
||||
|
||||
LLSD postRawAndSuspend(LLCore::HttpRequest::ptr_t request,
|
||||
const std::string & url, LLCore::BufferArray::ptr_t rawbody,
|
||||
LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()),
|
||||
LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders()));
|
||||
|
||||
LLSD postRawAndSuspend(LLCore::HttpRequest::ptr_t &request,
|
||||
const std::string & url, LLCore::BufferArray::ptr_t &rawbody,
|
||||
LLCore::HttpHeaders::ptr_t &headers)
|
||||
{
|
||||
return postRawAndSuspend(request, url, rawbody,
|
||||
LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), headers);
|
||||
}
|
||||
|
||||
LLSD postFileAndSuspend(LLCore::HttpRequest::ptr_t request,
|
||||
const std::string & url, std::string fileName,
|
||||
LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()),
|
||||
LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders()));
|
||||
|
||||
LLSD postFileAndSuspend(LLCore::HttpRequest::ptr_t &request,
|
||||
const std::string & url, std::string fileName,
|
||||
LLCore::HttpHeaders::ptr_t &headers)
|
||||
{
|
||||
return postFileAndSuspend(request, url, fileName,
|
||||
LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), headers);
|
||||
}
|
||||
|
||||
|
||||
LLSD postFileAndSuspend(LLCore::HttpRequest::ptr_t request,
|
||||
const std::string & url, LLUUID assetId, LLAssetType::EType assetType,
|
||||
LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()),
|
||||
LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders()));
|
||||
|
||||
LLSD postFileAndSuspend(LLCore::HttpRequest::ptr_t request,
|
||||
const std::string & url, LLUUID assetId, LLAssetType::EType assetType,
|
||||
LLCore::HttpHeaders::ptr_t &headers)
|
||||
{
|
||||
return postFileAndSuspend(request, url, assetId, assetType,
|
||||
LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), headers);
|
||||
}
|
||||
|
||||
LLSD postJsonAndSuspend(LLCore::HttpRequest::ptr_t request,
|
||||
const std::string & url, const LLSD & body,
|
||||
LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()),
|
||||
LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders()));
|
||||
LLSD postJsonAndSuspend(LLCore::HttpRequest::ptr_t &request,
|
||||
const std::string & url, const LLSD & body,
|
||||
LLCore::HttpHeaders::ptr_t &headers)
|
||||
{
|
||||
return postJsonAndSuspend(request, url, body,
|
||||
LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), headers);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Execute a Put transaction on the supplied URL and yield execution of
|
||||
/// the coroutine until a result is available.
|
||||
///
|
||||
/// @Note: the request's smart pointer is passed by value so that it will
|
||||
/// not be deallocated during the yield.
|
||||
LLSD putAndSuspend(LLCore::HttpRequest::ptr_t request,
|
||||
const std::string & url, const LLSD & body,
|
||||
LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()),
|
||||
LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders()));
|
||||
|
||||
LLSD putAndSuspend(LLCore::HttpRequest::ptr_t request,
|
||||
const std::string & url, const LLSD & body,
|
||||
LLCore::HttpHeaders::ptr_t headers)
|
||||
{
|
||||
return putAndSuspend(request, url, body,
|
||||
LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), headers);
|
||||
}
|
||||
|
||||
LLSD putJsonAndSuspend(LLCore::HttpRequest::ptr_t request,
|
||||
const std::string & url, const LLSD & body,
|
||||
LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()),
|
||||
LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders()));
|
||||
LLSD putJsonAndSuspend(LLCore::HttpRequest::ptr_t &request,
|
||||
const std::string & url, const LLSD & body,
|
||||
LLCore::HttpHeaders::ptr_t &headers)
|
||||
{
|
||||
return putJsonAndSuspend(request, url, body,
|
||||
LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), headers);
|
||||
}
|
||||
|
||||
/// Execute a Get transaction on the supplied URL and yield execution of
|
||||
/// the coroutine until a result is available.
|
||||
///
|
||||
/// @Note: the request's smart pointer is passed by value so that it will
|
||||
/// not be deallocated during the yield.
|
||||
///
|
||||
LLSD getAndSuspend(LLCore::HttpRequest::ptr_t request,
|
||||
const std::string & url,
|
||||
LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()),
|
||||
LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders()));
|
||||
LLSD getAndSuspend(LLCore::HttpRequest::ptr_t &request,
|
||||
const std::string & url, LLCore::HttpHeaders::ptr_t &headers)
|
||||
{
|
||||
return getAndSuspend(request, url,
|
||||
LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()),
|
||||
headers);
|
||||
}
|
||||
|
||||
LLSD getRawAndSuspend(LLCore::HttpRequest::ptr_t request,
|
||||
const std::string & url,
|
||||
LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()),
|
||||
LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders()));
|
||||
LLSD getRawAndSuspend(LLCore::HttpRequest::ptr_t &request,
|
||||
const std::string & url, LLCore::HttpHeaders::ptr_t &headers)
|
||||
{
|
||||
return getRawAndSuspend(request, url,
|
||||
LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()),
|
||||
headers);
|
||||
}
|
||||
|
||||
/// These methods have the same behavior as @getAndSuspend() however they are
|
||||
/// expecting the server to return the results formatted in a JSON string.
|
||||
/// On a successful GET call the JSON results will be converted into LLSD
|
||||
/// before being returned to the caller.
|
||||
LLSD getJsonAndSuspend(LLCore::HttpRequest::ptr_t request,
|
||||
const std::string & url,
|
||||
LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()),
|
||||
LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders()));
|
||||
LLSD getJsonAndSuspend(LLCore::HttpRequest::ptr_t &request,
|
||||
const std::string & url, LLCore::HttpHeaders::ptr_t &headers)
|
||||
{
|
||||
return getJsonAndSuspend(request, url,
|
||||
LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()),
|
||||
headers);
|
||||
}
|
||||
|
||||
|
||||
/// Execute a DELETE transaction on the supplied URL and yield execution of
|
||||
/// the coroutine until a result is available.
|
||||
///
|
||||
/// @Note: the request's smart pointer is passed by value so that it will
|
||||
/// not be deallocated during the yield.
|
||||
LLSD deleteAndSuspend(LLCore::HttpRequest::ptr_t request,
|
||||
const std::string & url,
|
||||
LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()),
|
||||
LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders()));
|
||||
LLSD deleteAndSuspend(LLCore::HttpRequest::ptr_t request,
|
||||
const std::string & url, LLCore::HttpHeaders::ptr_t headers)
|
||||
{
|
||||
return deleteAndSuspend(request, url,
|
||||
LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()),
|
||||
headers);
|
||||
}
|
||||
|
||||
/// These methods have the same behavior as @deleteAndSuspend() however they are
|
||||
/// expecting the server to return any results formatted in a JSON string.
|
||||
/// On a successful DELETE call the JSON results will be converted into LLSD
|
||||
/// before being returned to the caller.
|
||||
LLSD deleteJsonAndSuspend(LLCore::HttpRequest::ptr_t request,
|
||||
const std::string & url,
|
||||
LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()),
|
||||
LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders()));
|
||||
LLSD deleteJsonAndSuspend(LLCore::HttpRequest::ptr_t request,
|
||||
const std::string & url, LLCore::HttpHeaders::ptr_t headers)
|
||||
{
|
||||
return deleteJsonAndSuspend(request, url,
|
||||
LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()),
|
||||
headers);
|
||||
}
|
||||
|
||||
|
||||
/// Execute a PATCH transaction on the supplied URL and yield execution of
|
||||
/// the coroutine until a result is available.
|
||||
///
|
||||
/// @Note: the request's smart pointer is passed by value so that it will
|
||||
/// not be deallocated during the yield.
|
||||
LLSD patchAndSuspend(LLCore::HttpRequest::ptr_t request,
|
||||
const std::string & url, const LLSD & body,
|
||||
LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()),
|
||||
LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders()));
|
||||
LLSD patchAndSuspend(LLCore::HttpRequest::ptr_t &request,
|
||||
const std::string & url, const LLSD & body,
|
||||
LLCore::HttpHeaders::ptr_t &headers)
|
||||
{
|
||||
return patchAndSuspend(request, url, body,
|
||||
LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), headers);
|
||||
}
|
||||
|
||||
/// Execute a COPY transaction on the supplied URL and yield execution of
|
||||
/// the coroutine until a result is available.
|
||||
///
|
||||
/// @Note: The destination is passed through the HTTP pipe as a header
|
||||
/// The header used is defined as: HTTP_OUT_HEADER_DESTINATION("Destination");
|
||||
///
|
||||
/// @Note: the request's smart pointer is passed by value so that it will
|
||||
/// not be deallocated during the yield.
|
||||
LLSD copyAndSuspend(LLCore::HttpRequest::ptr_t request,
|
||||
const std::string & url, const std::string dest,
|
||||
LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()),
|
||||
LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders()));
|
||||
LLSD copyAndSuspend(LLCore::HttpRequest::ptr_t &request,
|
||||
const std::string & url, const std::string & dest,
|
||||
LLCore::HttpHeaders::ptr_t &headers)
|
||||
{
|
||||
return copyAndSuspend(request, url, dest,
|
||||
LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), headers);
|
||||
}
|
||||
|
||||
/// Execute a MOVE transaction on the supplied URL and yield execution of
|
||||
/// the coroutine until a result is available.
|
||||
///
|
||||
/// @Note: The destination is passed through the HTTP pipe in the headers.
|
||||
/// The header used is defined as: HTTP_OUT_HEADER_DESTINATION("Destination");
|
||||
///
|
||||
/// @Note: the request's smart pointer is passed by value so that it will
|
||||
/// not be deallocated during the yield.
|
||||
LLSD moveAndSuspend(LLCore::HttpRequest::ptr_t request,
|
||||
const std::string & url, const std::string dest,
|
||||
LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()),
|
||||
LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders()));
|
||||
LLSD moveAndSuspend(LLCore::HttpRequest::ptr_t &request,
|
||||
const std::string & url, const std::string & dest,
|
||||
LLCore::HttpHeaders::ptr_t &headers)
|
||||
{
|
||||
return moveAndSuspend(request, url, dest,
|
||||
LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), headers);
|
||||
}
|
||||
|
||||
///
|
||||
void cancelSuspendedOperation();
|
||||
|
||||
static LLCore::HttpStatus getStatusFromLLSD(const LLSD &httpResults);
|
||||
|
||||
/// The convenience routines below can be provided with callback functors
|
||||
/// which will be invoked in the case of success or failure. These callbacks
|
||||
/// should match this form.
|
||||
/// @sa callbackHttpGet
|
||||
/// @sa callbackHttpPost
|
||||
typedef boost::function<void(const LLSD &)> completionCallback_t;
|
||||
|
||||
static void callbackHttpGet(const std::string &url, LLCore::HttpRequest::policy_t policyId, completionCallback_t success = NULL, completionCallback_t failure = NULL);
|
||||
static void callbackHttpGet(const std::string &url, completionCallback_t success = NULL, completionCallback_t failure = NULL)
|
||||
{
|
||||
callbackHttpGet(url, LLCore::HttpRequest::DEFAULT_POLICY_ID, success, failure);
|
||||
}
|
||||
static void callbackHttpPost(const std::string &url, LLCore::HttpRequest::policy_t policyId, const LLSD &postData, completionCallback_t success = NULL, completionCallback_t failure = NULL);
|
||||
static void callbackHttpPost(const std::string &url, const LLSD &postData, completionCallback_t success = NULL, completionCallback_t failure = NULL)
|
||||
{
|
||||
callbackHttpPost(url, LLCore::HttpRequest::DEFAULT_POLICY_ID, postData, success, failure);
|
||||
}
|
||||
|
||||
/// Generic Get and post routines for HTTP via coroutines.
|
||||
/// These static methods do all required setup for the GET or POST operation.
|
||||
/// When the operation completes successfully they will put the success message in the log at INFO level,
|
||||
/// If the operation fails the failure message is written to the log at WARN level.
|
||||
///
|
||||
static void messageHttpGet(const std::string &url, const std::string &success = std::string(), const std::string &failure = std::string());
|
||||
static void messageHttpPost(const std::string &url, const LLSD &postData, const std::string &success, const std::string &failure);
|
||||
|
||||
|
||||
private:
|
||||
static LLSD buildImmediateErrorResult(const LLCore::HttpRequest::ptr_t &request, const std::string &url);
|
||||
|
||||
void saveState(LLCore::HttpHandle yieldingHandle, LLCore::HttpRequest::ptr_t &request,
|
||||
HttpCoroHandler::ptr_t &handler);
|
||||
void cleanState();
|
||||
|
||||
LLSD postAndSuspend_(LLCore::HttpRequest::ptr_t &request,
|
||||
const std::string & url, const LLSD & body,
|
||||
LLCore::HttpOptions::ptr_t &options, LLCore::HttpHeaders::ptr_t &headers,
|
||||
HttpCoroHandler::ptr_t &handler);
|
||||
|
||||
LLSD postAndSuspend_(LLCore::HttpRequest::ptr_t &request,
|
||||
const std::string & url, LLCore::BufferArray::ptr_t &rawbody,
|
||||
LLCore::HttpOptions::ptr_t &options, LLCore::HttpHeaders::ptr_t &headers,
|
||||
HttpCoroHandler::ptr_t &handler);
|
||||
|
||||
LLSD putAndSuspend_(LLCore::HttpRequest::ptr_t &request,
|
||||
const std::string & url, const LLSD & body,
|
||||
LLCore::HttpOptions::ptr_t &options, LLCore::HttpHeaders::ptr_t &headers,
|
||||
HttpCoroHandler::ptr_t &handler);
|
||||
|
||||
LLSD putAndSuspend_(LLCore::HttpRequest::ptr_t &request,
|
||||
const std::string & url, const LLCore::BufferArray::ptr_t & rawbody,
|
||||
LLCore::HttpOptions::ptr_t &options, LLCore::HttpHeaders::ptr_t &headers,
|
||||
HttpCoroHandler::ptr_t &handler);
|
||||
|
||||
LLSD getAndSuspend_(LLCore::HttpRequest::ptr_t &request,
|
||||
const std::string & url, LLCore::HttpOptions::ptr_t &options,
|
||||
LLCore::HttpHeaders::ptr_t &headers, HttpCoroHandler::ptr_t &handler);
|
||||
|
||||
LLSD deleteAndSuspend_(LLCore::HttpRequest::ptr_t &request,
|
||||
const std::string & url, LLCore::HttpOptions::ptr_t &options,
|
||||
LLCore::HttpHeaders::ptr_t &headers, HttpCoroHandler::ptr_t &handler);
|
||||
|
||||
LLSD patchAndSuspend_(LLCore::HttpRequest::ptr_t &request,
|
||||
const std::string & url, const LLSD & body,
|
||||
LLCore::HttpOptions::ptr_t &options, LLCore::HttpHeaders::ptr_t &headers,
|
||||
HttpCoroHandler::ptr_t &handler);
|
||||
|
||||
LLSD copyAndSuspend_(LLCore::HttpRequest::ptr_t &request,
|
||||
const std::string & url,
|
||||
LLCore::HttpOptions::ptr_t &options, LLCore::HttpHeaders::ptr_t &headers,
|
||||
HttpCoroHandler::ptr_t &handler);
|
||||
|
||||
LLSD moveAndSuspend_(LLCore::HttpRequest::ptr_t &request,
|
||||
const std::string & url,
|
||||
LLCore::HttpOptions::ptr_t &options, LLCore::HttpHeaders::ptr_t &headers,
|
||||
HttpCoroHandler::ptr_t &handler);
|
||||
|
||||
static void trivialGetCoro(std::string url, LLCore::HttpRequest::policy_t policyId, completionCallback_t success, completionCallback_t failure);
|
||||
static void trivialPostCoro(std::string url, LLCore::HttpRequest::policy_t policyId, LLSD postData, completionCallback_t success, completionCallback_t failure);
|
||||
|
||||
void checkDefaultHeaders(LLCore::HttpHeaders::ptr_t &headers);
|
||||
|
||||
std::string mAdapterName;
|
||||
LLCore::HttpRequest::priority_t mPriority;
|
||||
LLCore::HttpRequest::policy_t mPolicyId;
|
||||
|
||||
LLCore::HttpHandle mYieldingHandle;
|
||||
LLCore::HttpRequest::wptr_t mWeakRequest;
|
||||
HttpCoroHandler::wptr_t mWeakHandler;
|
||||
};
|
||||
|
||||
|
||||
} // end namespace LLCoreHttpUtil
|
||||
|
||||
|
||||
#endif // LL_LLCOREHTTPUTIL_H
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,557 +0,0 @@
|
|||
/**
|
||||
* @file llcurl.h
|
||||
* @author Zero / Donovan
|
||||
* @date 2006-10-15
|
||||
* @brief A wrapper around libcurl.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2006&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2010, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#ifndef LL_LLCURL_H
|
||||
#define LL_LLCURL_H
|
||||
|
||||
#include "linden_common.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/intrusive_ptr.hpp>
|
||||
#include <curl/curl.h> // TODO: remove dependency
|
||||
|
||||
#include "llbuffer.h"
|
||||
#include "llhttpconstants.h"
|
||||
#include "lliopipe.h"
|
||||
#include "llsd.h"
|
||||
#include "llqueuedthread.h"
|
||||
#include "llframetimer.h"
|
||||
#include "llpointer.h"
|
||||
#include "llsingleton.h"
|
||||
|
||||
class LLMutex;
|
||||
class LLCurlThread;
|
||||
|
||||
// For whatever reason, this is not typedef'd in curl.h
|
||||
typedef size_t (*curl_header_callback)(void *ptr, size_t size, size_t nmemb, void *stream);
|
||||
|
||||
class LLCurl
|
||||
{
|
||||
LOG_CLASS(LLCurl);
|
||||
|
||||
public:
|
||||
class Easy;
|
||||
class Multi;
|
||||
|
||||
struct TransferInfo
|
||||
{
|
||||
TransferInfo() : mSizeDownload(0.0), mTotalTime(0.0), mSpeedDownload(0.0) {}
|
||||
F64 mSizeDownload;
|
||||
F64 mTotalTime;
|
||||
F64 mSpeedDownload;
|
||||
};
|
||||
|
||||
class Responder : public LLThreadSafeRefCount
|
||||
{
|
||||
//LOG_CLASS(Responder);
|
||||
public:
|
||||
|
||||
Responder();
|
||||
virtual ~Responder();
|
||||
|
||||
virtual bool followRedir()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief return true if the status code indicates success.
|
||||
*/
|
||||
bool isGoodStatus() const { return isHttpGoodStatus(mStatus); }
|
||||
|
||||
S32 getStatus() const { return mStatus; }
|
||||
const std::string& getReason() const { return mReason; }
|
||||
const LLSD& getContent() const { return mContent; }
|
||||
bool hasResponseHeader(const std::string& header) const;
|
||||
const std::string& getResponseHeader(const std::string& header) const;
|
||||
const LLSD& getResponseHeaders() const { return mResponseHeaders; }
|
||||
const std::string& getURL() const { return mURL; }
|
||||
EHTTPMethod getHTTPMethod() const { return mHTTPMethod; }
|
||||
|
||||
// This formats response information for use in log spam. Includes content spam.
|
||||
std::string dumpResponse() const;
|
||||
|
||||
// Allows direct triggering of success/error with different results.
|
||||
void completeResult(S32 status, const std::string& reason, const LLSD& content = LLSD());
|
||||
void successResult(const LLSD& content);
|
||||
void failureResult(S32 status, const std::string& reason, const LLSD& content = LLSD());
|
||||
|
||||
// The default implementation will try to parse body content as an LLSD, however
|
||||
// it should not spam about parsing failures unless the server sent a
|
||||
// Content-Type: application/llsd+xml header.
|
||||
virtual void completedRaw(
|
||||
const LLChannelDescriptors& channels,
|
||||
const LLIOPipe::buffer_ptr_t& buffer);
|
||||
/**< Override point for clients that may want to use this
|
||||
class when the response is some other format besides LLSD
|
||||
*/
|
||||
|
||||
|
||||
// The http* methods are not public since these should be triggered internally
|
||||
// after status, reason, content, etc have been set.
|
||||
// If you need to trigger a completion method, use the *Result methods, above.
|
||||
protected:
|
||||
// These methods are the preferred way to process final results.
|
||||
// By default, when one of these is called the following information will be resolved:
|
||||
// * HTTP status code - getStatus()
|
||||
// * Reason string - getReason()
|
||||
// * Content - getContent()
|
||||
// * Response Headers - getResponseHeaders()
|
||||
|
||||
// By default, httpSuccess is triggered whenever httpCompleted is called with a 2xx status code.
|
||||
virtual void httpSuccess();
|
||||
//< called by completed for good status codes.
|
||||
|
||||
// By default, httpFailure is triggered whenever httpCompleted is called with a non-2xx status code.
|
||||
virtual void httpFailure();
|
||||
//< called by httpCompleted() on bad status
|
||||
|
||||
// httpCompleted does not generally need to be overridden, unless
|
||||
// you don't care about the status code (which determine httpFailure or httpSuccess)
|
||||
// or if you want to re-interpret what a 'good' vs' bad' status code is.
|
||||
virtual void httpCompleted();
|
||||
/**< The default implementation calls
|
||||
either:
|
||||
* httpSuccess(), or
|
||||
* httpFailure()
|
||||
*/
|
||||
|
||||
public:
|
||||
void setHTTPMethod(EHTTPMethod method);
|
||||
void setURL(const std::string& url);
|
||||
const std::string& getURL();
|
||||
void setResult(S32 status, const std::string& reason, const LLSD& content = LLSD());
|
||||
void setResponseHeader(const std::string& header, const std::string& value);
|
||||
|
||||
private:
|
||||
// These can be accessed by the get* methods. Treated as 'read-only' during completion handlers.
|
||||
EHTTPMethod mHTTPMethod;
|
||||
std::string mURL;
|
||||
LLSD mResponseHeaders;
|
||||
|
||||
protected:
|
||||
// These should also generally be treated as 'read-only' during completion handlers
|
||||
// and should be accessed by the get* methods. The exception to this rule would
|
||||
// be when overriding the completedRaw method in preparation for calling httpCompleted().
|
||||
S32 mStatus;
|
||||
std::string mReason;
|
||||
LLSD mContent;
|
||||
};
|
||||
typedef LLPointer<Responder> ResponderPtr;
|
||||
|
||||
|
||||
/**
|
||||
* @ brief Set certificate authority file used to verify HTTPS certs.
|
||||
*/
|
||||
static void setCAFile(const std::string& file);
|
||||
|
||||
/**
|
||||
* @ brief Set certificate authority path used to verify HTTPS certs.
|
||||
*/
|
||||
static void setCAPath(const std::string& path);
|
||||
|
||||
/**
|
||||
* @ brief Return human-readable string describing libcurl version.
|
||||
*/
|
||||
static std::string getVersionString();
|
||||
|
||||
/**
|
||||
* @ brief Get certificate authority file used to verify HTTPS certs.
|
||||
*/
|
||||
static const std::string& getCAFile() { return sCAFile; }
|
||||
|
||||
/**
|
||||
* @ brief Get certificate authority path used to verify HTTPS certs.
|
||||
*/
|
||||
static const std::string& getCAPath() { return sCAPath; }
|
||||
|
||||
/**
|
||||
* @ brief Initialize LLCurl class
|
||||
*/
|
||||
static void initClass(F32 curl_reuest_timeout = 120.f, S32 max_number_handles = 256, bool multi_threaded = false);
|
||||
|
||||
/**
|
||||
* @ brief Cleanup LLCurl class
|
||||
*/
|
||||
static void cleanupClass();
|
||||
|
||||
/**
|
||||
* @ brief curl error code -> string
|
||||
*/
|
||||
static std::string strerror(CURLcode errorcode);
|
||||
|
||||
// For OpenSSL callbacks
|
||||
static std::vector<LLMutex*> sSSLMutex;
|
||||
|
||||
// OpenSSL callbacks
|
||||
static void ssl_locking_callback(int mode, int type, const char *file, int line);
|
||||
static unsigned long ssl_thread_id(void);
|
||||
|
||||
static LLCurlThread* getCurlThread() { return sCurlThread ;}
|
||||
|
||||
static CURLM* newMultiHandle() ;
|
||||
static CURLMcode deleteMultiHandle(CURLM* handle) ;
|
||||
static CURL* newEasyHandle() ;
|
||||
static void deleteEasyHandle(CURL* handle) ;
|
||||
|
||||
static CURL* createStandardCurlHandle();
|
||||
|
||||
private:
|
||||
static std::string sCAPath;
|
||||
static std::string sCAFile;
|
||||
static const unsigned int MAX_REDIRECTS;
|
||||
static LLCurlThread* sCurlThread;
|
||||
|
||||
static LLMutex* sHandleMutexp ;
|
||||
static S32 sTotalHandles ;
|
||||
static S32 sMaxHandles;
|
||||
static CURL* sCurlTemplateStandardHandle;
|
||||
public:
|
||||
static bool sNotQuitting;
|
||||
static F32 sCurlRequestTimeOut;
|
||||
};
|
||||
|
||||
class LLCurl::Easy
|
||||
{
|
||||
LOG_CLASS(Easy);
|
||||
|
||||
private:
|
||||
Easy();
|
||||
|
||||
public:
|
||||
static Easy* getEasy();
|
||||
~Easy();
|
||||
|
||||
CURL* getCurlHandle() const { return mCurlEasyHandle; }
|
||||
|
||||
void setErrorBuffer();
|
||||
void setCA();
|
||||
|
||||
void setopt(CURLoption option, S32 value);
|
||||
// These assume the setter does not free value!
|
||||
void setopt(CURLoption option, void* value);
|
||||
void setopt(CURLoption option, char* value);
|
||||
// Copies the string so that it is guaranteed to stick around
|
||||
void setoptString(CURLoption option, const std::string& value);
|
||||
|
||||
void slist_append(const std::string& header, const std::string& value);
|
||||
void slist_append(const char* str);
|
||||
void setHeaders();
|
||||
|
||||
S32 report(CURLcode);
|
||||
void getTransferInfo(LLCurl::TransferInfo* info);
|
||||
|
||||
void prepRequest(const std::string& url, const std::vector<std::string>& headers, LLCurl::ResponderPtr, S32 time_out = 0, bool post = false);
|
||||
|
||||
const char* getErrorBuffer();
|
||||
|
||||
std::stringstream& getInput() { return mInput; }
|
||||
std::stringstream& getHeaderOutput() { return mHeaderOutput; }
|
||||
LLIOPipe::buffer_ptr_t& getOutput() { return mOutput; }
|
||||
const LLChannelDescriptors& getChannels() { return mChannels; }
|
||||
|
||||
void resetState();
|
||||
|
||||
static CURL* allocEasyHandle();
|
||||
static void releaseEasyHandle(CURL* handle);
|
||||
|
||||
private:
|
||||
friend class LLCurl;
|
||||
friend class LLCurl::Multi;
|
||||
|
||||
CURL* mCurlEasyHandle;
|
||||
struct curl_slist* mHeaders;
|
||||
|
||||
std::stringstream mRequest;
|
||||
LLChannelDescriptors mChannels;
|
||||
LLIOPipe::buffer_ptr_t mOutput;
|
||||
std::stringstream mInput;
|
||||
std::stringstream mHeaderOutput;
|
||||
char mErrorBuffer[CURL_ERROR_SIZE];
|
||||
|
||||
// Note: char*'s not strings since we pass pointers to curl
|
||||
std::vector<char*> mStrings;
|
||||
|
||||
LLCurl::ResponderPtr mResponder;
|
||||
|
||||
static std::set<CURL*> sFreeHandles;
|
||||
static std::set<CURL*> sActiveHandles;
|
||||
static LLMutex* sHandleMutexp ;
|
||||
|
||||
static void deleteAllActiveHandles();
|
||||
static void deleteAllFreeHandles();
|
||||
};
|
||||
|
||||
class LLCurl::Multi
|
||||
{
|
||||
LOG_CLASS(Multi);
|
||||
|
||||
friend class LLCurlThread ;
|
||||
|
||||
private:
|
||||
~Multi();
|
||||
|
||||
void markDead() ;
|
||||
bool doPerform();
|
||||
|
||||
public:
|
||||
|
||||
typedef enum
|
||||
{
|
||||
STATE_READY=0,
|
||||
STATE_PERFORMING=1,
|
||||
STATE_COMPLETED=2
|
||||
} ePerformState;
|
||||
|
||||
Multi(F32 idle_time_out = 0.f);
|
||||
|
||||
LLCurl::Easy* allocEasy();
|
||||
bool addEasy(LLCurl::Easy* easy);
|
||||
void removeEasy(LLCurl::Easy* easy);
|
||||
|
||||
void lock() ;
|
||||
void unlock() ;
|
||||
|
||||
void setState(ePerformState state) ;
|
||||
ePerformState getState() ;
|
||||
|
||||
bool isCompleted() ;
|
||||
bool isValid() {return mCurlMultiHandle != NULL && mValid;}
|
||||
bool isDead() {return mDead;}
|
||||
|
||||
bool waitToComplete() ;
|
||||
|
||||
S32 process();
|
||||
|
||||
CURLMsg* info_read(S32* msgs_in_queue);
|
||||
|
||||
S32 mQueued;
|
||||
S32 mErrorCount;
|
||||
|
||||
private:
|
||||
void easyFree(LLCurl::Easy*);
|
||||
void cleanup(bool deleted = false) ;
|
||||
|
||||
CURLM* mCurlMultiHandle;
|
||||
|
||||
typedef std::set<LLCurl::Easy*> easy_active_list_t;
|
||||
easy_active_list_t mEasyActiveList;
|
||||
typedef std::map<CURL*, LLCurl::Easy*> easy_active_map_t;
|
||||
easy_active_map_t mEasyActiveMap;
|
||||
typedef std::set<LLCurl::Easy*> easy_free_list_t;
|
||||
easy_free_list_t mEasyFreeList;
|
||||
|
||||
LLQueuedThread::handle_t mHandle ;
|
||||
ePerformState mState;
|
||||
|
||||
BOOL mDead ;
|
||||
BOOL mValid ;
|
||||
LLMutex* mMutexp ;
|
||||
LLMutex* mDeletionMutexp ;
|
||||
LLMutex* mEasyMutexp ;
|
||||
LLFrameTimer mIdleTimer ;
|
||||
F32 mIdleTimeOut;
|
||||
};
|
||||
|
||||
class LLCurlThread : public LLQueuedThread
|
||||
{
|
||||
public:
|
||||
|
||||
class CurlRequest : public LLQueuedThread::QueuedRequest
|
||||
{
|
||||
protected:
|
||||
virtual ~CurlRequest(); // use deleteRequest()
|
||||
|
||||
public:
|
||||
CurlRequest(handle_t handle, LLCurl::Multi* multi, LLCurlThread* curl_thread);
|
||||
|
||||
/*virtual*/ bool processRequest();
|
||||
/*virtual*/ void finishRequest(bool completed);
|
||||
|
||||
private:
|
||||
// input
|
||||
LLCurl::Multi* mMulti;
|
||||
LLCurlThread* mCurlThread;
|
||||
};
|
||||
friend class CurlRequest;
|
||||
|
||||
public:
|
||||
LLCurlThread(bool threaded = true) ;
|
||||
virtual ~LLCurlThread() ;
|
||||
|
||||
S32 update(F32 max_time_ms);
|
||||
|
||||
void addMulti(LLCurl::Multi* multi) ;
|
||||
void killMulti(LLCurl::Multi* multi) ;
|
||||
|
||||
private:
|
||||
bool doMultiPerform(LLCurl::Multi* multi) ;
|
||||
void deleteMulti(LLCurl::Multi* multi) ;
|
||||
void cleanupMulti(LLCurl::Multi* multi) ;
|
||||
} ;
|
||||
|
||||
|
||||
class LLCurlRequest
|
||||
{
|
||||
public:
|
||||
typedef std::vector<std::string> headers_t;
|
||||
|
||||
LLCurlRequest();
|
||||
~LLCurlRequest();
|
||||
|
||||
void get(const std::string& url, LLCurl::ResponderPtr responder);
|
||||
bool getByteRange(const std::string& url, const headers_t& headers, S32 offset, S32 length, LLCurl::ResponderPtr responder);
|
||||
bool post(const std::string& url, const headers_t& headers, const LLSD& data, LLCurl::ResponderPtr responder, S32 time_out = 0);
|
||||
bool post(const std::string& url, const headers_t& headers, const std::string& data, LLCurl::ResponderPtr responder, S32 time_out = 0);
|
||||
|
||||
S32 process();
|
||||
S32 getQueued();
|
||||
|
||||
private:
|
||||
void addMulti();
|
||||
LLCurl::Easy* allocEasy();
|
||||
bool addEasy(LLCurl::Easy* easy);
|
||||
|
||||
private:
|
||||
typedef std::set<LLCurl::Multi*> curlmulti_set_t;
|
||||
curlmulti_set_t mMultiSet;
|
||||
LLCurl::Multi* mActiveMulti;
|
||||
S32 mActiveRequestCount;
|
||||
BOOL mProcessing;
|
||||
};
|
||||
|
||||
//for texture fetch only
|
||||
class LLCurlTextureRequest : public LLCurlRequest
|
||||
{
|
||||
public:
|
||||
LLCurlTextureRequest(S32 concurrency);
|
||||
~LLCurlTextureRequest();
|
||||
|
||||
U32 getByteRange(const std::string& url, const headers_t& headers, S32 offset, S32 length, U32 pri, LLCurl::ResponderPtr responder, F32 delay_time = -1.f);
|
||||
void nextRequests();
|
||||
void completeRequest(S32 received_bytes);
|
||||
|
||||
void updatePriority(U32 handle, U32 pri);
|
||||
void removeRequest(U32 handle);
|
||||
|
||||
U32 getTotalReceivedBits();
|
||||
U32 getTotalIssuedRequests();
|
||||
S32 getNumRequests();
|
||||
bool isWaiting(U32 handle);
|
||||
|
||||
private:
|
||||
LLMutex mMutex;
|
||||
S32 mConcurrency;
|
||||
S32 mInQueue; //request currently in queue.
|
||||
U32 mHandleCounter;
|
||||
U32 mTotalIssuedRequests;
|
||||
U32 mTotalReceivedBits;
|
||||
|
||||
typedef struct _request_t
|
||||
{
|
||||
_request_t(U32 handle, const std::string& url, const headers_t& headers, S32 offset, S32 length, U32 pri, LLCurl::ResponderPtr responder) :
|
||||
mHandle(handle), mUrl(url), mHeaders(headers), mOffset(offset), mLength(length), mPriority(pri), mResponder(responder), mStartTime(0.f)
|
||||
{}
|
||||
|
||||
U32 mHandle;
|
||||
std::string mUrl;
|
||||
LLCurlRequest::headers_t mHeaders;
|
||||
S32 mOffset;
|
||||
S32 mLength;
|
||||
LLCurl::ResponderPtr mResponder;
|
||||
U32 mPriority;
|
||||
F32 mStartTime; //start time to issue this request
|
||||
} request_t;
|
||||
|
||||
struct request_compare
|
||||
{
|
||||
bool operator()(const request_t* lhs, const request_t* rhs) const
|
||||
{
|
||||
if(lhs->mPriority != rhs->mPriority)
|
||||
{
|
||||
return lhs->mPriority > rhs->mPriority; // higher priority in front of queue (set)
|
||||
}
|
||||
else
|
||||
{
|
||||
return (U32)lhs < (U32)rhs;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::set<request_t*, request_compare> req_queue_t;
|
||||
req_queue_t mCachedRequests;
|
||||
std::map<S32, request_t*> mRequestMap;
|
||||
|
||||
LLFrameTimer mGlobalTimer;
|
||||
};
|
||||
|
||||
class LLCurlEasyRequest
|
||||
{
|
||||
public:
|
||||
LLCurlEasyRequest();
|
||||
~LLCurlEasyRequest();
|
||||
void setopt(CURLoption option, S32 value);
|
||||
void setoptString(CURLoption option, const std::string& value);
|
||||
void setPost(char* postdata, S32 size);
|
||||
void setHeaderCallback(curl_header_callback callback, void* userdata);
|
||||
void setWriteCallback(curl_write_callback callback, void* userdata);
|
||||
void setReadCallback(curl_read_callback callback, void* userdata);
|
||||
void setSSLCtxCallback(curl_ssl_ctx_callback callback, void* userdata);
|
||||
void slist_append(const std::string& header, const std::string& value);
|
||||
void slist_append(const char* str);
|
||||
void sendRequest(const std::string& url);
|
||||
void requestComplete();
|
||||
bool getResult(CURLcode* result, LLCurl::TransferInfo* info = NULL);
|
||||
std::string getErrorString();
|
||||
bool isCompleted() {return mMulti->isCompleted() ;}
|
||||
bool wait() { return mMulti->waitToComplete(); }
|
||||
bool isValid() {return mMulti && mMulti->isValid(); }
|
||||
|
||||
LLCurl::Easy* getEasy() const { return mEasy; }
|
||||
|
||||
private:
|
||||
CURLMsg* info_read(S32* queue, LLCurl::TransferInfo* info);
|
||||
|
||||
private:
|
||||
LLCurl::Multi* mMulti;
|
||||
LLCurl::Easy* mEasy;
|
||||
bool mRequestSent;
|
||||
bool mResultReturned;
|
||||
};
|
||||
|
||||
// Provide access to LLCurl free functions outside of llcurl.cpp without polluting the global namespace.
|
||||
namespace LLCurlFF
|
||||
{
|
||||
void check_easy_code(CURLcode code);
|
||||
void check_multi_code(CURLMcode code);
|
||||
}
|
||||
|
||||
#endif // LL_LLCURL_H
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue