diff --git a/.hgtags b/.hgtags
index a0ff0c07f8..81fccf8afd 100755
--- a/.hgtags
+++ b/.hgtags
@@ -512,3 +512,5 @@ e821ef17c6edea4a59997719d8ba416d8c16e143 3.8.5-release
5a5bd148943bfb46cf2ff2ccf376c42dee93d19b 3.8.6-release
ae3297cdd03ab14f19f3811acbc4acd3eb600336 4.0.0-release
759710a9acef61aaf7b69f4bc4a5a913de87ad8a 4.0.1-release
+e9d350764dfbf5a46229e627547ef5c1b1eeef00 4.0.2-release
+86dfba7ec4332c323025ebeacd8bf343ed0d8cfd 4.0.3-release
diff --git a/BuildParams b/BuildParams
index 9180ae9092..1c5b1e0862 100755
--- a/BuildParams
+++ b/BuildParams
@@ -79,6 +79,11 @@ additional_packages = "EDU"
EDU_sourceid = ""
EDU_viewer_channel_suffix = "edu"
+# The EDU package allows us to create a separate release channel whose expirations
+# are synchronized as much as possible with the academic year
+EDU_sourceid = ""
+EDU_viewer_channel_suffix = "edu"
+
# Notifications - to configure email notices use the TeamCity parameter
# setting screen for your project or build configuration to set the
# environment variable 'email' to a space-separated list of email addresses
diff --git a/autobuild.xml b/autobuild.xml
index e290b9dafc..6c29d5cb18 100755
--- a/autobuild.xml
+++ b/autobuild.xml
@@ -22,9 +22,9 @@
archive
name
linux
@@ -87,60 +87,6 @@
version
1.4.5.297252
- ares
-
boost
copyright
@@ -212,9 +158,9 @@
archive
hash
- 40bd4dd220749a7f0fc8e4d62e61b4a2
+ 66849777a83cb69cec3c06b07da7cd3d
url
- http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/colladadom_3p-update-colladadom/rev/301371/arch/Darwin/installer/colladadom-2.3.301371-darwin-301371.tar.bz2
+ http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/colladadom_3p-update-colladadom/rev/297450/arch/Darwin/installer/colladadom-2.3.297450-darwin-297450.tar.bz2
name
darwin
@@ -224,9 +170,9 @@
archive
hash
- 7ff636034665555e4b3d918d86ef9566
+ d627c2a679f3afb8d3e090d42f53cd2e
url
- http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/colladadom_3p-update-colladadom/rev/301371/arch/Linux/installer/colladadom-2.3.301371-linux-301371.tar.bz2
+ http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/colladadom_3p-update-colladadom/rev/297450/arch/Linux/installer/colladadom-2.3.297450-linux-297450.tar.bz2
name
linux
@@ -236,16 +182,16 @@
archive
hash
- 24e1fac1fd6feef7915c958687fd7c56
+ 220897a1893a188aa9d31efb48909878
url
- http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/colladadom_3p-update-colladadom/rev/301371/arch/CYGWIN/installer/colladadom-2.3.301371-windows-301371.tar.bz2
+ http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/colladadom_3p-update-colladadom/rev/297450/arch/CYGWIN/installer/colladadom-2.3.297450-windows-297450.tar.bz2
name
windows
version
- 2.3.301371
+ 2.3.297450
curl
@@ -266,9 +212,9 @@
archive
hash
- 89db4a1aa22599cf377ae49630b7b5b1
+ ad0061db7188a1b9a974eb0512eeeb8d
url
- http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/curl_3p-update-curl/rev/301717/arch/Darwin/installer/curl-7.42.1.301717-darwin-301717.tar.bz2
+ 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
name
darwin
@@ -278,9 +224,9 @@
archive
hash
- de9e0c855ff6ee30c9e027a70bbef032
+ f49d4ed203b03852a3f6b01b18319f7a
url
- http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/curl_3p-update-curl/rev/301717/arch/Linux/installer/curl-7.42.1.301717-linux-301717.tar.bz2
+ 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
name
linux
@@ -290,16 +236,18 @@
archive
hash
- 98d15713de8c439b7f54cc14f2df07ac
+ 5e0d4f4a5a5bbcba610aafbb91c30b2b
+ hash_algorithm
+ md5
url
- http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/curl_3p-update-curl/rev/301717/arch/CYGWIN/installer/curl-7.42.1.301717-windows-301717.tar.bz2
+ 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
name
windows
version
- 7.42.1.301717
+ 7.47.0.312763
db
@@ -696,9 +644,9 @@
archive
hash
- f577144536fd7c9d26d9f989acf17857
+ 44c596c659d32a86972ac9c6f206cb68
url
- 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
+ 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
name
common
@@ -850,9 +798,9 @@
archive
hash
- e294e6ca721e271b4bae8046cfbc3c9b
+ 0bf69fbc829d964820b798a0494278c9
url
- http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/google-breakpad_3p-update-google-breakpad/rev/298127/arch/Linux/installer/google_breakpad-1413.298127-linux-298127.tar.bz2
+ http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/google-breakpad_3p-update-google-breakpad/rev/298033/arch/Linux/installer/google_breakpad-1413.298033-linux-298033.tar.bz2
name
linux
@@ -1056,9 +1004,9 @@
archive
hash
- 0d586709c1a2e4cf433390bbdd2498ed
+ 5c5b4820999ae9e398801d6a46f45897
url
- http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/havok-source_3p-update-havok-source/rev/301432/arch/Darwin/installer/havok_source-2012.1-darwin-301432.tar.bz2
+ http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/havok-source_3p-update-havok-source/rev/297312/arch/Darwin/installer/havok_source-2012.1-darwin-297312.tar.bz2
name
darwin
@@ -1068,9 +1016,9 @@
archive
hash
- 02c85c2c63c8d002b31382f866ca143b
+ 6b0f41ddddfa60d8424d8a2e0bc2077d
url
- http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/havok-source_3p-update-havok-source/rev/301432/arch/Linux/installer/havok_source-2012.1-linux-301432.tar.bz2
+ http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/havok-source_3p-update-havok-source/rev/296959/arch/Linux/installer/havok_source-2012.1-linux-296959.tar.bz2
name
linux
@@ -1080,9 +1028,9 @@
archive
hash
- ac8a27020182510fd404177e4a97b70f
+ ab30ae74a665950d73ea559f019ff358
url
- http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/havok-source_3p-update-havok-source/rev/301432/arch/CYGWIN/installer/havok_source-2012.1-windows-301432.tar.bz2
+ http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/havok-source_3p-update-havok-source/rev/297566/arch/CYGWIN/installer/havok_source-2012.1-windows-297566.tar.bz2
name
windows
@@ -1164,9 +1112,9 @@
archive
hash
- 8084ced172704ff09b364f7af82a2d6f
+ b25a4f480e07c670ffef00c3da578f87
url
- http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/jsoncpp_3p-update-jsoncpp/rev/297580/arch/Darwin/installer/jsoncpp-0.5.0.297580-darwin-297580.tar.bz2
+ http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/jsoncpp_3p-update-jsoncpp/rev/297281/arch/Darwin/installer/jsoncpp-0.5.0.297281-darwin-297281.tar.bz2
name
darwin
@@ -1176,9 +1124,9 @@
archive
hash
- 910bf12e4b4635170e462b739887cda9
+ 5b3b5dbf0c82c1046482a74ce9e11c38
url
- http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/jsoncpp_3p-update-jsoncpp/rev/297580/arch/Linux/installer/jsoncpp-0.5.0.297580-linux-297580.tar.bz2
+ http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/jsoncpp_3p-update-jsoncpp/rev/297281/arch/Linux/installer/jsoncpp-0.5.0.297281-linux-297281.tar.bz2
name
linux
@@ -1368,9 +1316,9 @@
archive
hash
- 0d134c36fcd87d00d91c99291906dde9
+ 14cb5c8686a472e9e60179e46cd196f7
url
- http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/libpng_3p-update-libpng/rev/301387/arch/Darwin/installer/libpng-1.6.8.301387-darwin-301387.tar.bz2
+ http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/libpng_3p-update-libpng/rev/297708/arch/Darwin/installer/libpng-1.6.8.297708-darwin-297708.tar.bz2
name
darwin
@@ -1380,9 +1328,9 @@
archive
hash
- 744e22c5fcaaf3483a60e29f217daa9c
+ 6dec32fc2527f8cafd616f9271ff3478
url
- http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/libpng_3p-update-libpng/rev/301387/arch/Linux/installer/libpng-1.6.8.301387-linux-301387.tar.bz2
+ http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/libpng_3p-update-libpng/rev/297051/arch/Linux/installer/libpng-1.6.8.297051-linux-297051.tar.bz2
name
linux
@@ -1392,16 +1340,16 @@
archive
hash
- 391158e9b5d92a8b69aeb7478144d2de
+ 09eb65e66e0230ab01e57e643647a4f1
url
- http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/libpng_3p-update-libpng/rev/301387/arch/CYGWIN/installer/libpng-1.6.8.301387-windows-301387.tar.bz2
+ http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/libpng_3p-update-libpng/rev/297708/arch/CYGWIN/installer/libpng-1.6.8.297708-windows-297708.tar.bz2
name
windows
version
- 1.6.8.301387
+ 1.6.8.297708
libuuid
@@ -1506,9 +1454,9 @@
archive
hash
- ce1261a54d877ab5530ae6a9134a77a3
+ faa1e5b7cf70c143caabe190fa5588ce
url
- 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
+ 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
name
linux
@@ -1800,9 +1748,9 @@
archive
hash
- 65f58cc0b17ebd29fe2b8bccc6bcbf64
+ b1245d467d5914a266efa16afeb55406
url
- http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/libndofdev_3p-update-libndofdev/rev/301464/arch/Linux/installer/open_libndofdev-0.3-linux-301464.tar.bz2
+ http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/libndofdev_3p-update-libndofdev/rev/297553/arch/Linux/installer/open_libndofdev-0.3-linux-297553.tar.bz2
name
linux
@@ -2068,9 +2016,9 @@
archive
hash
- 68a8fab5ad3a180487598d3a3e0d57b1
+ 3dd9bf4185bf2df413d890ca9eeab76c
url
- 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
+ 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
name
darwin
@@ -2080,9 +2028,9 @@
archive
hash
- 48ed7ddcf93fa3c751d677f5eb5f9367
+ 06c3a9b1005249f0c94a8b9f3775f3d3
url
- 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
+ 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
name
linux
@@ -2092,16 +2040,16 @@
archive
hash
- 399afab7047e6fa62e7b2fb1768059ea
+ 47a3316dae47cc4e7c1ea7b74ba8dd1e
url
- 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
+ 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
name
windows
version
- 4.6.0017.21209.2988329
+ 3.2.0002.10426.302004
tut
@@ -2318,12 +2266,11 @@
RelWithDebInfo
+ build
+
+
configure
- arguments
-
- ../indra
-
command
cmake
options
@@ -2360,12 +2307,11 @@
Release
+ build
+
+
configure
- arguments
-
- ../indra
-
command
cmake
options
@@ -2400,25 +2346,6 @@
name
ReleaseOS
- Doxygen
-
- build
-
- arguments
-
- doxygen/Doxyfile
-
- command
- doxygen
-
- configure
-
- command
- cmake
-
- name
- Doxygen
-
name
common
@@ -2429,28 +2356,6 @@
build-darwin-i386
configurations
- Doxygen
-
- build
-
-
- configure
-
- options
-
- -DCMAKE_BUILD_TYPE:STRING=Release
- -DWORD_SIZE:STRING=32
- -DROOT_PROJECT_NAME:STRING=SecondLife
- -DINSTALL_PROPRIETARY=TRUE
-
- arguments
-
- ../indra
-
-
- name
- Doxygen
-
RelWithDebInfo
build
@@ -2469,6 +2374,10 @@
configure
+ arguments
+
+ ../indra
+
options
-G
@@ -2523,6 +2432,10 @@
configure
+ arguments
+
+ ../indra
+
options
-G
@@ -2567,26 +2480,6 @@
build-linux-i686
configurations
- Doxygen
-
- build
-
-
- configure
-
- arguments
-
- ../indra
-
- options
-
- -G
- 'Unix Makefiles'
-
-
- name
- Doxygen
-
RelWithDebInfo
build
@@ -2600,6 +2493,10 @@
configure
+ arguments
+
+ ../indra
+
options
-G
@@ -2646,6 +2543,10 @@
configure
+ arguments
+
+ ../indra
+
options
-G
@@ -2745,11 +2646,16 @@
SecondLife.sln
command
- devenv
+ msbuild.exe
options
- /build
- "RelWithDebInfo|Win32"
+ /p:Configuration=RelWithDebInfo
+ /p:Platform=Win32
+ /t:Build
+ /p:useenv=true
+ /verbosity:minimal
+ /toolsversion:4.0
+ /p:"VCBuildAdditionalOptions= /incremental"
configure
@@ -2826,11 +2732,16 @@
SecondLife.sln
command
- devenv
+ msbuild.exe
options
- /build
- "Release|Win32"
+ /p:Configuration=Release
+ /p:Platform=Win32
+ /t:Build
+ /p:useenv=true
+ /verbosity:minimal
+ /toolsversion:4.0
+ /p:"VCBuildAdditionalOptions= /incremental"
configure
diff --git a/indra/cmake/00-COMPILE-LINK-RUN.txt b/indra/cmake/00-COMPILE-LINK-RUN.txt
index 49b899c50d..162b22865c 100644
--- a/indra/cmake/00-COMPILE-LINK-RUN.txt
+++ b/indra/cmake/00-COMPILE-LINK-RUN.txt
@@ -51,7 +51,6 @@ Compilation
WINVER=0x0501 " "
_WIN32_WINNT=0x0501 " "
LL_OS_DRAGDROP_ENABLED=1 " "
- CARES_STATICLIB " "
LIB_NDOF=1 " "
----------------------------------------------------------------------------
@@ -109,7 +108,6 @@ Compilation
DEBUG_INFO=1
LL_DARWIN=1 " "
LL_OS_DRAGDROP_ENABLED=1 " "
- CARES_STATICLIB " "
LIB_NDOF=1 " "
----------------------------------------------------------------------------
diff --git a/indra/cmake/Boost.cmake b/indra/cmake/Boost.cmake
index 25e54b7cbd..180a84dbcf 100644
--- a/indra/cmake/Boost.cmake
+++ b/indra/cmake/Boost.cmake
@@ -123,3 +123,9 @@ else (USESYSTEMLIBS)
debug boost_thread-mt-d)
endif (WINDOWS)
endif (USESYSTEMLIBS)
+
+if (LINUX)
+ set(BOOST_SYSTEM_LIBRARY ${BOOST_SYSTEM_LIBRARY} rt)
+ set(BOOST_THREAD_LIBRARY ${BOOST_THREAD_LIBRARY} rt)
+endif (LINUX)
+
diff --git a/indra/cmake/CARes.cmake b/indra/cmake/CARes.cmake
deleted file mode 100644
index baa55aa49d..0000000000
--- a/indra/cmake/CARes.cmake
+++ /dev/null
@@ -1,21 +0,0 @@
-# -*- cmake -*-
-include(Linking)
-include(Prebuilt)
-
-set(CARES_FIND_QUIETLY ON)
-set(CARES_FIND_REQUIRED ON)
-
-if (USESYSTEMLIBS)
- include(FindCARes)
-else (USESYSTEMLIBS)
- use_prebuilt_binary(ares)
- add_definitions("-DCARES_STATICLIB")
- if (WINDOWS)
- set(CARES_LIBRARIES areslib)
- elseif (DARWIN)
- set(CARES_LIBRARIES cares)
- else (WINDOWS)
- set(CARES_LIBRARIES cares)
- endif (WINDOWS)
- set(CARES_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/include/ares)
-endif (USESYSTEMLIBS)
diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt
index 88e64ffc97..6dc8e3dfbf 100644
--- a/indra/cmake/CMakeLists.txt
+++ b/indra/cmake/CMakeLists.txt
@@ -13,7 +13,7 @@ set(cmake_SOURCE_FILES
BerkeleyDB.cmake
Boost.cmake
BuildVersion.cmake
- CARes.cmake
+ CEFPlugin.cmake
CEFPlugin.cmake
CMakeCopyIfDifferent.cmake
ConfigurePkgConfig.cmake
@@ -28,7 +28,6 @@ set(cmake_SOURCE_FILES
FindAPR.cmake
FindAutobuild.cmake
FindBerkeleyDB.cmake
- FindCARes.cmake
FindFMODEX.cmake
FindGLH.cmake
FindGoogleBreakpad.cmake
@@ -59,7 +58,6 @@ set(cmake_SOURCE_FILES
JsonCpp.cmake
LLAddBuildTest.cmake
LLAppearance.cmake
- LLAppearanceUtility.cmake
LLAudio.cmake
LLCharacter.cmake
LLCommon.cmake
diff --git a/indra/cmake/Copy3rdPartyLibs.cmake b/indra/cmake/Copy3rdPartyLibs.cmake
index a6fd756c88..70d85b864c 100644
--- a/indra/cmake/Copy3rdPartyLibs.cmake
+++ b/indra/cmake/Copy3rdPartyLibs.cmake
@@ -22,10 +22,8 @@ if(WINDOWS)
SLVoice.exe
ca-bundle.crt
libsndfile-1.dll
- vivoxplatform.dll
vivoxsdk.dll
ortp.dll
- zlib1.dll
vivoxoal.dll
)
diff --git a/indra/cmake/FindCARes.cmake b/indra/cmake/FindCARes.cmake
deleted file mode 100644
index 1ed5b32913..0000000000
--- a/indra/cmake/FindCARes.cmake
+++ /dev/null
@@ -1,48 +0,0 @@
-# -*- cmake -*-
-
-# - Find c-ares
-# Find the c-ares includes and library
-# This module defines
-# CARES_INCLUDE_DIR, where to find ares.h, etc.
-# CARES_LIBRARIES, the libraries needed to use c-ares.
-# CARES_FOUND, If false, do not try to use c-ares.
-# also defined, but not for general use are
-# CARES_LIBRARY, where to find the c-ares library.
-
-FIND_PATH(CARES_INCLUDE_DIR ares.h
-/usr/local/include
-/usr/include
-)
-
-SET(CARES_NAMES ${CARES_NAMES} cares)
-FIND_LIBRARY(CARES_LIBRARY
- NAMES ${CARES_NAMES}
- PATHS /usr/lib /usr/local/lib
- )
-
-IF (CARES_LIBRARY AND CARES_INCLUDE_DIR)
- SET(CARES_LIBRARIES ${CARES_LIBRARY})
- SET(CARES_FOUND "YES")
-ELSE (CARES_LIBRARY AND CARES_INCLUDE_DIR)
- SET(CARES_FOUND "NO")
-ENDIF (CARES_LIBRARY AND CARES_INCLUDE_DIR)
-
-
-IF (CARES_FOUND)
- IF (NOT CARES_FIND_QUIETLY)
- MESSAGE(STATUS "Found c-ares: ${CARES_LIBRARIES}")
- ENDIF (NOT CARES_FIND_QUIETLY)
-ELSE (CARES_FOUND)
- IF (CARES_FIND_REQUIRED)
- MESSAGE(FATAL_ERROR "Could not find c-ares library")
- ENDIF (CARES_FIND_REQUIRED)
-ENDIF (CARES_FOUND)
-
-# Deprecated declarations.
-SET (NATIVE_CARES_INCLUDE_PATH ${CARES_INCLUDE_DIR} )
-GET_FILENAME_COMPONENT (NATIVE_CARES_LIB_PATH ${CARES_LIBRARY} PATH)
-
-MARK_AS_ADVANCED(
- CARES_LIBRARY
- CARES_INCLUDE_DIR
- )
diff --git a/indra/cmake/LLAddBuildTest.cmake b/indra/cmake/LLAddBuildTest.cmake
index ac5c5c6a2a..db8b95dbe2 100644
--- a/indra/cmake/LLAddBuildTest.cmake
+++ b/indra/cmake/LLAddBuildTest.cmake
@@ -35,6 +35,7 @@ INCLUDE(GoogleMock)
${APRUTIL_LIBRARIES}
${APR_LIBRARIES}
llcommon
+ llcorehttp
)
IF(NOT "${project}" STREQUAL "llmath")
# add llmath as a dep unless the tested module *is* llmath!
@@ -49,6 +50,9 @@ INCLUDE(GoogleMock)
${GOOGLEMOCK_INCLUDE_DIRS}
)
SET(alltest_LIBRARIES
+ ${BOOST_COROUTINE_LIBRARY}
+ ${BOOST_CONTEXT_LIBRARY}
+ ${BOOST_SYSTEM_LIBRARY}
${GOOGLEMOCK_LIBRARIES}
${PTHREAD_LIBRARY}
${WINDOWS_LIBRARIES}
@@ -191,6 +195,9 @@ FUNCTION(LL_ADD_INTEGRATION_TEST
SET(libraries
${library_dependencies}
+ ${BOOST_COROUTINE_LIBRARY}
+ ${BOOST_CONTEXT_LIBRARY}
+ ${BOOST_SYSTEM_LIBRARY}
${GOOGLEMOCK_LIBRARIES}
${PTHREAD_LIBRARY}
)
diff --git a/indra/cmake/LLAppearance.cmake b/indra/cmake/LLAppearance.cmake
index bd3795a526..ae265d07e3 100644
--- a/indra/cmake/LLAppearance.cmake
+++ b/indra/cmake/LLAppearance.cmake
@@ -1,6 +1,9 @@
# -*- cmake -*-
include(Variables)
+include(Boost)
+include(LLMessage)
+include(LLCoreHttp)
set(LLAPPEARANCE_INCLUDE_DIRS
${LIBS_OPEN_DIR}/llappearance
@@ -12,6 +15,13 @@ if (BUILD_HEADLESS)
)
endif (BUILD_HEADLESS)
-set(LLAPPEARANCE_LIBRARIES llappearance)
+set(LLAPPEARANCE_LIBRARIES llappearance
+ llmessage
+ llcorehttp
+ ${BOOST_COROUTINE_LIBRARY}
+ ${BOOST_CONTEXT_LIBRARY}
+ ${BOOST_SYSTEM_LIBRARY}
+ )
+
diff --git a/indra/cmake/LLAppearanceUtility.cmake b/indra/cmake/LLAppearanceUtility.cmake
index 709b91c134..28b49bf75f 100644
--- a/indra/cmake/LLAppearanceUtility.cmake
+++ b/indra/cmake/LLAppearanceUtility.cmake
@@ -1,5 +1,6 @@
# -*- cmake -*-
include(Prebuilt)
+include(Boost)
# Linux proprietary build only
if (INSTALL_PROPRIETARY)
@@ -10,3 +11,4 @@ if (INSTALL_PROPRIETARY)
endif (LINUX)
endif (INSTALL_PROPRIETARY)
+
diff --git a/indra/cmake/LLCommon.cmake b/indra/cmake/LLCommon.cmake
index b52556a73e..b50b4bcdb2 100644
--- a/indra/cmake/LLCommon.cmake
+++ b/indra/cmake/LLCommon.cmake
@@ -19,9 +19,19 @@ if (LINUX)
# In order to support using ld.gold on linux, we need to explicitely
# specify all libraries that llcommon uses.
# llcommon uses `clock_gettime' which is provided by librt on linux.
- set(LLCOMMON_LIBRARIES llcommon rt)
+ set(LLCOMMON_LIBRARIES llcommon
+ ${BOOST_COROUTINE_LIBRARY}
+ ${BOOST_CONTEXT_LIBRARY}
+ ${BOOST_THREAD_LIBRARY}
+ ${BOOST_SYSTEM_LIBRARY}
+ rt
+ )
else (LINUX)
- set(LLCOMMON_LIBRARIES llcommon)
+ set(LLCOMMON_LIBRARIES llcommon
+ ${BOOST_COROUTINE_LIBRARY}
+ ${BOOST_CONTEXT_LIBRARY}
+ ${BOOST_THREAD_LIBRARY}
+ ${BOOST_SYSTEM_LIBRARY} )
endif (LINUX)
# add_definitions(${TCMALLOC_FLAG})
diff --git a/indra/cmake/LLCoreHttp.cmake b/indra/cmake/LLCoreHttp.cmake
index 61e4b23d98..379ae207de 100644
--- a/indra/cmake/LLCoreHttp.cmake
+++ b/indra/cmake/LLCoreHttp.cmake
@@ -1,16 +1,17 @@
# -*- cmake -*-
-include(CARes)
include(CURL)
include(OpenSSL)
include(Boost)
set(LLCOREHTTP_INCLUDE_DIRS
${LIBS_OPEN_DIR}/llcorehttp
- ${CARES_INCLUDE_DIRS}
${CURL_INCLUDE_DIRS}
${OPENSSL_INCLUDE_DIRS}
${BOOST_INCLUDE_DIRS}
)
-set(LLCOREHTTP_LIBRARIES llcorehttp)
+set(LLCOREHTTP_LIBRARIES llcorehttp
+ ${BOOST_COROUTINE_LIBRARY}
+ ${BOOST_CONTEXT_LIBRARY}
+ ${BOOST_SYSTEM_LIBRARY})
diff --git a/indra/cmake/LLMessage.cmake b/indra/cmake/LLMessage.cmake
index 0143d04fd7..7be53ec0ec 100644
--- a/indra/cmake/LLMessage.cmake
+++ b/indra/cmake/LLMessage.cmake
@@ -1,13 +1,11 @@
# -*- cmake -*-
-include(CARes)
include(CURL)
include(OpenSSL)
include(XmlRpcEpi)
set(LLMESSAGE_INCLUDE_DIRS
${LIBS_OPEN_DIR}/llmessage
- ${CARES_INCLUDE_DIRS}
${CURL_INCLUDE_DIRS}
${OPENSSL_INCLUDE_DIRS}
)
diff --git a/indra/linux_crash_logger/CMakeLists.txt b/indra/linux_crash_logger/CMakeLists.txt
index c0fc1b2be0..029096df37 100644
--- a/indra/linux_crash_logger/CMakeLists.txt
+++ b/indra/linux_crash_logger/CMakeLists.txt
@@ -4,6 +4,7 @@ project(linux_crash_logger)
include(00-Common)
include(GLH)
+include(LLCoreHttp)
include(LLCommon)
include(LLCrashLogger)
include(LLMath)
@@ -13,8 +14,10 @@ include(LLXML)
include(Linking)
include(UI)
include(FreeType)
+include(Boost)
include_directories(
+ ${LLCOREHTTP_INCLUDE_DIRS}
${LLCOMMON_INCLUDE_DIRS}
${LLCRASHLOGGER_INCLUDE_DIRS}
${LLMATH_INCLUDE_DIRS}
@@ -53,6 +56,10 @@ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--as-needed")
add_executable(linux-crash-logger ${linux_crash_logger_SOURCE_FILES})
+# llcommon uses `clock_gettime' which is provided by librt on linux.
+set(LIBRT_LIBRARY rt)
+
+
target_link_libraries(linux-crash-logger
${LLCRASHLOGGER_LIBRARIES}
${LLVFS_LIBRARIES}
@@ -60,10 +67,14 @@ target_link_libraries(linux-crash-logger
${LLMESSAGE_LIBRARIES}
${LLVFS_LIBRARIES}
${LLMATH_LIBRARIES}
+ ${LLCOREHTTP_LIBRARIES}
${LLCOMMON_LIBRARIES}
+ ${BOOST_COROUTINE_LIBRARY}
+ ${BOOST_CONTEXT_LIBRARY}
${UI_LIBRARIES}
${DB_LIBRARIES}
${FREETYPE_LIBRARIES}
+ ${LIBRT_LIBRARY}
)
add_custom_target(linux-crash-logger-target ALL
diff --git a/indra/llappearance/CMakeLists.txt b/indra/llappearance/CMakeLists.txt
index 0dbd58b7cd..20eb4678dd 100644
--- a/indra/llappearance/CMakeLists.txt
+++ b/indra/llappearance/CMakeLists.txt
@@ -9,6 +9,7 @@ include(LLImage)
include(LLInventory)
include(LLMath)
include(LLMessage)
+include(LLCoreHttp)
include(LLRender)
include(LLVFS)
include(LLWindow)
@@ -86,6 +87,8 @@ target_link_libraries(llappearance
${LLMATH_LIBRARIES}
${LLXML_LIBRARIES}
${LLMATH_LIBRARIES}
+ ${LLMESSAGE_LIBRARIES}
+ ${LLCOREHTTP_LIBRARIES}
${LLCOMMON_LIBRARIES}
)
@@ -101,6 +104,8 @@ if (BUILD_HEADLESS)
${LLMATH_LIBRARIES}
${LLXML_LIBRARIES}
${LLMATH_LIBRARIES}
+ ${LLMESSAGE_LIBRARIES}
+ ${LLCOREHTTP_LIBRARIES}
${LLCOMMON_LIBRARIES}
)
endif (BUILD_HEADLESS)
diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt
index 5863310162..907dbab8f8 100644
--- a/indra/llcommon/CMakeLists.txt
+++ b/indra/llcommon/CMakeLists.txt
@@ -8,6 +8,7 @@ include(LLCommon)
include(Linking)
include(Boost)
include(LLSharedLibs)
+include(JsonCpp)
include(GoogleBreakpad)
include(GooglePerfTools)
include(Copy3rdPartyLibs)
@@ -17,6 +18,7 @@ include(URIPARSER)
include_directories(
${EXPAT_INCLUDE_DIRS}
${LLCOMMON_INCLUDE_DIRS}
+ ${JSONCPP_INCLUDE_DIR}
${ZLIB_INCLUDE_DIRS}
${BREAKPAD_INCLUDE_DIRECTORIES}
${URIPARSER_INCLUDE_DIRS}
@@ -86,6 +88,7 @@ set(llcommon_SOURCE_FILES
llrefcount.cpp
llrun.cpp
llsd.cpp
+ llsdjson.cpp
llsdparam.cpp
llsdserialize.cpp
llsdserialize_xml.cpp
@@ -195,6 +198,7 @@ set(llcommon_HEADER_FILES
llrefcount.h
llsafehandle.h
llsd.h
+ llsdjson.h
llsdparam.h
llsdserialize.h
llsdserialize_xml.h
@@ -262,10 +266,14 @@ target_link_libraries(
${APRUTIL_LIBRARIES}
${APR_LIBRARIES}
${EXPAT_LIBRARIES}
+ ${JSONCPP_LIBRARIES}
${ZLIB_LIBRARIES}
${WINDOWS_LIBRARIES}
+ ${BOOST_COROUTINE_LIBRARY}
+ ${BOOST_CONTEXT_LIBRARY}
${BOOST_PROGRAM_OPTIONS_LIBRARY}
${BOOST_REGEX_LIBRARY}
+ ${BOOST_SYSTEM_LIBRARY}
${GOOGLE_PERFTOOLS_LIBRARIES}
${URIPARSER_LIBRARIES}
)
@@ -286,7 +294,14 @@ if (LL_TESTS)
LL_ADD_PROJECT_UNIT_TESTS(llcommon "${llcommon_TEST_SOURCE_FILES}")
#set(TEST_DEBUG on)
- set(test_libs llcommon ${LLCOMMON_LIBRARIES} ${WINDOWS_LIBRARIES} ${GOOGLEMOCK_LIBRARIES})
+ set(test_libs llcommon
+ ${LLCOMMON_LIBRARIES}
+ ${WINDOWS_LIBRARIES}
+ ${GOOGLEMOCK_LIBRARIES}
+ ${BOOST_COROUTINE_LIBRARY}
+ ${BOOST_CONTEXT_LIBRARY}
+ ${BOOST_THREAD_LIBRARY}
+ ${BOOST_SYSTEM_LIBRARY})
LL_ADD_INTEGRATION_TEST(commonmisc "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(bitpack "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llbase64 "" "${test_libs}")
@@ -308,7 +323,7 @@ if (LL_TESTS)
LL_ADD_INTEGRATION_TEST(llunits "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(stringize "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(lleventdispatcher "" "${test_libs}")
- LL_ADD_INTEGRATION_TEST(lleventcoro "" "${test_libs};${BOOST_CONTEXT_LIBRARY};${BOOST_THREAD_LIBRARY};${BOOST_COROUTINE_LIBRARY};${BOOST_SYSTEM_LIBRARY}")
+ LL_ADD_INTEGRATION_TEST(lleventcoro "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llprocess "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llleap "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llstreamqueue "" "${test_libs}")
diff --git a/indra/llcommon/fix_macros.h b/indra/llcommon/fix_macros.h
index ef959decff..43c09c54bc 100644
--- a/indra/llcommon/fix_macros.h
+++ b/indra/llcommon/fix_macros.h
@@ -16,10 +16,6 @@
// these macros all over again.
// who injects MACROS with such generic names?! Grr.
-#ifdef equivalent
-#undef equivalent
-#endif
-
#ifdef check
#undef check
#endif
diff --git a/indra/llcommon/linden_common.h b/indra/llcommon/linden_common.h
index 5cfcdab41c..e5a913a6a9 100644
--- a/indra/llcommon/linden_common.h
+++ b/indra/llcommon/linden_common.h
@@ -51,6 +51,7 @@
#include
#include
#include
+#include
// Linden only libs in alpha-order other than stdtypes.h
// *NOTE: Please keep includes here to a minimum, see above.
diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp
index baaddcaed1..d16bf0160b 100644
--- a/indra/llcommon/llcoros.cpp
+++ b/indra/llcommon/llcoros.cpp
@@ -39,6 +39,62 @@
#include "llerror.h"
#include "stringize.h"
+// do nothing, when we need nothing done
+void LLCoros::no_cleanup(CoroData*) {}
+
+// CoroData for the currently-running coroutine. Use a thread_specific_ptr
+// because each thread potentially has its own distinct pool of coroutines.
+// This thread_specific_ptr does NOT own the CoroData object! That's owned by
+// LLCoros::mCoros. It merely identifies it. For this reason we instantiate
+// it with a no-op cleanup function.
+boost::thread_specific_ptr
+LLCoros::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(coro_private::coroutine_accessor::get_impl(const_cast(*mi->second)).get())
- == self_id)
- {
- return mi->first;
- }
+ // not in a coroutine
+ return "";
}
- return "";
+ return current->mName;
}
void LLCoros::setStackSize(S32 stacksize)
@@ -136,10 +227,24 @@ void LLCoros::setStackSize(S32 stacksize)
mStackSize = stacksize;
}
+// Top-level wrapper around caller's coroutine callable. This function accepts
+// the coroutine library's implicit coro::self& parameter and sets sCurrentSelf
+// but does not pass it down to the caller's callable.
+void LLCoros::toplevel(coro::self& self, CoroData* data, const callable_t& callable)
+{
+ // capture the 'self' param in CoroData
+ data->mSelf = &self;
+ // run the code the caller actually wants in the coroutine
+ callable();
+ // This cleanup isn't perfectly symmetrical with the way we initially set
+ // data->mPrev, but this is our last chance to reset mCurrentCoro.
+ sCurrentCoro.reset(data->mPrev);
+}
+
/*****************************************************************************
* MUST BE LAST
*****************************************************************************/
-// Turn off MSVC optimizations for just LLCoros::launchImpl() -- see
+// Turn off MSVC optimizations for just LLCoros::launch() -- see
// DEV-32777. But MSVC doesn't support push/pop for optimization flags as it
// does for warning suppression, and we really don't want to force
// optimization ON for other code even in Debug or RelWithDebInfo builds.
@@ -147,15 +252,35 @@ void LLCoros::setStackSize(S32 stacksize)
#if LL_MSVC
// work around broken optimizations
#pragma warning(disable: 4748)
+#pragma warning(disable: 4355) // 'this' used in initializer list: yes, intentionally
#pragma optimize("", off)
#endif // LL_MSVC
-std::string LLCoros::launchImpl(const std::string& prefix, coro* newCoro)
+LLCoros::CoroData::CoroData(CoroData* prev, const std::string& name,
+ const callable_t& callable, S32 stacksize):
+ mPrev(prev),
+ mName(name),
+ // Wrap the caller's callable in our toplevel() function so we can manage
+ // sCurrentCoro appropriately at startup and shutdown of each coroutine.
+ mCoro(boost::bind(toplevel, _1, this, callable), stacksize),
+ // don't consume events unless specifically directed
+ mConsuming(false),
+ mSelf(0)
+{
+}
+
+std::string LLCoros::launch(const std::string& prefix, const callable_t& callable)
{
std::string name(generateDistinctName(prefix));
+ // pass the current value of sCurrentCoro as previous context
+ CoroData* newCoro = new CoroData(sCurrentCoro.get(), name,
+ callable, mStackSize);
+ // Store it in our pointer map
mCoros.insert(name, newCoro);
+ // also set it as current
+ sCurrentCoro.reset(newCoro);
/* Run the coroutine until its first wait, then return here */
- (*newCoro)(std::nothrow);
+ (newCoro->mCoro)(std::nothrow);
return name;
}
diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h
index 01ee11da1a..39316ed0e6 100644
--- a/indra/llcommon/llcoros.h
+++ b/indra/llcommon/llcoros.h
@@ -30,14 +30,20 @@
#define LL_LLCOROS_H
#include
+#include
#include "llsingleton.h"
#include
+#include
+#include
#include
-#include
-#include
-#include
#include
+// 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
public:
/// Canonical boost::dcoroutines::coroutine signature we use
typedef boost::dcoroutines::coroutine coro;
- /// Canonical 'self' type
- typedef coro::self self;
+ /// Canonical callable type
+ typedef boost::function 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
- 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
- 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
+ class Future;
+
private:
- friend class LLSingleton;
LLCoros();
- std::string launchImpl(const std::string& prefix, coro* newCoro);
+ friend class LLSingleton;
+ 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 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 CoroMap;
CoroMap mCoros;
+
+ // identify the current coroutine's CoroData
+ static boost::thread_specific_ptr 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
+class LLCoros::Future
+{
+ typedef boost::dcoroutines::future dfuture;
+
+public:
+ Future():
+ mFuture(get_self())
+ {}
+
+ typedef typename boost::dcoroutines::make_callback_result::type callback_t;
+
+ callback_t make_callback()
+ {
+ return boost::dcoroutines::make_callback(mFuture);
+ }
+
+#ifndef LL_LINUX
+ explicit
+#endif
+ operator bool() const
+ {
+ return bool(mFuture);
+ }
+
+ bool operator!() const
+ {
+ return ! mFuture;
+ }
+
+ T get()
+ {
+ // instantiate Suspending to manage the "current" coroutine
+ llcoro::Suspending suspended;
+ return *mFuture;
+ }
+
+private:
+ dfuture mFuture;
};
#endif /* ! defined(LL_LLCOROS_H) */
diff --git a/indra/llcommon/llerror.h b/indra/llcommon/llerror.h
index 73544cb914..3beef65723 100644
--- a/indra/llcommon/llerror.h
+++ b/indra/llcommon/llerror.h
@@ -354,6 +354,7 @@ typedef LLError::NoClassInfo _LL_CLASS_TO_LOG;
#define LL_WARNS(...) lllog(LLError::LEVEL_WARN, false, ##__VA_ARGS__)
#define LL_ERRS(...) lllog(LLError::LEVEL_ERROR, false, ##__VA_ARGS__)
// alternative to llassert_always that prints explanatory message
+#define LL_WARNS_IF(exp, ...) if (exp) LL_WARNS(##__VA_ARGS__) << "(" #exp ")"
#define LL_ERRS_IF(exp, ...) if (exp) LL_ERRS(##__VA_ARGS__) << "(" #exp ")"
// Only print the log message once (good for warnings or infos that would otherwise
diff --git a/indra/llcommon/lleventcoro.cpp b/indra/llcommon/lleventcoro.cpp
index 81cc33fbba..578a2b62c8 100644
--- a/indra/llcommon/lleventcoro.cpp
+++ b/indra/llcommon/lleventcoro.cpp
@@ -38,34 +38,60 @@
#include "llsdserialize.h"
#include "llerror.h"
#include "llcoros.h"
+#include "llmake.h"
-std::string LLEventDetail::listenerNameForCoroImpl(const void* self_id)
+#include "lleventfilter.h"
+
+namespace
{
- // First, if this coroutine was launched by LLCoros::launch(), find that name.
- std::string name(LLCoros::instance().getNameByID(self_id));
+
+/**
+ * suspendUntilEventOn() permits a coroutine to temporarily listen on an
+ * LLEventPump any number of times. We don't really want to have to ask
+ * the caller to label each such call with a distinct string; the whole
+ * point of suspendUntilEventOn() is to present a nice sequential interface to
+ * the underlying LLEventPump-with-named-listeners machinery. So we'll use
+ * LLEventPump::inventName() to generate a distinct name for each
+ * temporary listener. On the other hand, because a given coroutine might
+ * call suspendUntilEventOn() any number of times, we don't really want to
+ * consume an arbitrary number of generated inventName()s: that namespace,
+ * though large, is nonetheless finite. So we memoize an invented name for
+ * each distinct coroutine instance.
+ */
+std::string listenerNameForCoro()
+{
+ // If this coroutine was launched by LLCoros::launch(), find that name.
+ std::string name(LLCoros::instance().getName());
if (! name.empty())
{
return name;
}
- // Apparently this coroutine wasn't launched by LLCoros::launch(). Check
- // whether we have a memo for this self_id.
- typedef std::map 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 path.isUndefined(), do nothing.
+ * * If path.isString(), @a dest is an LLSD map: store @a value
+ * into dest[path.asString()].
+ * * If path.isInteger(), @a dest is an LLSD array: store @a
+ * value into dest[path.asInteger()].
+ * * If path.isArray(), 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::make_callback(), the callback has a signature
+/// like void callback(LLSD), which isn't a valid LLEventPump listener: such
+/// listeners must return bool.
+template
+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 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(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
+class FutureListener2: public FutureListener
+{
+ typedef FutureListener 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 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(future.make_callback(), 0)));
+ LLTempBoundListener connection1(
+ replyPump1.getPump().listen(
+ name + "b",
+ llmake(future.make_callback(), 1)));
+ // skip the "post" part if requestPump is default-constructed
+ if (requestPump)
+ {
+ // If either replyPumpNamePath is non-empty, store the corresponding
+ // replyPump name in the request event.
+ LLSD modevent(event);
+ storeToLLSDPath(modevent, replyPump0NamePath,
+ replyPump0.getPump().getName());
+ storeToLLSDPath(modevent, replyPump1NamePath,
+ replyPump1.getPump().getName());
+ LL_DEBUGS("lleventcoro") << "postAndSuspend2(): coroutine " << name
+ << " posting to " << requestPump.getPump().getName()
+ << ": " << modevent << LL_ENDL;
+ requestPump.getPump().post(modevent);
+ }
+ LL_DEBUGS("lleventcoro") << "postAndSuspend2(): coroutine " << name
+ << " about to wait on LLEventPumps " << replyPump0.getPump().getName()
+ << ", " << replyPump1.getPump().getName() << LL_ENDL;
+ // calling get() on the future makes us wait for it
+ LLEventWithID value(future.get());
+ LL_DEBUGS("lleventcoro") << "postAndSuspend(): coroutine " << name
+ << " resuming with (" << value.first << ", " << value.second << ")"
+ << LL_ENDL;
+ // returning should disconnect both connections
+ return value;
+}
+
LLSD errorException(const LLEventWithID& result, const std::string& desc)
{
// If the result arrived on the error pump (pump 1), instead of
@@ -144,3 +349,5 @@ LLSD errorLog(const LLEventWithID& result, const std::string& desc)
// A simple return must therefore be from the reply pump (pump 0).
return result.first;
}
+
+} // namespace llcoro
diff --git a/indra/llcommon/lleventcoro.h b/indra/llcommon/lleventcoro.h
index abbeeaa373..2105faf861 100644
--- a/indra/llcommon/lleventcoro.h
+++ b/indra/llcommon/lleventcoro.h
@@ -29,11 +29,10 @@
#if ! defined(LL_LLEVENTCORO_H)
#define LL_LLEVENTCORO_H
-#include
-#include
#include
#include
#include
+#include // std::pair
#include "llevents.h"
#include "llerror.h"
@@ -74,111 +73,46 @@ private:
boost::optional 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
-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
-LLVoidListener voidlistener(const LISTENER& listener)
-{
- return LLVoidListener(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
- std::string listenerNameForCoro(COROUTINE_SELF& self)
- {
- return listenerNameForCoroImpl(self.get_id());
- }
-
- /**
- * Implement behavior described for postAndWait()'s @a replyPumpNamePath
- * parameter:
- *
- * * If path.isUndefined(), do nothing.
- * * If path.isString(), @a dest is an LLSD map: store @a value
- * into dest[path.asString()].
- * * If path.isInteger(), @a dest is an LLSD array: store @a
- * value into dest[path.asInteger()].
- * * If path.isArray(), 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 requestPump.post() returns. In the
* sequence above, the running coroutine isn't even listening on @a replyPump
- * until requestPump.post() returns and @c waitForEventOn() is
+ * until requestPump.post() 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 replyPump.getName(). 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 replyPump.getName().
*/
-template
-LLSD postAndWait(SELF& self, const LLSD& event, const LLEventPumpOrPumpName& requestPump,
- const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath=LLSD())
-{
- // declare the future
- boost::dcoroutines::future 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
-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 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
- 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
- WaitForEventOnHelper wfeoh(const LISTENER& listener, int discriminator)
- {
- return WaitForEventOnHelper(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
-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 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
+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
- LLSD wait(SELF& self)
+ LLSD suspend()
{
- return waitForEventOn(self, mPump);
+ return llcoro::suspendUntilEventOn(mPump);
}
- template
- 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
- 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
- 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
- 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
- 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
- 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
- LLSD postAndWaitWithLog(SELF& self, const LLSD& event,
+ LLSD postAndSuspendWithLog(const LLSD& event,
const LLEventPumpOrPumpName& requestPump,
const LLSD& replyPump0NamePath=LLSD(),
const LLSD& replyPump1NamePath=LLSD())
{
- return errorLog(postAndWait(self, event, requestPump,
- replyPump0NamePath, replyPump1NamePath),
- std::string("Error event on ") + getName1());
+ return llcoro::errorLog(postAndSuspend(event, requestPump,
+ replyPump0NamePath, replyPump1NamePath),
+ std::string("Error event on ") + getName1());
}
private:
diff --git a/indra/llcommon/llevents.cpp b/indra/llcommon/llevents.cpp
index 1c928b3db8..645c29d770 100644
--- a/indra/llcommon/llevents.cpp
+++ b/indra/llcommon/llevents.cpp
@@ -132,6 +132,17 @@ LLEventPump& LLEventPumps::obtain(const std::string& name)
return *newInstance;
}
+bool LLEventPumps::post(const std::string&name, const LLSD&message)
+{
+ PumpMap::iterator found = mPumpMap.find(name);
+
+ if (found == mPumpMap.end())
+ return false;
+
+ return (*found).second->post(message);
+}
+
+
void LLEventPumps::flush()
{
// Flush every known LLEventPump instance. Leave it up to each instance to
@@ -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
*****************************************************************************/
diff --git a/indra/llcommon/llevents.h b/indra/llcommon/llevents.h
index 0cbd1da32d..6175329a9d 100644
--- a/indra/llcommon/llevents.h
+++ b/indra/llcommon/llevents.h
@@ -217,6 +217,18 @@ public:
* an instance without conferring @em ownership.
*/
LLEventPump& obtain(const std::string& name);
+
+ /**
+ * Find the named LLEventPump instance. If it exists post the message to it.
+ * If the pump does not exist, do nothing.
+ *
+ * returns the result of the LLEventPump::post. If no pump exists returns false.
+ *
+ * This is syntactically similar to LLEventPumps::instance().post(name, message),
+ * however if the pump does not already exist it will not be created.
+ */
+ bool post(const std::string&, const LLSD&);
+
/**
* Flush all known LLEventPump instances
*/
@@ -496,7 +508,7 @@ public:
// at the boost::bind object itself before that happens.
return LLEventDetail::visit_and_connect(name,
listener,
- boost::bind(&LLEventPump::listen_impl,
+ boost::bind(&LLEventPump::listen_invoke,
this,
name,
_1,
@@ -541,13 +553,23 @@ private:
virtual void reset();
+
+
private:
- virtual LLBoundListener listen_impl(const std::string& name, const LLEventListener&,
- const NameList& after,
- const NameList& before);
+ LLBoundListener listen_invoke(const std::string& name, const LLEventListener& listener,
+ const NameList& after,
+ const NameList& before)
+ {
+ return this->listen_impl(name, listener, after, before);
+ }
+
std::string mName;
protected:
+ virtual LLBoundListener listen_impl(const std::string& name, const LLEventListener&,
+ const NameList& after,
+ const NameList& before);
+
/// implement the dispatching
boost::shared_ptr 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 EventList;
+ EventList mEventHistory;
+};
+
/*****************************************************************************
* LLEventQueue
*****************************************************************************/
/**
- * LLEventQueue isa LLEventPump whose post() method defers calling registered
+ * LLEventQueue is a LLEventPump whose post() method defers calling registered
* listeners until flush() is called.
*/
class LL_COMMON_API LLEventQueue: public LLEventPump
diff --git a/indra/llcommon/llmake.h b/indra/llcommon/llmake.h
new file mode 100644
index 0000000000..9a662a0640
--- /dev/null
+++ b/indra/llcommon/llmake.h
@@ -0,0 +1,63 @@
+/**
+ * @file llmake.h
+ * @author Nat Goodspeed
+ * @date 2015-12-18
+ * @brief Generic llmake(arg) function to instantiate
+ * Template(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 class CLASS_TEMPLATE, typename... ARGS>
+CLASS_TEMPLATE llmake(ARGS && ... args)
+{
+ return CLASS_TEMPLATE(std::forward(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(arg)
+ *
+ * Deduces the type T of 'arg' and returns an instance of SomeTemplate
+ * initialized with 'arg'. Assumes a constructor accepting T (by value,
+ * reference or whatever).
+ */
+template class CLASS_TEMPLATE, typename ARG1>
+CLASS_TEMPLATE llmake(const ARG1& arg1)
+{
+ return CLASS_TEMPLATE(arg1);
+}
+
+template class CLASS_TEMPLATE, typename ARG1, typename ARG2>
+CLASS_TEMPLATE llmake(const ARG1& arg1, const ARG2& arg2)
+{
+ return CLASS_TEMPLATE(arg1, arg2);
+}
+
+#endif /* ! defined(LL_LLMAKE_H) */
diff --git a/indra/llcommon/llprocess.cpp b/indra/llcommon/llprocess.cpp
index e0aa30cc1a..44f56daf2d 100644
--- a/indra/llcommon/llprocess.cpp
+++ b/indra/llcommon/llprocess.cpp
@@ -710,7 +710,7 @@ LLProcess::LLProcess(const LLSDOrParams& params):
// Tie the lifespan of this child process to the lifespan of our APR
// pool: on destruction of the pool, forcibly kill the process. Tell
- // APR to try SIGTERM and wait 3 seconds. If that didn't work, use
+ // APR to try SIGTERM and suspend 3 seconds. If that didn't work, use
// SIGKILL.
apr_pool_note_subprocess(gAPRPoolp, &mProcess, APR_KILL_AFTER_TIMEOUT);
|*==========================================================================*/
@@ -989,9 +989,9 @@ void LLProcess::handle_status(int reason, int status)
// wi->rv = apr_proc_wait(wi->child, &wi->rc, &wi->why, APR_NOWAIT);
// It's just wrong to call apr_proc_wait() here. The only way APR knows to
// call us with APR_OC_REASON_DEATH is that it's already reaped this child
- // process, so calling wait() will only produce "huh?" from the OS. We
+ // process, so calling suspend() will only produce "huh?" from the OS. We
// must rely on the status param passed in, which unfortunately comes
- // straight from the OS wait() call, which means we have to decode it by
+ // straight from the OS suspend() call, which means we have to decode it by
// hand.
mStatus = interpret_status(status);
LL_INFOS("LLProcess") << getStatusString() << LL_ENDL;
diff --git a/indra/llcommon/llrefcount.h b/indra/llcommon/llrefcount.h
index 3836a9b5fb..1107973569 100644
--- a/indra/llcommon/llrefcount.h
+++ b/indra/llcommon/llrefcount.h
@@ -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();
diff --git a/indra/llcommon/llsdjson.cpp b/indra/llcommon/llsdjson.cpp
new file mode 100644
index 0000000000..8caaaee534
--- /dev/null
+++ b/indra/llcommon/llsdjson.cpp
@@ -0,0 +1,126 @@
+/**
+ * @file llsdjson.cpp
+ * @brief LLSD flexible data system
+ *
+ * $LicenseInfo:firstyear=2015&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2015, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+// Must turn on conditional declarations in header file so definitions end up
+// with proper linkage.
+#define LLSD_DEBUG_INFO
+#include "linden_common.h"
+
+#include "llsdjson.h"
+
+#include "llerror.h"
+#include "../llmath/llmath.h"
+
+//=========================================================================
+LLSD LlsdFromJson(const Json::Value &val)
+{
+ LLSD result;
+
+ switch (val.type())
+ {
+ default:
+ case Json::nullValue:
+ break;
+ case Json::intValue:
+ result = LLSD(static_cast(val.asInt()));
+ break;
+ case Json::uintValue:
+ result = LLSD(static_cast(val.asUInt()));
+ break;
+ case Json::realValue:
+ result = LLSD(static_cast(val.asDouble()));
+ break;
+ case Json::stringValue:
+ result = LLSD(static_cast(val.asString()));
+ break;
+ case Json::booleanValue:
+ result = LLSD(static_cast(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(val.asBoolean()));
+ break;
+ case LLSD::TypeInteger:
+ result = Json::Value(static_cast(val.asInteger()));
+ break;
+ case LLSD::TypeReal:
+ result = Json::Value(static_cast(val.asReal()));
+ break;
+ case LLSD::TypeURI:
+ case LLSD::TypeDate:
+ case LLSD::TypeUUID:
+ case LLSD::TypeString:
+ result = Json::Value(val.asString());
+ break;
+ case LLSD::TypeMap:
+ result = Json::Value(Json::objectValue);
+ for (LLSD::map_const_iterator it = val.beginMap(); it != val.endMap(); ++it)
+ {
+ result[it->first] = LlsdToJson(it->second);
+ }
+ break;
+ case LLSD::TypeArray:
+ result = Json::Value(Json::arrayValue);
+ for (LLSD::array_const_iterator it = val.beginArray(); it != val.endArray(); ++it)
+ {
+ result.append(LlsdToJson(*it));
+ }
+ break;
+ case LLSD::TypeBinary:
+ default:
+ LL_ERRS("LlsdToJson") << "Unsupported conversion to JSON from LLSD type (" << val.type() << ")." << LL_ENDL;
+ break;
+ }
+
+ return result;
+}
diff --git a/indra/llcommon/llsdjson.h b/indra/llcommon/llsdjson.h
new file mode 100644
index 0000000000..2be7112404
--- /dev/null
+++ b/indra/llcommon/llsdjson.h
@@ -0,0 +1,77 @@
+/**
+ * @file llsdjson.cpp
+ * @brief LLSD flexible data system
+ *
+ * $LicenseInfo:firstyear=2015&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2015, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLSDJSON_H
+#define LL_LLSDJSON_H
+
+#include
+#include
+#include
+
+#include "stdtypes.h"
+
+#include "llsd.h"
+#include "value.h"
+
+/// Convert a parsed JSON structure into LLSD maintaining member names and
+/// array indexes.
+/// JSON/JavaScript types are converted as follows:
+///
+/// JSON Type | LLSD Type
+/// --------------+--------------
+/// null | undefined
+/// integer | LLSD::Integer
+/// unsigned | LLSD::Integer
+/// real/numeric | LLSD::Real
+/// string | LLSD::String
+/// boolean | LLSD::Boolean
+/// array | LLSD::Array
+/// object | LLSD::Map
+///
+/// For maps and arrays child entries will be converted and added to the structure.
+/// Order is preserved for an array but not for objects.
+LLSD LlsdFromJson(const Json::Value &val);
+
+/// Convert an LLSD object into Parsed JSON object maintaining member names and
+/// array indexs.
+///
+/// Types are converted as follows:
+/// LLSD Type | JSON Type
+/// --------------+----------------
+/// TypeUndefined | null
+/// TypeBoolean | boolean
+/// TypeInteger | integer
+/// TypeReal | real/numeric
+/// TypeString | string
+/// TypeURI | string
+/// TypeDate | string
+/// TypeUUID | string
+/// TypeMap | object
+/// TypeArray | array
+/// TypeBinary | unsupported
+Json::Value LlsdToJson(const LLSD &val);
+
+#endif // LL_LLSDJSON_H
diff --git a/indra/llcommon/llstring.h b/indra/llcommon/llstring.h
index 0177f48bf5..393f6d7a8c 100644
--- a/indra/llcommon/llstring.h
+++ b/indra/llcommon/llstring.h
@@ -1394,6 +1394,7 @@ BOOL LLStringUtilBase::containsNonprintable(const string_type& string)
return rv;
}
+// *TODO: reimplement in terms of algorithm
//static
template
void LLStringUtilBase::stripNonprintable(string_type& string)
@@ -1427,6 +1428,7 @@ void LLStringUtilBase::stripNonprintable(string_type& string)
delete []c_string;
}
+// *TODO: reimplement in terms of algorithm
template
std::basic_string LLStringUtilBase::quote(const string_type& str,
const string_type& triggers,
diff --git a/indra/llcommon/tests/lleventcoro_test.cpp b/indra/llcommon/tests/lleventcoro_test.cpp
index 2096807e53..a459d17fb8 100644
--- a/indra/llcommon/tests/lleventcoro_test.cpp
+++ b/indra/llcommon/tests/lleventcoro_test.cpp
@@ -59,17 +59,17 @@
// http://www.boost.org/LICENSE_1_0.txt)
/*****************************************************************************/
+#define BOOST_RESULT_OF_USE_TR1 1
// On some platforms, Boost.Coroutine must #define magic symbols before
// #including platform-API headers. Naturally, that's ineffective unless the
// Boost.Coroutine #include is the *first* #include of the platform header.
// That means that client code must generally #include Boost.Coroutine headers
// before anything else.
#include
-// Normally, lleventcoro.h obviates future.hpp. We only include this because
-// we implement a "by hand" test of future functionality.
-#include
#include
#include
+#include
+#include
#include "linden_common.h"
@@ -82,9 +82,12 @@
#include "llevents.h"
#include "tests/wrapllerrs.h"
#include "stringize.h"
+#include "llcoros.h"
#include "lleventcoro.h"
#include "../test/debug.h"
+using namespace llcoro;
+
/*****************************************************************************
* from the banana.cpp example program borrowed for test<1>()
*****************************************************************************/
@@ -121,13 +124,10 @@ typedef coroutine match_coroutine_type;
/*****************************************************************************
* Test helpers
*****************************************************************************/
-// I suspect this will be typical of coroutines used in Linden software
-typedef boost::dcoroutines::coroutine coroutine_type;
-
/// Simulate an event API whose response is immediate: sent on receipt of the
/// initial request, rather than after some delay. This is the case that
-/// distinguishes postAndWait() from calling post(), then calling
-/// waitForEventOn().
+/// distinguishes postAndSuspend() from calling post(), then calling
+/// suspendUntilEventOn().
class ImmediateAPI
{
public:
@@ -162,306 +162,7 @@ private:
*****************************************************************************/
namespace tut
{
- struct coroutine_data
- {
- // Define coroutine bodies as methods here so they can use ensure*()
-
- void explicit_wait(coroutine_type::self& self)
- {
- BEGIN
- {
- // ... do whatever preliminary stuff must happen ...
-
- // declare the future
- boost::dcoroutines::future future(self);
- // tell the future what to wait for
- LLTempBoundListener connection(
- LLEventPumps::instance().obtain("source").listen("coro", voidlistener(boost::dcoroutines::make_callback(future))));
- ensure("Not yet", ! future);
- // attempting to dereference ("resolve") the future causes the calling
- // coroutine to wait for it
- debug("about to wait");
- result = *future;
- ensure("Got it", future);
- }
- END
- }
-
- void waitForEventOn1(coroutine_type::self& self)
- {
- BEGIN
- {
- result = waitForEventOn(self, "source");
- }
- END
- }
-
- void waitForEventOn2(coroutine_type::self& self)
- {
- BEGIN
- {
- LLEventWithID pair = waitForEventOn(self, "reply", "error");
- result = pair.first;
- which = pair.second;
- debug(STRINGIZE("result = " << result << ", which = " << which));
- }
- END
- }
-
- void postAndWait1(coroutine_type::self& self)
- {
- BEGIN
- {
- result = postAndWait(self,
- LLSDMap("value", 17), // request event
- immediateAPI.getPump(), // requestPump
- "reply1", // replyPump
- "reply"); // request["reply"] = name
- }
- END
- }
-
- void postAndWait2(coroutine_type::self& self)
- {
- BEGIN
- {
- LLEventWithID pair = ::postAndWait2(self,
- LLSDMap("value", 18),
- immediateAPI.getPump(),
- "reply2",
- "error2",
- "reply",
- "error");
- result = pair.first;
- which = pair.second;
- debug(STRINGIZE("result = " << result << ", which = " << which));
- }
- END
- }
-
- void postAndWait2_1(coroutine_type::self& self)
- {
- BEGIN
- {
- LLEventWithID pair = ::postAndWait2(self,
- LLSDMap("value", 18)("fail", LLSD()),
- immediateAPI.getPump(),
- "reply2",
- "error2",
- "reply",
- "error");
- result = pair.first;
- which = pair.second;
- debug(STRINGIZE("result = " << result << ", which = " << which));
- }
- END
- }
-
- void coroPump(coroutine_type::self& self)
- {
- BEGIN
- {
- LLCoroEventPump waiter;
- replyName = waiter.getName();
- result = waiter.wait(self);
- }
- END
- }
-
- void coroPumpPost(coroutine_type::self& self)
- {
- BEGIN
- {
- LLCoroEventPump waiter;
- result = waiter.postAndWait(self, LLSDMap("value", 17),
- immediateAPI.getPump(), "reply");
- }
- END
- }
-
- void coroPumps(coroutine_type::self& self)
- {
- BEGIN
- {
- LLCoroEventPumps waiter;
- replyName = waiter.getName0();
- errorName = waiter.getName1();
- LLEventWithID pair(waiter.wait(self));
- result = pair.first;
- which = pair.second;
- }
- END
- }
-
- void coroPumpsNoEx(coroutine_type::self& self)
- {
- BEGIN
- {
- LLCoroEventPumps waiter;
- replyName = waiter.getName0();
- errorName = waiter.getName1();
- result = waiter.waitWithException(self);
- }
- END
- }
-
- void coroPumpsEx(coroutine_type::self& self)
- {
- BEGIN
- {
- LLCoroEventPumps waiter;
- replyName = waiter.getName0();
- errorName = waiter.getName1();
- try
- {
- result = waiter.waitWithException(self);
- debug("no exception");
- }
- catch (const LLErrorEvent& e)
- {
- debug(STRINGIZE("exception " << e.what()));
- errordata = e.getData();
- }
- }
- END
- }
-
- void coroPumpsNoLog(coroutine_type::self& self)
- {
- BEGIN
- {
- LLCoroEventPumps waiter;
- replyName = waiter.getName0();
- errorName = waiter.getName1();
- result = waiter.waitWithLog(self);
- }
- END
- }
-
- void coroPumpsLog(coroutine_type::self& self)
- {
- BEGIN
- {
- LLCoroEventPumps waiter;
- replyName = waiter.getName0();
- errorName = waiter.getName1();
- WrapLLErrs capture;
- try
- {
- result = waiter.waitWithLog(self);
- debug("no exception");
- }
- catch (const WrapLLErrs::FatalException& e)
- {
- debug(STRINGIZE("exception " << e.what()));
- threw = e.what();
- }
- }
- END
- }
-
- void coroPumpsPost(coroutine_type::self& self)
- {
- BEGIN
- {
- LLCoroEventPumps waiter;
- LLEventWithID pair(waiter.postAndWait(self, LLSDMap("value", 23),
- immediateAPI.getPump(), "reply", "error"));
- result = pair.first;
- which = pair.second;
- }
- END
- }
-
- void coroPumpsPost_1(coroutine_type::self& self)
- {
- BEGIN
- {
- LLCoroEventPumps waiter;
- LLEventWithID pair(
- waiter.postAndWait(self, LLSDMap("value", 23)("fail", LLSD()),
- immediateAPI.getPump(), "reply", "error"));
- result = pair.first;
- which = pair.second;
- }
- END
- }
-
- void coroPumpsPostNoEx(coroutine_type::self& self)
- {
- BEGIN
- {
- LLCoroEventPumps waiter;
- result = waiter.postAndWaitWithException(self, LLSDMap("value", 8),
- immediateAPI.getPump(), "reply", "error");
- }
- END
- }
-
- void coroPumpsPostEx(coroutine_type::self& self)
- {
- BEGIN
- {
- LLCoroEventPumps waiter;
- try
- {
- result = waiter.postAndWaitWithException(self,
- LLSDMap("value", 9)("fail", LLSD()),
- immediateAPI.getPump(), "reply", "error");
- debug("no exception");
- }
- catch (const LLErrorEvent& e)
- {
- debug(STRINGIZE("exception " << e.what()));
- errordata = e.getData();
- }
- }
- END
- }
-
- void coroPumpsPostNoLog(coroutine_type::self& self)
- {
- BEGIN
- {
- LLCoroEventPumps waiter;
- result = waiter.postAndWaitWithLog(self, LLSDMap("value", 30),
- immediateAPI.getPump(), "reply", "error");
- }
- END
- }
-
- void coroPumpsPostLog(coroutine_type::self& self)
- {
- BEGIN
- {
- LLCoroEventPumps waiter;
- WrapLLErrs capture;
- try
- {
- result = waiter.postAndWaitWithLog(self,
- LLSDMap("value", 31)("fail", LLSD()),
- immediateAPI.getPump(), "reply", "error");
- debug("no exception");
- }
- catch (const WrapLLErrs::FatalException& e)
- {
- debug(STRINGIZE("exception " << e.what()));
- threw = e.what();
- }
- }
- END
- }
-
- void ensure_done(coroutine_type& coro)
- {
- ensure("coroutine complete", ! coro);
- }
-
- ImmediateAPI immediateAPI;
- std::string replyName, errorName, threw;
- LLSD result, errordata;
- int which;
- };
+ struct coroutine_data {};
typedef test_group coroutine_group;
typedef coroutine_group::object object;
coroutine_group coroutinegrp("coroutine");
@@ -511,54 +212,122 @@ namespace tut
ensure("done", ! matcher);
}
+ // use static data so we can intersperse coroutine functions with the
+ // tests that engage them
+ ImmediateAPI immediateAPI;
+ std::string replyName, errorName, threw, stringdata;
+ LLSD result, errordata;
+ int which;
+
+ // reinit vars at the start of each test
+ void clear()
+ {
+ replyName.clear();
+ errorName.clear();
+ threw.clear();
+ stringdata.clear();
+ result = LLSD();
+ errordata = LLSD();
+ which = 0;
+ }
+
+ void explicit_wait(boost::shared_ptr::callback_t>& cbp)
+ {
+ BEGIN
+ {
+ // The point of this test is to verify / illustrate suspending a
+ // coroutine for something other than an LLEventPump. In other
+ // words, this shows how to adapt to any async operation that
+ // provides a callback-style notification (and prove that it
+ // works).
+
+ LLCoros::Future future;
+ // get the callback from that future
+ LLCoros::Future::callback_t callback(future.make_callback());
+
+ // Perhaps we would send a request to a remote server and arrange
+ // for 'callback' to be called on response. Of course that might
+ // involve an adapter object from the actual callback signature to
+ // the signature of 'callback' -- in this case, void(std::string).
+ // For test purposes, instead of handing 'callback' (or the
+ // adapter) off to some I/O subsystem, we'll just pass it back to
+ // our caller.
+ cbp.reset(new LLCoros::Future::callback_t(callback));
+
+ ensure("Not yet", ! future);
+ // calling get() on the future causes us to suspend
+ debug("about to suspend");
+ stringdata = future.get();
+ ensure("Got it", bool(future));
+ }
+ END
+ }
+
template<> template<>
void object::test<2>()
{
+ clear();
set_test_name("explicit_wait");
DEBUG;
// Construct the coroutine instance that will run explicit_wait.
- // Pass the ctor a callable that accepts the coroutine_type::self
- // param passed by the library.
- coroutine_type coro(boost::bind(&coroutine_data::explicit_wait, this, _1));
- // Start the coroutine
- coro(std::nothrow);
- // When the coroutine waits for the event pump, it returns here.
- debug("about to send");
- // Satisfy the wait.
- LLEventPumps::instance().obtain("source").post("received");
- // Now wait for the coroutine to complete.
- ensure_done(coro);
+ boost::shared_ptr::callback_t> respond;
+ LLCoros::instance().launch("test<2>",
+ boost::bind(explicit_wait, boost::ref(respond)));
+ // When the coroutine waits for the future, it returns here.
+ debug("about to respond");
+ // Now we're the I/O subsystem delivering a result. This immediately
+ // transfers control back to the coroutine.
+ (*respond)("received");
// ensure the coroutine ran and woke up again with the intended result
- ensure_equals(result.asString(), "received");
+ ensure_equals(stringdata, "received");
+ }
+
+ void waitForEventOn1()
+ {
+ BEGIN
+ {
+ result = suspendUntilEventOn("source");
+ }
+ END
}
template<> template<>
void object::test<3>()
{
+ clear();
set_test_name("waitForEventOn1");
DEBUG;
- coroutine_type coro(boost::bind(&coroutine_data::waitForEventOn1, this, _1));
- coro(std::nothrow);
+ LLCoros::instance().launch("test<3>", waitForEventOn1);
debug("about to send");
LLEventPumps::instance().obtain("source").post("received");
debug("back from send");
- ensure_done(coro);
ensure_equals(result.asString(), "received");
}
+ void waitForEventOn2()
+ {
+ BEGIN
+ {
+ LLEventWithID pair = suspendUntilEventOn("reply", "error");
+ result = pair.first;
+ which = pair.second;
+ debug(STRINGIZE("result = " << result << ", which = " << which));
+ }
+ END
+ }
+
template<> template<>
void object::test<4>()
{
+ clear();
set_test_name("waitForEventOn2 reply");
{
DEBUG;
- coroutine_type coro(boost::bind(&coroutine_data::waitForEventOn2, this, _1));
- coro(std::nothrow);
+ LLCoros::instance().launch("test<4>", waitForEventOn2);
debug("about to send");
LLEventPumps::instance().obtain("reply").post("received");
debug("back from send");
- ensure_done(coro);
}
ensure_equals(result.asString(), "received");
ensure_equals("which pump", which, 0);
@@ -567,43 +336,65 @@ namespace tut
template<> template<>
void object::test<5>()
{
+ clear();
set_test_name("waitForEventOn2 error");
DEBUG;
- coroutine_type coro(boost::bind(&coroutine_data::waitForEventOn2, this, _1));
- coro(std::nothrow);
+ LLCoros::instance().launch("test<5>", waitForEventOn2);
debug("about to send");
LLEventPumps::instance().obtain("error").post("badness");
debug("back from send");
- ensure_done(coro);
ensure_equals(result.asString(), "badness");
ensure_equals("which pump", which, 1);
}
+ void coroPump()
+ {
+ BEGIN
+ {
+ LLCoroEventPump waiter;
+ replyName = waiter.getName();
+ result = waiter.suspend();
+ }
+ END
+ }
+
template<> template<>
void object::test<6>()
{
+ clear();
set_test_name("coroPump");
DEBUG;
- coroutine_type coro(boost::bind(&coroutine_data::coroPump, this, _1));
- coro(std::nothrow);
+ LLCoros::instance().launch("test<6>", coroPump);
debug("about to send");
LLEventPumps::instance().obtain(replyName).post("received");
debug("back from send");
- ensure_done(coro);
ensure_equals(result.asString(), "received");
}
+ void coroPumps()
+ {
+ BEGIN
+ {
+ LLCoroEventPumps waiter;
+ replyName = waiter.getName0();
+ errorName = waiter.getName1();
+ LLEventWithID pair(waiter.suspend());
+ result = pair.first;
+ which = pair.second;
+ }
+ END
+ }
+
template<> template<>
void object::test<7>()
{
+ clear();
set_test_name("coroPumps reply");
DEBUG;
- coroutine_type coro(boost::bind(&coroutine_data::coroPumps, this, _1));
- coro(std::nothrow);
+ LLCoros::instance().launch("test<7>", coroPumps);
debug("about to send");
LLEventPumps::instance().obtain(replyName).post("received");
debug("back from send");
- ensure_done(coro);
ensure_equals(result.asString(), "received");
ensure_equals("which pump", which, 0);
}
@@ -611,188 +402,389 @@ namespace tut
template<> template<>
void object::test<8>()
{
+ clear();
set_test_name("coroPumps error");
DEBUG;
- coroutine_type coro(boost::bind(&coroutine_data::coroPumps, this, _1));
- coro(std::nothrow);
+ LLCoros::instance().launch("test<8>", coroPumps);
debug("about to send");
LLEventPumps::instance().obtain(errorName).post("badness");
debug("back from send");
- ensure_done(coro);
ensure_equals(result.asString(), "badness");
ensure_equals("which pump", which, 1);
}
+ void coroPumpsNoEx()
+ {
+ BEGIN
+ {
+ LLCoroEventPumps waiter;
+ replyName = waiter.getName0();
+ errorName = waiter.getName1();
+ result = waiter.suspendWithException();
+ }
+ END
+ }
+
template<> template<>
void object::test<9>()
{
+ clear();
set_test_name("coroPumpsNoEx");
DEBUG;
- coroutine_type coro(boost::bind(&coroutine_data::coroPumpsNoEx, this, _1));
- coro(std::nothrow);
+ LLCoros::instance().launch("test<9>", coroPumpsNoEx);
debug("about to send");
LLEventPumps::instance().obtain(replyName).post("received");
debug("back from send");
- ensure_done(coro);
ensure_equals(result.asString(), "received");
}
+ void coroPumpsEx()
+ {
+ BEGIN
+ {
+ LLCoroEventPumps waiter;
+ replyName = waiter.getName0();
+ errorName = waiter.getName1();
+ try
+ {
+ result = waiter.suspendWithException();
+ debug("no exception");
+ }
+ catch (const LLErrorEvent& e)
+ {
+ debug(STRINGIZE("exception " << e.what()));
+ errordata = e.getData();
+ }
+ }
+ END
+ }
+
template<> template<>
void object::test<10>()
{
+ clear();
set_test_name("coroPumpsEx");
DEBUG;
- coroutine_type coro(boost::bind(&coroutine_data::coroPumpsEx, this, _1));
- coro(std::nothrow);
+ LLCoros::instance().launch("test<10>", coroPumpsEx);
debug("about to send");
LLEventPumps::instance().obtain(errorName).post("badness");
debug("back from send");
- ensure_done(coro);
ensure("no result", result.isUndefined());
ensure_equals("got error", errordata.asString(), "badness");
}
+ void coroPumpsNoLog()
+ {
+ BEGIN
+ {
+ LLCoroEventPumps waiter;
+ replyName = waiter.getName0();
+ errorName = waiter.getName1();
+ result = waiter.suspendWithLog();
+ }
+ END
+ }
+
template<> template<>
void object::test<11>()
{
+ clear();
set_test_name("coroPumpsNoLog");
DEBUG;
- coroutine_type coro(boost::bind(&coroutine_data::coroPumpsNoLog, this, _1));
- coro(std::nothrow);
+ LLCoros::instance().launch("test<11>", coroPumpsNoLog);
debug("about to send");
LLEventPumps::instance().obtain(replyName).post("received");
debug("back from send");
- ensure_done(coro);
ensure_equals(result.asString(), "received");
}
+ void coroPumpsLog()
+ {
+ BEGIN
+ {
+ LLCoroEventPumps waiter;
+ replyName = waiter.getName0();
+ errorName = waiter.getName1();
+ WrapLLErrs capture;
+ try
+ {
+ result = waiter.suspendWithLog();
+ debug("no exception");
+ }
+ catch (const WrapLLErrs::FatalException& e)
+ {
+ debug(STRINGIZE("exception " << e.what()));
+ threw = e.what();
+ }
+ }
+ END
+ }
+
template<> template<>
void object::test<12>()
{
+ clear();
set_test_name("coroPumpsLog");
DEBUG;
- coroutine_type coro(boost::bind(&coroutine_data::coroPumpsLog, this, _1));
- coro(std::nothrow);
+ LLCoros::instance().launch("test<12>", coroPumpsLog);
debug("about to send");
LLEventPumps::instance().obtain(errorName).post("badness");
debug("back from send");
- ensure_done(coro);
ensure("no result", result.isUndefined());
ensure_contains("got error", threw, "badness");
}
+ void postAndWait1()
+ {
+ BEGIN
+ {
+ result = postAndSuspend(LLSDMap("value", 17), // request event
+ immediateAPI.getPump(), // requestPump
+ "reply1", // replyPump
+ "reply"); // request["reply"] = name
+ }
+ END
+ }
+
template<> template<>
void object::test<13>()
{
+ clear();
set_test_name("postAndWait1");
DEBUG;
- coroutine_type coro(boost::bind(&coroutine_data::postAndWait1, this, _1));
- coro(std::nothrow);
- ensure_done(coro);
+ LLCoros::instance().launch("test<13>", postAndWait1);
ensure_equals(result.asInteger(), 18);
}
+ void postAndWait2()
+ {
+ BEGIN
+ {
+ LLEventWithID pair = ::postAndSuspend2(LLSDMap("value", 18),
+ immediateAPI.getPump(),
+ "reply2",
+ "error2",
+ "reply",
+ "error");
+ result = pair.first;
+ which = pair.second;
+ debug(STRINGIZE("result = " << result << ", which = " << which));
+ }
+ END
+ }
+
template<> template<>
void object::test<14>()
{
+ clear();
set_test_name("postAndWait2");
DEBUG;
- coroutine_type coro(boost::bind(&coroutine_data::postAndWait2, this, _1));
- coro(std::nothrow);
- ensure_done(coro);
+ LLCoros::instance().launch("test<14>", postAndWait2);
ensure_equals(result.asInteger(), 19);
ensure_equals(which, 0);
}
+ void postAndWait2_1()
+ {
+ BEGIN
+ {
+ LLEventWithID pair = ::postAndSuspend2(LLSDMap("value", 18)("fail", LLSD()),
+ immediateAPI.getPump(),
+ "reply2",
+ "error2",
+ "reply",
+ "error");
+ result = pair.first;
+ which = pair.second;
+ debug(STRINGIZE("result = " << result << ", which = " << which));
+ }
+ END
+ }
+
template<> template<>
void object::test<15>()
{
+ clear();
set_test_name("postAndWait2_1");
DEBUG;
- coroutine_type coro(boost::bind(&coroutine_data::postAndWait2_1, this, _1));
- coro(std::nothrow);
- ensure_done(coro);
+ LLCoros::instance().launch("test<15>", postAndWait2_1);
ensure_equals(result.asInteger(), 19);
ensure_equals(which, 1);
}
+ void coroPumpPost()
+ {
+ BEGIN
+ {
+ LLCoroEventPump waiter;
+ result = waiter.postAndSuspend(LLSDMap("value", 17),
+ immediateAPI.getPump(), "reply");
+ }
+ END
+ }
+
template<> template<>
void object::test<16>()
{
+ clear();
set_test_name("coroPumpPost");
DEBUG;
- coroutine_type coro(boost::bind(&coroutine_data::coroPumpPost, this, _1));
- coro(std::nothrow);
- ensure_done(coro);
+ LLCoros::instance().launch("test<16>", coroPumpPost);
ensure_equals(result.asInteger(), 18);
}
+ void coroPumpsPost()
+ {
+ BEGIN
+ {
+ LLCoroEventPumps waiter;
+ LLEventWithID pair(waiter.postAndSuspend(LLSDMap("value", 23),
+ immediateAPI.getPump(), "reply", "error"));
+ result = pair.first;
+ which = pair.second;
+ }
+ END
+ }
+
template<> template<>
void object::test<17>()
{
+ clear();
set_test_name("coroPumpsPost reply");
DEBUG;
- coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPost, this, _1));
- coro(std::nothrow);
- ensure_done(coro);
+ LLCoros::instance().launch("test<17>", coroPumpsPost);
ensure_equals(result.asInteger(), 24);
ensure_equals("which pump", which, 0);
}
+ void coroPumpsPost_1()
+ {
+ BEGIN
+ {
+ LLCoroEventPumps waiter;
+ LLEventWithID pair(
+ waiter.postAndSuspend(LLSDMap("value", 23)("fail", LLSD()),
+ immediateAPI.getPump(), "reply", "error"));
+ result = pair.first;
+ which = pair.second;
+ }
+ END
+ }
+
template<> template<>
void object::test<18>()
{
+ clear();
set_test_name("coroPumpsPost error");
DEBUG;
- coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPost_1, this, _1));
- coro(std::nothrow);
- ensure_done(coro);
+ LLCoros::instance().launch("test<18>", coroPumpsPost_1);
ensure_equals(result.asInteger(), 24);
ensure_equals("which pump", which, 1);
}
+ void coroPumpsPostNoEx()
+ {
+ BEGIN
+ {
+ LLCoroEventPumps waiter;
+ result = waiter.postAndSuspendWithException(LLSDMap("value", 8),
+ immediateAPI.getPump(), "reply", "error");
+ }
+ END
+ }
+
template<> template<>
void object::test<19>()
{
+ clear();
set_test_name("coroPumpsPostNoEx");
DEBUG;
- coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPostNoEx, this, _1));
- coro(std::nothrow);
- ensure_done(coro);
+ LLCoros::instance().launch("test<19>", coroPumpsPostNoEx);
ensure_equals(result.asInteger(), 9);
}
+ void coroPumpsPostEx()
+ {
+ BEGIN
+ {
+ LLCoroEventPumps waiter;
+ try
+ {
+ result = waiter.postAndSuspendWithException(
+ LLSDMap("value", 9)("fail", LLSD()),
+ immediateAPI.getPump(), "reply", "error");
+ debug("no exception");
+ }
+ catch (const LLErrorEvent& e)
+ {
+ debug(STRINGIZE("exception " << e.what()));
+ errordata = e.getData();
+ }
+ }
+ END
+ }
+
template<> template<>
void object::test<20>()
{
+ clear();
set_test_name("coroPumpsPostEx");
DEBUG;
- coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPostEx, this, _1));
- coro(std::nothrow);
- ensure_done(coro);
+ LLCoros::instance().launch("test<20>", coroPumpsPostEx);
ensure("no result", result.isUndefined());
ensure_equals("got error", errordata.asInteger(), 10);
}
+ void coroPumpsPostNoLog()
+ {
+ BEGIN
+ {
+ LLCoroEventPumps waiter;
+ result = waiter.postAndSuspendWithLog(LLSDMap("value", 30),
+ immediateAPI.getPump(), "reply", "error");
+ }
+ END
+ }
+
template<> template<>
void object::test<21>()
{
+ clear();
set_test_name("coroPumpsPostNoLog");
DEBUG;
- coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPostNoLog, this, _1));
- coro(std::nothrow);
- ensure_done(coro);
+ LLCoros::instance().launch("test<21>", coroPumpsPostNoLog);
ensure_equals(result.asInteger(), 31);
}
+ void coroPumpsPostLog()
+ {
+ BEGIN
+ {
+ LLCoroEventPumps waiter;
+ WrapLLErrs capture;
+ try
+ {
+ result = waiter.postAndSuspendWithLog(
+ LLSDMap("value", 31)("fail", LLSD()),
+ immediateAPI.getPump(), "reply", "error");
+ debug("no exception");
+ }
+ catch (const WrapLLErrs::FatalException& e)
+ {
+ debug(STRINGIZE("exception " << e.what()));
+ threw = e.what();
+ }
+ }
+ END
+ }
+
template<> template<>
void object::test<22>()
{
+ clear();
set_test_name("coroPumpsPostLog");
DEBUG;
- coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPostLog, this, _1));
- coro(std::nothrow);
- ensure_done(coro);
+ LLCoros::instance().launch("test<22>", coroPumpsPostLog);
ensure("no result", result.isUndefined());
ensure_contains("got error", threw, "32");
}
diff --git a/indra/llcorehttp/CMakeLists.txt b/indra/llcorehttp/CMakeLists.txt
index ed61012bdb..0bb0348d26 100644
--- a/indra/llcorehttp/CMakeLists.txt
+++ b/indra/llcorehttp/CMakeLists.txt
@@ -5,7 +5,6 @@ project(llcorehttp)
include(00-Common)
include(GoogleMock)
include(CURL)
-include(CARes)
include(OpenSSL)
include(ZLIB)
include(LLCoreHttp)
@@ -26,6 +25,7 @@ set(llcorehttp_SOURCE_FILES
bufferarray.cpp
bufferstream.cpp
httpcommon.cpp
+ llhttpconstants.cpp
httpheaders.cpp
httpoptions.cpp
httprequest.cpp
@@ -51,6 +51,7 @@ set(llcorehttp_HEADER_FILES
bufferarray.h
bufferstream.h
httpcommon.h
+ llhttpconstants.h
httphandler.h
httpheaders.h
httpoptions.h
@@ -89,7 +90,6 @@ add_library (llcorehttp ${llcorehttp_SOURCE_FILES})
target_link_libraries(
llcorehttp
${CURL_LIBRARIES}
- ${CARES_LIBRARIES}
${OPENSSL_LIBRARIES}
${CRYPTO_LIBRARIES}
${BOOST_THREAD_LIBRARY}
@@ -127,7 +127,6 @@ if (LL_TESTS)
${LLCOMMON_LIBRARIES}
${GOOGLEMOCK_LIBRARIES}
${CURL_LIBRARIES}
- ${CARES_LIBRARIES}
${OPENSSL_LIBRARIES}
${CRYPTO_LIBRARIES}
${BOOST_SYSTEM_LIBRARY}
@@ -196,7 +195,6 @@ endif (DARWIN)
${LLCOMMON_LIBRARIES}
${GOOGLEMOCK_LIBRARIES}
${CURL_LIBRARIES}
- ${CARES_LIBRARIES}
${OPENSSL_LIBRARIES}
${CRYPTO_LIBRARIES}
${BOOST_SYSTEM_LIBRARY}
diff --git a/indra/llcorehttp/_httpinternal.h b/indra/llcorehttp/_httpinternal.h
index a2a60ca056..79c89d6c92 100644
--- a/indra/llcorehttp/_httpinternal.h
+++ b/indra/llcorehttp/_httpinternal.h
@@ -104,8 +104,9 @@ namespace LLCore
{
// Maxium number of policy classes that can be defined.
-// *TODO: Currently limited to the default class + 1, extend.
-const int HTTP_POLICY_CLASS_LIMIT = 8;
+// *TODO: Currently limited to the default class + 1, extend.
+// (TSN: should this be more dynamically sized. Is there a reason to hard limit the number of policies?)
+const int HTTP_POLICY_CLASS_LIMIT = 32;
// Debug/informational tracing. Used both
// as a global option and in per-request traces.
diff --git a/indra/llcorehttp/_httplibcurl.cpp b/indra/llcorehttp/_httplibcurl.cpp
index 81b44ab90b..c25e01a318 100644
--- a/indra/llcorehttp/_httplibcurl.cpp
+++ b/indra/llcorehttp/_httplibcurl.cpp
@@ -71,11 +71,10 @@ void HttpLibcurl::shutdown()
{
while (! mActiveOps.empty())
{
- HttpOpRequest * op(* mActiveOps.begin());
+ HttpOpRequest::ptr_t op(* mActiveOps.begin());
mActiveOps.erase(mActiveOps.begin());
cancelRequest(op);
- op->release();
}
if (mMultiHandles)
@@ -204,7 +203,7 @@ HttpService::ELoopSpeed HttpLibcurl::processTransport()
// Caller has provided us with a ref count on op.
-void HttpLibcurl::addOp(HttpOpRequest * op)
+void HttpLibcurl::addOp(const HttpOpRequest::ptr_t &op)
{
llassert_always(op->mReqPolicy < mPolicyCount);
llassert_always(mMultiHandles[op->mReqPolicy] != NULL);
@@ -235,21 +234,21 @@ void HttpLibcurl::addOp(HttpOpRequest * op)
HttpPolicy & policy(mService->getPolicy());
LL_INFOS(LOG_CORE) << "TRACE, ToActiveQueue, Handle: "
- << static_cast(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(handle));
+ HttpOpRequest::ptr_t op = HttpOpRequest::fromHandle(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(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(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(op)
- << ", Status: " << op->mStatus.toTerseString()
- << LL_ENDL;
+ << op->getHandle()
+ << ", Status: " << op->mStatus.toTerseString()
+ << LL_ENDL;
}
// Dispatch to next stage
@@ -554,7 +597,7 @@ void HttpLibcurl::HandleCache::freeHandle(CURL * handle)
// ---------------------------------------
-struct curl_slist * append_headers_to_slist(const HttpHeaders * headers, struct curl_slist * slist)
+struct curl_slist * append_headers_to_slist(const HttpHeaders::ptr_t &headers, struct curl_slist * slist)
{
const HttpHeaders::const_iterator end(headers->end());
for (HttpHeaders::const_iterator it(headers->begin()); end != it; ++it)
diff --git a/indra/llcorehttp/_httplibcurl.h b/indra/llcorehttp/_httplibcurl.h
index ffc24c63a8..a71eae59c0 100644
--- a/indra/llcorehttp/_httplibcurl.h
+++ b/indra/llcorehttp/_httplibcurl.h
@@ -65,6 +65,8 @@ private:
void operator=(const HttpLibcurl &); // Not defined
public:
+ typedef boost::shared_ptr 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 active_set_t;
+ typedef std::set active_set_t;
/// Simple request handle cache for libcurl.
///
diff --git a/indra/llcorehttp/_httpopcancel.h b/indra/llcorehttp/_httpopcancel.h
index 336dfdc573..86944eb159 100644
--- a/indra/llcorehttp/_httpopcancel.h
+++ b/indra/llcorehttp/_httpopcancel.h
@@ -56,13 +56,8 @@ public:
/// be canceled.
HttpOpCancel(HttpHandle handle);
-protected:
virtual ~HttpOpCancel(); // Use release()
-private:
- HttpOpCancel(const HttpOpCancel &); // Not defined
- void operator=(const HttpOpCancel &); // Not defined
-
public:
virtual void stageFromRequest(HttpService *);
diff --git a/indra/llcorehttp/_httpoperation.cpp b/indra/llcorehttp/_httpoperation.cpp
index fefe561f80..3fc4e28910 100644
--- a/indra/llcorehttp/_httpoperation.cpp
+++ b/indra/llcorehttp/_httpoperation.cpp
@@ -53,15 +53,18 @@ namespace LLCore
// ==================================
// HttpOperation
// ==================================
+/*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(),
+ 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(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(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(this)
+ << getHandle()
<< LL_ENDL;
}
if (mReplyQueue)
{
- addRef();
- mReplyQueue->addOp(this);
+ HttpOperation::ptr_t op = shared_from_this();
+ mReplyQueue->addOp(op);
}
}
@@ -244,11 +297,8 @@ void HttpOpSpin::stageFromRequest(HttpService * service)
else
{
ms_sleep(1); // backoff interlock plumbing a bit
- this->addRef();
- if (! service->getRequestQueue().addOp(this))
- {
- this->release();
- }
+ HttpOperation::ptr_t opptr = shared_from_this();
+ service->getRequestQueue().addOp(opptr);
}
}
diff --git a/indra/llcorehttp/_httpoperation.h b/indra/llcorehttp/_httpoperation.h
index 937a61187d..1a75921c09 100644
--- a/indra/llcorehttp/_httpoperation.h
+++ b/indra/llcorehttp/_httpoperation.h
@@ -30,8 +30,7 @@
#include "httpcommon.h"
#include "httprequest.h"
-#include "_refcounted.h"
-
+#include "_mutex.h"
namespace LLCore
{
@@ -69,19 +68,20 @@ class HttpService;
/// via queue-like interfaces that are thread compatible
/// and those interfaces establish the access rules.
-class HttpOperation : public LLCoreInt::RefCounted
+class HttpOperation : private boost::noncopyable,
+ public boost::enable_shared_from_this
{
public:
+ typedef boost::shared_ptr ptr_t;
+ typedef boost::weak_ptr wptr_t;
+ typedef boost::shared_ptr 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 handleMap_t;
+
+ HttpHandle createHandle();
+ void destroyHandle();
+ HttpHandle mMyHandle;
+
+ static handleMap_t mHandleMap;
+ static LLCoreInt::HttpMutex mOpMutex;
+
+protected:
+ static ptr_t findByHandle(HttpHandle handle);
+
+
}; // end class HttpOperation
@@ -195,7 +222,6 @@ class HttpOpStop : public HttpOperation
public:
HttpOpStop();
-protected:
virtual ~HttpOpStop();
private:
@@ -218,7 +244,6 @@ class HttpOpNull : public HttpOperation
public:
HttpOpNull();
-protected:
virtual ~HttpOpNull();
private:
@@ -241,7 +266,6 @@ public:
// 1 does a soft spin continuously requeuing itself
HttpOpSpin(int mode);
-protected:
virtual ~HttpOpSpin();
private:
diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp
index b9632a7921..db57869a1b 100644
--- a/indra/llcorehttp/_httpoprequest.cpp
+++ b/indra/llcorehttp/_httpoprequest.cpp
@@ -122,8 +122,8 @@ HttpOpRequest::HttpOpRequest()
mReqBody(NULL),
mReqOffset(0),
mReqLength(0),
- mReqHeaders(NULL),
- mReqOptions(NULL),
+ mReqHeaders(),
+ mReqOptions(),
mCurlActive(false),
mCurlHandle(NULL),
mCurlService(NULL),
@@ -135,11 +135,12 @@ HttpOpRequest::HttpOpRequest()
mReplyOffset(0),
mReplyLength(0),
mReplyFullLength(0),
- mReplyHeaders(NULL),
+ mReplyHeaders(),
mPolicyRetries(0),
mPolicy503Retries(0),
mPolicyRetryAt(HttpTime(0)),
- mPolicyRetryLimit(HTTP_RETRY_COUNT_DEFAULT)
+ mPolicyRetryLimit(HTTP_RETRY_COUNT_DEFAULT),
+ mCallbackSSLVerify(NULL)
{
// *NOTE: As members are added, retry initialization/cleanup
// may need to be extended in @see prepareRequest().
@@ -155,18 +156,6 @@ HttpOpRequest::~HttpOpRequest()
mReqBody = NULL;
}
- if (mReqOptions)
- {
- mReqOptions->release();
- mReqOptions = NULL;
- }
-
- if (mReqHeaders)
- {
- mReqHeaders->release();
- mReqHeaders = NULL;
- }
-
if (mCurlHandle)
{
// Uncertain of thread context so free using
@@ -193,25 +182,20 @@ HttpOpRequest::~HttpOpRequest()
mReplyBody = NULL;
}
- if (mReplyHeaders)
- {
- mReplyHeaders->release();
- mReplyHeaders = NULL;
- }
}
void HttpOpRequest::stageFromRequest(HttpService * service)
{
- addRef();
- service->getPolicy().addOp(this); // transfers refcount
+ HttpOpRequest::ptr_t self(boost::dynamic_pointer_cast(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(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(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((static_cast(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(userdata));
+ HttpOpRequest::ptr_t op(HttpOpRequest::fromHandle(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(userdata));
+ HttpOpRequest::ptr_t op(HttpOpRequest::fromHandle(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(userdata));
+
+ if (!op->mReqBody)
+ {
+ return 0;
+ }
+
+ size_t newPos = 0;
+ if (origin == SEEK_SET)
+ newPos = offset;
+ else if (origin == SEEK_END)
+ newPos = static_cast(op->mReqBody->size()) + offset;
+ else if (origin == SEEK_CUR)
+ newPos = static_cast(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(userdata));
+ HttpOpRequest::ptr_t op(HttpOpRequest::fromHandle(userdata));
const size_t hdr_size(size * nmemb);
const char * hdr_data(static_cast(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(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(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(userdata));
+ HttpOpRequest::ptr_t op(HttpOpRequest::fromHandle(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(op)
+ << op->getHandle()
<< ", Type: " << tag
<< ", Data: " << safe_line
<< LL_ENDL;
diff --git a/indra/llcorehttp/_httpoprequest.h b/indra/llcorehttp/_httpoprequest.h
index 2f628b5aba..dbcc57d0fd 100644
--- a/indra/llcorehttp/_httpoprequest.h
+++ b/indra/llcorehttp/_httpoprequest.h
@@ -33,19 +33,22 @@
#include
#include
+#include
+#include
+
#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 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
diff --git a/indra/llcorehttp/_httpopsetget.h b/indra/llcorehttp/_httpopsetget.h
index a1e76dd429..eabd41e79f 100644
--- a/indra/llcorehttp/_httpopsetget.h
+++ b/indra/llcorehttp/_httpopsetget.h
@@ -53,9 +53,10 @@ namespace LLCore
class HttpOpSetGet : public HttpOperation
{
public:
+ typedef boost::shared_ptr ptr_t;
+
HttpOpSetGet();
-protected:
virtual ~HttpOpSetGet(); // Use release()
private:
diff --git a/indra/llcorehttp/_httpopsetpriority.h b/indra/llcorehttp/_httpopsetpriority.h
index 31706b737c..43e2aa081b 100644
--- a/indra/llcorehttp/_httpopsetpriority.h
+++ b/indra/llcorehttp/_httpopsetpriority.h
@@ -51,7 +51,6 @@ class HttpOpSetPriority : public HttpOperation
public:
HttpOpSetPriority(HttpHandle handle, HttpRequest::priority_t priority);
-protected:
virtual ~HttpOpSetPriority();
private:
diff --git a/indra/llcorehttp/_httppolicy.cpp b/indra/llcorehttp/_httppolicy.cpp
index e5d6321401..b2709b53ec 100644
--- a/indra/llcorehttp/_httppolicy.cpp
+++ b/indra/llcorehttp/_httppolicy.cpp
@@ -116,21 +116,19 @@ void HttpPolicy::shutdown()
HttpRetryQueue & retryq(state.mRetryQueue);
while (! retryq.empty())
{
- HttpOpRequest * op(retryq.top());
+ HttpOpRequest::ptr_t op(retryq.top());
retryq.pop();
op->cancel();
- op->release();
}
HttpReadyQueue & readyq(state.mReadyQueue);
while (! readyq.empty())
{
- HttpOpRequest * op(readyq.top());
+ HttpOpRequest::ptr_t op(readyq.top());
readyq.pop();
op->cancel();
- op->release();
}
}
}
@@ -141,7 +139,7 @@ void HttpPolicy::start()
}
-void HttpPolicy::addOp(HttpOpRequest * op)
+void HttpPolicy::addOp(const HttpOpRequest::ptr_t &op)
{
const int policy_class(op->mReqPolicy);
@@ -151,7 +149,7 @@ void HttpPolicy::addOp(HttpOpRequest * op)
}
-void HttpPolicy::retryOp(HttpOpRequest * op)
+void HttpPolicy::retryOp(const HttpOpRequest::ptr_t &op)
{
static const HttpTime retry_deltas[] =
{
@@ -180,7 +178,7 @@ void HttpPolicy::retryOp(HttpOpRequest * op)
{
++op->mPolicy503Retries;
}
- LL_DEBUGS(LOG_CORE) << "HTTP request " << static_cast(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(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(*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(*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(*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(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(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(op)
+ LL_DEBUGS(LOG_CORE) << "HTTP request " << op->getHandle()
<< " succeeded on retry " << op->mPolicyRetries << "."
<< LL_ENDL;
}
op->stageFromActive(mService);
- op->release();
return false; // not active
}
diff --git a/indra/llcorehttp/_httppolicy.h b/indra/llcorehttp/_httppolicy.h
index 11cd89bbd1..3c4126e14b 100644
--- a/indra/llcorehttp/_httppolicy.h
+++ b/indra/llcorehttp/_httppolicy.h
@@ -60,6 +60,8 @@ private:
void operator=(const HttpPolicy &); // Not defined
public:
+ typedef boost::shared_ptr opReqPtr_t;
+
/// Threading: called by init thread.
HttpRequest::policy_t createPolicyClass();
@@ -96,7 +98,7 @@ public:
/// from queue.
///
/// Threading: called by worker thread
- void addOp(HttpOpRequest *);
+ void addOp(const opReqPtr_t &);
/// Similar to addOp, used when a caller wants to retry a
/// request that has failed. It's placed on a special retry
@@ -106,7 +108,7 @@ public:
/// order.
///
/// Threading: called by worker thread
- void retryOp(HttpOpRequest *);
+ void retryOp(const opReqPtr_t &);
/// Attempt to change the priority of an earlier request.
/// Request that Shadows HttpService's method
@@ -130,7 +132,7 @@ public:
/// sent on to the reply queue.
///
/// Threading: called by worker thread
- bool stageAfterCompletion(HttpOpRequest * op);
+ bool stageAfterCompletion(const opReqPtr_t &op);
/// Get a reference to global policy options. Caller is expected
/// to do context checks like no setting once running. These
diff --git a/indra/llcorehttp/_httppolicyglobal.cpp b/indra/llcorehttp/_httppolicyglobal.cpp
index 1dc95f3dce..3d0df96ade 100644
--- a/indra/llcorehttp/_httppolicyglobal.cpp
+++ b/indra/llcorehttp/_httppolicyglobal.cpp
@@ -106,6 +106,20 @@ HttpStatus HttpPolicyGlobal::set(HttpRequest::EPolicyOption opt, const std::stri
return HttpStatus();
}
+HttpStatus HttpPolicyGlobal::set(HttpRequest::EPolicyOption opt, HttpRequest::policyCallback_t value)
+{
+ switch (opt)
+ {
+ case HttpRequest::PO_SSL_VERIFY_CALLBACK:
+ mSslCtxCallback = value;
+ break;
+
+ default:
+ return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
+ }
+
+ return HttpStatus();
+}
HttpStatus HttpPolicyGlobal::get(HttpRequest::EPolicyOption opt, long * value) const
{
@@ -154,4 +168,20 @@ HttpStatus HttpPolicyGlobal::get(HttpRequest::EPolicyOption opt, std::string * v
return HttpStatus();
}
+
+HttpStatus HttpPolicyGlobal::get(HttpRequest::EPolicyOption opt, HttpRequest::policyCallback_t * value) const
+{
+ switch (opt)
+ {
+ case HttpRequest::PO_SSL_VERIFY_CALLBACK:
+ *value = mSslCtxCallback;
+ break;
+
+ default:
+ return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
+ }
+
+ return HttpStatus();
+}
+
} // end namespace LLCore
diff --git a/indra/llcorehttp/_httppolicyglobal.h b/indra/llcorehttp/_httppolicyglobal.h
index 67c4ba9481..e02da4386a 100644
--- a/indra/llcorehttp/_httppolicyglobal.h
+++ b/indra/llcorehttp/_httppolicyglobal.h
@@ -60,8 +60,10 @@ private:
public:
HttpStatus set(HttpRequest::EPolicyOption opt, long value);
HttpStatus set(HttpRequest::EPolicyOption opt, const std::string & value);
+ HttpStatus set(HttpRequest::EPolicyOption opt, HttpRequest::policyCallback_t value);
HttpStatus get(HttpRequest::EPolicyOption opt, long * value) const;
HttpStatus get(HttpRequest::EPolicyOption opt, std::string * value) const;
+ HttpStatus get(HttpRequest::EPolicyOption opt, HttpRequest::policyCallback_t * value) const;
public:
long mConnectionLimit;
@@ -70,6 +72,7 @@ public:
std::string mHttpProxy;
long mTrace;
long mUseLLProxy;
+ HttpRequest::policyCallback_t mSslCtxCallback;
}; // end class HttpPolicyGlobal
} // end namespace LLCore
diff --git a/indra/llcorehttp/_httpreadyqueue.h b/indra/llcorehttp/_httpreadyqueue.h
index 5f19a9c5f9..7418988ec1 100644
--- a/indra/llcorehttp/_httpreadyqueue.h
+++ b/indra/llcorehttp/_httpreadyqueue.h
@@ -56,12 +56,12 @@ namespace LLCore
#if LLCORE_HTTP_READY_QUEUE_IGNORES_PRIORITY
-typedef std::deque HttpReadyQueueBase;
+typedef std::deque HttpReadyQueueBase;
#else
-typedef std::priority_queue,
+typedef std::priority_queue,
LLCore::HttpOpRequestCompare> HttpReadyQueueBase;
#endif // LLCORE_HTTP_READY_QUEUE_IGNORES_PRIORITY
diff --git a/indra/llcorehttp/_httpreplyqueue.cpp b/indra/llcorehttp/_httpreplyqueue.cpp
index 558b7bdee9..2b138f3ad5 100644
--- a/indra/llcorehttp/_httpreplyqueue.cpp
+++ b/indra/llcorehttp/_httpreplyqueue.cpp
@@ -39,23 +39,17 @@ namespace LLCore
HttpReplyQueue::HttpReplyQueue()
- : RefCounted(true)
{
}
HttpReplyQueue::~HttpReplyQueue()
{
- while (! mQueue.empty())
- {
- HttpOperation * op = mQueue.back();
- mQueue.pop_back();
- op->release();
- }
+ mQueue.clear();
}
-void HttpReplyQueue::addOp(HttpOperation * op)
+void HttpReplyQueue::addOp(const HttpReplyQueue::opPtr_t &op)
{
{
HttpScopedLock lock(mQueueMutex);
@@ -66,15 +60,15 @@ void HttpReplyQueue::addOp(HttpOperation * op)
}
-HttpOperation * HttpReplyQueue::fetchOp()
+HttpReplyQueue::opPtr_t HttpReplyQueue::fetchOp()
{
- HttpOperation * result(NULL);
+ HttpOperation::ptr_t result;
{
HttpScopedLock lock(mQueueMutex);
if (mQueue.empty())
- return NULL;
+ return opPtr_t();
result = mQueue.front();
mQueue.erase(mQueue.begin());
@@ -98,9 +92,6 @@ void HttpReplyQueue::fetchAll(OpContainer & ops)
mQueue.swap(ops);
}
}
-
- // Caller also acquires the reference counts on each op.
- return;
}
diff --git a/indra/llcorehttp/_httpreplyqueue.h b/indra/llcorehttp/_httpreplyqueue.h
index 4220a09a3b..0e39e22dde 100644
--- a/indra/llcorehttp/_httpreplyqueue.h
+++ b/indra/llcorehttp/_httpreplyqueue.h
@@ -58,21 +58,19 @@ class HttpOperation;
/// will be coded anyway so it shouldn't be too much of a
/// burden.
-class HttpReplyQueue : public LLCoreInt::RefCounted
+class HttpReplyQueue : private boost::noncopyable
{
+
public:
- /// Caller acquires a Refcount on construction
+ typedef boost::shared_ptr opPtr_t;
+ typedef boost::shared_ptr 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 OpContainer;
+
+ typedef std::vector< opPtr_t > OpContainer;
/// Insert an object at the back of the reply queue.
///
@@ -80,7 +78,7 @@ public:
/// through the queue.
///
/// Threading: callable by any thread.
- void addOp(HttpOperation * op);
+ void addOp(const opPtr_t &op);
/// Fetch an operation from the head of the queue. Returns
/// NULL if none exists.
@@ -88,7 +86,7 @@ public:
/// Caller acquires reference count on returned operation.
///
/// Threading: callable by any thread.
- HttpOperation * fetchOp();
+ opPtr_t fetchOp();
/// Caller acquires reference count on each returned operation
///
@@ -96,6 +94,7 @@ public:
void fetchAll(OpContainer & ops);
protected:
+
OpContainer mQueue;
LLCoreInt::HttpMutex mQueueMutex;
LLCoreInt::HttpConditionVariable mQueueCV;
diff --git a/indra/llcorehttp/_httprequestqueue.cpp b/indra/llcorehttp/_httprequestqueue.cpp
index c16966d078..c6f4ad789f 100644
--- a/indra/llcorehttp/_httprequestqueue.cpp
+++ b/indra/llcorehttp/_httprequestqueue.cpp
@@ -47,12 +47,7 @@ HttpRequestQueue::HttpRequestQueue()
HttpRequestQueue::~HttpRequestQueue()
{
- while (! mQueue.empty())
- {
- HttpOperation * op = mQueue.back();
- mQueue.pop_back();
- op->release();
- }
+ mQueue.clear();
}
@@ -73,7 +68,7 @@ void HttpRequestQueue::term()
}
-HttpStatus HttpRequestQueue::addOp(HttpOperation * op)
+HttpStatus HttpRequestQueue::addOp(const HttpRequestQueue::opPtr_t &op)
{
bool wake(false);
{
@@ -95,9 +90,9 @@ HttpStatus HttpRequestQueue::addOp(HttpOperation * op)
}
-HttpOperation * HttpRequestQueue::fetchOp(bool wait)
+HttpRequestQueue::opPtr_t HttpRequestQueue::fetchOp(bool wait)
{
- HttpOperation * result(NULL);
+ HttpOperation::ptr_t result;
{
HttpScopedLock lock(mQueueMutex);
@@ -105,7 +100,7 @@ HttpOperation * HttpRequestQueue::fetchOp(bool wait)
while (mQueue.empty())
{
if (! wait || mQueueStopped)
- return NULL;
+ return HttpOperation::ptr_t();
mQueueCV.wait(lock);
}
diff --git a/indra/llcorehttp/_httprequestqueue.h b/indra/llcorehttp/_httprequestqueue.h
index c9c52b7233..3c3d134b07 100644
--- a/indra/llcorehttp/_httprequestqueue.h
+++ b/indra/llcorehttp/_httprequestqueue.h
@@ -61,6 +61,8 @@ private:
void operator=(const HttpRequestQueue &); // Not defined
public:
+ typedef boost::shared_ptr opPtr_t;
+
static void init();
static void term();
@@ -71,7 +73,7 @@ public:
}
public:
- typedef std::vector OpContainer;
+ typedef std::vector OpContainer;
/// Insert an object at the back of the request queue.
///
@@ -83,7 +85,7 @@ public:
/// an explicit release() call.
///
/// Threading: callable by any thread.
- HttpStatus addOp(HttpOperation * op);
+ HttpStatus addOp(const opPtr_t &op);
/// Return the operation on the front of the queue. If
/// the queue is empty and @wait is false, call returns
@@ -95,7 +97,7 @@ public:
/// Caller acquires reference count any returned operation
///
/// Threading: callable by any thread.
- HttpOperation * fetchOp(bool wait);
+ opPtr_t fetchOp(bool wait);
/// Return all queued requests to caller. The @ops argument
/// should be empty when called and will be swap()'d with
diff --git a/indra/llcorehttp/_httpretryqueue.h b/indra/llcorehttp/_httpretryqueue.h
index 745adec09d..5d8c529cff 100644
--- a/indra/llcorehttp/_httpretryqueue.h
+++ b/indra/llcorehttp/_httpretryqueue.h
@@ -49,15 +49,15 @@ namespace LLCore
struct HttpOpRetryCompare
{
- bool operator()(const HttpOpRequest * lhs, const HttpOpRequest * rhs)
+ bool operator()(const HttpOpRequest::ptr_t &lhs, const HttpOpRequest::ptr_t &rhs)
{
return lhs->mPolicyRetryAt < rhs->mPolicyRetryAt;
}
};
-typedef std::priority_queue,
+typedef std::priority_queue,
LLCore::HttpOpRetryCompare> HttpRetryQueueBase;
class HttpRetryQueue : public HttpRetryQueueBase
diff --git a/indra/llcorehttp/_httpservice.cpp b/indra/llcorehttp/_httpservice.cpp
index c673e1be1d..6c39fdc61b 100644
--- a/indra/llcorehttp/_httpservice.cpp
+++ b/indra/llcorehttp/_httpservice.cpp
@@ -53,15 +53,16 @@ namespace LLCore
const HttpService::OptionDescriptor HttpService::sOptionDesc[] =
{ // isLong isDynamic isGlobal isClass
- { true, true, true, true }, // PO_CONNECTION_LIMIT
- { true, true, false, true }, // PO_PER_HOST_CONNECTION_LIMIT
- { false, false, true, false }, // PO_CA_PATH
- { false, false, true, false }, // PO_CA_FILE
- { false, true, true, false }, // PO_HTTP_PROXY
- { true, true, true, false }, // PO_LLPROXY
- { true, true, true, false }, // PO_TRACE
- { true, true, false, true }, // PO_ENABLE_PIPELINING
- { true, true, false, true } // PO_THROTTLE_RATE
+ { true, true, true, true, false }, // PO_CONNECTION_LIMIT
+ { true, true, false, true, false }, // PO_PER_HOST_CONNECTION_LIMIT
+ { false, false, true, false, false }, // PO_CA_PATH
+ { false, false, true, false, false }, // PO_CA_FILE
+ { false, true, true, false, false }, // PO_HTTP_PROXY
+ { true, true, true, false, false }, // PO_LLPROXY
+ { true, true, true, false, false }, // PO_TRACE
+ { true, true, false, true, false }, // PO_ENABLE_PIPELINING
+ { true, true, false, true, false }, // PO_THROTTLE_RATE
+ { false, false, true, false, true } // PO_SSL_VERIFY_CALLBACK
};
HttpService * HttpService::sInstance(NULL);
volatile HttpService::EState HttpService::sState(NOT_INITIALIZED);
@@ -262,14 +263,13 @@ void HttpService::shutdown()
// Cancel requests already on the request queue
HttpRequestQueue::OpContainer ops;
mRequestQueue->fetchAll(false, ops);
- while (! ops.empty())
- {
- HttpOperation * op(ops.front());
- ops.erase(ops.begin());
- op->cancel();
- op->release();
- }
+ for (HttpRequestQueue::OpContainer::iterator it = ops.begin();
+ it != ops.end(); ++it)
+ {
+ (*it)->cancel();
+ }
+ ops.clear();
// Shutdown transport canceling requests, freeing resources
mTransport->shutdown();
@@ -323,7 +323,7 @@ HttpService::ELoopSpeed HttpService::processRequestQueue(ELoopSpeed loop)
mRequestQueue->fetchAll(wait_for_req, ops);
while (! ops.empty())
{
- HttpOperation * op(ops.front());
+ HttpOperation::ptr_t op(ops.front());
ops.erase(ops.begin());
// Process operation
@@ -337,7 +337,7 @@ HttpService::ELoopSpeed HttpService::processRequestQueue(ELoopSpeed loop)
if (op->mTracing > HTTP_TRACE_OFF)
{
LL_INFOS(LOG_CORE) << "TRACE, FromRequestQueue, Handle: "
- << static_cast(op)
+ << op->getHandle()
<< LL_ENDL;
}
@@ -346,7 +346,7 @@ HttpService::ELoopSpeed HttpService::processRequestQueue(ELoopSpeed loop)
}
// Done with operation
- op->release();
+ op.reset();
}
// Queue emptied, allow polling loop to sleep
@@ -413,6 +413,34 @@ HttpStatus HttpService::getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequ
return status;
}
+HttpStatus HttpService::getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass,
+ HttpRequest::policyCallback_t * ret_value)
+{
+ HttpStatus status(HttpStatus::LLCORE, LLCore::HE_INVALID_ARG);
+
+ if (opt < HttpRequest::PO_CONNECTION_LIMIT // option must be in range
+ || opt >= HttpRequest::PO_LAST // ditto
+ || (sOptionDesc[opt].mIsLong) // datatype is string
+ || (pclass != HttpRequest::GLOBAL_POLICY_ID && pclass > mLastPolicy) // pclass in valid range
+ || (pclass == HttpRequest::GLOBAL_POLICY_ID && !sOptionDesc[opt].mIsGlobal) // global setting permitted
+ || (pclass != HttpRequest::GLOBAL_POLICY_ID && !sOptionDesc[opt].mIsClass)) // class setting permitted
+ // can always get, no dynamic check
+ {
+ return status;
+ }
+
+ // Only global has callback values
+ if (pclass == HttpRequest::GLOBAL_POLICY_ID)
+ {
+ HttpPolicyGlobal & opts(mPolicy->getGlobalOptions());
+
+ status = opts.get(opt, ret_value);
+ }
+
+ return status;
+}
+
+
HttpStatus HttpService::setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass,
long value, long * ret_value)
@@ -489,6 +517,37 @@ HttpStatus HttpService::setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequ
return status;
}
-
+
+HttpStatus HttpService::setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass,
+ HttpRequest::policyCallback_t value, HttpRequest::policyCallback_t * ret_value)
+{
+ HttpStatus status(HttpStatus::LLCORE, LLCore::HE_INVALID_ARG);
+
+ if (opt < HttpRequest::PO_CONNECTION_LIMIT // option must be in range
+ || opt >= HttpRequest::PO_LAST // ditto
+ || (sOptionDesc[opt].mIsLong) // datatype is string
+ || (pclass != HttpRequest::GLOBAL_POLICY_ID && pclass > mLastPolicy) // pclass in valid range
+ || (pclass == HttpRequest::GLOBAL_POLICY_ID && !sOptionDesc[opt].mIsGlobal) // global setting permitted
+ || (pclass != HttpRequest::GLOBAL_POLICY_ID && !sOptionDesc[opt].mIsClass) // class setting permitted
+ || (RUNNING == sState && !sOptionDesc[opt].mIsDynamic)) // dynamic setting permitted
+ {
+ return status;
+ }
+
+ // Callbacks values are always global (at this time).
+ if (pclass == HttpRequest::GLOBAL_POLICY_ID)
+ {
+ HttpPolicyGlobal & opts(mPolicy->getGlobalOptions());
+
+ status = opts.set(opt, value);
+ if (status && ret_value)
+ {
+ status = opts.get(opt, ret_value);
+ }
+ }
+
+ return status;
+}
+
} // end namespace LLCore
diff --git a/indra/llcorehttp/_httpservice.h b/indra/llcorehttp/_httpservice.h
index cf23f3ab61..ac518a5de7 100644
--- a/indra/llcorehttp/_httpservice.h
+++ b/indra/llcorehttp/_httpservice.h
@@ -201,17 +201,24 @@ protected:
bool mIsDynamic;
bool mIsGlobal;
bool mIsClass;
+ bool mIsCallback;
};
HttpStatus getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t,
long * ret_value);
HttpStatus getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t,
std::string * ret_value);
+ HttpStatus getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t,
+ HttpRequest::policyCallback_t * ret_value);
+
HttpStatus setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t,
long value, long * ret_value);
HttpStatus setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t,
const std::string & value, std::string * ret_value);
-
+ HttpStatus setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t,
+ HttpRequest::policyCallback_t value,
+ HttpRequest::policyCallback_t * ret_value);
+
protected:
static const OptionDescriptor sOptionDesc[HttpRequest::PO_LAST];
static HttpService * sInstance;
diff --git a/indra/llcorehttp/_refcounted.h b/indra/llcorehttp/_refcounted.h
index 402e725152..7f713f2298 100644
--- a/indra/llcorehttp/_refcounted.h
+++ b/indra/llcorehttp/_refcounted.h
@@ -32,6 +32,7 @@
#include "fix_macros.h"
#include
+#include
#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(rawptr, false).
+ * IntrusivePtr encapsulates that for you.
+ */
+template
+struct IntrusivePtr: public boost::intrusive_ptr
+{
+ IntrusivePtr():
+ boost::intrusive_ptr()
+ {}
+ IntrusivePtr(T* p):
+ boost::intrusive_ptr(p, false)
+ {}
+};
+
+inline void intrusive_ptr_add_ref(RefCounted* p)
+{
+ p->addRef();
+}
+
+inline void intrusive_ptr_release(RefCounted* p)
+{
+ p->release();
+}
+
} // end namespace LLCoreInt
+
#endif // LLCOREINT__REFCOUNTED_H_
diff --git a/indra/llcorehttp/bufferarray.h b/indra/llcorehttp/bufferarray.h
index 1094a435b4..320adf2b8b 100644
--- a/indra/llcorehttp/bufferarray.h
+++ b/indra/llcorehttp/bufferarray.h
@@ -30,6 +30,7 @@
#include
#include
+#include "boost/intrusive_ptr.hpp"
#include "_refcounted.h"
@@ -73,6 +74,8 @@ public:
BufferArray();
+ typedef LLCoreInt::IntrusivePtr ptr_t;
+
protected:
virtual ~BufferArray(); // Use release()
@@ -129,6 +132,7 @@ protected:
container_t mBlocks;
size_t mLen;
+
}; // end class BufferArray
diff --git a/indra/llcorehttp/examples/http_texture_load.cpp b/indra/llcorehttp/examples/http_texture_load.cpp
index 9d9631b980..b91aaf0593 100644
--- a/indra/llcorehttp/examples/http_texture_load.cpp
+++ b/indra/llcorehttp/examples/http_texture_load.cpp
@@ -83,7 +83,7 @@ public:
WorkingSet();
~WorkingSet();
- bool reload(LLCore::HttpRequest *, LLCore::HttpOptions *);
+ bool reload(LLCore::HttpRequest *, LLCore::HttpOptions::ptr_t &);
virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response);
@@ -121,7 +121,7 @@ public:
int mRetriesHttp503;
int mSuccesses;
long mByteCount;
- LLCore::HttpHeaders * mHeaders;
+ LLCore::HttpHeaders::ptr_t mHeaders;
};
@@ -304,7 +304,7 @@ int main(int argc, char** argv)
LLCore::HttpRequest * hr = new LLCore::HttpRequest();
// Get request options
- LLCore::HttpOptions * opt = new LLCore::HttpOptions();
+ LLCore::HttpOptions::ptr_t opt = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions());
opt->setRetries(12);
opt->setUseRetryAfter(true);
@@ -361,10 +361,9 @@ int main(int argc, char** argv)
<< std::endl;
// Clean up
- hr->requestStopThread(NULL);
+ hr->requestStopThread(LLCore::HttpHandler::ptr_t());
ms_sleep(1000);
- opt->release();
- opt = NULL;
+ opt.reset();
delete hr;
LLCore::HttpRequest::destroyService();
term_curl();
@@ -427,22 +426,22 @@ WorkingSet::WorkingSet()
{
mAssets.reserve(30000);
- mHeaders = new LLCore::HttpHeaders;
+ mHeaders = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders);
mHeaders->append("Accept", "image/x-j2c");
}
WorkingSet::~WorkingSet()
{
- if (mHeaders)
- {
- mHeaders->release();
- mHeaders = NULL;
- }
}
+namespace
+{
+ void NoOpDeletor(LLCore::HttpHandler *)
+ { /*NoOp*/ }
+}
-bool WorkingSet::reload(LLCore::HttpRequest * hr, LLCore::HttpOptions * opt)
+bool WorkingSet::reload(LLCore::HttpRequest * hr, LLCore::HttpOptions::ptr_t & opt)
{
if (mRequestLowWater <= mHandles.size())
{
@@ -470,11 +469,11 @@ bool WorkingSet::reload(LLCore::HttpRequest * hr, LLCore::HttpOptions * opt)
LLCore::HttpHandle handle;
if (offset || length)
{
- handle = hr->requestGetByteRange(0, 0, buffer, offset, length, opt, mHeaders, this);
+ handle = hr->requestGetByteRange(0, 0, buffer, offset, length, opt, mHeaders, LLCore::HttpHandler::ptr_t(this, NoOpDeletor));
}
else
{
- handle = hr->requestGet(0, 0, buffer, opt, mHeaders, this);
+ handle = hr->requestGet(0, 0, buffer, opt, mHeaders, LLCore::HttpHandler::ptr_t(this, NoOpDeletor));
}
if (! handle)
{
diff --git a/indra/llcorehttp/httpcommon.cpp b/indra/llcorehttp/httpcommon.cpp
index 7907e958a4..c423047bb0 100644
--- a/indra/llcorehttp/httpcommon.cpp
+++ b/indra/llcorehttp/httpcommon.cpp
@@ -23,12 +23,24 @@
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
+#if LL_WINDOWS
+#define SAFE_SSL 1
+#elif LL_DARWIN
+#define SAFE_SSL 1
+#else
+#define SAFE_SSL 1
+#endif
+#include "linden_common.h" // Modifies curl/curl.h interfaces
#include "httpcommon.h"
-
+#include "llmutex.h"
+#include "llthread.h"
#include
#include
#include
+#if SAFE_SSL
+#include
+#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_ptr;
+std::vector sSSLMutex;
+CURL *getCurlTemplateHandle()
+{
+ static CURL *curlpTemplateHandle = NULL;
+
+ if (curlpTemplateHandle == NULL)
+ { // Late creation of the template curl handle
+ curlpTemplateHandle = curl_easy_init();
+ if (curlpTemplateHandle == NULL)
+ {
+ LL_WARNS() << "curl error calling curl_easy_init()" << LL_ENDL;
+ }
+ else
+ {
+ CURLcode result = curl_easy_setopt(curlpTemplateHandle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
+ check_curl_code(result, CURLOPT_IPRESOLVE);
+ result = curl_easy_setopt(curlpTemplateHandle, CURLOPT_NOSIGNAL, 1);
+ check_curl_code(result, CURLOPT_NOSIGNAL);
+ result = curl_easy_setopt(curlpTemplateHandle, CURLOPT_NOPROGRESS, 1);
+ check_curl_code(result, CURLOPT_NOPROGRESS);
+ result = curl_easy_setopt(curlpTemplateHandle, CURLOPT_ENCODING, "");
+ check_curl_code(result, CURLOPT_ENCODING);
+ result = curl_easy_setopt(curlpTemplateHandle, CURLOPT_AUTOREFERER, 1);
+ check_curl_code(result, CURLOPT_AUTOREFERER);
+ result = curl_easy_setopt(curlpTemplateHandle, CURLOPT_FOLLOWLOCATION, 1);
+ check_curl_code(result, CURLOPT_FOLLOWLOCATION);
+ result = curl_easy_setopt(curlpTemplateHandle, CURLOPT_SSL_VERIFYPEER, 1);
+ check_curl_code(result, CURLOPT_SSL_VERIFYPEER);
+ result = curl_easy_setopt(curlpTemplateHandle, CURLOPT_SSL_VERIFYHOST, 0);
+ check_curl_code(result, CURLOPT_SSL_VERIFYHOST);
+
+ // The Linksys WRT54G V5 router has an issue with frequent
+ // DNS lookups from LAN machines. If they happen too often,
+ // like for every HTTP request, the router gets annoyed after
+ // about 700 or so requests and starts issuing TCP RSTs to
+ // new connections. Reuse the DNS lookups for even a few
+ // seconds and no RSTs.
+ result = curl_easy_setopt(curlpTemplateHandle, CURLOPT_DNS_CACHE_TIMEOUT, 15);
+ check_curl_code(result, CURLOPT_DNS_CACHE_TIMEOUT);
+ }
+ }
+
+ return curlpTemplateHandle;
+}
+
+LLMutex *getCurlMutex()
+{
+ static LLMutex* sHandleMutexp = NULL;
+
+ if (!sHandleMutexp)
+ {
+ sHandleMutexp = new LLMutex(NULL);
+ }
+
+ return sHandleMutexp;
+}
+
+void deallocateEasyCurl(CURL *curlp)
+{
+ LLMutexLock lock(getCurlMutex());
+
+ curl_easy_cleanup(curlp);
+}
+
+
+#if SAFE_SSL
+//static
+void ssl_locking_callback(int mode, int type, const char *file, int line)
+{
+ if (type >= sSSLMutex.size())
+ {
+ LL_WARNS() << "Attempt to get unknown MUTEX in SSL Lock." << LL_ENDL;
+ }
+
+ if (mode & CRYPTO_LOCK)
+ {
+ sSSLMutex[type]->lock();
+ }
+ else
+ {
+ sSSLMutex[type]->unlock();
+ }
+}
+
+//static
+unsigned long ssl_thread_id(void)
+{
+ return LLThread::currentID();
+}
+#endif
+
+
+}
+
+void initialize()
+{
+ // Do not change this "unless you are familiar with and mean to control
+ // internal operations of libcurl"
+ // - http://curl.haxx.se/libcurl/c/curl_global_init.html
+ CURLcode code = curl_global_init(CURL_GLOBAL_ALL);
+
+ check_curl_code(code, CURL_GLOBAL_ALL);
+
+#if SAFE_SSL
+ S32 mutex_count = CRYPTO_num_locks();
+ for (S32 i = 0; i < mutex_count; i++)
+ {
+ sSSLMutex.push_back(LLMutex_ptr(new LLMutex(NULL)));
+ }
+ CRYPTO_set_id_callback(&ssl_thread_id);
+ CRYPTO_set_locking_callback(&ssl_locking_callback);
+#endif
+
+}
+
+
+void cleanup()
+{
+#if SAFE_SSL
+ CRYPTO_set_id_callback(NULL);
+ CRYPTO_set_locking_callback(NULL);
+ sSSLMutex.clear();
+#endif
+
+ curl_global_cleanup();
+}
+
+
+CURL_ptr createEasyHandle()
+{
+ LLMutexLock lock(getCurlMutex());
+
+ CURL* handle = curl_easy_duphandle(getCurlTemplateHandle());
+
+ return CURL_ptr(handle, &deallocateEasyCurl);
+}
+
+std::string getCURLVersion()
+{
+ return std::string(curl_version());
+}
+
+void check_curl_code(CURLcode code, int curl_setopt_option)
+{
+ if (CURLE_OK != code)
+ {
+ // Comment from old llcurl code which may no longer apply:
+ //
+ // linux appears to throw a curl error once per session for a bad initialization
+ // at a pretty random time (when enabling cookies).
+ LL_WARNS() << "libcurl error detected: " << curl_easy_strerror(code)
+ << ", curl_easy_setopt option: " << curl_setopt_option
+ << LL_ENDL;
+ }
+
+}
+
+}
+} // end namespace LLCore
diff --git a/indra/llcorehttp/httpcommon.h b/indra/llcorehttp/httpcommon.h
index 9601f94125..b2db01d038 100644
--- a/indra/llcorehttp/httpcommon.h
+++ b/indra/llcorehttp/httpcommon.h
@@ -188,9 +188,12 @@
///
#include "linden_common.h" // Modifies curl/curl.h interfaces
-
+#include "boost/intrusive_ptr.hpp"
+#include "boost/shared_ptr.hpp"
+#include "boost/weak_ptr.hpp"
+#include "boost/function.hpp"
#include
-
+#include
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(new Details(LLCORE, HE_SUCCESS));
+ }
HttpStatus(type_enum_t type, short status)
- : mType(type),
- mStatus(status)
- {}
+ {
+ mDetails = boost::shared_ptr(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(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(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(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 mDetails;
+
}; // end struct HttpStatus
+/// A namespace for several free methods and low level utilities.
+namespace LLHttp
+{
+ typedef boost::shared_ptr CURL_ptr;
+
+ void initialize();
+ void cleanup();
+
+ CURL_ptr createEasyHandle();
+ std::string getCURLVersion();
+
+ void check_curl_code(CURLcode code, int curl_setopt_option);
+}
+
} // end namespace LLCore
#endif // _LLCORE_HTTP_COMMON_H_
diff --git a/indra/llcorehttp/httphandler.h b/indra/llcorehttp/httphandler.h
index 9171e4e7b9..65e043f5d3 100644
--- a/indra/llcorehttp/httphandler.h
+++ b/indra/llcorehttp/httphandler.h
@@ -45,7 +45,7 @@ class HttpResponse;
/// be shared by any number of requests and across instances
/// of HttpRequest running in the same thread.
///
-/// Threading: HttpHandler itself is pure interface and is
+/// Threading: HttpHandler itself is interface and is
/// tread-compatible. Most derivations, however, will have
/// different constraints.
///
@@ -53,12 +53,16 @@ class HttpResponse;
/// that is rarely a good idea. Queued requests and replies keep
/// a naked pointer to the handler and this can result in a
/// dangling pointer if lifetimes aren't managed correctly.
-
-class HttpHandler
+///
+/// *TODO: public std::enable_shared_from_this
+class HttpHandler
{
public:
+ typedef boost::shared_ptr ptr_t;
+ typedef boost::weak_ptr wptr_t;
+
virtual ~HttpHandler()
- {}
+ { }
/// Method invoked during calls to @see update(). Each invocation
/// represents the completion of some requested operation. Caller
diff --git a/indra/llcorehttp/httpheaders.cpp b/indra/llcorehttp/httpheaders.cpp
index 23ebea361c..f586191a7c 100644
--- a/indra/llcorehttp/httpheaders.cpp
+++ b/indra/llcorehttp/httpheaders.cpp
@@ -34,7 +34,6 @@ namespace LLCore
HttpHeaders::HttpHeaders()
- : RefCounted(true)
{}
@@ -105,7 +104,7 @@ void HttpHeaders::appendNormal(const char * header, size_t size)
// Find from end to simulate a tradition of using single-valued
// std::map for this in the past.
-const std::string * HttpHeaders::find(const char * name) const
+const std::string * HttpHeaders::find(const std::string &name) const
{
const_reverse_iterator iend(rend());
for (const_reverse_iterator iter(rbegin()); iend != iter; ++iter)
@@ -118,6 +117,24 @@ const std::string * HttpHeaders::find(const char * name) const
return NULL;
}
+void HttpHeaders::remove(const char *name)
+{
+ remove(std::string(name));
+}
+
+void HttpHeaders::remove(const std::string &name)
+{
+ iterator iend(end());
+ for (iterator iter(begin()); iend != iter; ++iter)
+ {
+ if ((*iter).first == name)
+ {
+ mHeaders.erase(iter);
+ return;
+ }
+ }
+}
+
// Standard Iterators
HttpHeaders::iterator HttpHeaders::begin()
diff --git a/indra/llcorehttp/httpheaders.h b/indra/llcorehttp/httpheaders.h
index f70cd898f3..b9168cb6ec 100644
--- a/indra/llcorehttp/httpheaders.h
+++ b/indra/llcorehttp/httpheaders.h
@@ -28,8 +28,8 @@
#define _LLCORE_HTTP_HEADERS_H_
+#include "httpcommon.h"
#include
-
#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 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 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 ptr_t;
protected:
- virtual ~HttpHeaders(); // Use release()
HttpHeaders(const HttpHeaders &); // Not defined
void operator=(const HttpHeaders &); // Not defined
@@ -145,8 +147,16 @@ public:
// a pointer to a std::string in the container.
// Pointer is valid only for the lifetime of
// the container or until container is modifed.
- //
- const std::string * find(const char * name) const;
+ const std::string * find(const std::string &name) const;
+ const std::string * find(const char * name) const
+ {
+ return find(std::string(name));
+ }
+
+ // Remove the header from the list if found.
+ //
+ void remove(const std::string &name);
+ void remove(const char *name);
// Count of headers currently in the list.
size_type size() const
diff --git a/indra/llcorehttp/httpoptions.cpp b/indra/llcorehttp/httpoptions.cpp
index 5bf1ecb4a5..aab447f2dd 100644
--- a/indra/llcorehttp/httpoptions.cpp
+++ b/indra/llcorehttp/httpoptions.cpp
@@ -25,7 +25,7 @@
*/
#include "httpoptions.h"
-
+#include "lldefs.h"
#include "_httpinternal.h"
@@ -33,14 +33,18 @@ namespace LLCore
{
-HttpOptions::HttpOptions()
- : RefCounted(true),
- mWantHeaders(false),
- mTracing(HTTP_TRACE_OFF),
- mTimeout(HTTP_REQUEST_TIMEOUT_DEFAULT),
- mTransferTimeout(HTTP_REQUEST_XFER_TIMEOUT_DEFAULT),
- mRetries(HTTP_RETRY_COUNT_DEFAULT),
- mUseRetryAfter(HTTP_USE_RETRY_AFTER_DEFAULT)
+HttpOptions::HttpOptions() :
+ mWantHeaders(false),
+ mTracing(HTTP_TRACE_OFF),
+ mTimeout(HTTP_REQUEST_TIMEOUT_DEFAULT),
+ mTransferTimeout(HTTP_REQUEST_XFER_TIMEOUT_DEFAULT),
+ mRetries(HTTP_RETRY_COUNT_DEFAULT),
+ mUseRetryAfter(HTTP_USE_RETRY_AFTER_DEFAULT),
+ mFollowRedirects(true),
+ mVerifyPeer(false),
+ mVerifyHost(false),
+ mDNSCacheTimeout(-1L),
+ mNoBody(false)
{}
@@ -82,5 +86,31 @@ void HttpOptions::setUseRetryAfter(bool use_retry)
mUseRetryAfter = use_retry;
}
+void HttpOptions::setFollowRedirects(bool follow_redirect)
+{
+ mFollowRedirects = follow_redirect;
+}
+
+void HttpOptions::setSSLVerifyPeer(bool verify)
+{
+ mVerifyPeer = verify;
+}
+
+void HttpOptions::setSSLVerifyHost(bool verify)
+{
+ mVerifyHost = verify;
+}
+
+void HttpOptions::setDNSCacheTimeout(int timeout)
+{
+ mDNSCacheTimeout = timeout;
+}
+
+void HttpOptions::setHeadersOnly(bool nobody)
+{
+ mNoBody = nobody;
+ if (mNoBody)
+ setWantHeaders(true);
+}
} // end namespace LLCore
diff --git a/indra/llcorehttp/httpoptions.h b/indra/llcorehttp/httpoptions.h
index 4ab5ff18c4..510eaa45bb 100644
--- a/indra/llcorehttp/httpoptions.h
+++ b/indra/llcorehttp/httpoptions.h
@@ -29,7 +29,6 @@
#include "httpcommon.h"
-
#include "_refcounted.h"
@@ -56,59 +55,110 @@ namespace LLCore
/// Allocation: Refcounted, heap only. Caller of the constructor
/// is given a refcount.
///
-class HttpOptions : public LLCoreInt::RefCounted
+class HttpOptions : private boost::noncopyable
{
public:
HttpOptions();
+ typedef boost::shared_ptr ptr_t;
+
+ virtual ~HttpOptions(); // Use release()
+
protected:
- virtual ~HttpOptions(); // Use release()
HttpOptions(const HttpOptions &); // Not defined
void operator=(const HttpOptions &); // Not defined
public:
+
// Default: false
void setWantHeaders(bool wanted);
bool getWantHeaders() const
- {
- return mWantHeaders;
- }
+ {
+ return mWantHeaders;
+ }
// Default: 0
void setTrace(int long);
int getTrace() const
- {
- return mTracing;
- }
+ {
+ return mTracing;
+ }
// Default: 30
void setTimeout(unsigned int timeout);
unsigned int getTimeout() const
- {
- return mTimeout;
- }
+ {
+ return mTimeout;
+ }
// Default: 0
void setTransferTimeout(unsigned int timeout);
unsigned int getTransferTimeout() const
- {
- return mTransferTimeout;
- }
+ {
+ return mTransferTimeout;
+ }
+ /// Sets the number of retries on an LLCore::HTTPRequest before the
+ /// request fails.
// Default: 8
void setRetries(unsigned int retries);
unsigned int getRetries() const
- {
- return mRetries;
- }
+ {
+ return mRetries;
+ }
// Default: true
void setUseRetryAfter(bool use_retry);
bool getUseRetryAfter() const
- {
- return mUseRetryAfter;
- }
+ {
+ return mUseRetryAfter;
+ }
+
+ /// Instructs the LLCore::HTTPRequest to follow redirects
+ /// Default: false
+ void setFollowRedirects(bool follow_redirect);
+ bool getFollowRedirects() const
+ {
+ return mFollowRedirects;
+ }
+
+ /// Instructs the LLCore::HTTPRequest to verify that the exchanged security
+ /// certificate is authentic.
+ /// Default: false
+ void setSSLVerifyPeer(bool verify);
+ bool getSSLVerifyPeer() const
+ {
+ return mVerifyPeer;
+ }
+
+ /// Instructs the LLCore::HTTPRequest to verify that the name in the
+ /// security certificate matches the name of the host contacted.
+ /// Default: false
+ void setSSLVerifyHost(bool verify);
+ bool getSSLVerifyHost() const
+ {
+ return mVerifyHost;
+ }
+
+ /// Sets the time for DNS name caching in seconds. Setting this value
+ /// to 0 will disable name caching. Setting this value to -1 causes the
+ /// name cache to never time out.
+ /// Default: -1
+ void setDNSCacheTimeout(int timeout);
+ int getDNSCacheTimeout() const
+ {
+ return mDNSCacheTimeout;
+ }
+
+ /// Retrieve only the headers and status from the request. Setting this
+ /// to true implies setWantHeaders(true) as well.
+ /// Default: false
+ void setHeadersOnly(bool nobody);
+ bool getHeadersOnly() const
+ {
+ return mNoBody;
+ }
protected:
bool mWantHeaders;
@@ -117,6 +167,11 @@ protected:
unsigned int mTransferTimeout;
unsigned int mRetries;
bool mUseRetryAfter;
+ bool mFollowRedirects;
+ bool mVerifyPeer;
+ bool mVerifyHost;
+ int mDNSCacheTimeout;
+ bool mNoBody;
}; // end class HttpOptions
diff --git a/indra/llcorehttp/httprequest.cpp b/indra/llcorehttp/httprequest.cpp
index 7b1888e3eb..e09f0c3b18 100644
--- a/indra/llcorehttp/httprequest.cpp
+++ b/indra/llcorehttp/httprequest.cpp
@@ -55,13 +55,13 @@ namespace LLCore
HttpRequest::HttpRequest()
- : mReplyQueue(NULL),
+ : mReplyQueue(),
mRequestQueue(NULL)
{
mRequestQueue = HttpRequestQueue::instanceOf();
mRequestQueue->addRef();
- mReplyQueue = new HttpReplyQueue();
+ mReplyQueue.reset( new HttpReplyQueue() );
}
@@ -73,11 +73,7 @@ HttpRequest::~HttpRequest()
mRequestQueue = NULL;
}
- if (mReplyQueue)
- {
- mReplyQueue->release();
- mReplyQueue = NULL;
- }
+ mReplyQueue.reset();
}
@@ -117,60 +113,59 @@ HttpStatus HttpRequest::setStaticPolicyOption(EPolicyOption opt, policy_t pclass
return HttpService::instanceOf()->setPolicyOption(opt, pclass, value, ret_value);
}
+HttpStatus HttpRequest::setStaticPolicyOption(EPolicyOption opt, policy_t pclass, policyCallback_t value, policyCallback_t * ret_value)
+{
+ if (HttpService::RUNNING == HttpService::instanceOf()->getState())
+ {
+ return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC);
+ }
+
+ return HttpService::instanceOf()->setPolicyOption(opt, pclass, value, ret_value);
+}
HttpHandle HttpRequest::setPolicyOption(EPolicyOption opt, policy_t pclass,
- long value, HttpHandler * handler)
+ long value, HttpHandler::ptr_t handler)
{
HttpStatus status;
- HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID);
- HttpOpSetGet * op = new HttpOpSetGet();
+ HttpOpSetGet::ptr_t op(new HttpOpSetGet());
if (! (status = op->setupSet(opt, pclass, value)))
{
- op->release();
mLastReqStatus = status;
- return handle;
+ return LLCORE_HTTP_HANDLE_INVALID;
}
op->setReplyPath(mReplyQueue, handler);
if (! (status = mRequestQueue->addOp(op))) // transfers refcount
{
- op->release();
mLastReqStatus = status;
- return handle;
+ return LLCORE_HTTP_HANDLE_INVALID;
}
mLastReqStatus = status;
- handle = static_cast(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(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(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(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(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(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(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(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(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(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(op);
+ handle = op->getHandle();
return handle;
}
diff --git a/indra/llcorehttp/httprequest.h b/indra/llcorehttp/httprequest.h
index 7f23723b0b..17cfdcd7b6 100644
--- a/indra/llcorehttp/httprequest.h
+++ b/indra/llcorehttp/httprequest.h
@@ -31,6 +31,8 @@
#include "httpcommon.h"
#include "httphandler.h"
+#include "httpheaders.h"
+#include "httpoptions.h"
namespace LLCore
{
@@ -38,8 +40,6 @@ namespace LLCore
class HttpRequestQueue;
class HttpReplyQueue;
class HttpService;
-class HttpOptions;
-class HttpHeaders;
class HttpOperation;
class BufferArray;
@@ -97,6 +97,8 @@ public:
typedef unsigned int policy_t;
typedef unsigned int priority_t;
+ typedef boost::shared_ptr ptr_t;
+ typedef boost::weak_ptr 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 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 HttpReplyQueuePtr_t;
+
/// @name InstanceData
///
/// @{
HttpStatus mLastReqStatus;
- HttpReplyQueue * mReplyQueue;
+ HttpReplyQueuePtr_t mReplyQueue;
HttpRequestQueue * mRequestQueue;
/// @}
diff --git a/indra/llcorehttp/httpresponse.cpp b/indra/llcorehttp/httpresponse.cpp
index c974395b0a..f5ad2ebd47 100644
--- a/indra/llcorehttp/httpresponse.cpp
+++ b/indra/llcorehttp/httpresponse.cpp
@@ -39,16 +39,17 @@ HttpResponse::HttpResponse()
mReplyLength(0U),
mReplyFullLength(0U),
mBufferArray(NULL),
- mHeaders(NULL),
+ mHeaders(),
mRetries(0U),
- m503Retries(0U)
+ m503Retries(0U),
+ mRequestUrl()
{}
HttpResponse::~HttpResponse()
{
setBody(NULL);
- setHeaders(NULL);
+ //setHeaders();
}
@@ -71,23 +72,14 @@ void HttpResponse::setBody(BufferArray * ba)
}
-void HttpResponse::setHeaders(HttpHeaders * headers)
+void HttpResponse::setHeaders(HttpHeaders::ptr_t &headers)
{
- if (mHeaders == headers)
- return;
-
- if (mHeaders)
- {
- mHeaders->release();
- }
-
- if (headers)
- {
- headers->addRef();
- }
-
- mHeaders = headers;
+ mHeaders = headers;
}
+size_t HttpResponse::getBodySize() const
+{
+ return (mBufferArray) ? mBufferArray->size() : 0;
+}
} // end namespace LLCore
diff --git a/indra/llcorehttp/httpresponse.h b/indra/llcorehttp/httpresponse.h
index aee64e2878..0bfa4585c7 100644
--- a/indra/llcorehttp/httpresponse.h
+++ b/indra/llcorehttp/httpresponse.h
@@ -31,7 +31,7 @@
#include
#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 ptr_t;
+
+ TransferStats() : mSizeDownload(0.0), mTotalTime(0.0), mSpeedDownload(0.0) {}
+ F64 mSizeDownload;
+ F64 mTotalTime;
+ F64 mSpeedDownload;
+ };
+
+
/// Returns the final status of the requested operation.
///
HttpStatus getStatus() const
@@ -92,6 +104,10 @@ public:
return mBufferArray;
}
+ /// Safely get the size of the body buffer. If the body buffer is missing
+ /// return 0 as the size.
+ size_t getBodySize() const;
+
/// Set the response data in the instance. Will drop the reference
/// count to any existing data and increment the count of that passed
/// in. It is legal to set the data to NULL.
@@ -104,13 +120,13 @@ public:
///
/// Caller can hold onto the headers by incrementing the reference
/// count of the returned object.
- HttpHeaders * getHeaders() const
- {
+ HttpHeaders::ptr_t getHeaders() const
+ {
return mHeaders;
- }
+ }
/// Behaves like @see setResponse() but for header data.
- void setHeaders(HttpHeaders * headers);
+ void setHeaders(HttpHeaders::ptr_t &headers);
/// If a 'Range:' header was used, these methods are involved
/// in setting and returning data about the actual response.
@@ -168,6 +184,27 @@ public:
m503Retries = retries_503;
}
+ void setTransferStats(TransferStats::ptr_t &stats)
+ {
+ mStats = stats;
+ }
+
+ TransferStats::ptr_t getTransferStats()
+ {
+ return mStats;
+ }
+
+ void setRequestURL(const std::string &url)
+ {
+ mRequestUrl = url;
+ }
+
+ const std::string &getRequestURL() const
+ {
+ return mRequestUrl;
+ }
+
+
protected:
// Response data here
HttpStatus mStatus;
@@ -175,10 +212,13 @@ protected:
unsigned int mReplyLength;
unsigned int mReplyFullLength;
BufferArray * mBufferArray;
- HttpHeaders * mHeaders;
+ HttpHeaders::ptr_t mHeaders;
std::string mContentType;
unsigned int mRetries;
unsigned int m503Retries;
+ std::string mRequestUrl;
+
+ TransferStats::ptr_t mStats;
};
diff --git a/indra/llmessage/llhttpconstants.cpp b/indra/llcorehttp/llhttpconstants.cpp
old mode 100644
new mode 100755
similarity index 75%
rename from indra/llmessage/llhttpconstants.cpp
rename to indra/llcorehttp/llhttpconstants.cpp
index 32f76f0d70..71d4f19408
--- a/indra/llmessage/llhttpconstants.cpp
+++ b/indra/llcorehttp/llhttpconstants.cpp
@@ -133,94 +133,3 @@ const std::string HTTP_VERB_MOVE("MOVE");
const std::string HTTP_VERB_OPTIONS("OPTIONS");
const std::string HTTP_VERB_PATCH("PATCH");
const std::string HTTP_VERB_COPY("COPY");
-
-const std::string& httpMethodAsVerb(EHTTPMethod method)
-{
- static const std::string VERBS[] =
- {
- HTTP_VERB_INVALID,
- HTTP_VERB_HEAD,
- HTTP_VERB_GET,
- HTTP_VERB_PUT,
- HTTP_VERB_POST,
- HTTP_VERB_DELETE,
- HTTP_VERB_MOVE,
- HTTP_VERB_OPTIONS,
- HTTP_VERB_PATCH,
- HTTP_VERB_COPY
- };
- if(((S32)method <=0) || ((S32)method >= HTTP_METHOD_COUNT))
- {
- return VERBS[0];
- }
- return VERBS[method];
-}
-
-bool isHttpInformationalStatus(S32 status)
-{
- // Check for status 1xx.
- return((100 <= status) && (status < 200));
-}
-
-bool isHttpGoodStatus(S32 status)
-{
- // Check for status 2xx.
- return((200 <= status) && (status < 300));
-}
-
-bool isHttpRedirectStatus(S32 status)
-{
- // Check for status 3xx.
- return((300 <= status) && (status < 400));
-}
-
-bool isHttpClientErrorStatus(S32 status)
-{
- // Status 499 is sometimes used for re-interpreted status 2xx errors
- // based on body content. Treat these as potentially retryable 'server' status errors,
- // since we do not have enough context to know if this will always fail.
- if (HTTP_INTERNAL_ERROR == status) return false;
-
- // Check for status 5xx.
- return((400 <= status) && (status < 500));
-}
-
-bool isHttpServerErrorStatus(S32 status)
-{
- // Status 499 is sometimes used for re-interpreted status 2xx errors.
- // Allow retry of these, since we don't have enough information in this
- // context to know if this will always fail.
- if (HTTP_INTERNAL_ERROR == status) return true;
-
- // Check for status 5xx.
- return((500 <= status) && (status < 600));
-}
-
-// Parses 'Retry-After' header contents and returns seconds until retry should occur.
-bool getSecondsUntilRetryAfter(const std::string& retry_after, F32& seconds_to_wait)
-{
- // *TODO: This needs testing! Not in use yet.
- // Examples of Retry-After headers:
- // Retry-After: Fri, 31 Dec 1999 23:59:59 GMT
- // Retry-After: 120
-
- // Check for number of seconds version, first:
- char* end = 0;
- // Parse as double
- double seconds = std::strtod(retry_after.c_str(), &end);
- if ( end != 0 && *end == 0 )
- {
- // Successful parse
- seconds_to_wait = (F32) seconds;
- return true;
- }
-
- // Parse rfc1123 date.
- time_t date = curl_getdate(retry_after.c_str(), NULL );
- if (-1 == date) return false;
-
- seconds_to_wait = (F64)date - LLTimer::getTotalSeconds();
-
- return true;
-}
-
diff --git a/indra/llmessage/llhttpconstants.h b/indra/llcorehttp/llhttpconstants.h
old mode 100644
new mode 100755
similarity index 97%
rename from indra/llmessage/llhttpconstants.h
rename to indra/llcorehttp/llhttpconstants.h
index d6bcbd3c19..121448854e
--- a/indra/llmessage/llhttpconstants.h
+++ b/indra/llcorehttp/llhttpconstants.h
@@ -116,13 +116,6 @@ enum EHTTPMethod
HTTP_METHOD_COUNT
};
-const std::string& httpMethodAsVerb(EHTTPMethod method);
-bool isHttpInformationalStatus(S32 status);
-bool isHttpGoodStatus(S32 status);
-bool isHttpRedirectStatus(S32 status);
-bool isHttpClientErrorStatus(S32 status);
-bool isHttpServerErrorStatus(S32 status);
-
// Parses 'Retry-After' header contents and returns seconds until retry should occur.
bool getSecondsUntilRetryAfter(const std::string& retry_after, F32& seconds_to_wait);
diff --git a/indra/llcorehttp/tests/llcorehttp_test.cpp b/indra/llcorehttp/tests/llcorehttp_test.cpp
index e863ddd13f..bef762f5ce 100644
--- a/indra/llcorehttp/tests/llcorehttp_test.cpp
+++ b/indra/llcorehttp/tests/llcorehttp_test.cpp
@@ -160,7 +160,7 @@ void stop_thread(LLCore::HttpRequest * req)
{
if (req)
{
- req->requestStopThread(NULL);
+ req->requestStopThread(LLCore::HttpHandler::ptr_t());
int count = 0;
int limit = 10;
diff --git a/indra/llcorehttp/tests/test_httpheaders.hpp b/indra/llcorehttp/tests/test_httpheaders.hpp
index 668c36dc66..c05f1d9429 100644
--- a/indra/llcorehttp/tests/test_httpheaders.hpp
+++ b/indra/llcorehttp/tests/test_httpheaders.hpp
@@ -59,13 +59,12 @@ void HttpHeadersTestObjectType::test<1>()
mMemTotal = GetMemTotal();
// create a new ref counted object with an implicit reference
- HttpHeaders * headers = new HttpHeaders();
- ensure("One ref on construction of HttpHeaders", headers->getRefCount() == 1);
+ HttpHeaders::ptr_t headers = HttpHeaders::ptr_t(new HttpHeaders());
ensure("Memory being used", mMemTotal < GetMemTotal());
ensure("Nothing in headers", 0 == headers->size());
// release the implicit reference, causing the object to be released
- headers->release();
+ headers.reset();
// make sure we didn't leak any memory
ensure(mMemTotal == GetMemTotal());
@@ -80,7 +79,7 @@ void HttpHeadersTestObjectType::test<2>()
mMemTotal = GetMemTotal();
// create a new ref counted object with an implicit reference
- HttpHeaders * headers = new HttpHeaders();
+ HttpHeaders::ptr_t headers = HttpHeaders::ptr_t(new HttpHeaders());
{
// Append a few strings
@@ -101,7 +100,7 @@ void HttpHeadersTestObjectType::test<2>()
}
// release the implicit reference, causing the object to be released
- headers->release();
+ headers.reset();
// make sure we didn't leak any memory
ensure(mMemTotal == GetMemTotal());
@@ -116,7 +115,7 @@ void HttpHeadersTestObjectType::test<3>()
mMemTotal = GetMemTotal();
// create a new ref counted object with an implicit reference
- HttpHeaders * headers = new HttpHeaders();
+ HttpHeaders::ptr_t headers = HttpHeaders::ptr_t(new HttpHeaders());
{
// Append a few strings
@@ -151,7 +150,7 @@ void HttpHeadersTestObjectType::test<3>()
}
// release the implicit reference, causing the object to be released
- headers->release();
+ headers.reset();
// make sure we didn't leak any memory
ensure(mMemTotal == GetMemTotal());
@@ -166,8 +165,8 @@ void HttpHeadersTestObjectType::test<4>()
mMemTotal = GetMemTotal();
// create a new ref counted object with an implicit reference
- HttpHeaders * headers = new HttpHeaders();
-
+ HttpHeaders::ptr_t headers = HttpHeaders::ptr_t(new HttpHeaders());
+
{
static char line1[] = " AcCePT : image/yourfacehere";
static char line1v[] = "image/yourfacehere";
@@ -251,7 +250,7 @@ void HttpHeadersTestObjectType::test<4>()
}
// release the implicit reference, causing the object to be released
- headers->release();
+ headers.reset();
// make sure we didn't leak any memory
ensure(mMemTotal == GetMemTotal());
@@ -267,7 +266,7 @@ void HttpHeadersTestObjectType::test<5>()
mMemTotal = GetMemTotal();
// create a new ref counted object with an implicit reference
- HttpHeaders * headers = new HttpHeaders();
+ HttpHeaders::ptr_t headers = HttpHeaders::ptr_t(new HttpHeaders());
HttpHeaders::iterator end(headers->end()), begin(headers->begin());
ensure("Empty container has equal begin/end const iterators", end == begin);
@@ -337,7 +336,7 @@ void HttpHeadersTestObjectType::test<5>()
}
// release the implicit reference, causing the object to be released
- headers->release();
+ headers.reset();
// make sure we didn't leak any memory
ensure(mMemTotal == GetMemTotal());
@@ -353,7 +352,7 @@ void HttpHeadersTestObjectType::test<6>()
mMemTotal = GetMemTotal();
// create a new ref counted object with an implicit reference
- HttpHeaders * headers = new HttpHeaders();
+ HttpHeaders::ptr_t headers = HttpHeaders::ptr_t(new HttpHeaders());
HttpHeaders::reverse_iterator rend(headers->rend()), rbegin(headers->rbegin());
ensure("Empty container has equal rbegin/rend const iterators", rend == rbegin);
@@ -421,7 +420,7 @@ void HttpHeadersTestObjectType::test<6>()
}
// release the implicit reference, causing the object to be released
- headers->release();
+ headers.reset();
// make sure we didn't leak any memory
ensure(mMemTotal == GetMemTotal());
diff --git a/indra/llcorehttp/tests/test_httpoperation.hpp b/indra/llcorehttp/tests/test_httpoperation.hpp
index 17b1a96878..e7df2337de 100644
--- a/indra/llcorehttp/tests/test_httpoperation.hpp
+++ b/indra/llcorehttp/tests/test_httpoperation.hpp
@@ -76,12 +76,12 @@ namespace tut
mMemTotal = GetMemTotal();
// create a new ref counted object with an implicit reference
- HttpOpNull * op = new HttpOpNull();
- ensure(op->getRefCount() == 1);
+ HttpOperation::ptr_t op (new HttpOpNull());
+ ensure(op.use_count() == 1);
ensure(mMemTotal < GetMemTotal());
// release the implicit reference, causing the object to be released
- op->release();
+ op.reset();
// make sure we didn't leak any memory
ensure(mMemTotal == GetMemTotal());
@@ -96,26 +96,24 @@ namespace tut
mMemTotal = GetMemTotal();
// Get some handlers
- TestHandler * h1 = new TestHandler();
+ LLCore::HttpHandler::ptr_t h1 (new TestHandler());
// create a new ref counted object with an implicit reference
- HttpOpNull * op = new HttpOpNull();
+ HttpOperation::ptr_t op (new HttpOpNull());
// Add the handlers
- op->setReplyPath(NULL, h1);
+ op->setReplyPath(LLCore::HttpOperation::HttpReplyQueuePtr_t(), h1);
// Check ref count
- ensure(op->getRefCount() == 1);
+ ensure(op.unique() == 1);
// release the reference, releasing the operation but
// not the handlers.
- op->release();
- op = NULL;
+ op.reset();
ensure(mMemTotal != GetMemTotal());
// release the handlers
- delete h1;
- h1 = NULL;
+ h1.reset();
ensure(mMemTotal == GetMemTotal());
}
diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp
index 43f7e36da5..463e55dd7e 100644
--- a/indra/llcorehttp/tests/test_httprequest.hpp
+++ b/indra/llcorehttp/tests/test_httprequest.hpp
@@ -112,7 +112,7 @@ public:
if (! mHeadersRequired.empty() || ! mHeadersDisallowed.empty())
{
ensure("Response required with header check", response != NULL);
- HttpHeaders * header(response->getHeaders()); // Will not hold onto this
+ HttpHeaders::ptr_t header(response->getHeaders()); // Will not hold onto this
ensure("Some quantity of headers returned", header != NULL);
if (! mHeadersRequired.empty())
@@ -247,7 +247,7 @@ void HttpRequestTestObjectType::test<2>()
ensure("Memory being used", mMemTotal < GetMemTotal());
// Issue a NoOp
- HttpHandle handle = req->requestNoOp(NULL);
+ HttpHandle handle = req->requestNoOp(LLCore::HttpHandler::ptr_t());
ensure("Request issued", handle != LLCORE_HTTP_HANDLE_INVALID);
// release the request object
@@ -275,6 +275,10 @@ void HttpRequestTestObjectType::test<2>()
}
}
+namespace
+{
+ void NoOpDeletor(LLCore::HttpHandler *) { }
+}
template <> template <>
void HttpRequestTestObjectType::test<3>()
@@ -287,7 +291,8 @@ void HttpRequestTestObjectType::test<3>()
// references to it after completion of this method.
// Create before memory record as the string copy will bump numbers.
TestHandler2 handler(this, "handler");
-
+ LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor);
+
// record the total amount of dynamically allocated memory
mMemTotal = GetMemTotal();
mHandlerCalls = 0;
@@ -296,9 +301,8 @@ void HttpRequestTestObjectType::test<3>()
try
{
-
// Get singletons created
- HttpRequest::createService();
+ HttpRequest::createService();
// Start threading early so that thread memory is invariant
// over the test.
@@ -309,7 +313,7 @@ void HttpRequestTestObjectType::test<3>()
ensure("Memory allocated on construction", mMemTotal < GetMemTotal());
// Issue a NoOp
- HttpHandle handle = req->requestNoOp(&handler);
+ HttpHandle handle = req->requestNoOp(handlerp);
ensure("Valid handle returned for first request", handle != LLCORE_HTTP_HANDLE_INVALID);
// Run the notification pump.
@@ -324,7 +328,7 @@ void HttpRequestTestObjectType::test<3>()
ensure("One handler invocation for request", mHandlerCalls == 1);
// Okay, request a shutdown of the servicing thread
- handle = req->requestStopThread(&handler);
+ handle = req->requestStopThread(handlerp);
ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID);
// Run the notification pump again
@@ -378,7 +382,10 @@ void HttpRequestTestObjectType::test<4>()
// references to it after completion of this method.
TestHandler2 handler1(this, "handler1");
TestHandler2 handler2(this, "handler2");
-
+
+ LLCore::HttpHandler::ptr_t handler1p(&handler1, NoOpDeletor);
+ LLCore::HttpHandler::ptr_t handler2p(&handler2, NoOpDeletor);
+
// record the total amount of dynamically allocated memory
mMemTotal = GetMemTotal();
mHandlerCalls = 0;
@@ -388,7 +395,7 @@ void HttpRequestTestObjectType::test<4>()
try
{
-
+
// Get singletons created
HttpRequest::createService();
@@ -402,11 +409,11 @@ void HttpRequestTestObjectType::test<4>()
ensure("Memory allocated on construction", mMemTotal < GetMemTotal());
// Issue some NoOps
- HttpHandle handle = req1->requestNoOp(&handler1);
+ HttpHandle handle = req1->requestNoOp(handler1p);
ensure("Valid handle returned for first request", handle != LLCORE_HTTP_HANDLE_INVALID);
handler1.mExpectHandle = handle;
- handle = req2->requestNoOp(&handler2);
+ handle = req2->requestNoOp(handler2p);
ensure("Valid handle returned for first request", handle != LLCORE_HTTP_HANDLE_INVALID);
handler2.mExpectHandle = handle;
@@ -423,7 +430,7 @@ void HttpRequestTestObjectType::test<4>()
ensure("One handler invocation for request", mHandlerCalls == 2);
// Okay, request a shutdown of the servicing thread
- handle = req2->requestStopThread(&handler2);
+ handle = req2->requestStopThread(handler2p);
ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID);
handler2.mExpectHandle = handle;
@@ -482,7 +489,8 @@ void HttpRequestTestObjectType::test<5>()
// references to it after completion of this method.
// Create before memory record as the string copy will bump numbers.
TestHandler2 handler(this, "handler");
-
+ LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor);
+
// record the total amount of dynamically allocated memory
mMemTotal = GetMemTotal();
mHandlerCalls = 0;
@@ -491,7 +499,6 @@ void HttpRequestTestObjectType::test<5>()
try
{
-
// Get singletons created
HttpRequest::createService();
@@ -508,7 +515,7 @@ void HttpRequestTestObjectType::test<5>()
ensure("Valid handle returned for spin request", handle != LLCORE_HTTP_HANDLE_INVALID);
// Issue a NoOp
- handle = req->requestNoOp(&handler);
+ handle = req->requestNoOp(handlerp);
ensure("Valid handle returned for no-op request", handle != LLCORE_HTTP_HANDLE_INVALID);
// Run the notification pump.
@@ -567,7 +574,8 @@ void HttpRequestTestObjectType::test<6>()
try
{
-
+ LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor);
+
// Get singletons created
HttpRequest::createService();
@@ -584,7 +592,7 @@ void HttpRequestTestObjectType::test<6>()
ensure("Valid handle returned for spin request", handle != LLCORE_HTTP_HANDLE_INVALID);
// Issue a NoOp
- handle = req->requestNoOp(&handler);
+ handle = req->requestNoOp(handlerp);
ensure("Valid handle returned for no-op request", handle != LLCORE_HTTP_HANDLE_INVALID);
// Run the notification pump.
@@ -632,17 +640,19 @@ void HttpRequestTestObjectType::test<7>()
// references to it after completion of this method.
// Create before memory record as the string copy will bump numbers.
TestHandler2 handler(this, "handler");
-
+
+ LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor);
+
// record the total amount of dynamically allocated memory
mMemTotal = GetMemTotal();
mHandlerCalls = 0;
HttpRequest * req = NULL;
- HttpOptions * opts = NULL;
+ HttpOptions::ptr_t opts;
try
{
- // Get singletons created
+ // Get singletons created
HttpRequest::createService();
// Start threading early so that thread memory is invariant
@@ -653,7 +663,7 @@ void HttpRequestTestObjectType::test<7>()
req = new HttpRequest();
ensure("Memory allocated on construction", mMemTotal < GetMemTotal());
- opts = new HttpOptions();
+ opts = HttpOptions::ptr_t(new HttpOptions());
opts->setRetries(1); // Don't try for too long - default retries take about 18S
// Issue a GET that can't connect
@@ -664,8 +674,8 @@ void HttpRequestTestObjectType::test<7>()
0,
0,
opts,
- NULL,
- &handler);
+ HttpHeaders::ptr_t(),
+ handlerp);
ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID);
// Run the notification pump.
@@ -681,7 +691,7 @@ void HttpRequestTestObjectType::test<7>()
// Okay, request a shutdown of the servicing thread
mStatus = HttpStatus();
- handle = req->requestStopThread(&handler);
+ handle = req->requestStopThread(handlerp);
ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID);
// Run the notification pump again
@@ -705,8 +715,7 @@ void HttpRequestTestObjectType::test<7>()
ensure("Thread actually stopped running", HttpService::isStopped());
// release options
- opts->release();
- opts = NULL;
+ opts.reset();
// release the request object
delete req;
@@ -728,11 +737,7 @@ void HttpRequestTestObjectType::test<7>()
catch (...)
{
stop_thread(req);
- if (opts)
- {
- opts->release();
- opts = NULL;
- }
+ opts.reset();
delete req;
HttpRequest::destroyService();
throw;
@@ -754,7 +759,8 @@ void HttpRequestTestObjectType::test<8>()
// references to it after completion of this method.
// Create before memory record as the string copy will bump numbers.
TestHandler2 handler(this, "handler");
-
+ LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor);
+
// record the total amount of dynamically allocated memory
mMemTotal = GetMemTotal();
mHandlerCalls = 0;
@@ -763,7 +769,7 @@ void HttpRequestTestObjectType::test<8>()
try
{
- // Get singletons created
+ // Get singletons created
HttpRequest::createService();
// Start threading early so that thread memory is invariant
@@ -779,9 +785,9 @@ void HttpRequestTestObjectType::test<8>()
HttpHandle handle = req->requestGet(HttpRequest::DEFAULT_POLICY_ID,
0U,
url_base,
- NULL,
- NULL,
- &handler);
+ HttpOptions::ptr_t(),
+ HttpHeaders::ptr_t(),
+ handlerp);
ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID);
// Run the notification pump.
@@ -797,7 +803,7 @@ void HttpRequestTestObjectType::test<8>()
// Okay, request a shutdown of the servicing thread
mStatus = HttpStatus();
- handle = req->requestStopThread(&handler);
+ handle = req->requestStopThread(handlerp);
ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID);
// Run the notification pump again
@@ -862,7 +868,8 @@ void HttpRequestTestObjectType::test<9>()
// references to it after completion of this method.
// Create before memory record as the string copy will bump numbers.
TestHandler2 handler(this, "handler");
-
+ LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor);
+
// record the total amount of dynamically allocated memory
mMemTotal = GetMemTotal();
mHandlerCalls = 0;
@@ -871,7 +878,7 @@ void HttpRequestTestObjectType::test<9>()
try
{
- // Get singletons created
+ // Get singletons created
HttpRequest::createService();
// Start threading early so that thread memory is invariant
@@ -889,9 +896,9 @@ void HttpRequestTestObjectType::test<9>()
url_base,
0,
0,
- NULL,
- NULL,
- &handler);
+ HttpOptions::ptr_t(),
+ HttpHeaders::ptr_t(),
+ handlerp);
ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID);
// Run the notification pump.
@@ -907,7 +914,7 @@ void HttpRequestTestObjectType::test<9>()
// Okay, request a shutdown of the servicing thread
mStatus = HttpStatus();
- handle = req->requestStopThread(&handler);
+ handle = req->requestStopThread(handlerp);
ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID);
// Run the notification pump again
@@ -972,7 +979,8 @@ void HttpRequestTestObjectType::test<10>()
// references to it after completion of this method.
// Create before memory record as the string copy will bump numbers.
TestHandler2 handler(this, "handler");
-
+ LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor);
+
// record the total amount of dynamically allocated memory
mMemTotal = GetMemTotal();
mHandlerCalls = 0;
@@ -982,7 +990,7 @@ void HttpRequestTestObjectType::test<10>()
try
{
- // Get singletons created
+ // Get singletons created
HttpRequest::createService();
// Start threading early so that thread memory is invariant
@@ -1001,9 +1009,9 @@ void HttpRequestTestObjectType::test<10>()
0U,
url_base,
body,
- NULL,
- NULL,
- &handler);
+ HttpOptions::ptr_t(),
+ HttpHeaders::ptr_t(),
+ handlerp);
ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID);
// Run the notification pump.
@@ -1019,7 +1027,7 @@ void HttpRequestTestObjectType::test<10>()
// Okay, request a shutdown of the servicing thread
mStatus = HttpStatus();
- handle = req->requestStopThread(&handler);
+ handle = req->requestStopThread(handlerp);
ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID);
// Run the notification pump again
@@ -1090,7 +1098,8 @@ void HttpRequestTestObjectType::test<11>()
// references to it after completion of this method.
// Create before memory record as the string copy will bump numbers.
TestHandler2 handler(this, "handler");
-
+ LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor);
+
// record the total amount of dynamically allocated memory
mMemTotal = GetMemTotal();
mHandlerCalls = 0;
@@ -1100,7 +1109,7 @@ void HttpRequestTestObjectType::test<11>()
try
{
- // Get singletons created
+ // Get singletons created
HttpRequest::createService();
// Start threading early so that thread memory is invariant
@@ -1119,9 +1128,9 @@ void HttpRequestTestObjectType::test<11>()
0U,
url_base,
body,
- NULL,
- NULL,
- &handler);
+ HttpOptions::ptr_t(),
+ HttpHeaders::ptr_t(),
+ handlerp);
ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID);
// Run the notification pump.
@@ -1137,7 +1146,7 @@ void HttpRequestTestObjectType::test<11>()
// Okay, request a shutdown of the servicing thread
mStatus = HttpStatus();
- handle = req->requestStopThread(&handler);
+ handle = req->requestStopThread(handlerp);
ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID);
// Run the notification pump again
@@ -1209,7 +1218,8 @@ void HttpRequestTestObjectType::test<12>()
// references to it after completion of this method.
// Create before memory record as the string copy will bump numbers.
TestHandler2 handler(this, "handler");
-
+ LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor);
+
// record the total amount of dynamically allocated memory
mMemTotal = GetMemTotal();
mHandlerCalls = 0;
@@ -1218,7 +1228,7 @@ void HttpRequestTestObjectType::test<12>()
try
{
- // Get singletons created
+ // Get singletons created
HttpRequest::createService();
// Enable tracing
@@ -1239,9 +1249,9 @@ void HttpRequestTestObjectType::test<12>()
url_base,
0,
0,
- NULL,
- NULL,
- &handler);
+ HttpOptions::ptr_t(),
+ HttpHeaders::ptr_t(),
+ handlerp);
ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID);
// Run the notification pump.
@@ -1257,7 +1267,7 @@ void HttpRequestTestObjectType::test<12>()
// Okay, request a shutdown of the servicing thread
mStatus = HttpStatus();
- handle = req->requestStopThread(&handler);
+ handle = req->requestStopThread(handlerp);
ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID);
// Run the notification pump again
@@ -1326,17 +1336,18 @@ void HttpRequestTestObjectType::test<13>()
// Create before memory record as the string copy will bump numbers.
TestHandler2 handler(this, "handler");
handler.mHeadersRequired.reserve(20); // Avoid memory leak test failure
-
+ LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor);
+
// record the total amount of dynamically allocated memory
mMemTotal = GetMemTotal();
mHandlerCalls = 0;
HttpRequest * req = NULL;
- HttpOptions * opts = NULL;
+ HttpOptions::ptr_t opts;
try
{
- // Get singletons created
+ // Get singletons created
HttpRequest::createService();
// Enable tracing
@@ -1350,7 +1361,7 @@ void HttpRequestTestObjectType::test<13>()
req = new HttpRequest();
ensure("Memory allocated on construction", mMemTotal < GetMemTotal());
- opts = new HttpOptions();
+ opts = HttpOptions::ptr_t(new HttpOptions());
opts->setWantHeaders(true);
// Issue a GET that succeeds
@@ -1364,13 +1375,12 @@ void HttpRequestTestObjectType::test<13>()
0,
0,
opts,
- NULL,
- &handler);
+ HttpHeaders::ptr_t(),
+ handlerp);
ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID);
// release options
- opts->release();
- opts = NULL;
+ opts.reset();
// Run the notification pump.
int count(0);
@@ -1386,7 +1396,7 @@ void HttpRequestTestObjectType::test<13>()
// Okay, request a shutdown of the servicing thread
mStatus = HttpStatus();
handler.mHeadersRequired.clear();
- handle = req->requestStopThread(&handler);
+ handle = req->requestStopThread(handlerp);
ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID);
// Run the notification pump again
@@ -1430,11 +1440,7 @@ void HttpRequestTestObjectType::test<13>()
catch (...)
{
stop_thread(req);
- if (opts)
- {
- opts->release();
- opts = NULL;
- }
+ opts.reset();
delete req;
HttpRequest::destroyService();
throw;
@@ -1453,18 +1459,19 @@ void HttpRequestTestObjectType::test<14>()
// references to it after completion of this method.
// Create before memory record as the string copy will bump numbers.
TestHandler2 handler(this, "handler");
- std::string url_base(get_base_url() + "/sleep/"); // path to a 30-second sleep
+ LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor);
+ std::string url_base(get_base_url() + "/sleep/"); // path to a 30-second sleep
// record the total amount of dynamically allocated memory
mMemTotal = GetMemTotal();
mHandlerCalls = 0;
HttpRequest * req = NULL;
- HttpOptions * opts = NULL;
+ HttpOptions::ptr_t opts;
try
{
- // Get singletons created
+ // Get singletons created
HttpRequest::createService();
// Start threading early so that thread memory is invariant
@@ -1475,7 +1482,7 @@ void HttpRequestTestObjectType::test<14>()
req = new HttpRequest();
ensure("Memory allocated on construction", mMemTotal < GetMemTotal());
- opts = new HttpOptions();
+ opts = HttpOptions::ptr_t(new HttpOptions);
opts->setRetries(0); // Don't retry
opts->setTimeout(2);
@@ -1487,8 +1494,8 @@ void HttpRequestTestObjectType::test<14>()
0,
0,
opts,
- NULL,
- &handler);
+ HttpHeaders::ptr_t(),
+ handlerp);
ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID);
// Run the notification pump.
@@ -1504,7 +1511,7 @@ void HttpRequestTestObjectType::test<14>()
// Okay, request a shutdown of the servicing thread
mStatus = HttpStatus();
- handle = req->requestStopThread(&handler);
+ handle = req->requestStopThread(handlerp);
ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID);
// Run the notification pump again
@@ -1528,8 +1535,7 @@ void HttpRequestTestObjectType::test<14>()
ensure("Thread actually stopped running", HttpService::isStopped());
// release options
- opts->release();
- opts = NULL;
+ opts.reset();
// release the request object
delete req;
@@ -1552,11 +1558,7 @@ void HttpRequestTestObjectType::test<14>()
catch (...)
{
stop_thread(req);
- if (opts)
- {
- opts->release();
- opts = NULL;
- }
+ opts.reset();
delete req;
HttpRequest::destroyService();
throw;
@@ -1578,6 +1580,7 @@ void HttpRequestTestObjectType::test<15>()
// references to it after completion of this method.
// Create before memory record as the string copy will bump numbers.
TestHandler2 handler(this, "handler");
+ LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor);
// Load and clear the string setting to preload std::string object
// for memory return tests.
@@ -1592,7 +1595,7 @@ void HttpRequestTestObjectType::test<15>()
try
{
- // Get singletons created
+ // Get singletons created
HttpRequest::createService();
// Start threading early so that thread memory is invariant
@@ -1609,9 +1612,9 @@ void HttpRequestTestObjectType::test<15>()
HttpHandle handle = req->requestGet(HttpRequest::DEFAULT_POLICY_ID,
0U,
url_base,
- NULL,
- NULL,
- &handler);
+ HttpOptions::ptr_t(),
+ HttpHeaders::ptr_t(),
+ handlerp);
ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID);
// Run the notification pump.
@@ -1628,7 +1631,7 @@ void HttpRequestTestObjectType::test<15>()
// Okay, request a shutdown of the servicing thread
mStatus = HttpStatus();
handler.mCheckContentType.clear();
- handle = req->requestStopThread(&handler);
+ handle = req->requestStopThread(handlerp);
ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID);
// Run the notification pump again
@@ -1697,18 +1700,19 @@ void HttpRequestTestObjectType::test<16>()
// references to it after completion of this method.
// Create before memory record as the string copy will bump numbers.
TestHandler2 handler(this, "handler");
+ LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor);
// record the total amount of dynamically allocated memory
mMemTotal = GetMemTotal();
mHandlerCalls = 0;
HttpRequest * req = NULL;
- HttpOptions * options = NULL;
- HttpHeaders * headers = NULL;
+ HttpOptions::ptr_t options;
+ HttpHeaders::ptr_t headers;
try
{
- // Get singletons created
+ // Get singletons created
HttpRequest::createService();
// Start threading early so that thread memory is invariant
@@ -1719,7 +1723,7 @@ void HttpRequestTestObjectType::test<16>()
req = new HttpRequest();
// options set
- options = new HttpOptions();
+ options = HttpOptions::ptr_t(new HttpOptions());
options->setWantHeaders(true);
// Issue a GET that *can* connect
@@ -1776,8 +1780,8 @@ void HttpRequestTestObjectType::test<16>()
0U,
url_base + "reflect/",
options,
- NULL,
- &handler);
+ HttpHeaders::ptr_t(),
+ handlerp);
ensure("Valid handle returned for get request", handle != LLCORE_HTTP_HANDLE_INVALID);
// Run the notification pump.
@@ -1792,7 +1796,7 @@ void HttpRequestTestObjectType::test<16>()
ensure("One handler invocation for request", mHandlerCalls == 1);
// Do a texture-style fetch
- headers = new HttpHeaders;
+ headers = HttpHeaders::ptr_t(new HttpHeaders);
headers->append("Accept", "image/x-j2c");
mStatus = HttpStatus(200);
@@ -1854,7 +1858,7 @@ void HttpRequestTestObjectType::test<16>()
47,
options,
headers,
- &handler);
+ handlerp);
ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID);
// Run the notification pump.
@@ -1873,7 +1877,7 @@ void HttpRequestTestObjectType::test<16>()
mStatus = HttpStatus();
handler.mHeadersRequired.clear();
handler.mHeadersDisallowed.clear();
- handle = req->requestStopThread(&handler);
+ handle = req->requestStopThread(handlerp);
ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID);
// Run the notification pump again
@@ -1897,17 +1901,8 @@ void HttpRequestTestObjectType::test<16>()
ensure("Thread actually stopped running", HttpService::isStopped());
// release options & headers
- if (options)
- {
- options->release();
- }
- options = NULL;
-
- if (headers)
- {
- headers->release();
- }
- headers = NULL;
+ options.reset();
+ headers.reset();
// release the request object
delete req;
@@ -1919,16 +1914,9 @@ void HttpRequestTestObjectType::test<16>()
catch (...)
{
stop_thread(req);
- if (options)
- {
- options->release();
- options = NULL;
- }
- if (headers)
- {
- headers->release();
- headers = NULL;
- }
+ options.reset();
+ headers.reset();
+
delete req;
HttpRequest::destroyService();
throw;
@@ -1954,19 +1942,20 @@ void HttpRequestTestObjectType::test<17>()
// references to it after completion of this method.
// Create before memory record as the string copy will bump numbers.
TestHandler2 handler(this, "handler");
+ LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor);
// record the total amount of dynamically allocated memory
mMemTotal = GetMemTotal();
mHandlerCalls = 0;
HttpRequest * req = NULL;
- HttpOptions * options = NULL;
- HttpHeaders * headers = NULL;
+ HttpOptions::ptr_t options;
+ HttpHeaders::ptr_t headers;
BufferArray * ba = NULL;
try
{
- // Get singletons created
+ // Get singletons created
HttpRequest::createService();
// Start threading early so that thread memory is invariant
@@ -1977,7 +1966,7 @@ void HttpRequestTestObjectType::test<17>()
req = new HttpRequest();
// options set
- options = new HttpOptions();
+ options = HttpOptions::ptr_t(new HttpOptions());
options->setWantHeaders(true);
// And a buffer array
@@ -2049,8 +2038,8 @@ void HttpRequestTestObjectType::test<17>()
url_base + "reflect/",
ba,
options,
- NULL,
- &handler);
+ HttpHeaders::ptr_t(),
+ handlerp);
ensure("Valid handle returned for get request", handle != LLCORE_HTTP_HANDLE_INVALID);
ba->release();
ba = NULL;
@@ -2071,7 +2060,7 @@ void HttpRequestTestObjectType::test<17>()
mStatus = HttpStatus();
handler.mHeadersRequired.clear();
handler.mHeadersDisallowed.clear();
- handle = req->requestStopThread(&handler);
+ handle = req->requestStopThread(handlerp);
ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID);
// Run the notification pump again
@@ -2095,17 +2084,8 @@ void HttpRequestTestObjectType::test<17>()
ensure("Thread actually stopped running", HttpService::isStopped());
// release options & headers
- if (options)
- {
- options->release();
- }
- options = NULL;
-
- if (headers)
- {
- headers->release();
- }
- headers = NULL;
+ options.reset();
+ headers.reset();
// release the request object
delete req;
@@ -2122,17 +2102,10 @@ void HttpRequestTestObjectType::test<17>()
ba->release();
ba = NULL;
}
- if (options)
- {
- options->release();
- options = NULL;
- }
- if (headers)
- {
- headers->release();
- headers = NULL;
- }
- delete req;
+ options.reset();
+ headers.reset();
+
+ delete req;
HttpRequest::destroyService();
throw;
}
@@ -2157,19 +2130,20 @@ void HttpRequestTestObjectType::test<18>()
// references to it after completion of this method.
// Create before memory record as the string copy will bump numbers.
TestHandler2 handler(this, "handler");
+ LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor);
// record the total amount of dynamically allocated memory
mMemTotal = GetMemTotal();
mHandlerCalls = 0;
HttpRequest * req = NULL;
- HttpOptions * options = NULL;
- HttpHeaders * headers = NULL;
+ HttpOptions::ptr_t options;
+ HttpHeaders::ptr_t headers;
BufferArray * ba = NULL;
try
{
- // Get singletons created
+ // Get singletons created
HttpRequest::createService();
// Start threading early so that thread memory is invariant
@@ -2180,7 +2154,7 @@ void HttpRequestTestObjectType::test<18>()
req = new HttpRequest();
// options set
- options = new HttpOptions();
+ options = HttpOptions::ptr_t(new HttpOptions());
options->setWantHeaders(true);
// And a buffer array
@@ -2253,8 +2227,8 @@ void HttpRequestTestObjectType::test<18>()
url_base + "reflect/",
ba,
options,
- NULL,
- &handler);
+ HttpHeaders::ptr_t(),
+ handlerp);
ensure("Valid handle returned for get request", handle != LLCORE_HTTP_HANDLE_INVALID);
ba->release();
ba = NULL;
@@ -2275,7 +2249,7 @@ void HttpRequestTestObjectType::test<18>()
mStatus = HttpStatus();
handler.mHeadersRequired.clear();
handler.mHeadersDisallowed.clear();
- handle = req->requestStopThread(&handler);
+ handle = req->requestStopThread(handlerp);
ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID);
// Run the notification pump again
@@ -2299,17 +2273,8 @@ void HttpRequestTestObjectType::test<18>()
ensure("Thread actually stopped running", HttpService::isStopped());
// release options & headers
- if (options)
- {
- options->release();
- }
- options = NULL;
-
- if (headers)
- {
- headers->release();
- }
- headers = NULL;
+ options.reset();
+ headers.reset();
// release the request object
delete req;
@@ -2326,17 +2291,10 @@ void HttpRequestTestObjectType::test<18>()
ba->release();
ba = NULL;
}
- if (options)
- {
- options->release();
- options = NULL;
- }
- if (headers)
- {
- headers->release();
- headers = NULL;
- }
- delete req;
+ options.reset();
+ headers.reset();
+
+ delete req;
HttpRequest::destroyService();
throw;
}
@@ -2361,18 +2319,19 @@ void HttpRequestTestObjectType::test<19>()
// references to it after completion of this method.
// Create before memory record as the string copy will bump numbers.
TestHandler2 handler(this, "handler");
+ LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor);
// record the total amount of dynamically allocated memory
mMemTotal = GetMemTotal();
mHandlerCalls = 0;
HttpRequest * req = NULL;
- HttpOptions * options = NULL;
- HttpHeaders * headers = NULL;
+ HttpOptions::ptr_t options;
+ HttpHeaders::ptr_t headers;
try
{
- // Get singletons created
+ // Get singletons created
HttpRequest::createService();
// Start threading early so that thread memory is invariant
@@ -2383,11 +2342,11 @@ void HttpRequestTestObjectType::test<19>()
req = new HttpRequest();
// options set
- options = new HttpOptions();
+ options = HttpOptions::ptr_t(new HttpOptions());
options->setWantHeaders(true);
// headers
- headers = new HttpHeaders;
+ headers = HttpHeaders::ptr_t(new HttpHeaders);
headers->append("Keep-Alive", "120");
headers->append("Accept-encoding", "deflate");
headers->append("Accept", "text/plain");
@@ -2460,7 +2419,7 @@ void HttpRequestTestObjectType::test<19>()
url_base + "reflect/",
options,
headers,
- &handler);
+ handlerp);
ensure("Valid handle returned for get request", handle != LLCORE_HTTP_HANDLE_INVALID);
// Run the notification pump.
@@ -2478,7 +2437,7 @@ void HttpRequestTestObjectType::test<19>()
mStatus = HttpStatus();
handler.mHeadersRequired.clear();
handler.mHeadersDisallowed.clear();
- handle = req->requestStopThread(&handler);
+ handle = req->requestStopThread(handlerp);
ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID);
// Run the notification pump again
@@ -2502,17 +2461,8 @@ void HttpRequestTestObjectType::test<19>()
ensure("Thread actually stopped running", HttpService::isStopped());
// release options & headers
- if (options)
- {
- options->release();
- }
- options = NULL;
-
- if (headers)
- {
- headers->release();
- }
- headers = NULL;
+ options.reset();
+ headers.reset();
// release the request object
delete req;
@@ -2524,16 +2474,9 @@ void HttpRequestTestObjectType::test<19>()
catch (...)
{
stop_thread(req);
- if (options)
- {
- options->release();
- options = NULL;
- }
- if (headers)
- {
- headers->release();
- headers = NULL;
- }
+ options.reset();
+ headers.reset();
+
delete req;
HttpRequest::destroyService();
throw;
@@ -2559,19 +2502,21 @@ void HttpRequestTestObjectType::test<20>()
// references to it after completion of this method.
// Create before memory record as the string copy will bump numbers.
TestHandler2 handler(this, "handler");
+ LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor);
// record the total amount of dynamically allocated memory
mMemTotal = GetMemTotal();
mHandlerCalls = 0;
HttpRequest * req = NULL;
- HttpOptions * options = NULL;
- HttpHeaders * headers = NULL;
+ HttpOptions::ptr_t options;
+ HttpHeaders::ptr_t headers;
BufferArray * ba = NULL;
try
{
- // Get singletons created
+
+ // Get singletons created
HttpRequest::createService();
// Start threading early so that thread memory is invariant
@@ -2582,11 +2527,11 @@ void HttpRequestTestObjectType::test<20>()
req = new HttpRequest();
// options set
- options = new HttpOptions();
+ options = HttpOptions::ptr_t(new HttpOptions());
options->setWantHeaders(true);
// headers
- headers = new HttpHeaders();
+ headers = HttpHeaders::ptr_t(new HttpHeaders());
headers->append("keep-Alive", "120");
headers->append("Accept", "text/html");
headers->append("content-type", "application/llsd+xml");
@@ -2675,7 +2620,7 @@ void HttpRequestTestObjectType::test<20>()
ba,
options,
headers,
- &handler);
+ handlerp);
ensure("Valid handle returned for get request", handle != LLCORE_HTTP_HANDLE_INVALID);
ba->release();
ba = NULL;
@@ -2696,7 +2641,7 @@ void HttpRequestTestObjectType::test<20>()
mStatus = HttpStatus();
handler.mHeadersRequired.clear();
handler.mHeadersDisallowed.clear();
- handle = req->requestStopThread(&handler);
+ handle = req->requestStopThread(handlerp);
ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID);
// Run the notification pump again
@@ -2720,17 +2665,8 @@ void HttpRequestTestObjectType::test<20>()
ensure("Thread actually stopped running", HttpService::isStopped());
// release options & headers
- if (options)
- {
- options->release();
- }
- options = NULL;
-
- if (headers)
- {
- headers->release();
- }
- headers = NULL;
+ options.reset();
+ headers.reset();
// release the request object
delete req;
@@ -2747,16 +2683,8 @@ void HttpRequestTestObjectType::test<20>()
ba->release();
ba = NULL;
}
- if (options)
- {
- options->release();
- options = NULL;
- }
- if (headers)
- {
- headers->release();
- headers = NULL;
- }
+ options.reset();
+ headers.reset();
delete req;
HttpRequest::destroyService();
throw;
@@ -2782,19 +2710,20 @@ void HttpRequestTestObjectType::test<21>()
// references to it after completion of this method.
// Create before memory record as the string copy will bump numbers.
TestHandler2 handler(this, "handler");
+ LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor);
// record the total amount of dynamically allocated memory
mMemTotal = GetMemTotal();
mHandlerCalls = 0;
HttpRequest * req = NULL;
- HttpOptions * options = NULL;
- HttpHeaders * headers = NULL;
+ HttpOptions::ptr_t options;
+ HttpHeaders::ptr_t headers;
BufferArray * ba = NULL;
try
{
- // Get singletons created
+ // Get singletons created
HttpRequest::createService();
// Start threading early so that thread memory is invariant
@@ -2805,11 +2734,11 @@ void HttpRequestTestObjectType::test<21>()
req = new HttpRequest();
// options set
- options = new HttpOptions();
+ options = HttpOptions::ptr_t(new HttpOptions());
options->setWantHeaders(true);
// headers
- headers = new HttpHeaders;
+ headers = HttpHeaders::ptr_t(new HttpHeaders);
headers->append("content-type", "text/plain");
headers->append("content-type", "text/html");
headers->append("content-type", "application/llsd+xml");
@@ -2892,7 +2821,7 @@ void HttpRequestTestObjectType::test<21>()
ba,
options,
headers,
- &handler);
+ handlerp);
ensure("Valid handle returned for get request", handle != LLCORE_HTTP_HANDLE_INVALID);
ba->release();
ba = NULL;
@@ -2913,7 +2842,7 @@ void HttpRequestTestObjectType::test<21>()
mStatus = HttpStatus();
handler.mHeadersRequired.clear();
handler.mHeadersDisallowed.clear();
- handle = req->requestStopThread(&handler);
+ handle = req->requestStopThread(handlerp);
ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID);
// Run the notification pump again
@@ -2937,17 +2866,8 @@ void HttpRequestTestObjectType::test<21>()
ensure("Thread actually stopped running", HttpService::isStopped());
// release options & headers
- if (options)
- {
- options->release();
- }
- options = NULL;
-
- if (headers)
- {
- headers->release();
- }
- headers = NULL;
+ options.reset();
+ headers.reset();
// release the request object
delete req;
@@ -2964,16 +2884,8 @@ void HttpRequestTestObjectType::test<21>()
ba->release();
ba = NULL;
}
- if (options)
- {
- options->release();
- options = NULL;
- }
- if (headers)
- {
- headers->release();
- headers = NULL;
- }
+ options.reset();
+ headers.reset();
delete req;
HttpRequest::destroyService();
throw;
@@ -2995,18 +2907,19 @@ void HttpRequestTestObjectType::test<22>()
// references to it after completion of this method.
// Create before memory record as the string copy will bump numbers.
TestHandler2 handler(this, "handler");
-
+ LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor);
+
// record the total amount of dynamically allocated memory
mMemTotal = GetMemTotal();
mHandlerCalls = 0;
- HttpOptions * options = NULL;
+ HttpOptions::ptr_t options;
HttpRequest * req = NULL;
try
{
- // options set
- options = new HttpOptions();
+ // options set
+ options = HttpOptions::ptr_t(new HttpOptions());
options->setRetries(1); // Partial_File is retryable and can timeout in here
// Get singletons created
@@ -3035,8 +2948,8 @@ void HttpRequestTestObjectType::test<22>()
0,
25,
options,
- NULL,
- &handler);
+ HttpHeaders::ptr_t(),
+ handlerp);
ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID);
}
@@ -3067,8 +2980,8 @@ void HttpRequestTestObjectType::test<22>()
0,
25,
options,
- NULL,
- &handler);
+ HttpHeaders::ptr_t(),
+ handlerp);
ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID);
}
@@ -3099,8 +3012,8 @@ void HttpRequestTestObjectType::test<22>()
0,
25,
options,
- NULL,
- &handler);
+ HttpHeaders::ptr_t(),
+ handlerp);
ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID);
}
@@ -3120,7 +3033,7 @@ void HttpRequestTestObjectType::test<22>()
// ======================================
mStatus = HttpStatus();
mHandlerCalls = 0;
- HttpHandle handle = req->requestStopThread(&handler);
+ HttpHandle handle = req->requestStopThread(handlerp);
ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID);
// Run the notification pump again
@@ -3144,11 +3057,7 @@ void HttpRequestTestObjectType::test<22>()
ensure("Thread actually stopped running", HttpService::isStopped());
// release options
- if (options)
- {
- options->release();
- options = NULL;
- }
+ options.reset();
// release the request object
delete req;
@@ -3191,18 +3100,19 @@ void HttpRequestTestObjectType::test<23>()
// references to it after completion of this method.
// Create before memory record as the string copy will bump numbers.
TestHandler2 handler(this, "handler");
- std::string url_base(get_base_url() + "/503/"); // path to 503 generators
+ LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor);
+ std::string url_base(get_base_url() + "/503/"); // path to 503 generators
// record the total amount of dynamically allocated memory
mMemTotal = GetMemTotal();
mHandlerCalls = 0;
HttpRequest * req = NULL;
- HttpOptions * opts = NULL;
+ HttpOptions::ptr_t opts;
try
{
- // Get singletons created
+ // Get singletons created
HttpRequest::createService();
// Start threading early so that thread memory is invariant
@@ -3213,7 +3123,7 @@ void HttpRequestTestObjectType::test<23>()
req = new HttpRequest();
ensure("Memory allocated on construction", mMemTotal < GetMemTotal());
- opts = new HttpOptions();
+ opts = HttpOptions::ptr_t(new HttpOptions());
opts->setRetries(1); // Retry once only
opts->setUseRetryAfter(true); // Try to parse the retry-after header
@@ -3230,8 +3140,8 @@ void HttpRequestTestObjectType::test<23>()
0,
0,
opts,
- NULL,
- &handler);
+ HttpHeaders::ptr_t(),
+ handlerp);
std::ostringstream testtag;
testtag << "Valid handle returned for 503 request #" << i;
@@ -3253,7 +3163,7 @@ void HttpRequestTestObjectType::test<23>()
// Okay, request a shutdown of the servicing thread
mStatus = HttpStatus();
mHandlerCalls = 0;
- HttpHandle handle = req->requestStopThread(&handler);
+ HttpHandle handle = req->requestStopThread(handlerp);
ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID);
// Run the notification pump again
@@ -3277,8 +3187,7 @@ void HttpRequestTestObjectType::test<23>()
ensure("Thread actually stopped running", HttpService::isStopped());
// release options
- opts->release();
- opts = NULL;
+ opts.reset();
// release the request object
delete req;
@@ -3299,11 +3208,7 @@ void HttpRequestTestObjectType::test<23>()
catch (...)
{
stop_thread(req);
- if (opts)
- {
- opts->release();
- opts = NULL;
- }
+ opts.reset();
delete req;
HttpRequest::destroyService();
throw;
diff --git a/indra/llcorehttp/tests/test_httprequestqueue.hpp b/indra/llcorehttp/tests/test_httprequestqueue.hpp
index 1de2d8f9ab..ef4ce0479b 100644
--- a/indra/llcorehttp/tests/test_httprequestqueue.hpp
+++ b/indra/llcorehttp/tests/test_httprequestqueue.hpp
@@ -113,16 +113,16 @@ void HttpRequestqueueTestObjectType::test<3>()
HttpRequestQueue * rq = HttpRequestQueue::instanceOf();
- HttpOperation * op = new HttpOpNull();
+ HttpOperation::ptr_t op(new HttpOpNull());
rq->addOp(op); // transfer my refcount
op = rq->fetchOp(true); // Potentially hangs the test on failure
- ensure("One goes in, one comes out", NULL != op);
- op->release();
+ ensure("One goes in, one comes out", static_cast(op));
+ op.reset();
op = rq->fetchOp(false);
- ensure("Better not be two of them", NULL == op);
+ ensure("Better not be two of them", !op);
// release the singleton, hold on to the object
HttpRequestQueue::term();
@@ -144,13 +144,13 @@ void HttpRequestqueueTestObjectType::test<4>()
HttpRequestQueue * rq = HttpRequestQueue::instanceOf();
- HttpOperation * op = new HttpOpNull();
+ HttpOperation::ptr_t op (new HttpOpNull());
rq->addOp(op); // transfer my refcount
- op = new HttpOpNull();
+ op.reset(new HttpOpNull());
rq->addOp(op); // transfer my refcount
- op = new HttpOpNull();
+ op.reset(new HttpOpNull());
rq->addOp(op); // transfer my refcount
{
@@ -159,8 +159,9 @@ void HttpRequestqueueTestObjectType::test<4>()
ensure("Three go in, three come out", 3 == ops.size());
op = rq->fetchOp(false);
- ensure("Better not be any more of them", NULL == op);
-
+ ensure("Better not be any more of them", !op);
+ op.reset();
+
// release the singleton, hold on to the object
HttpRequestQueue::term();
@@ -168,12 +169,13 @@ void HttpRequestqueueTestObjectType::test<4>()
ensure(mMemTotal < GetMemTotal());
// Release them
- while (! ops.empty())
- {
- HttpOperation * op = ops.front();
- ops.erase(ops.begin());
- op->release();
- }
+ ops.clear();
+// while (! ops.empty())
+// {
+// HttpOperation * op = ops.front();
+// ops.erase(ops.begin());
+// op->release();
+// }
}
// Should be clean
diff --git a/indra/llcorehttp/tests/test_httpstatus.hpp b/indra/llcorehttp/tests/test_httpstatus.hpp
index 0b379836c9..4502d32fe1 100644
--- a/indra/llcorehttp/tests/test_httpstatus.hpp
+++ b/indra/llcorehttp/tests/test_httpstatus.hpp
@@ -55,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");
diff --git a/indra/llcrashlogger/CMakeLists.txt b/indra/llcrashlogger/CMakeLists.txt
index ba4e34d92b..da23b46b7b 100644
--- a/indra/llcrashlogger/CMakeLists.txt
+++ b/indra/llcrashlogger/CMakeLists.txt
@@ -3,6 +3,7 @@
project(llcrashlogger)
include(00-Common)
+include(LLCoreHttp)
include(LLCommon)
include(LLMath)
include(LLMessage)
@@ -10,6 +11,7 @@ include(LLVFS)
include(LLXML)
include_directories(
+ ${LLCOREHTTP_INCLUDE_DIRS}
${LLCOMMON_INCLUDE_DIRS}
${LLMATH_INCLUDE_DIRS}
${LLMESSAGE_INCLUDE_DIRS}
diff --git a/indra/llcrashlogger/llcrashlogger.cpp b/indra/llcrashlogger/llcrashlogger.cpp
index 7a97c16ea7..bc9cbc9cf1 100644
--- a/indra/llcrashlogger/llcrashlogger.cpp
+++ b/indra/llcrashlogger/llcrashlogger.cpp
@@ -40,38 +40,45 @@
#include "lldir.h"
#include "llfile.h"
#include "llsdserialize.h"
-#include "lliopipe.h"
-#include "llpumpio.h"
-#include "llhttpclient.h"
#include "llsdserialize.h"
#include "llproxy.h"
-
-LLPumpIO* gServicePump = NULL;
+#include "llcorehttputil.h"
+#include "llhttpsdhandler.h"
+#include "httpcommon.h"
+#include "httpresponse.h"
+
+#include
+#include
+
BOOL gBreak = false;
BOOL gSent = false;
-class LLCrashLoggerResponder : public LLHTTPClient::Responder
+int LLCrashLogger::ssl_mutex_count = 0;
+LLCoreInt::HttpMutex ** LLCrashLogger::ssl_mutex_list = NULL;
+
+class LLCrashLoggerHandler : public LLHttpSDHandler
{
- LOG_CLASS(LLCrashLoggerResponder);
+ LOG_CLASS(LLCrashLoggerHandler);
public:
- LLCrashLoggerResponder()
- {
- }
+ LLCrashLoggerHandler() {}
protected:
- virtual void httpFailure()
- {
- LL_WARNS() << dumpResponse() << LL_ENDL;
- gBreak = true;
- }
+ virtual void onSuccess(LLCore::HttpResponse * response, const LLSD &content);
+ virtual void onFailure(LLCore::HttpResponse * response, LLCore::HttpStatus status);
- virtual void httpSuccess()
- {
- gBreak = true;
- gSent = true;
- }
};
+void LLCrashLoggerHandler::onSuccess(LLCore::HttpResponse * response, const LLSD &content)
+{
+ gBreak = true;
+ gSent = true;
+}
+
+void LLCrashLoggerHandler::onFailure(LLCore::HttpResponse * response, LLCore::HttpStatus status)
+{
+ gBreak = true;
+}
+
LLCrashLogger::LLCrashLogger() :
mCrashBehavior(CRASH_BEHAVIOR_ALWAYS_SEND),
mCrashInPreviousExec(false),
@@ -207,11 +214,13 @@ void LLCrashLogger::gatherFiles()
mFileMap["SettingsXml"] = mDebugLog["SettingsFilename"].asString();
if(mDebugLog.has("CAFilename"))
{
- LLCurl::setCAFile(mDebugLog["CAFilename"].asString());
+ LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_CA_FILE,
+ LLCore::HttpRequest::GLOBAL_POLICY_ID, mDebugLog["CAFilename"].asString(), NULL);
}
else
{
- LLCurl::setCAFile(gDirUtilp->getCAFile());
+ LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_CA_FILE,
+ LLCore::HttpRequest::GLOBAL_POLICY_ID, gDirUtilp->getCAFile(), NULL);
}
LL_INFOS() << "Using log file from debug log " << mFileMap["SecondLifeLog"] << LL_ENDL;
@@ -220,7 +229,8 @@ void LLCrashLogger::gatherFiles()
else
{
// Figure out the filename of the second life log
- LLCurl::setCAFile(gDirUtilp->getCAFile());
+ LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_CA_FILE,
+ LLCore::HttpRequest::GLOBAL_POLICY_ID, gDirUtilp->getCAFile(), NULL);
mFileMap["SecondLifeLog"] = gDirUtilp->getExpandedFilename(LL_PATH_DUMP,"SecondLife.log");
mFileMap["SettingsXml"] = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS,"settings.xml");
@@ -389,19 +399,38 @@ bool LLCrashLogger::saveCrashBehaviorSetting(S32 crash_behavior)
bool LLCrashLogger::runCrashLogPost(std::string host, LLSD data, std::string msg, int retries, int timeout)
{
+ LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
+ LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions);
+
gBreak = false;
+ httpOpts->setTimeout(timeout);
+
for(int i = 0; i < retries; ++i)
{
updateApplication(llformat("%s, try %d...", msg.c_str(), i+1));
- LLHTTPClient::post(host, data, new LLCrashLoggerResponder(), timeout);
- while(!gBreak)
+
+ LLCore::HttpHandle handle = LLCoreHttpUtil::requestPostWithLLSD(httpRequest.get(), LLCore::HttpRequest::DEFAULT_POLICY_ID, 0,
+ host, data, httpOpts, LLCore::HttpHeaders::ptr_t(), LLCore::HttpHandler::ptr_t(new LLCrashLoggerHandler));
+
+ if (handle == LLCORE_HTTP_HANDLE_INVALID)
+ {
+ LLCore::HttpStatus status = httpRequest->getStatus();
+ LL_WARNS("CRASHREPORT") << "Request POST failed to " << host << " with status of [" <<
+ status.getType() << "]\"" << status.toString() << "\"" << LL_ENDL;
+ return false;
+ }
+
+ while(!gBreak)
{
updateApplication(); // No new message, just pump the IO
+ httpRequest->update(0L);
}
if(gSent)
{
return gSent;
}
+
+ LL_WARNS("CRASHREPORT") << "Failed to send crash report to \"" << host << "\"" << LL_ENDL;
}
return gSent;
}
@@ -510,14 +539,12 @@ bool LLCrashLogger::sendCrashLogs()
void LLCrashLogger::updateApplication(const std::string& message)
{
- gServicePump->pump();
- gServicePump->callback();
if (!message.empty()) LL_INFOS() << message << LL_ENDL;
}
bool LLCrashLogger::init()
{
- LLCurl::initClass(false);
+ LLCore::LLHttp::initialize();
// We assume that all the logs we're looking for reside on the current drive
gDirUtilp->initAppDirs("SecondLife");
@@ -576,16 +603,74 @@ bool LLCrashLogger::init()
return false;
}
- gServicePump = new LLPumpIO(gAPRPoolp);
- gServicePump->prime(gAPRPoolp);
- LLHTTPClient::setPump(*gServicePump);
-
+ init_curl();
+ LLCore::HttpRequest::createService();
+ LLCore::HttpRequest::startThread();
+
return true;
}
// For cleanup code common to all platforms.
void LLCrashLogger::commonCleanup()
{
+ term_curl();
LLError::logToFile(""); //close crashreport.log
LLProxy::cleanupClass();
}
+
+void LLCrashLogger::init_curl()
+{
+ curl_global_init(CURL_GLOBAL_ALL);
+
+ ssl_mutex_count = CRYPTO_num_locks();
+ if (ssl_mutex_count > 0)
+ {
+ ssl_mutex_list = new LLCoreInt::HttpMutex *[ssl_mutex_count];
+
+ for (int i(0); i < ssl_mutex_count; ++i)
+ {
+ ssl_mutex_list[i] = new LLCoreInt::HttpMutex;
+ }
+
+ CRYPTO_set_locking_callback(ssl_locking_callback);
+ CRYPTO_set_id_callback(ssl_thread_id_callback);
+ }
+}
+
+
+void LLCrashLogger::term_curl()
+{
+ CRYPTO_set_locking_callback(NULL);
+ for (int i(0); i < ssl_mutex_count; ++i)
+ {
+ delete ssl_mutex_list[i];
+ }
+ delete[] ssl_mutex_list;
+}
+
+
+unsigned long LLCrashLogger::ssl_thread_id_callback(void)
+{
+#if LL_WINDOWS
+ return (unsigned long)GetCurrentThread();
+#else
+ return (unsigned long)pthread_self();
+#endif
+}
+
+
+void LLCrashLogger::ssl_locking_callback(int mode, int type, const char * /* file */, int /* line */)
+{
+ if (type >= 0 && type < ssl_mutex_count)
+ {
+ if (mode & CRYPTO_LOCK)
+ {
+ ssl_mutex_list[type]->lock();
+ }
+ else
+ {
+ ssl_mutex_list[type]->unlock();
+ }
+ }
+}
+
diff --git a/indra/llcrashlogger/llcrashlogger.h b/indra/llcrashlogger/llcrashlogger.h
index a06bf1d6ac..f5383daefc 100644
--- a/indra/llcrashlogger/llcrashlogger.h
+++ b/indra/llcrashlogger/llcrashlogger.h
@@ -34,6 +34,7 @@
#include "llsd.h"
#include "llcontrol.h"
#include "llcrashlock.h"
+#include "_mutex.h"
// Crash reporter behavior
const S32 CRASH_BEHAVIOR_ASK = 0;
@@ -66,6 +67,11 @@ public:
bool readMinidump(std::string minidump_path);
protected:
+ static void init_curl();
+ static void term_curl();
+ static unsigned long ssl_thread_id_callback(void);
+ static void ssl_locking_callback(int mode, int type, const char * file, int line);
+
S32 mCrashBehavior;
BOOL mCrashInPreviousExec;
std::map mFileMap;
@@ -78,6 +84,10 @@ protected:
LLSD mDebugLog;
bool mSentCrashLogs;
LLCrashLock mKeyMaster;
+
+ static int ssl_mutex_count;
+ static LLCoreInt::HttpMutex ** ssl_mutex_list;
+
};
#endif //LLCRASHLOGGER_H
diff --git a/indra/llinventory/CMakeLists.txt b/indra/llinventory/CMakeLists.txt
index 0a1f93bd80..68dd00d880 100644
--- a/indra/llinventory/CMakeLists.txt
+++ b/indra/llinventory/CMakeLists.txt
@@ -4,6 +4,7 @@ project(llinventory)
include(00-Common)
include(LLCommon)
+include(LLCoreHttp)
include(LLMath)
include(LLMessage)
include(LLVFS)
@@ -71,7 +72,7 @@ if (LL_TESTS)
LL_ADD_PROJECT_UNIT_TESTS(llinventory "${llinventory_TEST_SOURCE_FILES}")
#set(TEST_DEBUG on)
- set(test_libs llinventory ${LLMESSAGE_LIBRARIES} ${LLVFS_LIBRARIES} ${LLMATH_LIBRARIES} ${LLCOMMON_LIBRARIES} ${WINDOWS_LIBRARIES})
+ set(test_libs llinventory ${LLMESSAGE_LIBRARIES} ${LLVFS_LIBRARIES} ${LLCOREHTTP_LIBRARIES} ${LLMATH_LIBRARIES} ${LLCOMMON_LIBRARIES} ${WINDOWS_LIBRARIES})
LL_ADD_INTEGRATION_TEST(inventorymisc "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llparcel "" "${test_libs}")
endif (LL_TESTS)
diff --git a/indra/llkdu/llimagej2ckdu.cpp b/indra/llkdu/llimagej2ckdu.cpp
index 6a8959517d..282c859e9e 100644
--- a/indra/llkdu/llimagej2ckdu.cpp
+++ b/indra/llkdu/llimagej2ckdu.cpp
@@ -1034,7 +1034,7 @@ all necessary level shifting, type conversion, rounding and truncation. */
val = (kdu_int32)(sp->fval*scale16);
val = (val+128)>>8; // May be faster than true rounding
val += 128;
- if (val & ((-1)<<8))
+ if (val & ((0xffffffffU)<<8))
{
val = (val < 0 ? 0 : 255);
}
@@ -1052,7 +1052,7 @@ all necessary level shifting, type conversion, rounding and truncation. */
val = sp->ival;
val = (val+offset)>>downshift;
val += 128;
- if (val & ((-1)<<8))
+ if (val & ((0xffffffffU)<<8))
{
val = (val < 0 ? 0 : 255);
}
@@ -1075,7 +1075,7 @@ all necessary level shifting, type conversion, rounding and truncation. */
val += (1<<(KDU_FIX_POINT-8))>>1;
val >>= (KDU_FIX_POINT-8);
val += 128;
- if (val & ((-1)<<8))
+ if (val & ((0xffffffffU)<<8))
{
val = (val < 0 ? 0 : 255);
}
@@ -1094,7 +1094,7 @@ all necessary level shifting, type conversion, rounding and truncation. */
val = (val+offset)>>downshift;
val <<= upshift;
val += 128;
- if (val & ((-1)<<8))
+ if (val & ((0xffffffffU)<<8))
{
val = (val < 0 ? 0 : 256 - (1<ival;
val = (val+offset)>>downshift;
val += 128;
- if (val & ((-1)<<8))
+ if (val & ((0xffffffffU)<<8))
{
val = (val < 0 ? 0 : 255);
}
@@ -1132,7 +1132,7 @@ all necessary level shifting, type conversion, rounding and truncation. */
val = sp->ival;
val <<= upshift;
val += 128;
- if (val & ((-1)<<8))
+ if (val & ((0xffffffffU)<<8))
{
val = (val < 0 ? 0 : 256 - (1<
-#include
-
-#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
-#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 *resp =
- (LLPointer *) 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(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