SH-3563. Pull and merge from viewer-development. Modest code changes to fix alignment issue in llAppearance.
commit
e0432f98ee
59
.hgtags
59
.hgtags
|
|
@ -69,6 +69,7 @@ b53a0576eec80614d7767ed72b40ed67aeff27c9 DRTVWR-38_2.5.2-release
|
|||
4e9eec6a347f89b2b3f295beb72f1cf7837dff66 2.6.0-start
|
||||
9283d6d1d7eb71dfe4c330e7c9144857e7356bde 2.6.0-beta1
|
||||
9283d6d1d7eb71dfe4c330e7c9144857e7356bde DRTVWR-40_2.6.0-beta1
|
||||
461c8c65b5c799ddfe365422f9be9c0095d91e7d 2.6.0-beta1-tip
|
||||
9e4641f4a7870c0f565a25a2971368d5a29516a1 2.6.0-beta2
|
||||
9e4641f4a7870c0f565a25a2971368d5a29516a1 DRTVWR-41_2.6.0-beta2
|
||||
42f32494bac475d0737799346f6831558ae8bf5d 2.6.0-release
|
||||
|
|
@ -274,7 +275,6 @@ cbea6356ce9cb0c313b6777f10c5c14783264fcc DRTVWR-174
|
|||
eb539c65e6ee26eea2bf373af2d0f4b52dc91289 DRTVWR-177
|
||||
a8057e1b9a1246b434a27405be35e030f7d28b0c 3.3.4-beta3
|
||||
888768f162d2c0a8de1dcc5fb9a08bd8bd120a6b DRTVWR-175
|
||||
4ad8a3afe40e0200309e3ada68932c4295ac2795 DRTVWR-179
|
||||
4281aa899fb2cedb7a9ca7ce91c5c29d4aa69594 DRTVWR-180
|
||||
5c08e1d8edd871807153603b690e3ee9dbb548aa DRTVWR-183
|
||||
6c75f220b103db1420919c8b635fe53e2177f318 3.3.4-beta4
|
||||
|
|
@ -311,3 +311,60 @@ de3be913f68813a9bac7d1c671fef96d1159bcd6 DRTVWR-202
|
|||
7b22c612fc756e0ea63b10b163e81d107f85dbf8 DRTVWR-206
|
||||
8c9085066c78ed5f6c9379dc054c82a6fcdb1851 DRTVWR-207
|
||||
351eea5f9dc192fc5ddea3b02958de97677a0a12 3.3.4-release3
|
||||
af7b28e75bd5a629cd9e0dc46fb3f1757626f493 DRTVWR-212
|
||||
015012c2b740ccdec8a8c3d6e5f898449ecfe0b8 DRTVWR-213
|
||||
62b07aa81b1957897c3846292bb9412977b0af6c 3.3.4-beta6
|
||||
ceed0b65a69f1eac20d523e0203320a32f9a3f3c DRTVWR-215
|
||||
733ceac77583874f3626ef7a15c105b83ef0f5bb 3.4.0-beta7
|
||||
97977c67245f52db20eb15f1918cc0f24778cabc 3.4.0-release
|
||||
5adb2b8f96c3cac88ad7c7d996d707f1b29df336 3.4.1-beta1
|
||||
b3f74858a1c8720c82d0978f3877a3fc8ba459ec 3.4.1-beta1a
|
||||
b61afe175b829c149d369524a4e974dfda99facf DRTVWR-219
|
||||
2b779f233ee6f38c89cb921650c773a96e63da92 DRTVWR-220
|
||||
0b9d95f4bfb6867cbf56eaec51633b0da2f1262d DRTVWR-221
|
||||
e6e553761829dc0270eaaa712b7cb0622535b076 3.4.1-beta3
|
||||
f00068a66a2e2f72acbe3f690b98b323e740b289 DRTVWR-222
|
||||
305950187c628a5d6743ee9ea711cc5b9177a18e 3.4.1-beta4
|
||||
dd23d4da3bcb2ffda58569e759feb7c119982973 DRTVWR-224
|
||||
0bd3744ff060452aa13ff4992eafb381df7b1012 3.4.1-beta5
|
||||
29075f8c1abed53dcf195a59f61744e27a91108f DRTVWR-226
|
||||
fba99f381b8d4ad1b7b42fa4993b29998d95be18 DRTVWR-179
|
||||
49ed253c80bed7410e238eeab35a9f14cb034364 3.4.1-beta6
|
||||
468ca3268229011a59df99229b24315844b33d34 DRTVWR-227
|
||||
524da902713e8b60322640b9825101add4a7c497 3.4.1-beta7
|
||||
173c2809f9873499c4b9d6bc044ec941c954d3fb DRTVWR-228
|
||||
1dc94555582f52718834081e7659e973ae4521f7 3.4.1-beta8
|
||||
52c164c8023a5e65f3dc1b0bbb7fa1dd0c631b6b DRTVWR-231
|
||||
464cf7a63a9a2f95bc4972dc022ca765e93de7d3 DRTVWR-233
|
||||
637fe8bbee5e24940448198c221d5ee0fa3247b4 3.4.1-beta9
|
||||
4e0d84e92132e9e95a1d52a1e49bad69c278ea05 3.4.1-beta10
|
||||
f7cbd60a3f57ff1101157eeb79ea21e8898bedae DRTVWR-235
|
||||
baf97f06ae17223614c5e31aa42e71d87cff07fe DRTVWR-236
|
||||
18498afcdb835d6fc4d36ed935347d3b65307bad 3.4.1-beta11
|
||||
b2f21e3442542283a80e7eaebae9f833e5a927b6 DRTVWR-237
|
||||
3f9be82de642d468c5fc272cb9d96b46b5498402 3.4.1-beta12
|
||||
e59ffd3fe0838ae6b09b242a6e9df71761b88f41 3.4.1-release
|
||||
32896d5e920ca9a29256ff3b747c2e99752aa5ae DRTVWR-217
|
||||
704bbae7b182a1f2811a47a054e680522966f54a 3.4.2-beta1
|
||||
d799593b53ed733862e9a13871e318e886469377 DRTVWR-208
|
||||
e497dcde7a3653e384eb223a8a460030e89c294c DRTVWR-223
|
||||
288539fc0408ed4b69a99665de33bbbc2c3c08fe DRTVWR-216
|
||||
e664473c16df1d82ffaff382e7b3e023da202d52 3.4.2-beta2
|
||||
93ab02d83f51e30a3cabad98aff89601befd9413 DRTVWR-240
|
||||
0891d7a773a31397dcad48be3fa66531d567a821 DRTVWR-242
|
||||
710785535362b3cb801b6a3dc4703be3373bd0cd 3.4.2-beta3
|
||||
2aa72e3372a83dece4df9cf72fb1e7c34f90b5e3 DRTVWR-209
|
||||
f7bedce18ad52283e6072814db23318907261487 DRTVWR-238
|
||||
7b64c96fbcadf360bd2feaae19d330166b70877c DRTVWR-210
|
||||
e9a5886052433d5db9e504ffaca10890f9932979 DRTVWR-243
|
||||
73b84b9864dc650fe7c8fc9f52361450f0849004 3.4.2-beta4
|
||||
16310aabccf315870f7cc9bf966926c0ad6954fa 3.4.2-release
|
||||
5e4e4128b256525bafc07a62e35ae8527aaa9c9d DRTVWR-241
|
||||
f1d3b3fcab28ed9ea532bf50db0ba96f5c8cc8e9 DRTVWR-232
|
||||
4918b150e75df6b516fb6c2616d32043fa6b4cac DRTVWR-245
|
||||
94ab2b49458ab372a95d2d6949fdf574f413068d 3.4.3-beta1
|
||||
4c3460cb1fb7c6da9965e09c734d282a8e9c81f0 DRTVWR-229
|
||||
f4481df42f9a4a92bf475a80f0c51d1a4bbdfd59 DRTVWR-246
|
||||
39c5204b6e800983a41ccac8ad6dc993120197c6 DRTVWR-247
|
||||
7c7d57d393e8ae7b61623279de06eb4a62ccae6a DRTVWR-249
|
||||
f72b50ef168c159d6e79e97aa2bcafaf8577ab99 DRTVWR-230
|
||||
|
|
|
|||
21
BuildParams
21
BuildParams
|
|
@ -3,6 +3,7 @@
|
|||
# Please refer to:
|
||||
# https://wiki.secondlife.com/wiki/Automated_Build_System
|
||||
|
||||
|
||||
# Global setting for now...
|
||||
Darwin.symbolfiles = "newview/Release/secondlife-symbols-darwin.tar.bz2"
|
||||
CYGWIN.symbolfiles = "newview/Release/secondlife-symbols-windows.tar.bz2"
|
||||
|
|
@ -25,8 +26,13 @@ codeticket_since = 3.3.0-release
|
|||
Linux.gcc_version = /usr/bin/gcc-4.6
|
||||
Linux.cxx_version = /usr/bin/g++-4.6
|
||||
|
||||
clean_on_success = false
|
||||
run_tests = false
|
||||
build_Darwin_Debug = false
|
||||
build_Darwin_RelWithDebInfo = false
|
||||
|
||||
# ========================================
|
||||
# Viewer Development
|
||||
# Viewer Development --
|
||||
# ========================================
|
||||
|
||||
# Report changes since...
|
||||
|
|
@ -63,6 +69,7 @@ viewer-release.build_debug_release_separately = true
|
|||
viewer-release.build_viewer_update_version_manager = true
|
||||
viewer-release.codeticket_add_context = false
|
||||
|
||||
|
||||
# ========================================
|
||||
# mesh-development
|
||||
# ========================================
|
||||
|
|
@ -119,17 +126,6 @@ viewer-mesh.login_channel = "Project Viewer - Mesh"
|
|||
viewer-mesh.viewer_grid = aditi
|
||||
viewer-mesh.email = shining@lists.lindenlab.com
|
||||
|
||||
# ========================================
|
||||
# viewer-adult-check
|
||||
# ========================================
|
||||
|
||||
viewer-adult-check.viewer_channel = "Project Viewer - AdultCheck"
|
||||
viewer-adult-check.login_channel = "Project Viewer - AdultCheck"
|
||||
viewer-adult-check.viewer_grid = agni
|
||||
viewer-adult-check.build_debug_release_separately = true
|
||||
viewer-adult-check.build_CYGWIN_Debug = false
|
||||
viewer-adult-check.build_viewer_update_version_manager = false
|
||||
|
||||
# ========================================
|
||||
# viewer-pathfinding
|
||||
# ========================================
|
||||
|
|
@ -202,4 +198,5 @@ runway.build_debug_release_separately = true
|
|||
runway.build_CYGWIN_Debug = false
|
||||
runway.build_viewer_update_version_manager = false
|
||||
|
||||
|
||||
# eof
|
||||
|
|
|
|||
|
|
@ -186,9 +186,9 @@
|
|||
<key>archive</key>
|
||||
<map>
|
||||
<key>hash</key>
|
||||
<string>d98078791ce345bf6168ce9ba53ca2d7</string>
|
||||
<string>610d3c3790b39d44b45ed1e471f7b34d</string>
|
||||
<key>url</key>
|
||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-boost/rev/222752/arch/Darwin/installer/boost-1.45.0-darwin-20110304.tar.bz2</string>
|
||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-boost/rev/267115/arch/Darwin/installer/boost-1.52.0-darwin-20121117.tar.bz2</string>
|
||||
</map>
|
||||
<key>name</key>
|
||||
<string>darwin</string>
|
||||
|
|
@ -198,9 +198,9 @@
|
|||
<key>archive</key>
|
||||
<map>
|
||||
<key>hash</key>
|
||||
<string>a34e7fffdb94a6a4d8a2966b1f216da3</string>
|
||||
<string>86b9108ab71bd6551365d1706e2fb178</string>
|
||||
<key>url</key>
|
||||
<string>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/boost-1.45.0-linux-20110310.tar.bz2</string>
|
||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-boost/rev/267115/arch/Linux/installer/boost-1.52.0-linux-20121117.tar.bz2</string>
|
||||
</map>
|
||||
<key>name</key>
|
||||
<string>linux</string>
|
||||
|
|
@ -210,9 +210,9 @@
|
|||
<key>archive</key>
|
||||
<map>
|
||||
<key>hash</key>
|
||||
<string>98be22c8833aa2bca184b9fa09fbb82b</string>
|
||||
<string>a3bf306ebd14036a93f7894f898a862c</string>
|
||||
<key>url</key>
|
||||
<string>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/boost-1.45.0-windows-20110124.tar.bz2</string>
|
||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-boost/rev/267115/arch/CYGWIN/installer/boost-1.52.0-windows-20121117.tar.bz2</string>
|
||||
</map>
|
||||
<key>name</key>
|
||||
<string>windows</string>
|
||||
|
|
|
|||
2
build.sh
2
build.sh
|
|
@ -78,7 +78,7 @@ pre_build()
|
|||
|
||||
check_for "After 'autobuild configure'" ${build_dir}/packages/dictionaries
|
||||
|
||||
end_section "Pre$variant"
|
||||
end_section "Pre$variant"
|
||||
}
|
||||
|
||||
package_llphysicsextensions_tpv()
|
||||
|
|
|
|||
|
|
@ -890,6 +890,8 @@ Nicholaz Beresford
|
|||
VWR-2682
|
||||
VWR-2684
|
||||
Nick Rhodes
|
||||
Nicky Dasmijn
|
||||
VWR-29228
|
||||
Nicky Perian
|
||||
OPEN-1
|
||||
STORM-1087
|
||||
|
|
@ -1122,6 +1124,7 @@ TankMaster Finesmith
|
|||
STORM-1602
|
||||
STORM-1868
|
||||
VWR-26622
|
||||
VWR-29224
|
||||
Talamasca
|
||||
Tali Rosca
|
||||
Tayra Dagostino
|
||||
|
|
@ -1268,6 +1271,8 @@ Whoops Babii
|
|||
Winter Ventura
|
||||
Wilton Lundquist
|
||||
VWR-7682
|
||||
Wolf Loonie
|
||||
STORM-1868
|
||||
WolfPup Lowenhar
|
||||
OPEN-1
|
||||
OPEN-37
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ add_subdirectory(${LIBS_OPEN_PREFIX}llaudio)
|
|||
add_subdirectory(${LIBS_OPEN_PREFIX}llappearance)
|
||||
add_subdirectory(${LIBS_OPEN_PREFIX}llcharacter)
|
||||
add_subdirectory(${LIBS_OPEN_PREFIX}llcommon)
|
||||
add_subdirectory(${LIBS_OPEN_PREFIX}llcorehttp)
|
||||
add_subdirectory(${LIBS_OPEN_PREFIX}llimage)
|
||||
add_subdirectory(${LIBS_OPEN_PREFIX}llkdu)
|
||||
add_subdirectory(${LIBS_OPEN_PREFIX}llimagej2coj)
|
||||
|
|
|
|||
|
|
@ -183,7 +183,7 @@ if (DARWIN)
|
|||
add_definitions(-DLL_DARWIN=1 -D_XOPEN_SOURCE)
|
||||
set(CMAKE_CXX_LINK_FLAGS "-Wl,-headerpad_max_install_names,-search_paths_first")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_CXX_LINK_FLAGS}")
|
||||
set(DARWIN_extra_cstar_flags "-mlong-branch")
|
||||
set(DARWIN_extra_cstar_flags "-mlong-branch -g")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${DARWIN_extra_cstar_flags}")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${DARWIN_extra_cstar_flags}")
|
||||
# NOTE: it's critical that the optimization flag is put in front.
|
||||
|
|
|
|||
|
|
@ -12,12 +12,13 @@ if (STANDALONE)
|
|||
set(BOOST_SIGNALS_LIBRARY boost_signals-mt)
|
||||
set(BOOST_SYSTEM_LIBRARY boost_system-mt)
|
||||
set(BOOST_FILESYSTEM_LIBRARY boost_filesystem-mt)
|
||||
set(BOOST_THREAD_LIBRARY boost_thread-mt)
|
||||
else (STANDALONE)
|
||||
use_prebuilt_binary(boost)
|
||||
set(Boost_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/include)
|
||||
set(BOOST_VERSION "1.52")
|
||||
|
||||
if (WINDOWS)
|
||||
set(BOOST_VERSION 1_45)
|
||||
if(MSVC80)
|
||||
set(BOOST_PROGRAM_OPTIONS_LIBRARY
|
||||
optimized libboost_program_options-vc80-mt-${BOOST_VERSION}
|
||||
|
|
@ -37,22 +38,55 @@ else (STANDALONE)
|
|||
else(MSVC80)
|
||||
# MSVC 10.0 config
|
||||
set(BOOST_PROGRAM_OPTIONS_LIBRARY
|
||||
optimized libboost_program_options-vc100-mt-${BOOST_VERSION}
|
||||
debug libboost_program_options-vc100-mt-gd-${BOOST_VERSION})
|
||||
optimized libboost_program_options-mt
|
||||
debug libboost_program_options-mt-gd)
|
||||
set(BOOST_REGEX_LIBRARY
|
||||
optimized libboost_regex-vc100-mt-${BOOST_VERSION}
|
||||
debug libboost_regex-vc100-mt-gd-${BOOST_VERSION})
|
||||
optimized libboost_regex-mt
|
||||
debug libboost_regex-mt-gd)
|
||||
set(BOOST_SYSTEM_LIBRARY
|
||||
optimized libboost_system-vc100-mt-${BOOST_VERSION}
|
||||
debug libboost_system-vc100-mt-gd-${BOOST_VERSION})
|
||||
optimized libboost_system-mt
|
||||
debug libboost_system-mt-gd)
|
||||
set(BOOST_FILESYSTEM_LIBRARY
|
||||
optimized libboost_filesystem-vc100-mt-${BOOST_VERSION}
|
||||
debug libboost_filesystem-vc100-mt-gd-${BOOST_VERSION})
|
||||
optimized libboost_filesystem-mt
|
||||
debug libboost_filesystem-mt-gd)
|
||||
set(BOOST_THREAD_LIBRARY
|
||||
optimized libboost_thread-mt
|
||||
debug libboost_thread-mt-gd)
|
||||
endif (MSVC80)
|
||||
elseif (DARWIN OR LINUX)
|
||||
set(BOOST_PROGRAM_OPTIONS_LIBRARY boost_program_options)
|
||||
set(BOOST_REGEX_LIBRARY boost_regex)
|
||||
set(BOOST_SYSTEM_LIBRARY boost_system)
|
||||
set(BOOST_FILESYSTEM_LIBRARY boost_filesystem)
|
||||
elseif (LINUX)
|
||||
set(BOOST_PROGRAM_OPTIONS_LIBRARY
|
||||
optimized boost_program_options-mt
|
||||
debug boost_program_options-mt-d)
|
||||
set(BOOST_REGEX_LIBRARY
|
||||
optimized boost_regex-mt
|
||||
debug boost_regex-mt-d)
|
||||
set(BOOST_SYSTEM_LIBRARY
|
||||
optimized boost_system-mt
|
||||
debug boost_system-mt-d)
|
||||
set(BOOST_FILESYSTEM_LIBRARY
|
||||
optimized boost_filesystem-mt
|
||||
debug boost_filesystem-mt-d)
|
||||
set(BOOST_THREAD_LIBRARY
|
||||
optimized boost_thread-mt
|
||||
debug boost_thread-mt-d)
|
||||
elseif (DARWIN)
|
||||
set(BOOST_PROGRAM_OPTIONS_LIBRARY
|
||||
optimized boost_program_options-mt
|
||||
debug boost_program_options-mt-d)
|
||||
set(BOOST_PROGRAM_OPTIONS_LIBRARY
|
||||
optimized boost_program_options-mt
|
||||
debug boost_program_options-mt-d)
|
||||
set(BOOST_REGEX_LIBRARY
|
||||
optimized boost_regex-mt
|
||||
debug boost_regex-mt-d)
|
||||
set(BOOST_SYSTEM_LIBRARY
|
||||
optimized boost_system-mt
|
||||
debug boost_system-mt-d)
|
||||
set(BOOST_FILESYSTEM_LIBRARY
|
||||
optimized boost_filesystem-mt
|
||||
debug boost_filesystem-mt-d)
|
||||
set(BOOST_THREAD_LIBRARY
|
||||
optimized boost_thread-mt
|
||||
debug boost_thread-mt-d)
|
||||
endif (WINDOWS)
|
||||
endif (STANDALONE)
|
||||
|
|
|
|||
|
|
@ -254,6 +254,12 @@ elseif(LINUX)
|
|||
libapr-1.so.0
|
||||
libaprutil-1.so.0
|
||||
libatk-1.0.so
|
||||
libboost_program_options-mt.so.${BOOST_VERSION}.0
|
||||
libboost_regex-mt.so.${BOOST_VERSION}.0
|
||||
libboost_thread-mt.so.${BOOST_VERSION}.0
|
||||
libboost_filesystem-mt.so.${BOOST_VERSION}.0
|
||||
libboost_signals-mt.so.${BOOST_VERSION}.0
|
||||
libboost_system-mt.so.${BOOST_VERSION}.0
|
||||
libbreakpad_client.so.0
|
||||
libcollada14dom.so
|
||||
libcrypto.so.1.0.0
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ include(Prebuilt)
|
|||
|
||||
# If you want to enable or disable TCMALLOC in viewer builds, this is the place.
|
||||
# set ON or OFF as desired.
|
||||
set (USE_TCMALLOC ON)
|
||||
set (USE_TCMALLOC OFF)
|
||||
|
||||
if (STANDALONE)
|
||||
include(FindGooglePerfTools)
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ endif (LINUX)
|
|||
|
||||
add_definitions(${TCMALLOC_FLAG})
|
||||
|
||||
set(LLCOMMON_LINK_SHARED OFF CACHE BOOL "Build the llcommon target as a shared library.")
|
||||
set(LLCOMMON_LINK_SHARED OFF CACHE BOOL "Build the llcommon target as a static library.")
|
||||
if(LLCOMMON_LINK_SHARED)
|
||||
add_definitions(-DLL_COMMON_LINK_SHARED=1)
|
||||
endif(LLCOMMON_LINK_SHARED)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
# -*- 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)
|
||||
|
|
@ -15,10 +15,10 @@ if (WINDOWS)
|
|||
optimized llprimitive
|
||||
debug libcollada14dom22-d
|
||||
optimized libcollada14dom22
|
||||
debug libboost_filesystem-vc100-mt-gd-1_45
|
||||
optimized libboost_filesystem-vc100-mt-1_45
|
||||
debug libboost_system-vc100-mt-gd-1_45
|
||||
optimized libboost_system-vc100-mt-1_45
|
||||
debug libboost_filesystem-mt-gd
|
||||
optimized libboost_filesystem-mt
|
||||
debug libboost_system-mt-gd
|
||||
optimized libboost_system-mt
|
||||
)
|
||||
else (WINDOWS)
|
||||
set(LLPRIMITIVE_LIBRARIES
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
Wed Nov 7 00:25:19 UTC 2012
|
||||
|
|
@ -240,7 +240,7 @@ void store_input_file(std::list<std::string> &input_filenames, const std::string
|
|||
LLDirIterator iter(dir, name);
|
||||
while (iter.next(next_name))
|
||||
{
|
||||
std::string file_name = dir + gDirUtilp->getDirDelimiter() + next_name;
|
||||
std::string file_name = gDirUtilp->add(dir, next_name);
|
||||
input_filenames.push_back(file_name);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -107,12 +107,6 @@ public:
|
|||
};
|
||||
TestImageProvider gTestImageProvider;
|
||||
|
||||
static std::string get_xui_dir()
|
||||
{
|
||||
std::string delim = gDirUtilp->getDirDelimiter();
|
||||
return gDirUtilp->getSkinBaseDir() + delim + "default" + delim + "xui" + delim;
|
||||
}
|
||||
|
||||
void init_llui()
|
||||
{
|
||||
// Font lookup needs directory support
|
||||
|
|
@ -122,13 +116,12 @@ void init_llui()
|
|||
const char* newview_path = "../../../newview";
|
||||
#endif
|
||||
gDirUtilp->initAppDirs("SecondLife", newview_path);
|
||||
gDirUtilp->setSkinFolder("default");
|
||||
gDirUtilp->setSkinFolder("default", "en");
|
||||
|
||||
// colors are no longer stored in a LLControlGroup file
|
||||
LLUIColorTable::instance().loadFromSettings();
|
||||
|
||||
std::string config_filename = gDirUtilp->getExpandedFilename(
|
||||
LL_PATH_APP_SETTINGS, "settings.xml");
|
||||
std::string config_filename = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "settings.xml");
|
||||
gSavedSettings.loadFromFile(config_filename);
|
||||
|
||||
// See LLAppViewer::init()
|
||||
|
|
@ -143,9 +136,7 @@ void init_llui()
|
|||
|
||||
const bool no_register_widgets = false;
|
||||
LLWidgetReg::initClass( no_register_widgets );
|
||||
|
||||
// Unclear if this is needed
|
||||
LLUI::setupPaths();
|
||||
|
||||
// Otherwise we get translation warnings when setting up floaters
|
||||
// (tooltips for buttons)
|
||||
std::set<std::string> default_args;
|
||||
|
|
@ -157,7 +148,6 @@ void init_llui()
|
|||
// otherwise it crashes.
|
||||
LLFontGL::initClass(96.f, 1.f, 1.f,
|
||||
gDirUtilp->getAppRODataDir(),
|
||||
LLUI::getXUIPaths(),
|
||||
false ); // don't create gl textures
|
||||
|
||||
LLFloaterView::Params fvparams;
|
||||
|
|
@ -169,6 +159,14 @@ void init_llui()
|
|||
gFloaterView = LLUICtrlFactory::create<LLFloaterView> (fvparams);
|
||||
}
|
||||
|
||||
/*==========================================================================*|
|
||||
static std::string get_xui_dir()
|
||||
{
|
||||
std::string delim = gDirUtilp->getDirDelimiter();
|
||||
return gDirUtilp->getSkinBaseDir() + delim + "default" + delim + "xui" + delim;
|
||||
}
|
||||
|
||||
// buildFromFile() no longer supports generate-output-LLXMLNode
|
||||
void export_test_floaters()
|
||||
{
|
||||
// Convert all test floaters to new XML format
|
||||
|
|
@ -191,7 +189,7 @@ void export_test_floaters()
|
|||
floater->buildFromFile( filename,
|
||||
// FALSE, // don't open floater
|
||||
output_node);
|
||||
std::string out_filename = xui_dir + filename;
|
||||
std::string out_filename = gDirUtilp->add(xui_dir, filename);
|
||||
std::string::size_type extension_pos = out_filename.rfind(".xml");
|
||||
out_filename.resize(extension_pos);
|
||||
out_filename += "_new.xml";
|
||||
|
|
@ -203,6 +201,7 @@ void export_test_floaters()
|
|||
fclose(floater_file);
|
||||
}
|
||||
}
|
||||
|*==========================================================================*/
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
|
|
@ -211,7 +210,7 @@ int main(int argc, char** argv)
|
|||
|
||||
init_llui();
|
||||
|
||||
export_test_floaters();
|
||||
// export_test_floaters();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -626,6 +626,23 @@ class LLManifest(object):
|
|||
d = src_re.sub(d_template, s.replace('\\', '/'))
|
||||
yield os.path.normpath(s), os.path.normpath(d)
|
||||
|
||||
def path2basename(self, path, file):
|
||||
"""
|
||||
It is a common idiom to write:
|
||||
self.path(os.path.join(somedir, somefile), somefile)
|
||||
|
||||
So instead you can write:
|
||||
self.path2basename(somedir, somefile)
|
||||
|
||||
Note that this is NOT the same as:
|
||||
self.path(os.path.join(somedir, somefile))
|
||||
|
||||
which is the same as:
|
||||
temppath = os.path.join(somedir, somefile)
|
||||
self.path(temppath, temppath)
|
||||
"""
|
||||
return self.path(os.path.join(path, file), file)
|
||||
|
||||
def path(self, src, dst=None):
|
||||
sys.stdout.write("Processing %s => %s ... " % (src, dst))
|
||||
sys.stdout.flush()
|
||||
|
|
@ -671,6 +688,10 @@ class LLManifest(object):
|
|||
|
||||
print "%d files" % count
|
||||
|
||||
# Let caller check whether we processed as many files as expected. In
|
||||
# particular, let caller notice 0.
|
||||
return count
|
||||
|
||||
def do(self, *actions):
|
||||
self.actions = actions
|
||||
self.construct()
|
||||
|
|
|
|||
|
|
@ -251,7 +251,7 @@ std::string next_image_filename(std::string& image_path, LLDirIterator& iter)
|
|||
{
|
||||
std::string image_filename;
|
||||
iter.next(image_filename);
|
||||
return image_path + "/" + image_filename;
|
||||
return gDirUtilp->add(image_path, image_filename);
|
||||
}
|
||||
|
||||
void on_window_closed(GtkWidget *sender, GdkEvent* event, gpointer data)
|
||||
|
|
|
|||
|
|
@ -85,6 +85,16 @@ public:
|
|||
LLDriverParam(LLAvatarAppearance *appearance, LLWearable* wearable = NULL);
|
||||
~LLDriverParam();
|
||||
|
||||
void* operator new(size_t size)
|
||||
{
|
||||
return ll_aligned_malloc_16(size);
|
||||
}
|
||||
|
||||
void operator delete(void* ptr)
|
||||
{
|
||||
ll_aligned_free_16(ptr);
|
||||
}
|
||||
|
||||
// Special: These functions are overridden by child classes
|
||||
LLDriverParamInfo* getInfo() const { return (LLDriverParamInfo*)mInfo; }
|
||||
// This sets mInfo and calls initialization functions
|
||||
|
|
|
|||
|
|
@ -74,9 +74,9 @@ LLPolyMorphData::LLPolyMorphData(const LLPolyMorphData &rhs) :
|
|||
{
|
||||
const S32 numVertices = mNumIndices;
|
||||
|
||||
mCoords = new LLVector4a[numVertices];
|
||||
mNormals = new LLVector4a[numVertices];
|
||||
mBinormals = new LLVector4a[numVertices];
|
||||
mCoords = static_cast<LLVector4a*>(ll_aligned_malloc_16(numVertices * sizeof(LLVector4a)));
|
||||
mNormals = static_cast<LLVector4a*>(ll_aligned_malloc_16(numVertices * sizeof(LLVector4a)));
|
||||
mBinormals = static_cast<LLVector4a*>(ll_aligned_malloc_16(numVertices * sizeof(LLVector4a)));
|
||||
mTexCoords = new LLVector2[numVertices];
|
||||
mVertexIndices = new U32[numVertices];
|
||||
|
||||
|
|
@ -90,17 +90,12 @@ LLPolyMorphData::LLPolyMorphData(const LLPolyMorphData &rhs) :
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// ~LLPolyMorphData()
|
||||
//-----------------------------------------------------------------------------
|
||||
LLPolyMorphData::~LLPolyMorphData()
|
||||
{
|
||||
delete [] mVertexIndices;
|
||||
delete [] mCoords;
|
||||
delete [] mNormals;
|
||||
delete [] mBinormals;
|
||||
delete [] mTexCoords;
|
||||
freeData();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
@ -119,12 +114,17 @@ BOOL LLPolyMorphData::loadBinary(LLFILE *fp, LLPolyMeshSharedData *mesh)
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// free any existing data
|
||||
//-------------------------------------------------------------------------
|
||||
freeData();
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// allocate vertices
|
||||
//-------------------------------------------------------------------------
|
||||
mCoords = new LLVector4a[numVertices];
|
||||
mNormals = new LLVector4a[numVertices];
|
||||
mBinormals = new LLVector4a[numVertices];
|
||||
mCoords = static_cast<LLVector4a*>(ll_aligned_malloc_16(numVertices * sizeof(LLVector4a)));
|
||||
mNormals = static_cast<LLVector4a*>(ll_aligned_malloc_16(numVertices * sizeof(LLVector4a)));
|
||||
mBinormals = static_cast<LLVector4a*>(ll_aligned_malloc_16(numVertices * sizeof(LLVector4a)));
|
||||
mTexCoords = new LLVector2[numVertices];
|
||||
// Actually, we are allocating more space than we need for the skiplist
|
||||
mVertexIndices = new U32[numVertices];
|
||||
|
|
@ -207,6 +207,42 @@ BOOL LLPolyMorphData::loadBinary(LLFILE *fp, LLPolyMeshSharedData *mesh)
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// freeData()
|
||||
//-----------------------------------------------------------------------------
|
||||
void LLPolyMorphData::freeData()
|
||||
{
|
||||
if (mCoords != NULL)
|
||||
{
|
||||
ll_aligned_free_16(mCoords);
|
||||
mCoords = NULL;
|
||||
}
|
||||
|
||||
if (mNormals != NULL)
|
||||
{
|
||||
ll_aligned_free_16(mNormals);
|
||||
mNormals = NULL;
|
||||
}
|
||||
|
||||
if (mBinormals != NULL)
|
||||
{
|
||||
ll_aligned_free_16(mBinormals);
|
||||
mBinormals = NULL;
|
||||
}
|
||||
|
||||
if (mTexCoords != NULL)
|
||||
{
|
||||
delete [] mTexCoords;
|
||||
mTexCoords = NULL;
|
||||
}
|
||||
|
||||
if (mVertexIndices != NULL)
|
||||
{
|
||||
delete [] mVertexIndices;
|
||||
mVertexIndices = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// LLPolyMorphTargetInfo()
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -48,6 +48,16 @@ public:
|
|||
~LLPolyMorphData();
|
||||
LLPolyMorphData(const LLPolyMorphData &rhs);
|
||||
|
||||
void* operator new(size_t size)
|
||||
{
|
||||
return ll_aligned_malloc_16(size);
|
||||
}
|
||||
|
||||
void operator delete(void* ptr)
|
||||
{
|
||||
ll_aligned_free_16(ptr);
|
||||
}
|
||||
|
||||
BOOL loadBinary(LLFILE* fp, LLPolyMeshSharedData *mesh);
|
||||
const std::string& getName() { return mName; }
|
||||
|
||||
|
|
@ -67,6 +77,9 @@ public:
|
|||
F32 mMaxDistortion; // maximum single vertex distortion in a given morph
|
||||
LLVector4a mAvgDistortion; // average vertex distortion, to infer directionality of the morph
|
||||
LLPolyMeshSharedData* mMesh;
|
||||
|
||||
private:
|
||||
void freeData();
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ class LLPolySkeletalDistortionInfo : public LLViewerVisualParamInfo
|
|||
{
|
||||
friend class LLPolySkeletalDistortion;
|
||||
public:
|
||||
|
||||
LLPolySkeletalDistortionInfo();
|
||||
/*virtual*/ ~LLPolySkeletalDistortionInfo() {};
|
||||
|
||||
|
|
@ -84,6 +85,16 @@ protected:
|
|||
class LLPolySkeletalDistortion : public LLViewerVisualParam
|
||||
{
|
||||
public:
|
||||
void* operator new(size_t size)
|
||||
{
|
||||
return ll_aligned_malloc_16(size);
|
||||
}
|
||||
|
||||
void operator delete(void* ptr)
|
||||
{
|
||||
ll_aligned_free_16(ptr);
|
||||
}
|
||||
|
||||
LLPolySkeletalDistortion(LLAvatarAppearance *avatarp);
|
||||
~LLPolySkeletalDistortion();
|
||||
|
||||
|
|
|
|||
|
|
@ -69,6 +69,16 @@ public:
|
|||
|
||||
/*virtual*/ LLViewerVisualParam* cloneParam(LLWearable* wearable = NULL) const;
|
||||
|
||||
void* operator new(size_t size)
|
||||
{
|
||||
return ll_aligned_malloc_16(size);
|
||||
}
|
||||
|
||||
void operator delete(void* ptr)
|
||||
{
|
||||
ll_aligned_free_16(ptr);
|
||||
}
|
||||
|
||||
// LLVisualParam Virtual functions
|
||||
///*virtual*/ BOOL parseData(LLXmlTreeNode* node);
|
||||
/*virtual*/ void apply( ESex avatar_sex ) {}
|
||||
|
|
@ -145,6 +155,16 @@ public:
|
|||
LLTexLayerParamColor( LLAvatarAppearance* appearance );
|
||||
/* virtual */ ~LLTexLayerParamColor();
|
||||
|
||||
void* operator new(size_t size)
|
||||
{
|
||||
return ll_aligned_malloc_16(size);
|
||||
}
|
||||
|
||||
void operator delete(void* ptr)
|
||||
{
|
||||
ll_aligned_free_16(ptr);
|
||||
}
|
||||
|
||||
/*virtual*/ LLViewerVisualParam* cloneParam(LLWearable* wearable = NULL) const;
|
||||
|
||||
// LLVisualParam Virtual functions
|
||||
|
|
|
|||
|
|
@ -1792,5 +1792,3 @@ bool LLAudioData::load()
|
|||
mBufferp->mAudioDatap = this;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -214,8 +214,10 @@ BOOL LLEditingMotion::onUpdate(F32 time, U8* joint_mask)
|
|||
target = target * target_dist;
|
||||
if (!target.isFinite())
|
||||
{
|
||||
llerrs << "Non finite target in editing motion with target distance of " << target_dist <<
|
||||
// Don't error out here, set a fail-safe target vector
|
||||
llwarns << "Non finite target in editing motion with target distance of " << target_dist <<
|
||||
" and focus point " << focus_pt << llendl;
|
||||
target.setVec(1.f, 1.f, 1.f);
|
||||
}
|
||||
|
||||
mTarget.setPosition( target + mParentJoint.getPosition());
|
||||
|
|
|
|||
|
|
@ -116,6 +116,7 @@ set(llcommon_HEADER_FILES
|
|||
bitpack.h
|
||||
ctype_workaround.h
|
||||
doublelinkedlist.h
|
||||
fix_macros.h
|
||||
imageids.h
|
||||
indra_constants.h
|
||||
linden_common.h
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
/**
|
||||
* @file fix_macros.h
|
||||
* @author Nat Goodspeed
|
||||
* @date 2012-11-16
|
||||
* @brief The Mac system headers seem to #define macros with obnoxiously
|
||||
* generic names, preventing any library from using those names. We've
|
||||
* had to fix these in so many places that it's worth making a header
|
||||
* file to handle it.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Copyright (c) 2012, Linden Research, Inc.
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
// DON'T use an #include guard: every time we encounter this header, #undef
|
||||
// these macros all over again.
|
||||
|
||||
// who injects MACROS with such generic names?! Grr.
|
||||
#ifdef equivalent
|
||||
#undef equivalent
|
||||
#endif
|
||||
|
||||
#ifdef check
|
||||
#undef check
|
||||
#endif
|
||||
|
|
@ -289,6 +289,7 @@ void LLApp::setupErrorHandling()
|
|||
// occasionally checks to see if the app is in an error state, and sees if it needs to be run.
|
||||
|
||||
#if LL_WINDOWS
|
||||
#if LL_SEND_CRASH_REPORTS
|
||||
// This sets a callback to handle w32 signals to the console window.
|
||||
// The viewer shouldn't be affected, sicne its a windowed app.
|
||||
SetConsoleCtrlHandler( (PHANDLER_ROUTINE) ConsoleCtrlHandler, TRUE);
|
||||
|
|
@ -300,7 +301,7 @@ void LLApp::setupErrorHandling()
|
|||
mExceptionHandler = new google_breakpad::ExceptionHandler(
|
||||
L"C:\\Temp\\", 0, windows_post_minidump_callback, 0, google_breakpad::ExceptionHandler::HANDLER_ALL);
|
||||
}
|
||||
|
||||
#endif
|
||||
#else
|
||||
//
|
||||
// Start up signal handling.
|
||||
|
|
|
|||
|
|
@ -168,7 +168,7 @@ public:
|
|||
void operator -=(Type x) { apr_atomic_sub32(&mData, apr_uint32_t(x)); }
|
||||
void operator +=(Type x) { apr_atomic_add32(&mData, apr_uint32_t(x)); }
|
||||
Type operator ++(int) { return apr_atomic_inc32(&mData); } // Type++
|
||||
Type operator --(int) { return apr_atomic_dec32(&mData); } // Type--
|
||||
Type operator --(int) { return apr_atomic_dec32(&mData); } // approximately --Type (0 if final is 0, non-zero otherwise)
|
||||
|
||||
private:
|
||||
apr_uint32_t mData;
|
||||
|
|
|
|||
|
|
@ -252,21 +252,6 @@ U32 LLMemory::getAllocatedMemKB()
|
|||
return sAllocatedMemInKB ;
|
||||
}
|
||||
|
||||
void* ll_allocate (size_t size)
|
||||
{
|
||||
if (size == 0)
|
||||
{
|
||||
llwarns << "Null allocation" << llendl;
|
||||
}
|
||||
void *p = malloc(size);
|
||||
if (p == NULL)
|
||||
{
|
||||
LLMemory::freeReserve();
|
||||
llerrs << "Out of memory Error" << llendl;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
#if defined(LL_WINDOWS)
|
||||
|
|
@ -1365,7 +1350,7 @@ char* LLPrivateMemoryPool::allocate(U32 size)
|
|||
//if the asked size larger than MAX_BLOCK_SIZE, fetch from heap directly, the pool does not manage it
|
||||
if(size >= CHUNK_SIZE)
|
||||
{
|
||||
return (char*)malloc(size) ;
|
||||
return (char*)ll_aligned_malloc_16(size) ;
|
||||
}
|
||||
|
||||
char* p = NULL ;
|
||||
|
|
@ -1422,7 +1407,7 @@ char* LLPrivateMemoryPool::allocate(U32 size)
|
|||
to_log = false ;
|
||||
}
|
||||
|
||||
return (char*)malloc(size) ;
|
||||
return (char*)ll_aligned_malloc_16(size) ;
|
||||
}
|
||||
|
||||
return p ;
|
||||
|
|
@ -1441,7 +1426,7 @@ void LLPrivateMemoryPool::freeMem(void* addr)
|
|||
|
||||
if(!chunk)
|
||||
{
|
||||
free(addr) ; //release from heap
|
||||
ll_aligned_free_16(addr) ; //release from heap
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -1565,7 +1550,7 @@ LLPrivateMemoryPool::LLMemoryChunk* LLPrivateMemoryPool::addChunk(S32 chunk_inde
|
|||
|
||||
mReservedPoolSize += preferred_size + overhead ;
|
||||
|
||||
char* buffer = (char*)malloc(preferred_size + overhead) ;
|
||||
char* buffer = (char*)ll_aligned_malloc_16(preferred_size + overhead) ;
|
||||
if(!buffer)
|
||||
{
|
||||
return NULL ;
|
||||
|
|
@ -1633,7 +1618,7 @@ void LLPrivateMemoryPool::removeChunk(LLMemoryChunk* chunk)
|
|||
mReservedPoolSize -= chunk->getBufferSize() ;
|
||||
|
||||
//release memory
|
||||
free(chunk->getBuffer()) ;
|
||||
ll_aligned_free_16(chunk->getBuffer()) ;
|
||||
}
|
||||
|
||||
U16 LLPrivateMemoryPool::findHashKey(const char* addr)
|
||||
|
|
@ -1977,7 +1962,7 @@ char* LLPrivateMemoryPoolManager::allocate(LLPrivateMemoryPool* poolp, U32 size,
|
|||
|
||||
if(!poolp)
|
||||
{
|
||||
p = (char*)malloc(size) ;
|
||||
p = (char*)ll_aligned_malloc_16(size) ;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -2006,7 +1991,7 @@ char* LLPrivateMemoryPoolManager::allocate(LLPrivateMemoryPool* poolp, U32 size)
|
|||
}
|
||||
else
|
||||
{
|
||||
return (char*)malloc(size) ;
|
||||
return (char*)ll_aligned_malloc_16(size) ;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -2031,7 +2016,7 @@ void LLPrivateMemoryPoolManager::freeMem(LLPrivateMemoryPool* poolp, void* addr
|
|||
{
|
||||
if(!sPrivatePoolEnabled)
|
||||
{
|
||||
free(addr) ; //private pool is disabled.
|
||||
ll_aligned_free_16(addr) ; //private pool is disabled.
|
||||
}
|
||||
else if(!sInstance) //the private memory manager is destroyed, try the dangling list
|
||||
{
|
||||
|
|
|
|||
|
|
@ -27,6 +27,13 @@
|
|||
#define LLMEMORY_H
|
||||
|
||||
#include "llmemtype.h"
|
||||
|
||||
#if LL_WINDOWS && LL_DEBUG
|
||||
#define LL_CHECK_MEMORY llassert(_CrtCheckMemory());
|
||||
#else
|
||||
#define LL_CHECK_MEMORY
|
||||
#endif
|
||||
|
||||
inline void* ll_aligned_malloc( size_t size, int align )
|
||||
{
|
||||
void* mem = malloc( size + (align - 1) + sizeof(void*) );
|
||||
|
|
@ -58,17 +65,6 @@ inline void* ll_aligned_malloc_16(size_t size) // returned hunk MUST be freed wi
|
|||
#endif
|
||||
}
|
||||
|
||||
inline void* ll_aligned_realloc_16(void* ptr, size_t size) // returned hunk MUST be freed with ll_aligned_free_16().
|
||||
{
|
||||
#if defined(LL_WINDOWS)
|
||||
return _aligned_realloc(ptr, size, 16);
|
||||
#elif defined(LL_DARWIN)
|
||||
return realloc(ptr,size); // default osx malloc is 16 byte aligned.
|
||||
#else
|
||||
return realloc(ptr,size); // FIXME not guaranteed to be aligned.
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void ll_aligned_free_16(void *p)
|
||||
{
|
||||
#if defined(LL_WINDOWS)
|
||||
|
|
@ -79,10 +75,33 @@ inline void ll_aligned_free_16(void *p)
|
|||
free(p); // posix_memalign() is compatible with heap deallocator
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void* ll_aligned_realloc_16(void* ptr, size_t size, size_t old_size) // returned hunk MUST be freed with ll_aligned_free_16().
|
||||
{
|
||||
#if defined(LL_WINDOWS)
|
||||
return _aligned_realloc(ptr, size, 16);
|
||||
#elif defined(LL_DARWIN)
|
||||
return realloc(ptr,size); // default osx malloc is 16 byte aligned.
|
||||
#else
|
||||
//FIXME: memcpy is SLOW
|
||||
void* ret = ll_aligned_malloc_16(size);
|
||||
if (ptr)
|
||||
{
|
||||
if (ret)
|
||||
{
|
||||
// Only copy the size of the smallest memory block to avoid memory corruption.
|
||||
memcpy(ret, ptr, llmin(old_size, size));
|
||||
}
|
||||
ll_aligned_free_16(ptr);
|
||||
}
|
||||
return ret;
|
||||
#endif
|
||||
}
|
||||
|
||||
#else // USE_TCMALLOC
|
||||
// ll_aligned_foo_16 are not needed with tcmalloc
|
||||
#define ll_aligned_malloc_16 malloc
|
||||
#define ll_aligned_realloc_16 realloc
|
||||
#define ll_aligned_realloc_16(a,b,c) realloc(a,b)
|
||||
#define ll_aligned_free_16 free
|
||||
#endif // USE_TCMALLOC
|
||||
|
||||
|
|
|
|||
|
|
@ -134,8 +134,8 @@ S32 LLQueuedThread::updateQueue(F32 max_time_ms)
|
|||
pending = getPending();
|
||||
if(pending > 0)
|
||||
{
|
||||
unpause();
|
||||
}
|
||||
unpause();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -47,7 +47,8 @@ std::string ll_safe_string(const char* in)
|
|||
|
||||
std::string ll_safe_string(const char* in, S32 maxlen)
|
||||
{
|
||||
if(in) return std::string(in, maxlen);
|
||||
if(in && maxlen > 0 ) return std::string(in, maxlen);
|
||||
|
||||
return std::string();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -944,13 +944,15 @@ LLSD LLMemoryInfo::loadStatsMap()
|
|||
state.dwLength = sizeof(state);
|
||||
GlobalMemoryStatusEx(&state);
|
||||
|
||||
stats.add("Percent Memory use", state.dwMemoryLoad);
|
||||
stats.add("Total Physical KB", state.ullTotalPhys/1024);
|
||||
stats.add("Avail Physical KB", state.ullAvailPhys/1024);
|
||||
stats.add("Total page KB", state.ullTotalPageFile/1024);
|
||||
stats.add("Avail page KB", state.ullAvailPageFile/1024);
|
||||
stats.add("Total Virtual KB", state.ullTotalVirtual/1024);
|
||||
stats.add("Avail Virtual KB", state.ullAvailVirtual/1024);
|
||||
DWORDLONG div = 1024;
|
||||
|
||||
stats.add("Percent Memory use", state.dwMemoryLoad/div);
|
||||
stats.add("Total Physical KB", state.ullTotalPhys/div);
|
||||
stats.add("Avail Physical KB", state.ullAvailPhys/div);
|
||||
stats.add("Total page KB", state.ullTotalPageFile/div);
|
||||
stats.add("Avail page KB", state.ullAvailPageFile/div);
|
||||
stats.add("Total Virtual KB", state.ullTotalVirtual/div);
|
||||
stats.add("Avail Virtual KB", state.ullAvailVirtual/div);
|
||||
|
||||
PERFORMANCE_INFORMATION perf;
|
||||
perf.cb = sizeof(perf);
|
||||
|
|
@ -982,15 +984,15 @@ LLSD LLMemoryInfo::loadStatsMap()
|
|||
GetProcessMemoryInfo(GetCurrentProcess(), PPROCESS_MEMORY_COUNTERS(&pmem), sizeof(pmem));
|
||||
|
||||
stats.add("Page Fault Count", pmem.PageFaultCount);
|
||||
stats.add("PeakWorkingSetSize KB", pmem.PeakWorkingSetSize/1024);
|
||||
stats.add("WorkingSetSize KB", pmem.WorkingSetSize/1024);
|
||||
stats.add("QutaPeakPagedPoolUsage KB", pmem.QuotaPeakPagedPoolUsage/1024);
|
||||
stats.add("QuotaPagedPoolUsage KB", pmem.QuotaPagedPoolUsage/1024);
|
||||
stats.add("QuotaPeakNonPagedPoolUsage KB", pmem.QuotaPeakNonPagedPoolUsage/1024);
|
||||
stats.add("QuotaNonPagedPoolUsage KB", pmem.QuotaNonPagedPoolUsage/1024);
|
||||
stats.add("PagefileUsage KB", pmem.PagefileUsage/1024);
|
||||
stats.add("PeakPagefileUsage KB", pmem.PeakPagefileUsage/1024);
|
||||
stats.add("PrivateUsage KB", pmem.PrivateUsage/1024);
|
||||
stats.add("PeakWorkingSetSize KB", pmem.PeakWorkingSetSize/div);
|
||||
stats.add("WorkingSetSize KB", pmem.WorkingSetSize/div);
|
||||
stats.add("QutaPeakPagedPoolUsage KB", pmem.QuotaPeakPagedPoolUsage/div);
|
||||
stats.add("QuotaPagedPoolUsage KB", pmem.QuotaPagedPoolUsage/div);
|
||||
stats.add("QuotaPeakNonPagedPoolUsage KB", pmem.QuotaPeakNonPagedPoolUsage/div);
|
||||
stats.add("QuotaNonPagedPoolUsage KB", pmem.QuotaNonPagedPoolUsage/div);
|
||||
stats.add("PagefileUsage KB", pmem.PagefileUsage/div);
|
||||
stats.add("PeakPagefileUsage KB", pmem.PeakPagefileUsage/div);
|
||||
stats.add("PrivateUsage KB", pmem.PrivateUsage/div);
|
||||
|
||||
#elif LL_DARWIN
|
||||
|
||||
|
|
|
|||
|
|
@ -71,6 +71,13 @@ LL_COMMON_API void assert_main_thread()
|
|||
}
|
||||
}
|
||||
|
||||
void LLThread::registerThreadID()
|
||||
{
|
||||
#if !LL_DARWIN
|
||||
sThreadID = ++sIDIter;
|
||||
#endif
|
||||
}
|
||||
|
||||
//
|
||||
// Handed to the APR thread creation function
|
||||
//
|
||||
|
|
@ -114,7 +121,7 @@ LLThread::LLThread(const std::string& name, apr_pool_t *poolp) :
|
|||
apr_pool_create(&mAPRPoolp, NULL); // Create a subpool for this thread
|
||||
}
|
||||
mRunCondition = new LLCondition(mAPRPoolp);
|
||||
|
||||
mDataLock = new LLMutex(mAPRPoolp);
|
||||
mLocalAPRFilePoolp = NULL ;
|
||||
}
|
||||
|
||||
|
|
@ -173,7 +180,10 @@ void LLThread::shutdown()
|
|||
}
|
||||
|
||||
delete mRunCondition;
|
||||
mRunCondition = 0;
|
||||
mRunCondition = NULL;
|
||||
|
||||
delete mDataLock;
|
||||
mDataLock = NULL;
|
||||
|
||||
if (mIsLocalPool && mAPRPoolp)
|
||||
{
|
||||
|
|
@ -242,28 +252,30 @@ bool LLThread::runCondition(void)
|
|||
// Stop thread execution if requested until unpaused.
|
||||
void LLThread::checkPause()
|
||||
{
|
||||
mRunCondition->lock();
|
||||
mDataLock->lock();
|
||||
|
||||
// This is in a while loop because the pthread API allows for spurious wakeups.
|
||||
while(shouldSleep())
|
||||
{
|
||||
mDataLock->unlock();
|
||||
mRunCondition->wait(); // unlocks mRunCondition
|
||||
mDataLock->lock();
|
||||
// mRunCondition is locked when the thread wakes up
|
||||
}
|
||||
|
||||
mRunCondition->unlock();
|
||||
mDataLock->unlock();
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
|
||||
void LLThread::setQuitting()
|
||||
{
|
||||
mRunCondition->lock();
|
||||
mDataLock->lock();
|
||||
if (mStatus == RUNNING)
|
||||
{
|
||||
mStatus = QUITTING;
|
||||
}
|
||||
mRunCondition->unlock();
|
||||
mDataLock->unlock();
|
||||
wake();
|
||||
}
|
||||
|
||||
|
|
@ -285,12 +297,12 @@ void LLThread::yield()
|
|||
|
||||
void LLThread::wake()
|
||||
{
|
||||
mRunCondition->lock();
|
||||
mDataLock->lock();
|
||||
if(!shouldSleep())
|
||||
{
|
||||
mRunCondition->signal();
|
||||
}
|
||||
mRunCondition->unlock();
|
||||
mDataLock->unlock();
|
||||
}
|
||||
|
||||
void LLThread::wakeLocked()
|
||||
|
|
@ -481,6 +493,19 @@ LLThreadSafeRefCount::LLThreadSafeRefCount() :
|
|||
{
|
||||
}
|
||||
|
||||
LLThreadSafeRefCount::LLThreadSafeRefCount(const LLThreadSafeRefCount& src)
|
||||
{
|
||||
if (sMutex)
|
||||
{
|
||||
sMutex->lock();
|
||||
}
|
||||
mRef = 0;
|
||||
if (sMutex)
|
||||
{
|
||||
sMutex->unlock();
|
||||
}
|
||||
}
|
||||
|
||||
LLThreadSafeRefCount::~LLThreadSafeRefCount()
|
||||
{
|
||||
if (mRef != 0)
|
||||
|
|
@ -489,6 +514,7 @@ LLThreadSafeRefCount::~LLThreadSafeRefCount()
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
|
||||
LLResponder::~LLResponder()
|
||||
|
|
|
|||
|
|
@ -88,6 +88,11 @@ public:
|
|||
|
||||
U32 getID() const { return mID; }
|
||||
|
||||
// Called by threads *not* created via LLThread to register some
|
||||
// internal state used by LLMutex. You must call this once early
|
||||
// in the running thread to prevent collisions with the main thread.
|
||||
static void registerThreadID();
|
||||
|
||||
private:
|
||||
BOOL mPaused;
|
||||
|
||||
|
|
@ -97,6 +102,7 @@ private:
|
|||
protected:
|
||||
std::string mName;
|
||||
LLCondition* mRunCondition;
|
||||
LLMutex* mDataLock;
|
||||
|
||||
apr_thread_t *mAPRThreadp;
|
||||
apr_pool_t *mAPRPoolp;
|
||||
|
|
@ -122,15 +128,15 @@ protected:
|
|||
inline void unlockData();
|
||||
|
||||
// This is the predicate that decides whether the thread should sleep.
|
||||
// It should only be called with mRunCondition locked, since the virtual runCondition() function may need to access
|
||||
// It should only be called with mDataLock locked, since the virtual runCondition() function may need to access
|
||||
// data structures that are thread-unsafe.
|
||||
bool shouldSleep(void) { return (mStatus == RUNNING) && (isPaused() || (!runCondition())); }
|
||||
|
||||
// To avoid spurious signals (and the associated context switches) when the condition may or may not have changed, you can do the following:
|
||||
// mRunCondition->lock();
|
||||
// mDataLock->lock();
|
||||
// if(!shouldSleep())
|
||||
// mRunCondition->signal();
|
||||
// mRunCondition->unlock();
|
||||
// mDataLock->unlock();
|
||||
};
|
||||
|
||||
//============================================================================
|
||||
|
|
@ -205,12 +211,12 @@ private:
|
|||
|
||||
void LLThread::lockData()
|
||||
{
|
||||
mRunCondition->lock();
|
||||
mDataLock->lock();
|
||||
}
|
||||
|
||||
void LLThread::unlockData()
|
||||
{
|
||||
mRunCondition->unlock();
|
||||
mDataLock->unlock();
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -227,15 +233,27 @@ public:
|
|||
private:
|
||||
static LLMutex* sMutex;
|
||||
|
||||
private:
|
||||
LLThreadSafeRefCount(const LLThreadSafeRefCount&); // not implemented
|
||||
LLThreadSafeRefCount&operator=(const LLThreadSafeRefCount&); // not implemented
|
||||
|
||||
protected:
|
||||
virtual ~LLThreadSafeRefCount(); // use unref()
|
||||
|
||||
public:
|
||||
LLThreadSafeRefCount();
|
||||
LLThreadSafeRefCount(const LLThreadSafeRefCount&);
|
||||
LLThreadSafeRefCount& operator=(const LLThreadSafeRefCount& ref)
|
||||
{
|
||||
if (sMutex)
|
||||
{
|
||||
sMutex->lock();
|
||||
}
|
||||
mRef = 0;
|
||||
if (sMutex)
|
||||
{
|
||||
sMutex->unlock();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ref()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@
|
|||
|
||||
// system includes
|
||||
#include <boost/tokenizer.hpp>
|
||||
#include <boost/algorithm/string/find_iterator.hpp>
|
||||
#include <boost/algorithm/string/finder.hpp>
|
||||
|
||||
void encode_character(std::ostream& ostr, std::string::value_type val)
|
||||
{
|
||||
|
|
@ -317,7 +319,7 @@ LLURI LLURI::buildHTTP(const std::string& prefix,
|
|||
const LLSD& path)
|
||||
{
|
||||
LLURI result;
|
||||
|
||||
|
||||
// TODO: deal with '/' '?' '#' in host_port
|
||||
if (prefix.find("://") != prefix.npos)
|
||||
{
|
||||
|
|
@ -342,15 +344,41 @@ LLURI LLURI::buildHTTP(const std::string& prefix,
|
|||
result.mEscapedPath += "/" + escapePathComponent(it->asString());
|
||||
}
|
||||
}
|
||||
else if(path.isString())
|
||||
else if (path.isString())
|
||||
{
|
||||
result.mEscapedPath += "/" + escapePathComponent(path.asString());
|
||||
std::string pathstr(path);
|
||||
// Trailing slash is significant in HTTP land. If caller specified,
|
||||
// make a point of preserving.
|
||||
std::string last_slash;
|
||||
std::string::size_type len(pathstr.length());
|
||||
if (len && pathstr[len-1] == '/')
|
||||
{
|
||||
last_slash = "/";
|
||||
}
|
||||
|
||||
// Escape every individual path component, recombining with slashes.
|
||||
for (boost::split_iterator<std::string::const_iterator>
|
||||
ti(pathstr, boost::first_finder("/")), tend;
|
||||
ti != tend; ++ti)
|
||||
{
|
||||
// Eliminate a leading slash or duplicate slashes anywhere. (Extra
|
||||
// slashes show up here as empty components.) This test also
|
||||
// eliminates a trailing slash, hence last_slash above.
|
||||
if (! ti->empty())
|
||||
{
|
||||
result.mEscapedPath
|
||||
+= "/" + escapePathComponent(std::string(ti->begin(), ti->end()));
|
||||
}
|
||||
}
|
||||
|
||||
// Reinstate trailing slash, if any.
|
||||
result.mEscapedPath += last_slash;
|
||||
}
|
||||
else if(path.isUndefined())
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
llwarns << "Valid path arguments to buildHTTP are array, string, or undef, you passed type"
|
||||
<< path.type() << llendl;
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
const S32 LL_VERSION_MAJOR = 3;
|
||||
const S32 LL_VERSION_MINOR = 4;
|
||||
const S32 LL_VERSION_PATCH = 1;
|
||||
const S32 LL_VERSION_PATCH = 4;
|
||||
const S32 LL_VERSION_BUILD = 264760;
|
||||
|
||||
const char * const LL_CHANNEL = "Second Life Developer";
|
||||
|
|
|
|||
|
|
@ -94,6 +94,7 @@ namespace tut
|
|||
ensure("bitPack: individual unpack: 5", unpackbuffer[0] == (U8) str[5]);
|
||||
bitunpack.bitUnpack(unpackbuffer, 8*4); // Life
|
||||
ensure_memory_matches("bitPack: 4 bytes unpack:", unpackbuffer, 4, str+6, 4);
|
||||
ensure("keep compiler quiet", unpack_bufsize == unpack_bufsize);
|
||||
}
|
||||
|
||||
// U32 packing
|
||||
|
|
|
|||
|
|
@ -58,12 +58,12 @@ namespace tut
|
|||
ensure_equals("escape/unescape escaped", uri_esc_2, uri_esc_1);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
typedef test_group<URITestData> URITestGroup;
|
||||
typedef URITestGroup::object URITestObject;
|
||||
|
||||
URITestGroup uriTestGroup("LLURI");
|
||||
|
||||
|
||||
template<> template<>
|
||||
void URITestObject::test<1>()
|
||||
{
|
||||
|
|
@ -89,14 +89,14 @@ namespace tut
|
|||
template<> template<>
|
||||
void URITestObject::test<2>()
|
||||
{
|
||||
// empty string
|
||||
set_test_name("empty string");
|
||||
checkParts(LLURI(""), "", "", "", "");
|
||||
}
|
||||
|
||||
|
||||
template<> template<>
|
||||
void URITestObject::test<3>()
|
||||
{
|
||||
// no scheme
|
||||
set_test_name("no scheme");
|
||||
checkParts(LLURI("foo"), "", "foo", "", "");
|
||||
checkParts(LLURI("foo%3A"), "", "foo:", "", "");
|
||||
}
|
||||
|
|
@ -104,7 +104,7 @@ namespace tut
|
|||
template<> template<>
|
||||
void URITestObject::test<4>()
|
||||
{
|
||||
// scheme w/o paths
|
||||
set_test_name("scheme w/o paths");
|
||||
checkParts(LLURI("mailto:zero@ll.com"),
|
||||
"mailto", "zero@ll.com", "", "");
|
||||
checkParts(LLURI("silly://abc/def?foo"),
|
||||
|
|
@ -114,16 +114,16 @@ namespace tut
|
|||
template<> template<>
|
||||
void URITestObject::test<5>()
|
||||
{
|
||||
// authority section
|
||||
set_test_name("authority section");
|
||||
checkParts(LLURI("http:///"),
|
||||
"http", "///", "", "/");
|
||||
|
||||
|
||||
checkParts(LLURI("http://abc"),
|
||||
"http", "//abc", "abc", "");
|
||||
|
||||
|
||||
checkParts(LLURI("http://a%2Fb/cd"),
|
||||
"http", "//a/b/cd", "a/b", "/cd");
|
||||
|
||||
|
||||
checkParts(LLURI("http://host?"),
|
||||
"http", "//host?", "host", "");
|
||||
}
|
||||
|
|
@ -131,13 +131,13 @@ namespace tut
|
|||
template<> template<>
|
||||
void URITestObject::test<6>()
|
||||
{
|
||||
// path section
|
||||
set_test_name("path section");
|
||||
checkParts(LLURI("http://host/a/b/"),
|
||||
"http", "//host/a/b/", "host", "/a/b/");
|
||||
|
||||
|
||||
checkParts(LLURI("http://host/a%3Fb/"),
|
||||
"http", "//host/a?b/", "host", "/a?b/");
|
||||
|
||||
|
||||
checkParts(LLURI("http://host/a:b/"),
|
||||
"http", "//host/a:b/", "host", "/a:b/");
|
||||
}
|
||||
|
|
@ -145,16 +145,16 @@ namespace tut
|
|||
template<> template<>
|
||||
void URITestObject::test<7>()
|
||||
{
|
||||
// query string
|
||||
set_test_name("query string");
|
||||
checkParts(LLURI("http://host/?"),
|
||||
"http", "//host/?", "host", "/", "");
|
||||
|
||||
|
||||
checkParts(LLURI("http://host/?x"),
|
||||
"http", "//host/?x", "host", "/", "x");
|
||||
|
||||
|
||||
checkParts(LLURI("http://host/??"),
|
||||
"http", "//host/??", "host", "/", "?");
|
||||
|
||||
|
||||
checkParts(LLURI("http://host/?%3F"),
|
||||
"http", "//host/??", "host", "/", "?");
|
||||
}
|
||||
|
|
@ -167,19 +167,44 @@ namespace tut
|
|||
path.append("123");
|
||||
checkParts(LLURI::buildHTTP("host", path),
|
||||
"http", "//host/x/123", "host", "/x/123");
|
||||
|
||||
|
||||
LLSD query;
|
||||
query["123"] = "12";
|
||||
query["abcd"] = "abc";
|
||||
checkParts(LLURI::buildHTTP("host", path, query),
|
||||
"http", "//host/x/123?123=12&abcd=abc",
|
||||
"host", "/x/123", "123=12&abcd=abc");
|
||||
|
||||
ensure_equals(LLURI::buildHTTP("host", "").asString(),
|
||||
"http://host");
|
||||
ensure_equals(LLURI::buildHTTP("host", "/").asString(),
|
||||
"http://host/");
|
||||
ensure_equals(LLURI::buildHTTP("host", "//").asString(),
|
||||
"http://host/");
|
||||
ensure_equals(LLURI::buildHTTP("host", "dir name").asString(),
|
||||
"http://host/dir%20name");
|
||||
ensure_equals(LLURI::buildHTTP("host", "dir name/").asString(),
|
||||
"http://host/dir%20name/");
|
||||
ensure_equals(LLURI::buildHTTP("host", "/dir name").asString(),
|
||||
"http://host/dir%20name");
|
||||
ensure_equals(LLURI::buildHTTP("host", "/dir name/").asString(),
|
||||
"http://host/dir%20name/");
|
||||
ensure_equals(LLURI::buildHTTP("host", "dir name/subdir name").asString(),
|
||||
"http://host/dir%20name/subdir%20name");
|
||||
ensure_equals(LLURI::buildHTTP("host", "dir name/subdir name/").asString(),
|
||||
"http://host/dir%20name/subdir%20name/");
|
||||
ensure_equals(LLURI::buildHTTP("host", "/dir name/subdir name").asString(),
|
||||
"http://host/dir%20name/subdir%20name");
|
||||
ensure_equals(LLURI::buildHTTP("host", "/dir name/subdir name/").asString(),
|
||||
"http://host/dir%20name/subdir%20name/");
|
||||
ensure_equals(LLURI::buildHTTP("host", "//dir name//subdir name//").asString(),
|
||||
"http://host/dir%20name/subdir%20name/");
|
||||
}
|
||||
|
||||
template<> template<>
|
||||
void URITestObject::test<9>()
|
||||
{
|
||||
// test unescaped path components
|
||||
set_test_name("test unescaped path components");
|
||||
LLSD path;
|
||||
path.append("x@*//*$&^");
|
||||
path.append("123");
|
||||
|
|
@ -190,7 +215,7 @@ namespace tut
|
|||
template<> template<>
|
||||
void URITestObject::test<10>()
|
||||
{
|
||||
// test unescaped query components
|
||||
set_test_name("test unescaped query components");
|
||||
LLSD path;
|
||||
path.append("x");
|
||||
path.append("123");
|
||||
|
|
@ -205,7 +230,7 @@ namespace tut
|
|||
template<> template<>
|
||||
void URITestObject::test<11>()
|
||||
{
|
||||
// test unescaped host components
|
||||
set_test_name("test unescaped host components");
|
||||
LLSD path;
|
||||
path.append("x");
|
||||
path.append("123");
|
||||
|
|
@ -216,16 +241,16 @@ namespace tut
|
|||
"http", "//hi123*33--}{:portstuffs/x/123?123=12&abcd=abc",
|
||||
"hi123*33--}{:portstuffs", "/x/123", "123=12&abcd=abc");
|
||||
}
|
||||
|
||||
|
||||
template<> template<>
|
||||
void URITestObject::test<12>()
|
||||
{
|
||||
// test funky host_port values that are actually prefixes
|
||||
|
||||
set_test_name("test funky host_port values that are actually prefixes");
|
||||
|
||||
checkParts(LLURI::buildHTTP("http://example.com:8080", LLSD()),
|
||||
"http", "//example.com:8080",
|
||||
"example.com:8080", "");
|
||||
|
||||
|
||||
checkParts(LLURI::buildHTTP("http://example.com:8080/", LLSD()),
|
||||
"http", "//example.com:8080/",
|
||||
"example.com:8080", "/");
|
||||
|
|
@ -242,7 +267,7 @@ namespace tut
|
|||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789"
|
||||
"-._~";
|
||||
// test escape
|
||||
set_test_name("test escape");
|
||||
ensure_equals("escaping", LLURI::escape("abcdefg", "abcdef"), "abcdef%67");
|
||||
ensure_equals("escaping", LLURI::escape("|/&\\+-_!@", ""), "%7C%2F%26%5C%2B%2D%5F%21%40");
|
||||
ensure_equals("escaping as query variable",
|
||||
|
|
@ -259,13 +284,12 @@ namespace tut
|
|||
cedilla.push_back( (char)0xA7 );
|
||||
ensure_equals("escape UTF8", LLURI::escape( cedilla, unreserved), "%C3%A7");
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<> template<>
|
||||
void URITestObject::test<14>()
|
||||
{
|
||||
// make sure escape and unescape of empty strings return empty
|
||||
// strings.
|
||||
set_test_name("make sure escape and unescape of empty strings return empty strings.");
|
||||
std::string uri_esc(LLURI::escape(""));
|
||||
ensure("escape string empty", uri_esc.empty());
|
||||
std::string uri_raw(LLURI::unescape(""));
|
||||
|
|
@ -275,7 +299,7 @@ namespace tut
|
|||
template<> template<>
|
||||
void URITestObject::test<15>()
|
||||
{
|
||||
// do some round-trip tests
|
||||
set_test_name("do some round-trip tests");
|
||||
escapeRoundTrip("http://secondlife.com");
|
||||
escapeRoundTrip("http://secondlife.com/url with spaces");
|
||||
escapeRoundTrip("http://bad[domain]name.com/");
|
||||
|
|
@ -286,7 +310,7 @@ namespace tut
|
|||
template<> template<>
|
||||
void URITestObject::test<16>()
|
||||
{
|
||||
// Test the default escaping
|
||||
set_test_name("Test the default escaping");
|
||||
// yes -- this mangles the url. This is expected behavior
|
||||
std::string simple("http://secondlife.com");
|
||||
ensure_equals(
|
||||
|
|
@ -302,7 +326,7 @@ namespace tut
|
|||
template<> template<>
|
||||
void URITestObject::test<17>()
|
||||
{
|
||||
// do some round-trip tests with very long strings.
|
||||
set_test_name("do some round-trip tests with very long strings.");
|
||||
escapeRoundTrip("Welcome to Second Life.We hope you'll have a richly rewarding experience, filled with creativity, self expression and fun.The goals of the Community Standards are simple: treat each other with respect and without harassment, adhere to local standards as indicated by simulator ratings, and refrain from any hate activity which slurs a real-world individual or real-world community. Behavioral Guidelines - The Big Six");
|
||||
escapeRoundTrip(
|
||||
"'asset_data':b(12100){'task_id':ucc706f2d-0b68-68f8-11a4-f1043ff35ca0}\n{\n\tname\tObject|\n\tpermissions 0\n\t{\n\t\tbase_mask\t7fffffff\n\t\towner_mask\t7fffffff\n\t\tgroup_mask\t00000000\n\t\teveryone_mask\t00000000\n\t\tnext_owner_mask\t7fffffff\n\t\tcreator_id\t13fd9595-a47b-4d64-a5fb-6da645f038e0\n\t\towner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tlast_owner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tgroup_id\t00000000-0000-0000-0000-000000000000\n\t}\n\tlocal_id\t217444921\n\ttotal_crc\t323\n\ttype\t2\n\ttask_valid\t2\n\ttravel_access\t13\n\tdisplayopts\t2\n\tdisplaytype\tv\n\tpos\t-0.368634403\t0.00781063363\t-0.569040775\n\toldpos\t150.117996\t25.8658009\t8.19664001\n\trotation\t-0.06293071806430816650390625\t-0.6995697021484375\t-0.7002241611480712890625\t0.1277817934751510620117188\n\tchildpos\t-0.00499999989\t-0.0359999985\t0.307999998\n\tchildrot\t-0.515492737293243408203125\t-0.46601200103759765625\t0.529055416584014892578125\t0.4870323240756988525390625\n\tscale"
|
||||
|
|
@ -322,7 +346,7 @@ namespace tut
|
|||
"D STRING RW SV 20f36c3a-b44b-9bc7-87f3-018bfdfc8cda\n\tscratchpad\t0\n\t{\n\t\n\t}\n\tsale_info\t0\n\t{\n\t\tsale_type\tnot\n\t\tsale_price\t10\n\t}\n\torig_asset_id\t8747acbc-d391-1e59-69f1-41d06830e6c0\n\torig_item_id\t20f36c3a-b44b-9bc7-87f3-018bfdfc8cda\n\tfrom_task_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\tcorrect_family_id\t00000000-0000-0000-0000-000000000000\n\thas_rezzed\t0\n\tpre_link_base_mask\t7fffffff\n\tlinked \tlinked\n\tdefault_pay_price\t-2\t1\t5\t10\t20\n}\n");
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<> template<>
|
||||
void URITestObject::test<18>()
|
||||
{
|
||||
|
|
@ -335,7 +359,7 @@ namespace tut
|
|||
ensure_equals("pathmap", u.pathArray()[1].asString(), "login");
|
||||
ensure_equals("query", u.query(), "first_name=Testert4&last_name=Tester&web_login_key=test");
|
||||
ensure_equals("query map element", u.queryMap()["last_name"].asString(), "Tester");
|
||||
|
||||
|
||||
u = LLURI("secondlife://Da Boom/128/128/128");
|
||||
// if secondlife is the scheme, LLURI should parse /128/128/128 as path, with Da Boom as authority
|
||||
ensure_equals("scheme", u.scheme(), "secondlife");
|
||||
|
|
@ -350,7 +374,7 @@ namespace tut
|
|||
template<> template<>
|
||||
void URITestObject::test<19>()
|
||||
{
|
||||
// Parse about: schemes
|
||||
set_test_name("Parse about: schemes");
|
||||
LLURI u("about:blank?redirect-http-hack=secondlife%3A%2F%2F%2Fapp%2Flogin%3Ffirst_name%3DCallum%26last_name%3DLinden%26location%3Dspecify%26grid%3Dvaak%26region%3D%2FMorris%2F128%2F128%26web_login_key%3Defaa4795-c2aa-4c58-8966-763c27931e78");
|
||||
ensure_equals("scheme", u.scheme(), "about");
|
||||
ensure_equals("authority", u.authority(), "");
|
||||
|
|
|
|||
|
|
@ -0,0 +1,186 @@
|
|||
# -*- cmake -*-
|
||||
|
||||
project(llcorehttp)
|
||||
|
||||
include(00-Common)
|
||||
include(GoogleMock)
|
||||
include(CURL)
|
||||
include(CARes)
|
||||
include(OpenSSL)
|
||||
include(ZLIB)
|
||||
include(LLCoreHttp)
|
||||
include(LLAddBuildTest)
|
||||
include(LLMessage)
|
||||
include(LLCommon)
|
||||
include(Tut)
|
||||
|
||||
include_directories (${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
include_directories(
|
||||
${LLMESSAGE_INCLUDE_DIRS}
|
||||
${LLCOMMON_INCLUDE_DIRS}
|
||||
${LLCOREHTTP_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
set(llcorehttp_SOURCE_FILES
|
||||
bufferarray.cpp
|
||||
bufferstream.cpp
|
||||
httpcommon.cpp
|
||||
httpheaders.cpp
|
||||
httpoptions.cpp
|
||||
httprequest.cpp
|
||||
httpresponse.cpp
|
||||
_httplibcurl.cpp
|
||||
_httpopcancel.cpp
|
||||
_httpoperation.cpp
|
||||
_httpoprequest.cpp
|
||||
_httpopsetget.cpp
|
||||
_httpopsetpriority.cpp
|
||||
_httppolicy.cpp
|
||||
_httppolicyclass.cpp
|
||||
_httppolicyglobal.cpp
|
||||
_httpreplyqueue.cpp
|
||||
_httprequestqueue.cpp
|
||||
_httpservice.cpp
|
||||
_refcounted.cpp
|
||||
)
|
||||
|
||||
set(llcorehttp_HEADER_FILES
|
||||
CMakeLists.txt
|
||||
|
||||
bufferarray.h
|
||||
bufferstream.h
|
||||
httpcommon.h
|
||||
httphandler.h
|
||||
httpheaders.h
|
||||
httpoptions.h
|
||||
httprequest.h
|
||||
httpresponse.h
|
||||
_httpinternal.h
|
||||
_httplibcurl.h
|
||||
_httpopcancel.h
|
||||
_httpoperation.h
|
||||
_httpoprequest.h
|
||||
_httpopsetget.h
|
||||
_httpopsetpriority.h
|
||||
_httppolicy.h
|
||||
_httppolicyclass.h
|
||||
_httppolicyglobal.h
|
||||
_httpreadyqueue.h
|
||||
_httpreplyqueue.h
|
||||
_httprequestqueue.h
|
||||
_httpservice.h
|
||||
_mutex.h
|
||||
_refcounted.h
|
||||
_thread.h
|
||||
)
|
||||
|
||||
set_source_files_properties(${llcorehttp_HEADER_FILES}
|
||||
PROPERTIES HEADER_FILE_ONLY TRUE)
|
||||
if (DARWIN OR LINUX)
|
||||
# Boost headers define unused members in condition_variable so...
|
||||
set_source_files_properties(${llcorehttp_SOURCE_FILES}
|
||||
PROPERTIES COMPILE_FLAGS -Wno-unused-variable)
|
||||
endif (DARWIN OR LINUX)
|
||||
|
||||
list(APPEND llcorehttp_SOURCE_FILES ${llcorehttp_HEADER_FILES})
|
||||
|
||||
add_library (llcorehttp ${llcorehttp_SOURCE_FILES})
|
||||
target_link_libraries(
|
||||
llcorehttp
|
||||
${CURL_LIBRARIES}
|
||||
${CARES_LIBRARIES}
|
||||
${OPENSSL_LIBRARIES}
|
||||
${CRYPTO_LIBRARIES}
|
||||
${BOOST_THREAD_LIBRARY}
|
||||
)
|
||||
|
||||
# tests
|
||||
if (LL_TESTS)
|
||||
SET(llcorehttp_TEST_SOURCE_FILES
|
||||
tests/test_allocator.cpp
|
||||
)
|
||||
|
||||
set(llcorehttp_TEST_HEADER_FILS
|
||||
tests/test_httpstatus.hpp
|
||||
tests/test_refcounted.hpp
|
||||
tests/test_httpoperation.hpp
|
||||
tests/test_httprequest.hpp
|
||||
tests/test_httprequestqueue.hpp
|
||||
tests/test_httpheaders.hpp
|
||||
tests/test_bufferarray.hpp
|
||||
tests/test_bufferstream.hpp
|
||||
)
|
||||
|
||||
set_source_files_properties(${llcorehttp_TEST_HEADER_FILES}
|
||||
PROPERTIES HEADER_FILE_ONLY TRUE)
|
||||
|
||||
list(APPEND llcorehttp_TEST_SOURCE_FILES ${llcorehttp_TEST_HEADER_FILES})
|
||||
|
||||
# LL_ADD_PROJECT_UNIT_TESTS(llcorehttp "${llcorehttp_TEST_SOURCE_FILES}")
|
||||
|
||||
# set(TEST_DEBUG on)
|
||||
set(test_libs
|
||||
${LLCOREHTTP_LIBRARIES}
|
||||
${WINDOWS_LIBRARIES}
|
||||
${LLMESSAGE_LIBRARIES}
|
||||
${LLCOMMON_LIBRARIES}
|
||||
${GOOGLEMOCK_LIBRARIES}
|
||||
${CURL_LIBRARIES}
|
||||
${CARES_LIBRARIES}
|
||||
${OPENSSL_LIBRARIES}
|
||||
${CRYPTO_LIBRARIES}
|
||||
${BOOST_SYSTEM_LIBRARY}
|
||||
${BOOST_THREAD_LIBRARY}
|
||||
)
|
||||
|
||||
LL_ADD_INTEGRATION_TEST(llcorehttp
|
||||
"${llcorehttp_TEST_SOURCE_FILES}"
|
||||
"${test_libs}"
|
||||
${PYTHON_EXECUTABLE}
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/tests/test_llcorehttp_peer.py"
|
||||
)
|
||||
|
||||
#
|
||||
# Example Programs
|
||||
#
|
||||
SET(llcorehttp_EXAMPLE_SOURCE_FILES
|
||||
examples/http_texture_load.cpp
|
||||
)
|
||||
|
||||
set(example_libs
|
||||
${LLCOREHTTP_LIBRARIES}
|
||||
${WINDOWS_LIBRARIES}
|
||||
${LLMESSAGE_LIBRARIES}
|
||||
${LLCOMMON_LIBRARIES}
|
||||
${GOOGLEMOCK_LIBRARIES}
|
||||
${CURL_LIBRARIES}
|
||||
${CARES_LIBRARIES}
|
||||
${OPENSSL_LIBRARIES}
|
||||
${CRYPTO_LIBRARIES}
|
||||
${BOOST_SYSTEM_LIBRARY}
|
||||
${BOOST_THREAD_LIBRARY}
|
||||
)
|
||||
|
||||
add_executable(http_texture_load
|
||||
${llcorehttp_EXAMPLE_SOURCE_FILES}
|
||||
)
|
||||
set_target_properties(http_texture_load
|
||||
PROPERTIES
|
||||
RUNTIME_OUTPUT_DIRECTORY "${EXE_STAGING_DIR}"
|
||||
)
|
||||
|
||||
if (WINDOWS)
|
||||
# The following come from LLAddBuildTest.cmake's INTEGRATION_TEST_xxxx target.
|
||||
set_target_properties(http_texture_load
|
||||
PROPERTIES
|
||||
LINK_FLAGS "/debug /NODEFAULTLIB:LIBCMT /SUBSYSTEM:WINDOWS /INCLUDE:__tcmalloc"
|
||||
LINK_FLAGS_DEBUG "/NODEFAULTLIB:\"LIBCMT;LIBCMTD;MSVCRT\" /INCREMENTAL:NO"
|
||||
LINK_FLAGS_RELEASE ""
|
||||
)
|
||||
endif (WINDOWS)
|
||||
|
||||
target_link_libraries(http_texture_load ${example_libs})
|
||||
|
||||
endif (LL_TESTS)
|
||||
|
||||
|
|
@ -0,0 +1,146 @@
|
|||
/**
|
||||
* @file _httpinternal.h
|
||||
* @brief Implementation constants and magic numbers
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, 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 _LLCORE_HTTP_INTERNAL_H_
|
||||
#define _LLCORE_HTTP_INTERNAL_H_
|
||||
|
||||
|
||||
// If you find this included in a public interface header,
|
||||
// something wrong is probably happening.
|
||||
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// General library to-do list
|
||||
//
|
||||
// - Implement policy classes. Structure is mostly there just didn't
|
||||
// need it for the first consumer.
|
||||
// - Consider Removing 'priority' from the request interface. Its use
|
||||
// in an always active class can lead to starvation of low-priority
|
||||
// requests. Requires coodination of priority values across all
|
||||
// components that share a class. Changing priority across threads
|
||||
// is slightly expensive (relative to gain) and hasn't been completely
|
||||
// implemented. And the major user of priority, texture fetches,
|
||||
// may not really need it.
|
||||
// - Set/get for global policy and policy classes is clumsy. Rework
|
||||
// it heading in a direction that allows for more dynamic behavior.
|
||||
// - Move HttpOpRequest::prepareRequest() to HttpLibcurl for the
|
||||
// pedantic.
|
||||
// - Update downloader and other long-duration services are going to
|
||||
// need a progress notification. Initial idea is to introduce a
|
||||
// 'repeating request' which can piggyback on another request and
|
||||
// persist until canceled or carrier completes. Current queue
|
||||
// structures allow an HttpOperation object to be enqueued
|
||||
// repeatedly, so...
|
||||
// - Investigate making c-ares' re-implementation of a resolver library
|
||||
// more resilient or more intelligent on Mac. Part of the DNS failure
|
||||
// lies in here. The mechanism also looks a little less dynamic
|
||||
// than needed in an environments where networking is changing.
|
||||
// - Global optimizations: 'borrowing' connections from other classes,
|
||||
// HTTP pipelining.
|
||||
// - Dynamic/control system stuff: detect problems and self-adjust.
|
||||
// This won't help in the face of the router problems we've looked
|
||||
// at, however. Detect starvation due to UDP activity and provide
|
||||
// feedback to it.
|
||||
//
|
||||
// Integration to-do list
|
||||
// - LLTextureFetch still needs a major refactor. The use of
|
||||
// LLQueuedThread makes it hard to inspect workers and do the
|
||||
// resource waiting we're now doing. Rebuild along simpler lines
|
||||
// some of which are suggested in new commentary at the top of
|
||||
// the main source file.
|
||||
// - Expand areas of usage eventually leading to the removal of LLCurl.
|
||||
// Rough order of expansion:
|
||||
// . Mesh fetch
|
||||
// . Avatar names
|
||||
// . Group membership lists
|
||||
// . Caps access in general
|
||||
// . 'The rest'
|
||||
// - Adapt texture cache, image decode and other image consumers to
|
||||
// the BufferArray model to reduce data copying. Alternatively,
|
||||
// adapt this library to something else.
|
||||
//
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
|
||||
// If '1', internal ready queues will not order ready
|
||||
// requests by priority, instead it's first-come-first-served.
|
||||
// Reprioritization requests have the side-effect of then
|
||||
// putting the modified request at the back of the ready queue.
|
||||
|
||||
#define LLCORE_HTTP_READY_QUEUE_IGNORES_PRIORITY 1
|
||||
|
||||
|
||||
namespace LLCore
|
||||
{
|
||||
|
||||
// Maxium number of policy classes that can be defined.
|
||||
// *TODO: Currently limited to the default class, extend.
|
||||
const int HTTP_POLICY_CLASS_LIMIT = 1;
|
||||
|
||||
// Debug/informational tracing. Used both
|
||||
// as a global option and in per-request traces.
|
||||
const int HTTP_TRACE_OFF = 0;
|
||||
const int HTTP_TRACE_LOW = 1;
|
||||
const int HTTP_TRACE_CURL_HEADERS = 2;
|
||||
const int HTTP_TRACE_CURL_BODIES = 3;
|
||||
|
||||
const int HTTP_TRACE_MIN = HTTP_TRACE_OFF;
|
||||
const int HTTP_TRACE_MAX = HTTP_TRACE_CURL_BODIES;
|
||||
|
||||
// Request retry limits
|
||||
const int HTTP_RETRY_COUNT_DEFAULT = 5;
|
||||
const int HTTP_RETRY_COUNT_MIN = 0;
|
||||
const int HTTP_RETRY_COUNT_MAX = 100;
|
||||
|
||||
const int HTTP_REDIRECTS_DEFAULT = 10;
|
||||
|
||||
// Timeout value used for both connect and protocol exchange.
|
||||
// Retries and time-on-queue are not included and aren't
|
||||
// accounted for.
|
||||
const long HTTP_REQUEST_TIMEOUT_DEFAULT = 30L;
|
||||
const long HTTP_REQUEST_TIMEOUT_MIN = 0L;
|
||||
const long HTTP_REQUEST_TIMEOUT_MAX = 3600L;
|
||||
|
||||
// Limits on connection counts
|
||||
const int HTTP_CONNECTION_LIMIT_DEFAULT = 8;
|
||||
const int HTTP_CONNECTION_LIMIT_MIN = 1;
|
||||
const int HTTP_CONNECTION_LIMIT_MAX = 256;
|
||||
|
||||
// Tuning parameters
|
||||
|
||||
// Time worker thread sleeps after a pass through the
|
||||
// request, ready and active queues.
|
||||
const int HTTP_SERVICE_LOOP_SLEEP_NORMAL_MS = 2;
|
||||
|
||||
// Block allocation size (a tuning parameter) is found
|
||||
// in bufferarray.h.
|
||||
|
||||
// Compatibility controls
|
||||
const bool HTTP_ENABLE_LINKSYS_WRT54G_V5_DNS_FIX = true;
|
||||
|
||||
} // end namespace LLCore
|
||||
|
||||
#endif // _LLCORE_HTTP_INTERNAL_H_
|
||||
|
|
@ -0,0 +1,373 @@
|
|||
/**
|
||||
* @file _httplibcurl.cpp
|
||||
* @brief Internal definitions of the Http libcurl thread
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include "_httplibcurl.h"
|
||||
|
||||
#include "httpheaders.h"
|
||||
#include "bufferarray.h"
|
||||
#include "_httpoprequest.h"
|
||||
#include "_httppolicy.h"
|
||||
|
||||
#include "llhttpstatuscodes.h"
|
||||
|
||||
|
||||
namespace LLCore
|
||||
{
|
||||
|
||||
|
||||
HttpLibcurl::HttpLibcurl(HttpService * service)
|
||||
: mService(service),
|
||||
mPolicyCount(0),
|
||||
mMultiHandles(NULL)
|
||||
{}
|
||||
|
||||
|
||||
HttpLibcurl::~HttpLibcurl()
|
||||
{
|
||||
shutdown();
|
||||
|
||||
mService = NULL;
|
||||
}
|
||||
|
||||
|
||||
void HttpLibcurl::shutdown()
|
||||
{
|
||||
while (! mActiveOps.empty())
|
||||
{
|
||||
HttpOpRequest * op(* mActiveOps.begin());
|
||||
mActiveOps.erase(mActiveOps.begin());
|
||||
|
||||
cancelRequest(op);
|
||||
op->release();
|
||||
}
|
||||
|
||||
if (mMultiHandles)
|
||||
{
|
||||
for (int policy_class(0); policy_class < mPolicyCount; ++policy_class)
|
||||
{
|
||||
if (mMultiHandles[policy_class])
|
||||
{
|
||||
curl_multi_cleanup(mMultiHandles[policy_class]);
|
||||
mMultiHandles[policy_class] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
delete [] mMultiHandles;
|
||||
mMultiHandles = NULL;
|
||||
}
|
||||
|
||||
mPolicyCount = 0;
|
||||
}
|
||||
|
||||
|
||||
void HttpLibcurl::start(int policy_count)
|
||||
{
|
||||
llassert_always(policy_count <= HTTP_POLICY_CLASS_LIMIT);
|
||||
llassert_always(! mMultiHandles); // One-time call only
|
||||
|
||||
mPolicyCount = policy_count;
|
||||
mMultiHandles = new CURLM * [mPolicyCount];
|
||||
for (int policy_class(0); policy_class < mPolicyCount; ++policy_class)
|
||||
{
|
||||
mMultiHandles[policy_class] = curl_multi_init();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Give libcurl some cycles, invoke it's callbacks, process
|
||||
// completed requests finalizing or issuing retries as needed.
|
||||
//
|
||||
// If active list goes empty *and* we didn't queue any
|
||||
// requests for retry, we return a request for a hard
|
||||
// sleep otherwise ask for a normal polling interval.
|
||||
HttpService::ELoopSpeed HttpLibcurl::processTransport()
|
||||
{
|
||||
HttpService::ELoopSpeed ret(HttpService::REQUEST_SLEEP);
|
||||
|
||||
// Give libcurl some cycles to do I/O & callbacks
|
||||
for (int policy_class(0); policy_class < mPolicyCount; ++policy_class)
|
||||
{
|
||||
if (! mMultiHandles[policy_class])
|
||||
continue;
|
||||
|
||||
int running(0);
|
||||
CURLMcode status(CURLM_CALL_MULTI_PERFORM);
|
||||
do
|
||||
{
|
||||
running = 0;
|
||||
status = curl_multi_perform(mMultiHandles[policy_class], &running);
|
||||
}
|
||||
while (0 != running && CURLM_CALL_MULTI_PERFORM == status);
|
||||
|
||||
// Run completion on anything done
|
||||
CURLMsg * msg(NULL);
|
||||
int msgs_in_queue(0);
|
||||
while ((msg = curl_multi_info_read(mMultiHandles[policy_class], &msgs_in_queue)))
|
||||
{
|
||||
if (CURLMSG_DONE == msg->msg)
|
||||
{
|
||||
CURL * handle(msg->easy_handle);
|
||||
CURLcode result(msg->data.result);
|
||||
|
||||
if (completeRequest(mMultiHandles[policy_class], handle, result))
|
||||
{
|
||||
// Request is still active, don't get too sleepy
|
||||
ret = HttpService::NORMAL;
|
||||
}
|
||||
handle = NULL; // No longer valid on return
|
||||
}
|
||||
else if (CURLMSG_NONE == msg->msg)
|
||||
{
|
||||
// Ignore this... it shouldn't mean anything.
|
||||
;
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS_ONCE("CoreHttp") << "Unexpected message from libcurl. Msg code: "
|
||||
<< msg->msg
|
||||
<< LL_ENDL;
|
||||
}
|
||||
msgs_in_queue = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (! mActiveOps.empty())
|
||||
{
|
||||
ret = HttpService::NORMAL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
// Caller has provided us with a ref count on op.
|
||||
void HttpLibcurl::addOp(HttpOpRequest * op)
|
||||
{
|
||||
llassert_always(op->mReqPolicy < mPolicyCount);
|
||||
llassert_always(mMultiHandles[op->mReqPolicy] != NULL);
|
||||
|
||||
// Create standard handle
|
||||
if (! op->prepareRequest(mService))
|
||||
{
|
||||
// Couldn't issue request, fail with notification
|
||||
// *TODO: Need failure path
|
||||
return;
|
||||
}
|
||||
|
||||
// Make the request live
|
||||
curl_multi_add_handle(mMultiHandles[op->mReqPolicy], op->mCurlHandle);
|
||||
op->mCurlActive = true;
|
||||
|
||||
if (op->mTracing > HTTP_TRACE_OFF)
|
||||
{
|
||||
HttpPolicy & policy(mService->getPolicy());
|
||||
|
||||
LL_INFOS("CoreHttp") << "TRACE, ToActiveQueue, Handle: "
|
||||
<< static_cast<HttpHandle>(op)
|
||||
<< ", Actives: " << mActiveOps.size()
|
||||
<< ", Readies: " << policy.getReadyCount(op->mReqPolicy)
|
||||
<< LL_ENDL;
|
||||
}
|
||||
|
||||
// On success, make operation active
|
||||
mActiveOps.insert(op);
|
||||
}
|
||||
|
||||
|
||||
// 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
|
||||
// method to kill the request.
|
||||
bool HttpLibcurl::cancel(HttpHandle handle)
|
||||
{
|
||||
HttpOpRequest * op(static_cast<HttpOpRequest *>(handle));
|
||||
active_set_t::iterator it(mActiveOps.find(op));
|
||||
if (mActiveOps.end() == it)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Cancel request
|
||||
cancelRequest(op);
|
||||
|
||||
// Drop references
|
||||
mActiveOps.erase(it);
|
||||
op->release();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// *NOTE: cancelRequest logic parallels completeRequest logic.
|
||||
// Keep them synchronized as necessary. Caller is expected to
|
||||
// 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)
|
||||
{
|
||||
// Deactivate request
|
||||
op->mCurlActive = false;
|
||||
|
||||
// Detach from multi and recycle handle
|
||||
curl_multi_remove_handle(mMultiHandles[op->mReqPolicy], op->mCurlHandle);
|
||||
curl_easy_cleanup(op->mCurlHandle);
|
||||
op->mCurlHandle = NULL;
|
||||
|
||||
// Tracing
|
||||
if (op->mTracing > HTTP_TRACE_OFF)
|
||||
{
|
||||
LL_INFOS("CoreHttp") << "TRACE, RequestCanceled, Handle: "
|
||||
<< static_cast<HttpHandle>(op)
|
||||
<< ", Status: " << op->mStatus.toHex()
|
||||
<< LL_ENDL;
|
||||
}
|
||||
|
||||
// Cancel op and deliver for notification
|
||||
op->cancel();
|
||||
}
|
||||
|
||||
|
||||
// *NOTE: cancelRequest logic parallels completeRequest logic.
|
||||
// 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);
|
||||
|
||||
if (handle != op->mCurlHandle || ! op->mCurlActive)
|
||||
{
|
||||
LL_WARNS("CoreHttp") << "libcurl handle and HttpOpRequest handle in disagreement or inactive request."
|
||||
<< " Handle: " << static_cast<HttpHandle>(handle)
|
||||
<< LL_ENDL;
|
||||
return false;
|
||||
}
|
||||
|
||||
active_set_t::iterator it(mActiveOps.find(op));
|
||||
if (mActiveOps.end() == it)
|
||||
{
|
||||
LL_WARNS("CoreHttp") << "libcurl completion for request not on active list. Continuing."
|
||||
<< " Handle: " << static_cast<HttpHandle>(handle)
|
||||
<< LL_ENDL;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Deactivate request
|
||||
mActiveOps.erase(it);
|
||||
op->mCurlActive = false;
|
||||
|
||||
// Set final status of request if it hasn't failed by other mechanisms yet
|
||||
if (op->mStatus)
|
||||
{
|
||||
op->mStatus = HttpStatus(HttpStatus::EXT_CURL_EASY, status);
|
||||
}
|
||||
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("CoreHttp") << "Invalid HTTP response code ("
|
||||
<< http_status << ") received from server."
|
||||
<< LL_ENDL;
|
||||
op->mStatus = HttpStatus(HttpStatus::LLCORE, HE_INVALID_HTTP_STATUS);
|
||||
}
|
||||
}
|
||||
|
||||
// Detach from multi and recycle handle
|
||||
curl_multi_remove_handle(multi_handle, handle);
|
||||
curl_easy_cleanup(handle);
|
||||
op->mCurlHandle = NULL;
|
||||
|
||||
// Tracing
|
||||
if (op->mTracing > HTTP_TRACE_OFF)
|
||||
{
|
||||
LL_INFOS("CoreHttp") << "TRACE, RequestComplete, Handle: "
|
||||
<< static_cast<HttpHandle>(op)
|
||||
<< ", Status: " << op->mStatus.toHex()
|
||||
<< LL_ENDL;
|
||||
}
|
||||
|
||||
// Dispatch to next stage
|
||||
HttpPolicy & policy(mService->getPolicy());
|
||||
bool still_active(policy.stageAfterCompletion(op));
|
||||
|
||||
return still_active;
|
||||
}
|
||||
|
||||
|
||||
int HttpLibcurl::getActiveCount() const
|
||||
{
|
||||
return mActiveOps.size();
|
||||
}
|
||||
|
||||
|
||||
int HttpLibcurl::getActiveCountInClass(int policy_class) const
|
||||
{
|
||||
int count(0);
|
||||
|
||||
for (active_set_t::const_iterator iter(mActiveOps.begin());
|
||||
mActiveOps.end() != iter;
|
||||
++iter)
|
||||
{
|
||||
if ((*iter)->mReqPolicy == policy_class)
|
||||
{
|
||||
++count;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------
|
||||
// Free functions
|
||||
// ---------------------------------------
|
||||
|
||||
|
||||
struct curl_slist * append_headers_to_slist(const HttpHeaders * headers, struct curl_slist * slist)
|
||||
{
|
||||
for (HttpHeaders::container_t::const_iterator it(headers->mHeaders.begin());
|
||||
|
||||
headers->mHeaders.end() != it;
|
||||
++it)
|
||||
{
|
||||
slist = curl_slist_append(slist, (*it).c_str());
|
||||
}
|
||||
return slist;
|
||||
}
|
||||
|
||||
|
||||
} // end namespace LLCore
|
||||
|
|
@ -0,0 +1,129 @@
|
|||
/**
|
||||
* @file _httplibcurl.h
|
||||
* @brief Declarations for internal class providing libcurl transport.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, 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 _LLCORE_HTTP_LIBCURL_H_
|
||||
#define _LLCORE_HTTP_LIBCURL_H_
|
||||
|
||||
#include "linden_common.h" // Modifies curl/curl.h interfaces
|
||||
|
||||
#include <curl/curl.h>
|
||||
#include <curl/multi.h>
|
||||
|
||||
#include <set>
|
||||
|
||||
#include "httprequest.h"
|
||||
#include "_httpservice.h"
|
||||
#include "_httpinternal.h"
|
||||
|
||||
|
||||
namespace LLCore
|
||||
{
|
||||
|
||||
|
||||
class HttpPolicy;
|
||||
class HttpOpRequest;
|
||||
class HttpHeaders;
|
||||
|
||||
|
||||
/// Implements libcurl-based transport for an HttpService instance.
|
||||
///
|
||||
/// Threading: Single-threaded. Other than for construction/destruction,
|
||||
/// all methods are expected to be invoked in a single thread, typically
|
||||
/// a worker thread of some sort.
|
||||
|
||||
class HttpLibcurl
|
||||
{
|
||||
public:
|
||||
HttpLibcurl(HttpService * service);
|
||||
virtual ~HttpLibcurl();
|
||||
|
||||
private:
|
||||
HttpLibcurl(const HttpLibcurl &); // Not defined
|
||||
void operator=(const HttpLibcurl &); // Not defined
|
||||
|
||||
public:
|
||||
/// 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.
|
||||
///
|
||||
/// @return Indication of how long this method is
|
||||
/// willing to wait for next service call.
|
||||
HttpService::ELoopSpeed processTransport();
|
||||
|
||||
/// Add request to the active list. Caller is expected to have
|
||||
/// provided us with a reference count on the op to hold the
|
||||
/// request. (No additional references will be added.)
|
||||
void addOp(HttpOpRequest * op);
|
||||
|
||||
/// One-time call to set the number of policy classes to be
|
||||
/// serviced and to create the resources for each. Value
|
||||
/// must agree with HttpPolicy::setPolicies() call.
|
||||
void start(int policy_count);
|
||||
|
||||
/// Synchronously stop libcurl operations. All active requests
|
||||
/// are canceled and removed from libcurl's handling. Easy
|
||||
/// handles are detached from their multi handles and released.
|
||||
/// Multi handles are also released. Canceled requests are
|
||||
/// completed with canceled status and made available on their
|
||||
/// respective reply queues.
|
||||
///
|
||||
/// Can be restarted with a start() call.
|
||||
void shutdown();
|
||||
|
||||
/// Return global and per-class counts of active requests.
|
||||
int getActiveCount() const;
|
||||
int getActiveCountInClass(int policy_class) const;
|
||||
|
||||
/// Attempt to cancel a request identified by handle.
|
||||
///
|
||||
/// Interface shadows HttpService's method.
|
||||
///
|
||||
/// @return True if handle was found and operation canceled.
|
||||
///
|
||||
bool cancel(HttpHandle handle);
|
||||
|
||||
protected:
|
||||
/// Invoked when libcurl has indicated a request has been processed
|
||||
/// to completion and we need to move the request to a new state.
|
||||
bool completeRequest(CURLM * multi_handle, CURL * handle, CURLcode status);
|
||||
|
||||
/// Invoked to cancel an active request, mainly during shutdown
|
||||
/// and destroy.
|
||||
void cancelRequest(HttpOpRequest * op);
|
||||
|
||||
protected:
|
||||
typedef std::set<HttpOpRequest *> active_set_t;
|
||||
|
||||
protected:
|
||||
HttpService * mService; // Simple reference, not owner
|
||||
active_set_t mActiveOps;
|
||||
int mPolicyCount;
|
||||
CURLM ** mMultiHandles;
|
||||
}; // end class HttpLibcurl
|
||||
|
||||
} // end namespace LLCore
|
||||
|
||||
#endif // _LLCORE_HTTP_LIBCURL_H_
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
/**
|
||||
* @file _httpopcancel.cpp
|
||||
* @brief Definitions for internal class HttpOpCancel
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include "_httpopcancel.h"
|
||||
|
||||
#include "httpcommon.h"
|
||||
#include "httphandler.h"
|
||||
#include "httpresponse.h"
|
||||
|
||||
#include "_httpservice.h"
|
||||
|
||||
|
||||
namespace LLCore
|
||||
{
|
||||
|
||||
|
||||
// ==================================
|
||||
// HttpOpCancel
|
||||
// ==================================
|
||||
|
||||
|
||||
HttpOpCancel::HttpOpCancel(HttpHandle handle)
|
||||
: HttpOperation(),
|
||||
mHandle(handle)
|
||||
{}
|
||||
|
||||
|
||||
HttpOpCancel::~HttpOpCancel()
|
||||
{}
|
||||
|
||||
|
||||
// Immediately search for the request on various queues
|
||||
// and cancel operations if found. Return the status of
|
||||
// the search and cancel as the status of this request.
|
||||
// The canceled request will return a canceled status to
|
||||
// its handler.
|
||||
void HttpOpCancel::stageFromRequest(HttpService * service)
|
||||
{
|
||||
if (! service->cancel(mHandle))
|
||||
{
|
||||
mStatus = HttpStatus(HttpStatus::LLCORE, HE_HANDLE_NOT_FOUND);
|
||||
}
|
||||
|
||||
addAsReply();
|
||||
}
|
||||
|
||||
|
||||
} // end namespace LLCore
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
/**
|
||||
* @file _httpopcancel.h
|
||||
* @brief Internal declarations for the HttpOpCancel subclass
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, 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 _LLCORE_HTTP_OPCANCEL_H_
|
||||
#define _LLCORE_HTTP_OPCANCEL_H_
|
||||
|
||||
|
||||
#include "linden_common.h" // Modifies curl/curl.h interfaces
|
||||
|
||||
#include "httpcommon.h"
|
||||
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include "_httpoperation.h"
|
||||
#include "_refcounted.h"
|
||||
|
||||
|
||||
namespace LLCore
|
||||
{
|
||||
|
||||
|
||||
/// HttpOpCancel requests that a previously issued request
|
||||
/// be canceled, if possible. This includes active requests
|
||||
/// that may be in the middle of an HTTP transaction. Any
|
||||
/// completed request will not be canceled and will return
|
||||
/// its final status unchanged and *this* request will complete
|
||||
/// with an HE_HANDLE_NOT_FOUND error status.
|
||||
|
||||
class HttpOpCancel : public HttpOperation
|
||||
{
|
||||
public:
|
||||
/// @param handle Handle of previously-issued request to
|
||||
/// 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 *);
|
||||
|
||||
public:
|
||||
// Request data
|
||||
HttpHandle mHandle;
|
||||
}; // end class HttpOpCancel
|
||||
|
||||
|
||||
} // end namespace LLCore
|
||||
|
||||
#endif // _LLCORE_HTTP_OPCANCEL_H_
|
||||
|
||||
|
|
@ -0,0 +1,248 @@
|
|||
/**
|
||||
* @file _httpoperation.cpp
|
||||
* @brief Definitions for internal classes based on HttpOperation
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include "_httpoperation.h"
|
||||
|
||||
#include "httphandler.h"
|
||||
#include "httpresponse.h"
|
||||
#include "httprequest.h"
|
||||
|
||||
#include "_httprequestqueue.h"
|
||||
#include "_httpreplyqueue.h"
|
||||
#include "_httpservice.h"
|
||||
#include "_httpinternal.h"
|
||||
|
||||
#include "lltimer.h"
|
||||
|
||||
|
||||
namespace LLCore
|
||||
{
|
||||
|
||||
|
||||
// ==================================
|
||||
// HttpOperation
|
||||
// ==================================
|
||||
|
||||
|
||||
HttpOperation::HttpOperation()
|
||||
: LLCoreInt::RefCounted(true),
|
||||
mReplyQueue(NULL),
|
||||
mUserHandler(NULL),
|
||||
mReqPolicy(HttpRequest::DEFAULT_POLICY_ID),
|
||||
mReqPriority(0U),
|
||||
mTracing(0)
|
||||
{
|
||||
mMetricCreated = totalTime();
|
||||
}
|
||||
|
||||
|
||||
HttpOperation::~HttpOperation()
|
||||
{
|
||||
setReplyPath(NULL, NULL);
|
||||
}
|
||||
|
||||
|
||||
void HttpOperation::setReplyPath(HttpReplyQueue * reply_queue,
|
||||
HttpHandler * user_handler)
|
||||
{
|
||||
if (reply_queue != mReplyQueue)
|
||||
{
|
||||
if (mReplyQueue)
|
||||
{
|
||||
mReplyQueue->release();
|
||||
}
|
||||
|
||||
if (reply_queue)
|
||||
{
|
||||
reply_queue->addRef();
|
||||
}
|
||||
|
||||
mReplyQueue = reply_queue;
|
||||
}
|
||||
|
||||
// Not refcounted
|
||||
mUserHandler = user_handler;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void HttpOperation::stageFromRequest(HttpService *)
|
||||
{
|
||||
// Default implementation should never be called. This
|
||||
// indicates an operation making a transition that isn't
|
||||
// defined.
|
||||
LL_ERRS("HttpCore") << "Default stageFromRequest method may not be called."
|
||||
<< LL_ENDL;
|
||||
}
|
||||
|
||||
|
||||
void HttpOperation::stageFromReady(HttpService *)
|
||||
{
|
||||
// Default implementation should never be called. This
|
||||
// indicates an operation making a transition that isn't
|
||||
// defined.
|
||||
LL_ERRS("HttpCore") << "Default stageFromReady method may not be called."
|
||||
<< LL_ENDL;
|
||||
}
|
||||
|
||||
|
||||
void HttpOperation::stageFromActive(HttpService *)
|
||||
{
|
||||
// Default implementation should never be called. This
|
||||
// indicates an operation making a transition that isn't
|
||||
// defined.
|
||||
LL_ERRS("HttpCore") << "Default stageFromActive method may not be called."
|
||||
<< LL_ENDL;
|
||||
}
|
||||
|
||||
|
||||
void HttpOperation::visitNotifier(HttpRequest *)
|
||||
{
|
||||
if (mUserHandler)
|
||||
{
|
||||
HttpResponse * response = new HttpResponse();
|
||||
|
||||
response->setStatus(mStatus);
|
||||
mUserHandler->onCompleted(static_cast<HttpHandle>(this), response);
|
||||
|
||||
response->release();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
HttpStatus HttpOperation::cancel()
|
||||
{
|
||||
HttpStatus status;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
void HttpOperation::addAsReply()
|
||||
{
|
||||
if (mTracing > HTTP_TRACE_OFF)
|
||||
{
|
||||
LL_INFOS("CoreHttp") << "TRACE, ToReplyQueue, Handle: "
|
||||
<< static_cast<HttpHandle>(this)
|
||||
<< LL_ENDL;
|
||||
}
|
||||
|
||||
if (mReplyQueue)
|
||||
{
|
||||
addRef();
|
||||
mReplyQueue->addOp(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ==================================
|
||||
// HttpOpStop
|
||||
// ==================================
|
||||
|
||||
|
||||
HttpOpStop::HttpOpStop()
|
||||
: HttpOperation()
|
||||
{}
|
||||
|
||||
|
||||
HttpOpStop::~HttpOpStop()
|
||||
{}
|
||||
|
||||
|
||||
void HttpOpStop::stageFromRequest(HttpService * service)
|
||||
{
|
||||
// Do operations
|
||||
service->stopRequested();
|
||||
|
||||
// Prepare response if needed
|
||||
addAsReply();
|
||||
}
|
||||
|
||||
|
||||
// ==================================
|
||||
// HttpOpNull
|
||||
// ==================================
|
||||
|
||||
|
||||
HttpOpNull::HttpOpNull()
|
||||
: HttpOperation()
|
||||
{}
|
||||
|
||||
|
||||
HttpOpNull::~HttpOpNull()
|
||||
{}
|
||||
|
||||
|
||||
void HttpOpNull::stageFromRequest(HttpService * service)
|
||||
{
|
||||
// Perform op
|
||||
// Nothing to perform. This doesn't fall into the libcurl
|
||||
// ready/active queues, it just bounces over to the reply
|
||||
// queue directly.
|
||||
|
||||
// Prepare response if needed
|
||||
addAsReply();
|
||||
}
|
||||
|
||||
|
||||
// ==================================
|
||||
// HttpOpSpin
|
||||
// ==================================
|
||||
|
||||
|
||||
HttpOpSpin::HttpOpSpin(int mode)
|
||||
: HttpOperation(),
|
||||
mMode(mode)
|
||||
{}
|
||||
|
||||
|
||||
HttpOpSpin::~HttpOpSpin()
|
||||
{}
|
||||
|
||||
|
||||
void HttpOpSpin::stageFromRequest(HttpService * service)
|
||||
{
|
||||
if (0 == mMode)
|
||||
{
|
||||
// Spin forever
|
||||
while (true)
|
||||
{
|
||||
ms_sleep(100);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ms_sleep(1); // backoff interlock plumbing a bit
|
||||
this->addRef();
|
||||
if (! service->getRequestQueue().addOp(this))
|
||||
{
|
||||
this->release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // end namespace LLCore
|
||||
|
|
@ -0,0 +1,262 @@
|
|||
/**
|
||||
* @file _httpoperation.h
|
||||
* @brief Internal declarations for HttpOperation and sub-classes
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, 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 _LLCORE_HTTP_OPERATION_H_
|
||||
#define _LLCORE_HTTP_OPERATION_H_
|
||||
|
||||
|
||||
#include "httpcommon.h"
|
||||
#include "httprequest.h"
|
||||
#include "_refcounted.h"
|
||||
|
||||
|
||||
namespace LLCore
|
||||
{
|
||||
|
||||
class HttpReplyQueue;
|
||||
class HttpHandler;
|
||||
class HttpService;
|
||||
|
||||
/// HttpOperation is the base class for all request/reply
|
||||
/// pairs.
|
||||
///
|
||||
/// Operations are expected to be of two types: immediate
|
||||
/// and queued. Immediate requests go to the singleton
|
||||
/// request queue and when picked up by the worker thread
|
||||
/// are executed immediately and there results placed on
|
||||
/// the supplied reply queue. Queued requests (namely for
|
||||
/// HTTP operations), go to the request queue, are picked
|
||||
/// up and moved to a ready queue where they're ordered by
|
||||
/// priority and managed by the policy component, are
|
||||
/// then activated issuing HTTP requests and moved to an
|
||||
/// active list managed by the transport (libcurl) component
|
||||
/// and eventually finalized when a response is available
|
||||
/// and status and data return via reply queue.
|
||||
///
|
||||
/// To manage these transitions, derived classes implement
|
||||
/// three methods: stageFromRequest, stageFromReady and
|
||||
/// stageFromActive. Immediate requests will only override
|
||||
/// stageFromRequest which will perform the operation and
|
||||
/// return the result by invoking addAsReply() to put the
|
||||
/// request on a reply queue. Queued requests will involve
|
||||
/// all three stage methods.
|
||||
///
|
||||
/// Threading: not thread-safe. Base and derived classes
|
||||
/// provide no locking. Instances move across threads
|
||||
/// via queue-like interfaces that are thread compatible
|
||||
/// and those interfaces establish the access rules.
|
||||
|
||||
class HttpOperation : public LLCoreInt::RefCounted
|
||||
{
|
||||
public:
|
||||
/// Threading: called by a consumer/application 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.
|
||||
///
|
||||
/// Invokers of operations that want to receive notification that an
|
||||
/// operation has been completed do so by binding a reply queue and
|
||||
/// a handler object to the request.
|
||||
///
|
||||
/// @param reply_queue Pointer to the reply queue where completion
|
||||
/// notifications are to be queued (typically
|
||||
/// by addAsReply()). This will typically be
|
||||
/// the reply queue referenced by the request
|
||||
/// object. This method will increment the
|
||||
/// refcount on the queue holding the queue
|
||||
/// until delivery is complete. Using a reply_queue
|
||||
/// even if the handler is NULL has some benefits
|
||||
/// for memory deallocation by keeping it in the
|
||||
/// originating thread.
|
||||
///
|
||||
/// @param handler Possibly NULL pointer to a non-refcounted
|
||||
//// handler object to be invoked (onCompleted)
|
||||
/// when the operation is finished. Note that
|
||||
/// the handler object is never dereferenced
|
||||
/// by the worker thread. This is passible data
|
||||
/// until notification is performed.
|
||||
///
|
||||
/// Threading: called by application thread.
|
||||
///
|
||||
void setReplyPath(HttpReplyQueue * reply_queue,
|
||||
HttpHandler * handler);
|
||||
|
||||
/// The three possible staging steps in an operation's lifecycle.
|
||||
/// Asynchronous requests like HTTP operations move from the
|
||||
/// request queue to the ready queue via stageFromRequest. Then
|
||||
/// from the ready queue to the active queue by stageFromReady. And
|
||||
/// when complete, to the reply queue via stageFromActive and the
|
||||
/// addAsReply utility.
|
||||
///
|
||||
/// Immediate mode operations (everything else) move from the
|
||||
/// request queue to the reply queue directly via stageFromRequest
|
||||
/// and addAsReply with no existence on the ready or active queues.
|
||||
///
|
||||
/// These methods will take out a reference count on the request,
|
||||
/// caller only needs to dispose of its reference when done with
|
||||
/// the request.
|
||||
///
|
||||
/// Threading: called by worker thread.
|
||||
///
|
||||
virtual void stageFromRequest(HttpService *);
|
||||
virtual void stageFromReady(HttpService *);
|
||||
virtual void stageFromActive(HttpService *);
|
||||
|
||||
/// Delivers a notification to a handler object on completion.
|
||||
///
|
||||
/// Once a request is complete and it has been removed from its
|
||||
/// reply queue, a handler notification may be delivered by a
|
||||
/// call to HttpRequest::update(). This method does the necessary
|
||||
/// dispatching.
|
||||
///
|
||||
/// Threading: called by application thread.
|
||||
///
|
||||
virtual void visitNotifier(HttpRequest *);
|
||||
|
||||
/// Cancels the operation whether queued or active.
|
||||
/// Final status of the request becomes canceled (an error) and
|
||||
/// that will be delivered to caller via notification scheme.
|
||||
///
|
||||
/// Threading: called by worker thread.
|
||||
///
|
||||
virtual HttpStatus cancel();
|
||||
|
||||
protected:
|
||||
/// Delivers request to reply queue on completion. After this
|
||||
/// call, worker thread no longer accesses the object and it
|
||||
/// is owned by the reply queue.
|
||||
///
|
||||
/// Threading: called by worker thread.
|
||||
///
|
||||
void addAsReply();
|
||||
|
||||
protected:
|
||||
HttpReplyQueue * mReplyQueue; // Have refcount
|
||||
HttpHandler * mUserHandler; // Naked pointer
|
||||
|
||||
public:
|
||||
// Request Data
|
||||
HttpRequest::policy_t mReqPolicy;
|
||||
HttpRequest::priority_t mReqPriority;
|
||||
|
||||
// Reply Data
|
||||
HttpStatus mStatus;
|
||||
|
||||
// Tracing, debug and metrics
|
||||
HttpTime mMetricCreated;
|
||||
int mTracing;
|
||||
}; // end class HttpOperation
|
||||
|
||||
|
||||
/// HttpOpStop requests the servicing thread to shutdown
|
||||
/// operations, cease pulling requests from the request
|
||||
/// queue and release shared resources (particularly
|
||||
/// those shared via reference count). The servicing
|
||||
/// thread will then exit. The underlying thread object
|
||||
/// remains so that another thread can join on the
|
||||
/// servicing thread prior to final cleanup. The
|
||||
/// request *does* generate a reply on the response
|
||||
/// queue, if requested.
|
||||
|
||||
class HttpOpStop : public HttpOperation
|
||||
{
|
||||
public:
|
||||
HttpOpStop();
|
||||
|
||||
protected:
|
||||
virtual ~HttpOpStop();
|
||||
|
||||
private:
|
||||
HttpOpStop(const HttpOpStop &); // Not defined
|
||||
void operator=(const HttpOpStop &); // Not defined
|
||||
|
||||
public:
|
||||
virtual void stageFromRequest(HttpService *);
|
||||
|
||||
}; // end class HttpOpStop
|
||||
|
||||
|
||||
/// HttpOpNull is a do-nothing operation used for testing via
|
||||
/// a basic loopback pattern. It's executed immediately by
|
||||
/// the servicing thread which bounces a reply back to the
|
||||
/// caller without any further delay.
|
||||
|
||||
class HttpOpNull : public HttpOperation
|
||||
{
|
||||
public:
|
||||
HttpOpNull();
|
||||
|
||||
protected:
|
||||
virtual ~HttpOpNull();
|
||||
|
||||
private:
|
||||
HttpOpNull(const HttpOpNull &); // Not defined
|
||||
void operator=(const HttpOpNull &); // Not defined
|
||||
|
||||
public:
|
||||
virtual void stageFromRequest(HttpService *);
|
||||
|
||||
}; // end class HttpOpNull
|
||||
|
||||
|
||||
/// HttpOpSpin is a test-only request that puts the worker
|
||||
/// thread into a cpu spin. Used for unit tests and cleanup
|
||||
/// evaluation. You do not want to use this in production.
|
||||
class HttpOpSpin : public HttpOperation
|
||||
{
|
||||
public:
|
||||
// 0 does a hard spin in the operation
|
||||
// 1 does a soft spin continuously requeuing itself
|
||||
HttpOpSpin(int mode);
|
||||
|
||||
protected:
|
||||
virtual ~HttpOpSpin();
|
||||
|
||||
private:
|
||||
HttpOpSpin(const HttpOpSpin &); // Not defined
|
||||
void operator=(const HttpOpSpin &); // Not defined
|
||||
|
||||
public:
|
||||
virtual void stageFromRequest(HttpService *);
|
||||
|
||||
protected:
|
||||
int mMode;
|
||||
}; // end class HttpOpSpin
|
||||
|
||||
|
||||
} // end namespace LLCore
|
||||
|
||||
#endif // _LLCORE_HTTP_OPERATION_H_
|
||||
|
||||
|
|
@ -0,0 +1,906 @@
|
|||
/**
|
||||
* @file _httpoprequest.cpp
|
||||
* @brief Definitions for internal class HttpOpRequest
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include "_httpoprequest.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <algorithm>
|
||||
|
||||
#include "httpcommon.h"
|
||||
#include "httphandler.h"
|
||||
#include "httpresponse.h"
|
||||
#include "bufferarray.h"
|
||||
#include "httpheaders.h"
|
||||
#include "httpoptions.h"
|
||||
|
||||
#include "_httprequestqueue.h"
|
||||
#include "_httpreplyqueue.h"
|
||||
#include "_httpservice.h"
|
||||
#include "_httppolicy.h"
|
||||
#include "_httppolicyglobal.h"
|
||||
#include "_httplibcurl.h"
|
||||
#include "_httpinternal.h"
|
||||
|
||||
#include "llhttpstatuscodes.h"
|
||||
#include "llproxy.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
// Attempts to parse a 'Content-Range:' header. Caller must already
|
||||
// have verified that the header tag is present. The 'buffer' argument
|
||||
// will be processed by strtok_r calls which will modify the buffer.
|
||||
//
|
||||
// @return -1 if invalid and response should be dropped, 0 if valid an
|
||||
// correct, 1 if couldn't be parsed. If 0, the first, last,
|
||||
// and length arguments are also written. 'length' may be
|
||||
// 0 if the length wasn't available to the server.
|
||||
//
|
||||
int parse_content_range_header(char * buffer,
|
||||
unsigned int * first,
|
||||
unsigned int * last,
|
||||
unsigned int * length);
|
||||
|
||||
|
||||
// Take data from libcurl's CURLOPT_DEBUGFUNCTION callback and
|
||||
// escape and format it for a tracing line in logging. Absolutely
|
||||
// anything including NULs can be in the data. If @scrub is true,
|
||||
// non-printing or non-ascii characters are replaced with spaces
|
||||
// otherwise a %XX form of escaping is used.
|
||||
void escape_libcurl_debug_data(char * buffer, size_t len, bool scrub,
|
||||
std::string & safe_line);
|
||||
|
||||
|
||||
// OS-neutral string comparisons of various types
|
||||
int os_strncasecmp(const char *s1, const char *s2, size_t n);
|
||||
int os_strcasecmp(const char *s1, const char *s2);
|
||||
char * os_strtok_r(char *str, const char *delim, char **saveptr);
|
||||
|
||||
|
||||
static const char * const hdr_whitespace(" \t");
|
||||
static const char * const hdr_separator(": \t");
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
|
||||
namespace LLCore
|
||||
{
|
||||
|
||||
|
||||
HttpOpRequest::HttpOpRequest()
|
||||
: HttpOperation(),
|
||||
mProcFlags(0U),
|
||||
mReqMethod(HOR_GET),
|
||||
mReqBody(NULL),
|
||||
mReqOffset(0),
|
||||
mReqLength(0),
|
||||
mReqHeaders(NULL),
|
||||
mReqOptions(NULL),
|
||||
mCurlActive(false),
|
||||
mCurlHandle(NULL),
|
||||
mCurlService(NULL),
|
||||
mCurlHeaders(NULL),
|
||||
mCurlBodyPos(0),
|
||||
mReplyBody(NULL),
|
||||
mReplyOffset(0),
|
||||
mReplyLength(0),
|
||||
mReplyFullLength(0),
|
||||
mReplyHeaders(NULL),
|
||||
mPolicyRetries(0),
|
||||
mPolicyRetryAt(HttpTime(0)),
|
||||
mPolicyRetryLimit(HTTP_RETRY_COUNT_DEFAULT)
|
||||
{
|
||||
// *NOTE: As members are added, retry initialization/cleanup
|
||||
// may need to be extended in @see prepareRequest().
|
||||
}
|
||||
|
||||
|
||||
|
||||
HttpOpRequest::~HttpOpRequest()
|
||||
{
|
||||
if (mReqBody)
|
||||
{
|
||||
mReqBody->release();
|
||||
mReqBody = NULL;
|
||||
}
|
||||
|
||||
if (mReqOptions)
|
||||
{
|
||||
mReqOptions->release();
|
||||
mReqOptions = NULL;
|
||||
}
|
||||
|
||||
if (mReqHeaders)
|
||||
{
|
||||
mReqHeaders->release();
|
||||
mReqHeaders = NULL;
|
||||
}
|
||||
|
||||
if (mCurlHandle)
|
||||
{
|
||||
curl_easy_cleanup(mCurlHandle);
|
||||
mCurlHandle = NULL;
|
||||
}
|
||||
|
||||
mCurlService = NULL;
|
||||
|
||||
if (mCurlHeaders)
|
||||
{
|
||||
curl_slist_free_all(mCurlHeaders);
|
||||
mCurlHeaders = NULL;
|
||||
}
|
||||
|
||||
if (mReplyBody)
|
||||
{
|
||||
mReplyBody->release();
|
||||
mReplyBody = NULL;
|
||||
}
|
||||
|
||||
if (mReplyHeaders)
|
||||
{
|
||||
mReplyHeaders->release();
|
||||
mReplyHeaders = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void HttpOpRequest::stageFromRequest(HttpService * service)
|
||||
{
|
||||
addRef();
|
||||
service->getPolicy().addOp(this); // transfers refcount
|
||||
}
|
||||
|
||||
|
||||
void HttpOpRequest::stageFromReady(HttpService * service)
|
||||
{
|
||||
addRef();
|
||||
service->getTransport().addOp(this); // transfers refcount
|
||||
}
|
||||
|
||||
|
||||
void HttpOpRequest::stageFromActive(HttpService * service)
|
||||
{
|
||||
if (mReplyLength)
|
||||
{
|
||||
// If non-zero, we received and processed a Content-Range
|
||||
// header with the response. Verify that what it says
|
||||
// is consistent with the received data.
|
||||
if (mReplyLength != mReplyBody->size())
|
||||
{
|
||||
// Not as expected, fail the request
|
||||
mStatus = HttpStatus(HttpStatus::LLCORE, HE_INV_CONTENT_RANGE_HDR);
|
||||
}
|
||||
}
|
||||
|
||||
if (mCurlHeaders)
|
||||
{
|
||||
// We take these headers out of the request now as they were
|
||||
// allocated originally in this thread and the notifier doesn't
|
||||
// need them. This eliminates one source of heap moving across
|
||||
// threads.
|
||||
|
||||
curl_slist_free_all(mCurlHeaders);
|
||||
mCurlHeaders = NULL;
|
||||
}
|
||||
|
||||
addAsReply();
|
||||
}
|
||||
|
||||
|
||||
void HttpOpRequest::visitNotifier(HttpRequest * request)
|
||||
{
|
||||
if (mUserHandler)
|
||||
{
|
||||
HttpResponse * response = new HttpResponse();
|
||||
response->setStatus(mStatus);
|
||||
response->setBody(mReplyBody);
|
||||
response->setHeaders(mReplyHeaders);
|
||||
if (mReplyOffset || mReplyLength)
|
||||
{
|
||||
// Got an explicit offset/length in response
|
||||
response->setRange(mReplyOffset, mReplyLength, mReplyFullLength);
|
||||
}
|
||||
response->setContentType(mReplyConType);
|
||||
|
||||
mUserHandler->onCompleted(static_cast<HttpHandle>(this), response);
|
||||
|
||||
response->release();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
HttpStatus HttpOpRequest::cancel()
|
||||
{
|
||||
mStatus = HttpStatus(HttpStatus::LLCORE, HE_OP_CANCELED);
|
||||
|
||||
addAsReply();
|
||||
|
||||
return HttpStatus();
|
||||
}
|
||||
|
||||
|
||||
HttpStatus HttpOpRequest::setupGet(HttpRequest::policy_t policy_id,
|
||||
HttpRequest::priority_t priority,
|
||||
const std::string & url,
|
||||
HttpOptions * options,
|
||||
HttpHeaders * headers)
|
||||
{
|
||||
setupCommon(policy_id, priority, url, NULL, options, headers);
|
||||
mReqMethod = HOR_GET;
|
||||
|
||||
return HttpStatus();
|
||||
}
|
||||
|
||||
|
||||
HttpStatus HttpOpRequest::setupGetByteRange(HttpRequest::policy_t policy_id,
|
||||
HttpRequest::priority_t priority,
|
||||
const std::string & url,
|
||||
size_t offset,
|
||||
size_t len,
|
||||
HttpOptions * options,
|
||||
HttpHeaders * headers)
|
||||
{
|
||||
setupCommon(policy_id, priority, url, NULL, options, headers);
|
||||
mReqMethod = HOR_GET;
|
||||
mReqOffset = offset;
|
||||
mReqLength = len;
|
||||
if (offset || len)
|
||||
{
|
||||
mProcFlags |= PF_SCAN_RANGE_HEADER;
|
||||
}
|
||||
|
||||
return HttpStatus();
|
||||
}
|
||||
|
||||
|
||||
HttpStatus HttpOpRequest::setupPost(HttpRequest::policy_t policy_id,
|
||||
HttpRequest::priority_t priority,
|
||||
const std::string & url,
|
||||
BufferArray * body,
|
||||
HttpOptions * options,
|
||||
HttpHeaders * headers)
|
||||
{
|
||||
setupCommon(policy_id, priority, url, body, options, headers);
|
||||
mReqMethod = HOR_POST;
|
||||
|
||||
return HttpStatus();
|
||||
}
|
||||
|
||||
|
||||
HttpStatus HttpOpRequest::setupPut(HttpRequest::policy_t policy_id,
|
||||
HttpRequest::priority_t priority,
|
||||
const std::string & url,
|
||||
BufferArray * body,
|
||||
HttpOptions * options,
|
||||
HttpHeaders * headers)
|
||||
{
|
||||
setupCommon(policy_id, priority, url, body, options, headers);
|
||||
mReqMethod = HOR_PUT;
|
||||
|
||||
return HttpStatus();
|
||||
}
|
||||
|
||||
|
||||
void HttpOpRequest::setupCommon(HttpRequest::policy_t policy_id,
|
||||
HttpRequest::priority_t priority,
|
||||
const std::string & url,
|
||||
BufferArray * body,
|
||||
HttpOptions * options,
|
||||
HttpHeaders * headers)
|
||||
{
|
||||
mProcFlags = 0U;
|
||||
mReqPolicy = policy_id;
|
||||
mReqPriority = priority;
|
||||
mReqURL = url;
|
||||
if (body)
|
||||
{
|
||||
body->addRef();
|
||||
mReqBody = body;
|
||||
}
|
||||
if (headers && ! mReqHeaders)
|
||||
{
|
||||
headers->addRef();
|
||||
mReqHeaders = headers;
|
||||
}
|
||||
if (options && ! mReqOptions)
|
||||
{
|
||||
options->addRef();
|
||||
mReqOptions = options;
|
||||
if (options->getWantHeaders())
|
||||
{
|
||||
mProcFlags |= PF_SAVE_HEADERS;
|
||||
}
|
||||
mPolicyRetryLimit = options->getRetries();
|
||||
mPolicyRetryLimit = llclamp(mPolicyRetryLimit, HTTP_RETRY_COUNT_MIN, HTTP_RETRY_COUNT_MAX);
|
||||
mTracing = (std::max)(mTracing, llclamp(options->getTrace(), HTTP_TRACE_MIN, HTTP_TRACE_MAX));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Sets all libcurl options and data for a request.
|
||||
//
|
||||
// Used both for initial requests and to 'reload' for
|
||||
// a retry, generally with a different CURL handle.
|
||||
// Junk may be left around from a failed request and that
|
||||
// needs to be cleaned out.
|
||||
//
|
||||
HttpStatus HttpOpRequest::prepareRequest(HttpService * service)
|
||||
{
|
||||
// Scrub transport and result data for retried op case
|
||||
mCurlActive = false;
|
||||
mCurlHandle = NULL;
|
||||
mCurlService = NULL;
|
||||
if (mCurlHeaders)
|
||||
{
|
||||
curl_slist_free_all(mCurlHeaders);
|
||||
mCurlHeaders = NULL;
|
||||
}
|
||||
mCurlBodyPos = 0;
|
||||
|
||||
if (mReplyBody)
|
||||
{
|
||||
mReplyBody->release();
|
||||
mReplyBody = NULL;
|
||||
}
|
||||
mReplyOffset = 0;
|
||||
mReplyLength = 0;
|
||||
mReplyFullLength = 0;
|
||||
if (mReplyHeaders)
|
||||
{
|
||||
mReplyHeaders->release();
|
||||
mReplyHeaders = NULL;
|
||||
}
|
||||
mReplyConType.clear();
|
||||
|
||||
// *FIXME: better error handling later
|
||||
HttpStatus status;
|
||||
|
||||
// Get policy options
|
||||
HttpPolicyGlobal & policy(service->getPolicy().getGlobalOptions());
|
||||
|
||||
mCurlHandle = curl_easy_init();
|
||||
curl_easy_setopt(mCurlHandle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
|
||||
curl_easy_setopt(mCurlHandle, CURLOPT_NOSIGNAL, 1);
|
||||
curl_easy_setopt(mCurlHandle, CURLOPT_NOPROGRESS, 1);
|
||||
curl_easy_setopt(mCurlHandle, CURLOPT_URL, mReqURL.c_str());
|
||||
curl_easy_setopt(mCurlHandle, CURLOPT_PRIVATE, this);
|
||||
curl_easy_setopt(mCurlHandle, CURLOPT_ENCODING, "");
|
||||
|
||||
if (HTTP_ENABLE_LINKSYS_WRT54G_V5_DNS_FIX)
|
||||
{
|
||||
// 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.
|
||||
curl_easy_setopt(mCurlHandle, CURLOPT_DNS_CACHE_TIMEOUT, 15);
|
||||
}
|
||||
else
|
||||
{
|
||||
// *TODO: Revisit this old DNS timeout setting - may no longer be valid
|
||||
// I don't think this is valid anymore, the Multi shared DNS
|
||||
// cache is working well. For the case of naked easy handles,
|
||||
// consider using a shared DNS object.
|
||||
curl_easy_setopt(mCurlHandle, CURLOPT_DNS_CACHE_TIMEOUT, 0);
|
||||
}
|
||||
curl_easy_setopt(mCurlHandle, CURLOPT_AUTOREFERER, 1);
|
||||
curl_easy_setopt(mCurlHandle, CURLOPT_FOLLOWLOCATION, 1);
|
||||
curl_easy_setopt(mCurlHandle, CURLOPT_MAXREDIRS, HTTP_REDIRECTS_DEFAULT);
|
||||
curl_easy_setopt(mCurlHandle, CURLOPT_WRITEFUNCTION, writeCallback);
|
||||
curl_easy_setopt(mCurlHandle, CURLOPT_WRITEDATA, this);
|
||||
curl_easy_setopt(mCurlHandle, CURLOPT_READFUNCTION, readCallback);
|
||||
curl_easy_setopt(mCurlHandle, CURLOPT_READDATA, this);
|
||||
curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYPEER, 1);
|
||||
curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYHOST, 0);
|
||||
|
||||
const std::string * opt_value(NULL);
|
||||
long opt_long(0L);
|
||||
policy.get(HttpRequest::GP_LLPROXY, &opt_long);
|
||||
if (opt_long)
|
||||
{
|
||||
// Use the viewer-based thread-safe API which has a
|
||||
// fast/safe check for proxy enable. Would like to
|
||||
// encapsulate this someway...
|
||||
LLProxy::getInstance()->applyProxySettings(mCurlHandle);
|
||||
}
|
||||
else if (policy.get(HttpRequest::GP_HTTP_PROXY, &opt_value))
|
||||
{
|
||||
// *TODO: This is fine for now but get fuller socks5/
|
||||
// authentication thing going later....
|
||||
curl_easy_setopt(mCurlHandle, CURLOPT_PROXY, opt_value->c_str());
|
||||
curl_easy_setopt(mCurlHandle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
|
||||
}
|
||||
if (policy.get(HttpRequest::GP_CA_PATH, &opt_value))
|
||||
{
|
||||
curl_easy_setopt(mCurlHandle, CURLOPT_CAPATH, opt_value->c_str());
|
||||
}
|
||||
if (policy.get(HttpRequest::GP_CA_FILE, &opt_value))
|
||||
{
|
||||
curl_easy_setopt(mCurlHandle, CURLOPT_CAINFO, opt_value->c_str());
|
||||
}
|
||||
|
||||
switch (mReqMethod)
|
||||
{
|
||||
case HOR_GET:
|
||||
curl_easy_setopt(mCurlHandle, CURLOPT_HTTPGET, 1);
|
||||
mCurlHeaders = curl_slist_append(mCurlHeaders, "Connection: keep-alive");
|
||||
mCurlHeaders = curl_slist_append(mCurlHeaders, "Keep-alive: 300");
|
||||
break;
|
||||
|
||||
case HOR_POST:
|
||||
{
|
||||
curl_easy_setopt(mCurlHandle, CURLOPT_POST, 1);
|
||||
curl_easy_setopt(mCurlHandle, CURLOPT_ENCODING, "");
|
||||
long data_size(0);
|
||||
if (mReqBody)
|
||||
{
|
||||
data_size = mReqBody->size();
|
||||
}
|
||||
curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDS, static_cast<void *>(NULL));
|
||||
curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDSIZE, data_size);
|
||||
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:
|
||||
{
|
||||
curl_easy_setopt(mCurlHandle, CURLOPT_UPLOAD, 1);
|
||||
long data_size(0);
|
||||
if (mReqBody)
|
||||
{
|
||||
data_size = mReqBody->size();
|
||||
}
|
||||
curl_easy_setopt(mCurlHandle, CURLOPT_INFILESIZE, data_size);
|
||||
curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDS, (void *) NULL);
|
||||
mCurlHeaders = curl_slist_append(mCurlHeaders, "Expect:");
|
||||
mCurlHeaders = curl_slist_append(mCurlHeaders, "Connection: keep-alive");
|
||||
mCurlHeaders = curl_slist_append(mCurlHeaders, "Keep-alive: 300");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
LL_ERRS("CoreHttp") << "Invalid HTTP method in request: "
|
||||
<< int(mReqMethod) << ". Can't recover."
|
||||
<< LL_ENDL;
|
||||
break;
|
||||
}
|
||||
|
||||
// Tracing
|
||||
if (mTracing >= HTTP_TRACE_CURL_HEADERS)
|
||||
{
|
||||
curl_easy_setopt(mCurlHandle, CURLOPT_VERBOSE, 1);
|
||||
curl_easy_setopt(mCurlHandle, CURLOPT_DEBUGDATA, this);
|
||||
curl_easy_setopt(mCurlHandle, CURLOPT_DEBUGFUNCTION, debugCallback);
|
||||
}
|
||||
|
||||
// There's a CURLOPT for this now...
|
||||
if ((mReqOffset || mReqLength) && HOR_GET == mReqMethod)
|
||||
{
|
||||
static const char * const fmt1("Range: bytes=%lu-%lu");
|
||||
static const char * const fmt2("Range: bytes=%lu-");
|
||||
|
||||
char range_line[64];
|
||||
|
||||
#if LL_WINDOWS
|
||||
_snprintf_s(range_line, sizeof(range_line), sizeof(range_line) - 1,
|
||||
(mReqLength ? fmt1 : fmt2),
|
||||
(unsigned long) mReqOffset, (unsigned long) (mReqOffset + mReqLength - 1));
|
||||
#else
|
||||
snprintf(range_line, sizeof(range_line),
|
||||
(mReqLength ? fmt1 : fmt2),
|
||||
(unsigned long) mReqOffset, (unsigned long) (mReqOffset + mReqLength - 1));
|
||||
#endif // LL_WINDOWS
|
||||
range_line[sizeof(range_line) - 1] = '\0';
|
||||
mCurlHeaders = curl_slist_append(mCurlHeaders, range_line);
|
||||
}
|
||||
|
||||
mCurlHeaders = curl_slist_append(mCurlHeaders, "Pragma:");
|
||||
|
||||
// Request options
|
||||
long timeout(HTTP_REQUEST_TIMEOUT_DEFAULT);
|
||||
if (mReqOptions)
|
||||
{
|
||||
timeout = mReqOptions->getTimeout();
|
||||
timeout = llclamp(timeout, HTTP_REQUEST_TIMEOUT_MIN, HTTP_REQUEST_TIMEOUT_MAX);
|
||||
}
|
||||
curl_easy_setopt(mCurlHandle, CURLOPT_TIMEOUT, timeout);
|
||||
curl_easy_setopt(mCurlHandle, CURLOPT_CONNECTTIMEOUT, timeout);
|
||||
|
||||
// Request headers
|
||||
if (mReqHeaders)
|
||||
{
|
||||
// Caller's headers last to override
|
||||
mCurlHeaders = append_headers_to_slist(mReqHeaders, mCurlHeaders);
|
||||
}
|
||||
curl_easy_setopt(mCurlHandle, CURLOPT_HTTPHEADER, mCurlHeaders);
|
||||
|
||||
if (mProcFlags & (PF_SCAN_RANGE_HEADER | PF_SAVE_HEADERS))
|
||||
{
|
||||
curl_easy_setopt(mCurlHandle, CURLOPT_HEADERFUNCTION, headerCallback);
|
||||
curl_easy_setopt(mCurlHandle, CURLOPT_HEADERDATA, this);
|
||||
}
|
||||
|
||||
if (status)
|
||||
{
|
||||
mCurlService = service;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
size_t HttpOpRequest::writeCallback(void * data, size_t size, size_t nmemb, void * userdata)
|
||||
{
|
||||
HttpOpRequest * op(static_cast<HttpOpRequest *>(userdata));
|
||||
|
||||
if (! op->mReplyBody)
|
||||
{
|
||||
op->mReplyBody = new BufferArray();
|
||||
}
|
||||
const size_t req_size(size * nmemb);
|
||||
const size_t write_size(op->mReplyBody->append(static_cast<char *>(data), req_size));
|
||||
return write_size;
|
||||
}
|
||||
|
||||
|
||||
size_t HttpOpRequest::readCallback(void * data, size_t size, size_t nmemb, void * userdata)
|
||||
{
|
||||
HttpOpRequest * op(static_cast<HttpOpRequest *>(userdata));
|
||||
|
||||
if (! op->mReqBody)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
const size_t req_size(size * nmemb);
|
||||
const size_t body_size(op->mReqBody->size());
|
||||
if (body_size <= op->mCurlBodyPos)
|
||||
{
|
||||
LL_WARNS("HttpCore") << "Request body position beyond body size. Aborting request."
|
||||
<< LL_ENDL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const size_t do_size((std::min)(req_size, body_size - op->mCurlBodyPos));
|
||||
const size_t read_size(op->mReqBody->read(op->mCurlBodyPos, static_cast<char *>(data), do_size));
|
||||
op->mCurlBodyPos += read_size;
|
||||
return read_size;
|
||||
}
|
||||
|
||||
|
||||
size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, void * userdata)
|
||||
{
|
||||
static const char status_line[] = "HTTP/";
|
||||
static const size_t status_line_len = sizeof(status_line) - 1;
|
||||
|
||||
static const char con_ran_line[] = "content-range:";
|
||||
static const size_t con_ran_line_len = sizeof(con_ran_line) - 1;
|
||||
|
||||
HttpOpRequest * op(static_cast<HttpOpRequest *>(userdata));
|
||||
|
||||
const size_t hdr_size(size * nmemb);
|
||||
const char * hdr_data(static_cast<const char *>(data)); // Not null terminated
|
||||
|
||||
if (hdr_size >= status_line_len && ! strncmp(status_line, hdr_data, status_line_len))
|
||||
{
|
||||
// One of possibly several status lines. Reset what we know and start over
|
||||
// taking results from the last header stanza we receive.
|
||||
op->mReplyOffset = 0;
|
||||
op->mReplyLength = 0;
|
||||
op->mReplyFullLength = 0;
|
||||
op->mStatus = HttpStatus();
|
||||
if (op->mReplyHeaders)
|
||||
{
|
||||
op->mReplyHeaders->mHeaders.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// Nothing in here wants a final CR/LF combination. Remove
|
||||
// it as much as possible.
|
||||
size_t wanted_hdr_size(hdr_size);
|
||||
if (wanted_hdr_size && '\n' == hdr_data[wanted_hdr_size - 1])
|
||||
{
|
||||
if (--wanted_hdr_size && '\r' == hdr_data[wanted_hdr_size - 1])
|
||||
{
|
||||
--wanted_hdr_size;
|
||||
}
|
||||
}
|
||||
|
||||
// Save header if caller wants them in the response
|
||||
if (op->mProcFlags & PF_SAVE_HEADERS)
|
||||
{
|
||||
// Save headers in response
|
||||
if (! op->mReplyHeaders)
|
||||
{
|
||||
op->mReplyHeaders = new HttpHeaders;
|
||||
}
|
||||
op->mReplyHeaders->mHeaders.push_back(std::string(hdr_data, wanted_hdr_size));
|
||||
}
|
||||
|
||||
// Detect and parse 'Content-Range' headers
|
||||
if (op->mProcFlags & PF_SCAN_RANGE_HEADER)
|
||||
{
|
||||
char hdr_buffer[128]; // Enough for a reasonable header
|
||||
size_t frag_size((std::min)(wanted_hdr_size, sizeof(hdr_buffer) - 1));
|
||||
|
||||
memcpy(hdr_buffer, hdr_data, frag_size);
|
||||
hdr_buffer[frag_size] = '\0';
|
||||
if (frag_size > con_ran_line_len &&
|
||||
! os_strncasecmp(hdr_buffer, con_ran_line, con_ran_line_len))
|
||||
{
|
||||
unsigned int first(0), last(0), length(0);
|
||||
int status;
|
||||
|
||||
if (! (status = parse_content_range_header(hdr_buffer, &first, &last, &length)))
|
||||
{
|
||||
// Success, record the fragment position
|
||||
op->mReplyOffset = first;
|
||||
op->mReplyLength = last - first + 1;
|
||||
op->mReplyFullLength = length;
|
||||
}
|
||||
else if (-1 == status)
|
||||
{
|
||||
// Response is badly formed and shouldn't be accepted
|
||||
op->mStatus = HttpStatus(HttpStatus::LLCORE, HE_INV_CONTENT_RANGE_HDR);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Ignore the unparsable.
|
||||
LL_INFOS_ONCE("CoreHttp") << "Problem parsing odd Content-Range header: '"
|
||||
<< std::string(hdr_data, frag_size)
|
||||
<< "'. Ignoring."
|
||||
<< LL_ENDL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return hdr_size;
|
||||
}
|
||||
|
||||
|
||||
int HttpOpRequest::debugCallback(CURL * handle, curl_infotype info, char * buffer, size_t len, void * userdata)
|
||||
{
|
||||
HttpOpRequest * op(static_cast<HttpOpRequest *>(userdata));
|
||||
|
||||
std::string safe_line;
|
||||
std::string tag;
|
||||
bool logit(false);
|
||||
len = (std::min)(len, size_t(256)); // Keep things reasonable in all cases
|
||||
|
||||
switch (info)
|
||||
{
|
||||
case CURLINFO_TEXT:
|
||||
if (op->mTracing >= HTTP_TRACE_CURL_HEADERS)
|
||||
{
|
||||
tag = "TEXT";
|
||||
escape_libcurl_debug_data(buffer, len, true, safe_line);
|
||||
logit = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case CURLINFO_HEADER_IN:
|
||||
if (op->mTracing >= HTTP_TRACE_CURL_HEADERS)
|
||||
{
|
||||
tag = "HEADERIN";
|
||||
escape_libcurl_debug_data(buffer, len, true, safe_line);
|
||||
logit = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case CURLINFO_HEADER_OUT:
|
||||
if (op->mTracing >= HTTP_TRACE_CURL_HEADERS)
|
||||
{
|
||||
tag = "HEADEROUT";
|
||||
escape_libcurl_debug_data(buffer, 2 * len, true, safe_line); // Goes out as one line
|
||||
logit = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case CURLINFO_DATA_IN:
|
||||
if (op->mTracing >= HTTP_TRACE_CURL_HEADERS)
|
||||
{
|
||||
tag = "DATAIN";
|
||||
logit = true;
|
||||
if (op->mTracing >= HTTP_TRACE_CURL_BODIES)
|
||||
{
|
||||
escape_libcurl_debug_data(buffer, len, false, safe_line);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::ostringstream out;
|
||||
out << len << " Bytes";
|
||||
safe_line = out.str();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case CURLINFO_DATA_OUT:
|
||||
if (op->mTracing >= HTTP_TRACE_CURL_HEADERS)
|
||||
{
|
||||
tag = "DATAOUT";
|
||||
logit = true;
|
||||
if (op->mTracing >= HTTP_TRACE_CURL_BODIES)
|
||||
{
|
||||
escape_libcurl_debug_data(buffer, len, false, safe_line);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::ostringstream out;
|
||||
out << len << " Bytes";
|
||||
safe_line = out.str();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
logit = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (logit)
|
||||
{
|
||||
LL_INFOS("CoreHttp") << "TRACE, LibcurlDebug, Handle: "
|
||||
<< static_cast<HttpHandle>(op)
|
||||
<< ", Type: " << tag
|
||||
<< ", Data: " << safe_line
|
||||
<< LL_ENDL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
} // end namespace LLCore
|
||||
|
||||
|
||||
// =======================================
|
||||
// Anonymous Namespace
|
||||
// =======================================
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
int parse_content_range_header(char * buffer,
|
||||
unsigned int * first,
|
||||
unsigned int * last,
|
||||
unsigned int * length)
|
||||
{
|
||||
char * tok_state(NULL), * tok(NULL);
|
||||
bool match(true);
|
||||
|
||||
if (! os_strtok_r(buffer, hdr_separator, &tok_state))
|
||||
match = false;
|
||||
if (match && (tok = os_strtok_r(NULL, hdr_whitespace, &tok_state)))
|
||||
match = 0 == os_strcasecmp("bytes", tok);
|
||||
if (match && ! (tok = os_strtok_r(NULL, " \t", &tok_state)))
|
||||
match = false;
|
||||
if (match)
|
||||
{
|
||||
unsigned int lcl_first(0), lcl_last(0), lcl_len(0);
|
||||
|
||||
#if LL_WINDOWS
|
||||
if (3 == sscanf_s(tok, "%u-%u/%u", &lcl_first, &lcl_last, &lcl_len))
|
||||
#else
|
||||
if (3 == sscanf(tok, "%u-%u/%u", &lcl_first, &lcl_last, &lcl_len))
|
||||
#endif // LL_WINDOWS
|
||||
{
|
||||
if (lcl_first > lcl_last || lcl_last >= lcl_len)
|
||||
return -1;
|
||||
*first = lcl_first;
|
||||
*last = lcl_last;
|
||||
*length = lcl_len;
|
||||
return 0;
|
||||
}
|
||||
#if LL_WINDOWS
|
||||
if (2 == sscanf_s(tok, "%u-%u/*", &lcl_first, &lcl_last))
|
||||
#else
|
||||
if (2 == sscanf(tok, "%u-%u/*", &lcl_first, &lcl_last))
|
||||
#endif // LL_WINDOWS
|
||||
{
|
||||
if (lcl_first > lcl_last)
|
||||
return -1;
|
||||
*first = lcl_first;
|
||||
*last = lcl_last;
|
||||
*length = 0;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Header is there but badly/unexpectedly formed, try to ignore it.
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
void escape_libcurl_debug_data(char * buffer, size_t len, bool scrub, std::string & safe_line)
|
||||
{
|
||||
std::string out;
|
||||
len = (std::min)(len, size_t(200));
|
||||
out.reserve(3 * len);
|
||||
for (int i(0); i < len; ++i)
|
||||
{
|
||||
unsigned char uc(static_cast<unsigned char>(buffer[i]));
|
||||
|
||||
if (uc < 32 || uc > 126)
|
||||
{
|
||||
if (scrub)
|
||||
{
|
||||
out.append(1, ' ');
|
||||
}
|
||||
else
|
||||
{
|
||||
static const char hex[] = "0123456789ABCDEF";
|
||||
char convert[4];
|
||||
|
||||
convert[0] = '%';
|
||||
convert[1] = hex[(uc >> 4) % 16];
|
||||
convert[2] = hex[uc % 16];
|
||||
convert[3] = '\0';
|
||||
out.append(convert);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
out.append(1, buffer[i]);
|
||||
}
|
||||
}
|
||||
safe_line.swap(out);
|
||||
}
|
||||
|
||||
|
||||
int os_strncasecmp(const char *s1, const char *s2, size_t n)
|
||||
{
|
||||
#if LL_WINDOWS
|
||||
return _strnicmp(s1, s2, n);
|
||||
#else
|
||||
return strncasecmp(s1, s2, n);
|
||||
#endif // LL_WINDOWS
|
||||
}
|
||||
|
||||
|
||||
int os_strcasecmp(const char *s1, const char *s2)
|
||||
{
|
||||
#if LL_WINDOWS
|
||||
return _stricmp(s1, s2);
|
||||
#else
|
||||
return strcasecmp(s1, s2);
|
||||
#endif // LL_WINDOWS
|
||||
}
|
||||
|
||||
|
||||
char * os_strtok_r(char *str, const char *delim, char ** savestate)
|
||||
{
|
||||
#if LL_WINDOWS
|
||||
return strtok_s(str, delim, savestate);
|
||||
#else
|
||||
return strtok_r(str, delim, savestate);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,219 @@
|
|||
/**
|
||||
* @file _httpoprequest.h
|
||||
* @brief Internal declarations for the HttpOpRequest subclass
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, 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 _LLCORE_HTTP_OPREQUEST_H_
|
||||
#define _LLCORE_HTTP_OPREQUEST_H_
|
||||
|
||||
|
||||
#include "linden_common.h" // Modifies curl/curl.h interfaces
|
||||
|
||||
#include <string>
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include "httpcommon.h"
|
||||
#include "httprequest.h"
|
||||
#include "_httpoperation.h"
|
||||
#include "_refcounted.h"
|
||||
|
||||
|
||||
namespace LLCore
|
||||
{
|
||||
|
||||
|
||||
class BufferArray;
|
||||
class HttpHeaders;
|
||||
class HttpOptions;
|
||||
|
||||
|
||||
/// HttpOpRequest requests a supported HTTP method invocation with
|
||||
/// option and header overrides.
|
||||
///
|
||||
/// Essentially an RPC to get an HTTP GET, POST or PUT executed
|
||||
/// asynchronously with options to override behaviors and HTTP
|
||||
/// headers.
|
||||
///
|
||||
/// Constructor creates a raw object incapable of useful work.
|
||||
/// A subsequent call to one of the setupXXX() methods provides
|
||||
/// the information needed to make a working request which can
|
||||
/// then be enqueued to a request queue.
|
||||
///
|
||||
|
||||
class HttpOpRequest : public HttpOperation
|
||||
{
|
||||
public:
|
||||
HttpOpRequest();
|
||||
|
||||
protected:
|
||||
virtual ~HttpOpRequest(); // Use release()
|
||||
|
||||
private:
|
||||
HttpOpRequest(const HttpOpRequest &); // Not defined
|
||||
void operator=(const HttpOpRequest &); // Not defined
|
||||
|
||||
public:
|
||||
enum EMethod
|
||||
{
|
||||
HOR_GET,
|
||||
HOR_POST,
|
||||
HOR_PUT
|
||||
};
|
||||
|
||||
virtual void stageFromRequest(HttpService *);
|
||||
virtual void stageFromReady(HttpService *);
|
||||
virtual void stageFromActive(HttpService *);
|
||||
|
||||
virtual void visitNotifier(HttpRequest * request);
|
||||
|
||||
public:
|
||||
/// Setup Methods
|
||||
///
|
||||
/// Basically an RPC setup for each type of HTTP method
|
||||
/// invocation with one per method type. These are
|
||||
/// generally invoked right after construction.
|
||||
///
|
||||
/// Threading: called by application thread
|
||||
///
|
||||
HttpStatus setupGet(HttpRequest::policy_t policy_id,
|
||||
HttpRequest::priority_t priority,
|
||||
const std::string & url,
|
||||
HttpOptions * options,
|
||||
HttpHeaders * 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);
|
||||
|
||||
HttpStatus setupPost(HttpRequest::policy_t policy_id,
|
||||
HttpRequest::priority_t priority,
|
||||
const std::string & url,
|
||||
BufferArray * body,
|
||||
HttpOptions * options,
|
||||
HttpHeaders * headers);
|
||||
|
||||
HttpStatus setupPut(HttpRequest::policy_t policy_id,
|
||||
HttpRequest::priority_t priority,
|
||||
const std::string & url,
|
||||
BufferArray * body,
|
||||
HttpOptions * options,
|
||||
HttpHeaders * 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
|
||||
//
|
||||
HttpStatus prepareRequest(HttpService * service);
|
||||
|
||||
virtual HttpStatus cancel();
|
||||
|
||||
protected:
|
||||
// Common setup for all the request methods.
|
||||
//
|
||||
// Threading: called by application thread
|
||||
//
|
||||
void setupCommon(HttpRequest::policy_t policy_id,
|
||||
HttpRequest::priority_t priority,
|
||||
const std::string & url,
|
||||
BufferArray * body,
|
||||
HttpOptions * options,
|
||||
HttpHeaders * headers);
|
||||
|
||||
// libcurl operational callbacks
|
||||
//
|
||||
// Threading: called by worker thread
|
||||
//
|
||||
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 size_t headerCallback(void * data, size_t size, size_t nmemb, void * userdata);
|
||||
static int debugCallback(CURL *, curl_infotype info, char * buffer, size_t len, void * userdata);
|
||||
|
||||
protected:
|
||||
unsigned int mProcFlags;
|
||||
static const unsigned int PF_SCAN_RANGE_HEADER = 0x00000001U;
|
||||
static const unsigned int PF_SAVE_HEADERS = 0x00000002U;
|
||||
|
||||
public:
|
||||
// Request data
|
||||
EMethod mReqMethod;
|
||||
std::string mReqURL;
|
||||
BufferArray * mReqBody;
|
||||
off_t mReqOffset;
|
||||
size_t mReqLength;
|
||||
HttpHeaders * mReqHeaders;
|
||||
HttpOptions * mReqOptions;
|
||||
|
||||
// Transport data
|
||||
bool mCurlActive;
|
||||
CURL * mCurlHandle;
|
||||
HttpService * mCurlService;
|
||||
curl_slist * mCurlHeaders;
|
||||
size_t mCurlBodyPos;
|
||||
|
||||
// Result data
|
||||
HttpStatus mStatus;
|
||||
BufferArray * mReplyBody;
|
||||
off_t mReplyOffset;
|
||||
size_t mReplyLength;
|
||||
size_t mReplyFullLength;
|
||||
HttpHeaders * mReplyHeaders;
|
||||
std::string mReplyConType;
|
||||
|
||||
// Policy data
|
||||
int mPolicyRetries;
|
||||
HttpTime mPolicyRetryAt;
|
||||
int mPolicyRetryLimit;
|
||||
}; // end class HttpOpRequest
|
||||
|
||||
|
||||
/// HttpOpRequestCompare isn't an operation but a uniform comparison
|
||||
/// functor for STL containers that order by priority. Mainly
|
||||
/// used for the ready queue container but defined here.
|
||||
class HttpOpRequestCompare
|
||||
{
|
||||
public:
|
||||
bool operator()(const HttpOpRequest * lhs, const HttpOpRequest * rhs)
|
||||
{
|
||||
return lhs->mReqPriority > rhs->mReqPriority;
|
||||
}
|
||||
}; // end class HttpOpRequestCompare
|
||||
|
||||
|
||||
// ---------------------------------------
|
||||
// Free functions
|
||||
// ---------------------------------------
|
||||
|
||||
// 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);
|
||||
|
||||
} // end namespace LLCore
|
||||
|
||||
#endif // _LLCORE_HTTP_OPREQUEST_H_
|
||||
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
/**
|
||||
* @file _httpopsetget.cpp
|
||||
* @brief Definitions for internal class HttpOpSetGet
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include "_httpopsetget.h"
|
||||
|
||||
#include "httpcommon.h"
|
||||
|
||||
#include "_httpservice.h"
|
||||
#include "_httppolicy.h"
|
||||
|
||||
|
||||
namespace LLCore
|
||||
{
|
||||
|
||||
|
||||
// ==================================
|
||||
// HttpOpSetget
|
||||
// ==================================
|
||||
|
||||
|
||||
HttpOpSetGet::HttpOpSetGet()
|
||||
: HttpOperation(),
|
||||
mIsGlobal(false),
|
||||
mDoSet(false),
|
||||
mSetting(-1), // Nothing requested
|
||||
mLongValue(0L)
|
||||
{}
|
||||
|
||||
|
||||
HttpOpSetGet::~HttpOpSetGet()
|
||||
{}
|
||||
|
||||
|
||||
void HttpOpSetGet::setupGet(HttpRequest::EGlobalPolicy setting)
|
||||
{
|
||||
mIsGlobal = true;
|
||||
mSetting = setting;
|
||||
}
|
||||
|
||||
|
||||
void HttpOpSetGet::setupSet(HttpRequest::EGlobalPolicy setting, const std::string & value)
|
||||
{
|
||||
mIsGlobal = true;
|
||||
mDoSet = true;
|
||||
mSetting = setting;
|
||||
mStrValue = value;
|
||||
}
|
||||
|
||||
|
||||
void HttpOpSetGet::stageFromRequest(HttpService * service)
|
||||
{
|
||||
HttpPolicyGlobal & pol_opt(service->getPolicy().getGlobalOptions());
|
||||
HttpRequest::EGlobalPolicy setting(static_cast<HttpRequest::EGlobalPolicy>(mSetting));
|
||||
|
||||
if (mDoSet)
|
||||
{
|
||||
mStatus = pol_opt.set(setting, mStrValue);
|
||||
}
|
||||
if (mStatus)
|
||||
{
|
||||
const std::string * value(NULL);
|
||||
if ((mStatus = pol_opt.get(setting, &value)))
|
||||
{
|
||||
mStrValue = *value;
|
||||
}
|
||||
}
|
||||
|
||||
addAsReply();
|
||||
}
|
||||
|
||||
|
||||
} // end namespace LLCore
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
/**
|
||||
* @file _httpopsetget.h
|
||||
* @brief Internal declarations for the HttpOpSetGet subclass
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, 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 _LLCORE_HTTP_OPSETGET_H_
|
||||
#define _LLCORE_HTTP_OPSETGET_H_
|
||||
|
||||
|
||||
#include "linden_common.h" // Modifies curl/curl.h interfaces
|
||||
|
||||
#include "httpcommon.h"
|
||||
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include "_httpoperation.h"
|
||||
#include "_refcounted.h"
|
||||
|
||||
|
||||
namespace LLCore
|
||||
{
|
||||
|
||||
|
||||
/// HttpOpSetGet requests dynamic changes to policy and
|
||||
/// configuration settings.
|
||||
///
|
||||
/// *NOTE: Expect this to change. Don't really like it yet.
|
||||
|
||||
class HttpOpSetGet : public HttpOperation
|
||||
{
|
||||
public:
|
||||
HttpOpSetGet();
|
||||
|
||||
protected:
|
||||
virtual ~HttpOpSetGet(); // Use release()
|
||||
|
||||
private:
|
||||
HttpOpSetGet(const HttpOpSetGet &); // Not defined
|
||||
void operator=(const HttpOpSetGet &); // Not defined
|
||||
|
||||
public:
|
||||
/// Threading: called by application thread
|
||||
void setupGet(HttpRequest::EGlobalPolicy setting);
|
||||
void setupSet(HttpRequest::EGlobalPolicy setting, const std::string & value);
|
||||
|
||||
virtual void stageFromRequest(HttpService *);
|
||||
|
||||
public:
|
||||
// Request data
|
||||
bool mIsGlobal;
|
||||
bool mDoSet;
|
||||
int mSetting;
|
||||
long mLongValue;
|
||||
std::string mStrValue;
|
||||
|
||||
}; // end class HttpOpSetGet
|
||||
|
||||
|
||||
} // end namespace LLCore
|
||||
|
||||
#endif // _LLCORE_HTTP_OPSETGET_H_
|
||||
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
/**
|
||||
* @file _httpopsetpriority.cpp
|
||||
* @brief Definitions for internal classes based on HttpOpSetPriority
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include "_httpopsetpriority.h"
|
||||
|
||||
#include "httpresponse.h"
|
||||
#include "httphandler.h"
|
||||
#include "_httpservice.h"
|
||||
|
||||
|
||||
namespace LLCore
|
||||
{
|
||||
|
||||
|
||||
HttpOpSetPriority::HttpOpSetPriority(HttpHandle handle, HttpRequest::priority_t priority)
|
||||
: HttpOperation(),
|
||||
mHandle(handle),
|
||||
mPriority(priority)
|
||||
{}
|
||||
|
||||
|
||||
HttpOpSetPriority::~HttpOpSetPriority()
|
||||
{}
|
||||
|
||||
|
||||
void HttpOpSetPriority::stageFromRequest(HttpService * service)
|
||||
{
|
||||
// Do operations
|
||||
if (! service->changePriority(mHandle, mPriority))
|
||||
{
|
||||
// Request not found, fail the final status
|
||||
mStatus = HttpStatus(HttpStatus::LLCORE, HE_HANDLE_NOT_FOUND);
|
||||
}
|
||||
|
||||
// Move directly to response queue
|
||||
addAsReply();
|
||||
}
|
||||
|
||||
|
||||
} // end namespace LLCore
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
/**
|
||||
* @file _httpsetpriority.h
|
||||
* @brief Internal declarations for HttpSetPriority
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, 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 _LLCORE_HTTP_SETPRIORITY_H_
|
||||
#define _LLCORE_HTTP_SETPRIORITY_H_
|
||||
|
||||
|
||||
#include "httpcommon.h"
|
||||
#include "httprequest.h"
|
||||
#include "_httpoperation.h"
|
||||
#include "_refcounted.h"
|
||||
|
||||
|
||||
namespace LLCore
|
||||
{
|
||||
|
||||
|
||||
/// HttpOpSetPriority is an immediate request that
|
||||
/// searches the various queues looking for a given
|
||||
/// request handle and changing it's priority if
|
||||
/// found.
|
||||
///
|
||||
/// *NOTE: This will very likely be removed in the near future
|
||||
/// when priority is removed from the library.
|
||||
|
||||
class HttpOpSetPriority : public HttpOperation
|
||||
{
|
||||
public:
|
||||
HttpOpSetPriority(HttpHandle handle, HttpRequest::priority_t priority);
|
||||
|
||||
protected:
|
||||
virtual ~HttpOpSetPriority();
|
||||
|
||||
private:
|
||||
HttpOpSetPriority(const HttpOpSetPriority &); // Not defined
|
||||
void operator=(const HttpOpSetPriority &); // Not defined
|
||||
|
||||
public:
|
||||
virtual void stageFromRequest(HttpService *);
|
||||
|
||||
protected:
|
||||
// Request Data
|
||||
HttpHandle mHandle;
|
||||
HttpRequest::priority_t mPriority;
|
||||
}; // end class HttpOpSetPriority
|
||||
|
||||
} // end namespace LLCore
|
||||
|
||||
#endif // _LLCORE_HTTP_SETPRIORITY_H_
|
||||
|
||||
|
|
@ -0,0 +1,387 @@
|
|||
/**
|
||||
* @file _httppolicy.cpp
|
||||
* @brief Internal definitions of the Http policy thread
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include "linden_common.h"
|
||||
|
||||
#include "_httppolicy.h"
|
||||
|
||||
#include "_httpoprequest.h"
|
||||
#include "_httpservice.h"
|
||||
#include "_httplibcurl.h"
|
||||
#include "_httppolicyclass.h"
|
||||
|
||||
#include "lltimer.h"
|
||||
|
||||
|
||||
namespace LLCore
|
||||
{
|
||||
|
||||
|
||||
// Per-policy-class data for a running system.
|
||||
// Collection of queues, parameters, history, metrics, etc.
|
||||
// for a single policy class.
|
||||
//
|
||||
// Threading: accessed only by worker thread
|
||||
struct HttpPolicy::State
|
||||
{
|
||||
public:
|
||||
State()
|
||||
: mConnMax(HTTP_CONNECTION_LIMIT_DEFAULT),
|
||||
mConnAt(HTTP_CONNECTION_LIMIT_DEFAULT),
|
||||
mConnMin(1),
|
||||
mNextSample(0),
|
||||
mErrorCount(0),
|
||||
mErrorFactor(0)
|
||||
{}
|
||||
|
||||
HttpReadyQueue mReadyQueue;
|
||||
HttpRetryQueue mRetryQueue;
|
||||
|
||||
HttpPolicyClass mOptions;
|
||||
|
||||
long mConnMax;
|
||||
long mConnAt;
|
||||
long mConnMin;
|
||||
|
||||
HttpTime mNextSample;
|
||||
unsigned long mErrorCount;
|
||||
unsigned long mErrorFactor;
|
||||
};
|
||||
|
||||
|
||||
HttpPolicy::HttpPolicy(HttpService * service)
|
||||
: mActiveClasses(0),
|
||||
mState(NULL),
|
||||
mService(service)
|
||||
{}
|
||||
|
||||
|
||||
HttpPolicy::~HttpPolicy()
|
||||
{
|
||||
shutdown();
|
||||
|
||||
mService = NULL;
|
||||
}
|
||||
|
||||
|
||||
void HttpPolicy::shutdown()
|
||||
{
|
||||
for (int policy_class(0); policy_class < mActiveClasses; ++policy_class)
|
||||
{
|
||||
HttpRetryQueue & retryq(mState[policy_class].mRetryQueue);
|
||||
while (! retryq.empty())
|
||||
{
|
||||
HttpOpRequest * op(retryq.top());
|
||||
retryq.pop();
|
||||
|
||||
op->cancel();
|
||||
op->release();
|
||||
}
|
||||
|
||||
HttpReadyQueue & readyq(mState[policy_class].mReadyQueue);
|
||||
while (! readyq.empty())
|
||||
{
|
||||
HttpOpRequest * op(readyq.top());
|
||||
readyq.pop();
|
||||
|
||||
op->cancel();
|
||||
op->release();
|
||||
}
|
||||
}
|
||||
delete [] mState;
|
||||
mState = NULL;
|
||||
mActiveClasses = 0;
|
||||
}
|
||||
|
||||
|
||||
void HttpPolicy::start(const HttpPolicyGlobal & global,
|
||||
const std::vector<HttpPolicyClass> & classes)
|
||||
{
|
||||
llassert_always(! mState);
|
||||
|
||||
mGlobalOptions = global;
|
||||
mActiveClasses = classes.size();
|
||||
mState = new State [mActiveClasses];
|
||||
for (int i(0); i < mActiveClasses; ++i)
|
||||
{
|
||||
mState[i].mOptions = classes[i];
|
||||
mState[i].mConnMax = classes[i].mConnectionLimit;
|
||||
mState[i].mConnAt = mState[i].mConnMax;
|
||||
mState[i].mConnMin = 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void HttpPolicy::addOp(HttpOpRequest * op)
|
||||
{
|
||||
const int policy_class(op->mReqPolicy);
|
||||
|
||||
op->mPolicyRetries = 0;
|
||||
mState[policy_class].mReadyQueue.push(op);
|
||||
}
|
||||
|
||||
|
||||
void HttpPolicy::retryOp(HttpOpRequest * op)
|
||||
{
|
||||
static const HttpTime retry_deltas[] =
|
||||
{
|
||||
250000, // 1st retry in 0.25 S, etc...
|
||||
500000,
|
||||
1000000,
|
||||
2000000,
|
||||
5000000 // ... to every 5.0 S.
|
||||
};
|
||||
static const int delta_max(int(LL_ARRAY_SIZE(retry_deltas)) - 1);
|
||||
|
||||
const HttpTime now(totalTime());
|
||||
const int policy_class(op->mReqPolicy);
|
||||
|
||||
const HttpTime delta(retry_deltas[llclamp(op->mPolicyRetries, 0, delta_max)]);
|
||||
op->mPolicyRetryAt = now + delta;
|
||||
++op->mPolicyRetries;
|
||||
LL_WARNS("CoreHttp") << "HTTP request " << static_cast<HttpHandle>(op)
|
||||
<< " retry " << op->mPolicyRetries
|
||||
<< " scheduled for +" << (delta / HttpTime(1000))
|
||||
<< " mS. Status: " << op->mStatus.toHex()
|
||||
<< LL_ENDL;
|
||||
if (op->mTracing > 0)
|
||||
{
|
||||
LL_INFOS("CoreHttp") << "TRACE, ToRetryQueue, Handle: "
|
||||
<< static_cast<HttpHandle>(op)
|
||||
<< LL_ENDL;
|
||||
}
|
||||
mState[policy_class].mRetryQueue.push(op);
|
||||
}
|
||||
|
||||
|
||||
// Attempt to deliver requests to the transport layer.
|
||||
//
|
||||
// Tries to find HTTP requests for each policy class with
|
||||
// available capacity. Starts with the retry queue first
|
||||
// looking for requests that have waited long enough then
|
||||
// moves on to the ready queue.
|
||||
//
|
||||
// If all queues are empty, will return an indication that
|
||||
// the worker thread may sleep hard otherwise will ask for
|
||||
// normal polling frequency.
|
||||
//
|
||||
HttpService::ELoopSpeed HttpPolicy::processReadyQueue()
|
||||
{
|
||||
const HttpTime now(totalTime());
|
||||
HttpService::ELoopSpeed result(HttpService::REQUEST_SLEEP);
|
||||
HttpLibcurl & transport(mService->getTransport());
|
||||
|
||||
for (int policy_class(0); policy_class < mActiveClasses; ++policy_class)
|
||||
{
|
||||
State & state(mState[policy_class]);
|
||||
int active(transport.getActiveCountInClass(policy_class));
|
||||
int needed(state.mConnAt - active); // Expect negatives here
|
||||
|
||||
HttpRetryQueue & retryq(state.mRetryQueue);
|
||||
HttpReadyQueue & readyq(state.mReadyQueue);
|
||||
|
||||
if (needed > 0)
|
||||
{
|
||||
// First see if we have any retries...
|
||||
while (needed > 0 && ! retryq.empty())
|
||||
{
|
||||
HttpOpRequest * op(retryq.top());
|
||||
if (op->mPolicyRetryAt > now)
|
||||
break;
|
||||
|
||||
retryq.pop();
|
||||
|
||||
op->stageFromReady(mService);
|
||||
op->release();
|
||||
|
||||
--needed;
|
||||
}
|
||||
|
||||
// Now go on to the new requests...
|
||||
while (needed > 0 && ! readyq.empty())
|
||||
{
|
||||
HttpOpRequest * op(readyq.top());
|
||||
readyq.pop();
|
||||
|
||||
op->stageFromReady(mService);
|
||||
op->release();
|
||||
|
||||
--needed;
|
||||
}
|
||||
}
|
||||
|
||||
if (! readyq.empty() || ! retryq.empty())
|
||||
{
|
||||
// If anything is ready, continue looping...
|
||||
result = HttpService::NORMAL;
|
||||
}
|
||||
} // end foreach policy_class
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
bool HttpPolicy::changePriority(HttpHandle handle, HttpRequest::priority_t priority)
|
||||
{
|
||||
for (int policy_class(0); policy_class < mActiveClasses; ++policy_class)
|
||||
{
|
||||
State & state(mState[policy_class]);
|
||||
// We don't scan retry queue because a priority change there
|
||||
// is meaningless. The request will be issued based on retry
|
||||
// intervals not priority value, which is now moot.
|
||||
|
||||
// Scan ready queue for requests that match policy
|
||||
HttpReadyQueue::container_type & c(state.mReadyQueue.get_container());
|
||||
for (HttpReadyQueue::container_type::iterator iter(c.begin()); c.end() != iter;)
|
||||
{
|
||||
HttpReadyQueue::container_type::iterator cur(iter++);
|
||||
|
||||
if (static_cast<HttpHandle>(*cur) == handle)
|
||||
{
|
||||
HttpOpRequest * op(*cur);
|
||||
c.erase(cur); // All iterators are now invalidated
|
||||
op->mReqPriority = priority;
|
||||
state.mReadyQueue.push(op); // Re-insert using adapter class
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool HttpPolicy::cancel(HttpHandle handle)
|
||||
{
|
||||
for (int policy_class(0); policy_class < mActiveClasses; ++policy_class)
|
||||
{
|
||||
State & state(mState[policy_class]);
|
||||
|
||||
// Scan retry queue
|
||||
HttpRetryQueue::container_type & c1(state.mRetryQueue.get_container());
|
||||
for (HttpRetryQueue::container_type::iterator iter(c1.begin()); c1.end() != iter;)
|
||||
{
|
||||
HttpRetryQueue::container_type::iterator cur(iter++);
|
||||
|
||||
if (static_cast<HttpHandle>(*cur) == handle)
|
||||
{
|
||||
HttpOpRequest * op(*cur);
|
||||
c1.erase(cur); // All iterators are now invalidated
|
||||
op->cancel();
|
||||
op->release();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Scan ready queue
|
||||
HttpReadyQueue::container_type & c2(state.mReadyQueue.get_container());
|
||||
for (HttpReadyQueue::container_type::iterator iter(c2.begin()); c2.end() != iter;)
|
||||
{
|
||||
HttpReadyQueue::container_type::iterator cur(iter++);
|
||||
|
||||
if (static_cast<HttpHandle>(*cur) == handle)
|
||||
{
|
||||
HttpOpRequest * op(*cur);
|
||||
c2.erase(cur); // All iterators are now invalidated
|
||||
op->cancel();
|
||||
op->release();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op)
|
||||
{
|
||||
static const HttpStatus cant_connect(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT);
|
||||
static const HttpStatus cant_res_proxy(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_RESOLVE_PROXY);
|
||||
static const HttpStatus cant_res_host(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_RESOLVE_HOST);
|
||||
static const HttpStatus send_error(HttpStatus::EXT_CURL_EASY, CURLE_SEND_ERROR);
|
||||
static const HttpStatus recv_error(HttpStatus::EXT_CURL_EASY, CURLE_RECV_ERROR);
|
||||
static const HttpStatus upload_failed(HttpStatus::EXT_CURL_EASY, CURLE_UPLOAD_FAILED);
|
||||
static const HttpStatus op_timedout(HttpStatus::EXT_CURL_EASY, CURLE_OPERATION_TIMEDOUT);
|
||||
static const HttpStatus post_error(HttpStatus::EXT_CURL_EASY, CURLE_HTTP_POST_ERROR);
|
||||
|
||||
// Retry or finalize
|
||||
if (! op->mStatus)
|
||||
{
|
||||
// If this failed, we might want to retry. Have to inspect
|
||||
// the status a little more deeply for those reasons worth retrying...
|
||||
if (op->mPolicyRetries < op->mPolicyRetryLimit &&
|
||||
((op->mStatus.isHttpStatus() && op->mStatus.mType >= 499 && op->mStatus.mType <= 599) ||
|
||||
cant_connect == op->mStatus ||
|
||||
cant_res_proxy == op->mStatus ||
|
||||
cant_res_host == op->mStatus ||
|
||||
send_error == op->mStatus ||
|
||||
recv_error == op->mStatus ||
|
||||
upload_failed == op->mStatus ||
|
||||
op_timedout == op->mStatus ||
|
||||
post_error == op->mStatus))
|
||||
{
|
||||
// Okay, worth a retry. We include 499 in this test as
|
||||
// it's the old 'who knows?' error from many grid services...
|
||||
retryOp(op);
|
||||
return true; // still active/ready
|
||||
}
|
||||
}
|
||||
|
||||
// This op is done, finalize it delivering it to the reply queue...
|
||||
if (! op->mStatus)
|
||||
{
|
||||
LL_WARNS("CoreHttp") << "HTTP request " << static_cast<HttpHandle>(op)
|
||||
<< " failed after " << op->mPolicyRetries
|
||||
<< " retries. Reason: " << op->mStatus.toString()
|
||||
<< " (" << op->mStatus.toHex() << ")"
|
||||
<< LL_ENDL;
|
||||
}
|
||||
else if (op->mPolicyRetries)
|
||||
{
|
||||
LL_WARNS("CoreHttp") << "HTTP request " << static_cast<HttpHandle>(op)
|
||||
<< " succeeded on retry " << op->mPolicyRetries << "."
|
||||
<< LL_ENDL;
|
||||
}
|
||||
|
||||
op->stageFromActive(mService);
|
||||
op->release();
|
||||
return false; // not active
|
||||
}
|
||||
|
||||
|
||||
int HttpPolicy::getReadyCount(HttpRequest::policy_t policy_class) const
|
||||
{
|
||||
if (policy_class < mActiveClasses)
|
||||
{
|
||||
return (mState[policy_class].mReadyQueue.size()
|
||||
+ mState[policy_class].mRetryQueue.size());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
} // end namespace LLCore
|
||||
|
|
@ -0,0 +1,161 @@
|
|||
/**
|
||||
* @file _httppolicy.h
|
||||
* @brief Declarations for internal class enforcing policy decisions.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, 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 _LLCORE_HTTP_POLICY_H_
|
||||
#define _LLCORE_HTTP_POLICY_H_
|
||||
|
||||
|
||||
#include "httprequest.h"
|
||||
#include "_httpservice.h"
|
||||
#include "_httpreadyqueue.h"
|
||||
#include "_httpretryqueue.h"
|
||||
#include "_httppolicyglobal.h"
|
||||
#include "_httppolicyclass.h"
|
||||
#include "_httpinternal.h"
|
||||
|
||||
|
||||
namespace LLCore
|
||||
{
|
||||
|
||||
class HttpReadyQueue;
|
||||
class HttpOpRequest;
|
||||
|
||||
|
||||
/// Implements class-based queuing policies for an HttpService instance.
|
||||
///
|
||||
/// Threading: Single-threaded. Other than for construction/destruction,
|
||||
/// all methods are expected to be invoked in a single thread, typically
|
||||
/// a worker thread of some sort.
|
||||
class HttpPolicy
|
||||
{
|
||||
public:
|
||||
HttpPolicy(HttpService *);
|
||||
virtual ~HttpPolicy();
|
||||
|
||||
private:
|
||||
HttpPolicy(const HttpPolicy &); // Not defined
|
||||
void operator=(const HttpPolicy &); // Not defined
|
||||
|
||||
public:
|
||||
/// Cancel all ready and retry requests sending them to
|
||||
/// their notification queues. Release state resources
|
||||
/// making further request handling impossible.
|
||||
///
|
||||
/// Threading: called by worker thread
|
||||
void shutdown();
|
||||
|
||||
/// Deliver policy definitions and enable handling of
|
||||
/// requests. One-time call invoked before starting
|
||||
/// the worker thread.
|
||||
///
|
||||
/// Threading: called by application thread
|
||||
void start(const HttpPolicyGlobal & global,
|
||||
const std::vector<HttpPolicyClass> & classes);
|
||||
|
||||
/// Give the policy layer some cycles to scan the ready
|
||||
/// queue promoting higher-priority requests to active
|
||||
/// as permited.
|
||||
///
|
||||
/// @return Indication of how soon this method
|
||||
/// should be called again.
|
||||
///
|
||||
/// Threading: called by worker thread
|
||||
HttpService::ELoopSpeed processReadyQueue();
|
||||
|
||||
/// Add request to a ready queue. Caller is expected to have
|
||||
/// provided us with a reference count to hold the request. (No
|
||||
/// additional references will be added.)
|
||||
///
|
||||
/// OpRequest is owned by the request queue after this call
|
||||
/// and should not be modified by anyone until retrieved
|
||||
/// from queue.
|
||||
///
|
||||
/// Threading: called by any thread
|
||||
void addOp(HttpOpRequest *);
|
||||
|
||||
/// Similar to addOp, used when a caller wants to retry a
|
||||
/// request that has failed. It's placed on a special retry
|
||||
/// queue but ordered by retry time not priority. Otherwise,
|
||||
/// handling is the same and retried operations are considered
|
||||
/// before new ones but that doesn't guarantee completion
|
||||
/// order.
|
||||
///
|
||||
/// Threading: called by worker thread
|
||||
void retryOp(HttpOpRequest *);
|
||||
|
||||
/// Attempt to change the priority of an earlier request.
|
||||
/// Request that Shadows HttpService's method
|
||||
///
|
||||
/// Threading: called by worker thread
|
||||
bool changePriority(HttpHandle handle, HttpRequest::priority_t priority);
|
||||
|
||||
/// Attempt to cancel a previous request.
|
||||
/// Shadows HttpService's method as well
|
||||
///
|
||||
/// Threading: called by worker thread
|
||||
bool cancel(HttpHandle handle);
|
||||
|
||||
/// When transport is finished with an op and takes it off the
|
||||
/// active queue, it is delivered here for dispatch. Policy
|
||||
/// may send it back to the ready/retry queues if it needs another
|
||||
/// go or we may finalize it and send it on to the reply queue.
|
||||
///
|
||||
/// @return Returns true of the request is still active
|
||||
/// or ready after staging, false if has been
|
||||
/// sent on to the reply queue.
|
||||
///
|
||||
/// Threading: called by worker thread
|
||||
bool stageAfterCompletion(HttpOpRequest * op);
|
||||
|
||||
// Get pointer to global policy options. Caller is expected
|
||||
// to do context checks like no setting once running.
|
||||
///
|
||||
/// Threading: called by any thread *but* the object may
|
||||
/// only be modified by the worker thread once running.
|
||||
///
|
||||
HttpPolicyGlobal & getGlobalOptions()
|
||||
{
|
||||
return mGlobalOptions;
|
||||
}
|
||||
|
||||
/// Get ready counts for a particular policy class
|
||||
///
|
||||
/// Threading: called by worker thread
|
||||
int getReadyCount(HttpRequest::policy_t policy_class) const;
|
||||
|
||||
protected:
|
||||
struct State;
|
||||
|
||||
int mActiveClasses;
|
||||
State * mState;
|
||||
HttpService * mService; // Naked pointer, not refcounted, not owner
|
||||
HttpPolicyGlobal mGlobalOptions;
|
||||
|
||||
}; // end class HttpPolicy
|
||||
|
||||
} // end namespace LLCore
|
||||
|
||||
#endif // _LLCORE_HTTP_POLICY_H_
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
/**
|
||||
* @file _httppolicyclass.cpp
|
||||
* @brief Definitions for internal class defining class policy option.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include "_httppolicyclass.h"
|
||||
|
||||
#include "_httpinternal.h"
|
||||
|
||||
|
||||
namespace LLCore
|
||||
{
|
||||
|
||||
|
||||
HttpPolicyClass::HttpPolicyClass()
|
||||
: mSetMask(0UL),
|
||||
mConnectionLimit(HTTP_CONNECTION_LIMIT_DEFAULT),
|
||||
mPerHostConnectionLimit(HTTP_CONNECTION_LIMIT_DEFAULT),
|
||||
mPipelining(0)
|
||||
{}
|
||||
|
||||
|
||||
HttpPolicyClass::~HttpPolicyClass()
|
||||
{}
|
||||
|
||||
|
||||
HttpPolicyClass & HttpPolicyClass::operator=(const HttpPolicyClass & other)
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
mSetMask = other.mSetMask;
|
||||
mConnectionLimit = other.mConnectionLimit;
|
||||
mPerHostConnectionLimit = other.mPerHostConnectionLimit;
|
||||
mPipelining = other.mPipelining;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
HttpPolicyClass::HttpPolicyClass(const HttpPolicyClass & other)
|
||||
: mSetMask(other.mSetMask),
|
||||
mConnectionLimit(other.mConnectionLimit),
|
||||
mPerHostConnectionLimit(other.mPerHostConnectionLimit),
|
||||
mPipelining(other.mPipelining)
|
||||
{}
|
||||
|
||||
|
||||
HttpStatus HttpPolicyClass::set(HttpRequest::EClassPolicy opt, long value)
|
||||
{
|
||||
switch (opt)
|
||||
{
|
||||
case HttpRequest::CP_CONNECTION_LIMIT:
|
||||
mConnectionLimit = llclamp(value, long(HTTP_CONNECTION_LIMIT_MIN), long(HTTP_CONNECTION_LIMIT_MAX));
|
||||
break;
|
||||
|
||||
case HttpRequest::CP_PER_HOST_CONNECTION_LIMIT:
|
||||
mPerHostConnectionLimit = llclamp(value, long(HTTP_CONNECTION_LIMIT_MIN), mConnectionLimit);
|
||||
break;
|
||||
|
||||
case HttpRequest::CP_ENABLE_PIPELINING:
|
||||
mPipelining = llclamp(value, 0L, 1L);
|
||||
break;
|
||||
|
||||
default:
|
||||
return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
|
||||
}
|
||||
|
||||
mSetMask |= 1UL << int(opt);
|
||||
return HttpStatus();
|
||||
}
|
||||
|
||||
|
||||
HttpStatus HttpPolicyClass::get(HttpRequest::EClassPolicy opt, long * value)
|
||||
{
|
||||
static const HttpStatus not_set(HttpStatus::LLCORE, HE_OPT_NOT_SET);
|
||||
long * src(NULL);
|
||||
|
||||
switch (opt)
|
||||
{
|
||||
case HttpRequest::CP_CONNECTION_LIMIT:
|
||||
src = &mConnectionLimit;
|
||||
break;
|
||||
|
||||
case HttpRequest::CP_PER_HOST_CONNECTION_LIMIT:
|
||||
src = &mPerHostConnectionLimit;
|
||||
break;
|
||||
|
||||
case HttpRequest::CP_ENABLE_PIPELINING:
|
||||
src = &mPipelining;
|
||||
break;
|
||||
|
||||
default:
|
||||
return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
|
||||
}
|
||||
|
||||
if (! (mSetMask & (1UL << int(opt))))
|
||||
return not_set;
|
||||
|
||||
*value = *src;
|
||||
return HttpStatus();
|
||||
}
|
||||
|
||||
|
||||
} // end namespace LLCore
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
/**
|
||||
* @file _httppolicyclass.h
|
||||
* @brief Declarations for internal class defining policy class options.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, 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 _LLCORE_HTTP_POLICY_CLASS_H_
|
||||
#define _LLCORE_HTTP_POLICY_CLASS_H_
|
||||
|
||||
|
||||
#include "httprequest.h"
|
||||
|
||||
|
||||
namespace LLCore
|
||||
{
|
||||
|
||||
class HttpPolicyClass
|
||||
{
|
||||
public:
|
||||
HttpPolicyClass();
|
||||
~HttpPolicyClass();
|
||||
|
||||
HttpPolicyClass & operator=(const HttpPolicyClass &);
|
||||
HttpPolicyClass(const HttpPolicyClass &); // Not defined
|
||||
|
||||
public:
|
||||
HttpStatus set(HttpRequest::EClassPolicy opt, long value);
|
||||
HttpStatus get(HttpRequest::EClassPolicy opt, long * value);
|
||||
|
||||
public:
|
||||
unsigned long mSetMask;
|
||||
long mConnectionLimit;
|
||||
long mPerHostConnectionLimit;
|
||||
long mPipelining;
|
||||
}; // end class HttpPolicyClass
|
||||
|
||||
} // end namespace LLCore
|
||||
|
||||
#endif // _LLCORE_HTTP_POLICY_CLASS_H_
|
||||
|
|
@ -0,0 +1,175 @@
|
|||
/**
|
||||
* @file _httppolicyglobal.cpp
|
||||
* @brief Definitions for internal class defining global policy option.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include "_httppolicyglobal.h"
|
||||
|
||||
#include "_httpinternal.h"
|
||||
|
||||
|
||||
namespace LLCore
|
||||
{
|
||||
|
||||
|
||||
HttpPolicyGlobal::HttpPolicyGlobal()
|
||||
: mSetMask(0UL),
|
||||
mConnectionLimit(HTTP_CONNECTION_LIMIT_DEFAULT),
|
||||
mTrace(HTTP_TRACE_OFF),
|
||||
mUseLLProxy(0)
|
||||
{}
|
||||
|
||||
|
||||
HttpPolicyGlobal::~HttpPolicyGlobal()
|
||||
{}
|
||||
|
||||
|
||||
HttpPolicyGlobal & HttpPolicyGlobal::operator=(const HttpPolicyGlobal & other)
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
mSetMask = other.mSetMask;
|
||||
mConnectionLimit = other.mConnectionLimit;
|
||||
mCAPath = other.mCAPath;
|
||||
mCAFile = other.mCAFile;
|
||||
mHttpProxy = other.mHttpProxy;
|
||||
mTrace = other.mTrace;
|
||||
mUseLLProxy = other.mUseLLProxy;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
HttpStatus HttpPolicyGlobal::set(HttpRequest::EGlobalPolicy opt, long value)
|
||||
{
|
||||
switch (opt)
|
||||
{
|
||||
case HttpRequest::GP_CONNECTION_LIMIT:
|
||||
mConnectionLimit = llclamp(value, long(HTTP_CONNECTION_LIMIT_MIN), long(HTTP_CONNECTION_LIMIT_MAX));
|
||||
break;
|
||||
|
||||
case HttpRequest::GP_TRACE:
|
||||
mTrace = llclamp(value, long(HTTP_TRACE_MIN), long(HTTP_TRACE_MAX));
|
||||
break;
|
||||
|
||||
case HttpRequest::GP_LLPROXY:
|
||||
mUseLLProxy = llclamp(value, 0L, 1L);
|
||||
break;
|
||||
|
||||
default:
|
||||
return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
|
||||
}
|
||||
|
||||
mSetMask |= 1UL << int(opt);
|
||||
return HttpStatus();
|
||||
}
|
||||
|
||||
|
||||
HttpStatus HttpPolicyGlobal::set(HttpRequest::EGlobalPolicy opt, const std::string & value)
|
||||
{
|
||||
switch (opt)
|
||||
{
|
||||
case HttpRequest::GP_CA_PATH:
|
||||
mCAPath = value;
|
||||
break;
|
||||
|
||||
case HttpRequest::GP_CA_FILE:
|
||||
mCAFile = value;
|
||||
break;
|
||||
|
||||
case HttpRequest::GP_HTTP_PROXY:
|
||||
mCAFile = value;
|
||||
break;
|
||||
|
||||
default:
|
||||
return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
|
||||
}
|
||||
|
||||
mSetMask |= 1UL << int(opt);
|
||||
return HttpStatus();
|
||||
}
|
||||
|
||||
|
||||
HttpStatus HttpPolicyGlobal::get(HttpRequest::EGlobalPolicy opt, long * value)
|
||||
{
|
||||
static const HttpStatus not_set(HttpStatus::LLCORE, HE_OPT_NOT_SET);
|
||||
long * src(NULL);
|
||||
|
||||
switch (opt)
|
||||
{
|
||||
case HttpRequest::GP_CONNECTION_LIMIT:
|
||||
src = &mConnectionLimit;
|
||||
break;
|
||||
|
||||
case HttpRequest::GP_TRACE:
|
||||
src = &mTrace;
|
||||
break;
|
||||
|
||||
case HttpRequest::GP_LLPROXY:
|
||||
src = &mUseLLProxy;
|
||||
break;
|
||||
|
||||
default:
|
||||
return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
|
||||
}
|
||||
|
||||
if (! (mSetMask & (1UL << int(opt))))
|
||||
return not_set;
|
||||
|
||||
*value = *src;
|
||||
return HttpStatus();
|
||||
}
|
||||
|
||||
|
||||
HttpStatus HttpPolicyGlobal::get(HttpRequest::EGlobalPolicy opt, const std::string ** value)
|
||||
{
|
||||
static const HttpStatus not_set(HttpStatus::LLCORE, HE_OPT_NOT_SET);
|
||||
const std::string * src(NULL);
|
||||
|
||||
switch (opt)
|
||||
{
|
||||
case HttpRequest::GP_CA_PATH:
|
||||
src = &mCAPath;
|
||||
break;
|
||||
|
||||
case HttpRequest::GP_CA_FILE:
|
||||
src = &mCAFile;
|
||||
break;
|
||||
|
||||
case HttpRequest::GP_HTTP_PROXY:
|
||||
src = &mHttpProxy;
|
||||
break;
|
||||
|
||||
default:
|
||||
return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
|
||||
}
|
||||
|
||||
if (! (mSetMask & (1UL << int(opt))))
|
||||
return not_set;
|
||||
|
||||
*value = src;
|
||||
return HttpStatus();
|
||||
}
|
||||
|
||||
} // end namespace LLCore
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
/**
|
||||
* @file _httppolicyglobal.h
|
||||
* @brief Declarations for internal class defining global policy option.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, 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 _LLCORE_HTTP_POLICY_GLOBAL_H_
|
||||
#define _LLCORE_HTTP_POLICY_GLOBAL_H_
|
||||
|
||||
|
||||
#include "httprequest.h"
|
||||
|
||||
|
||||
namespace LLCore
|
||||
{
|
||||
|
||||
class HttpPolicyGlobal
|
||||
{
|
||||
public:
|
||||
HttpPolicyGlobal();
|
||||
~HttpPolicyGlobal();
|
||||
|
||||
HttpPolicyGlobal & operator=(const HttpPolicyGlobal &);
|
||||
|
||||
private:
|
||||
HttpPolicyGlobal(const HttpPolicyGlobal &); // Not defined
|
||||
|
||||
public:
|
||||
HttpStatus set(HttpRequest::EGlobalPolicy opt, long value);
|
||||
HttpStatus set(HttpRequest::EGlobalPolicy opt, const std::string & value);
|
||||
HttpStatus get(HttpRequest::EGlobalPolicy opt, long * value);
|
||||
HttpStatus get(HttpRequest::EGlobalPolicy opt, const std::string ** value);
|
||||
|
||||
public:
|
||||
unsigned long mSetMask;
|
||||
long mConnectionLimit;
|
||||
std::string mCAPath;
|
||||
std::string mCAFile;
|
||||
std::string mHttpProxy;
|
||||
long mTrace;
|
||||
long mUseLLProxy;
|
||||
}; // end class HttpPolicyGlobal
|
||||
|
||||
} // end namespace LLCore
|
||||
|
||||
#endif // _LLCORE_HTTP_POLICY_GLOBAL_H_
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
/**
|
||||
* @file _httpreadyqueue.h
|
||||
* @brief Internal declaration for the operation ready queue
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, 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 _LLCORE_HTTP_READY_QUEUE_H_
|
||||
#define _LLCORE_HTTP_READY_QUEUE_H_
|
||||
|
||||
|
||||
#include <queue>
|
||||
|
||||
#include "_httpinternal.h"
|
||||
#include "_httpoprequest.h"
|
||||
|
||||
|
||||
namespace LLCore
|
||||
{
|
||||
|
||||
/// HttpReadyQueue provides a simple priority queue for HttpOpRequest objects.
|
||||
///
|
||||
/// This uses the priority_queue adaptor class to provide the queue
|
||||
/// as well as the ordering scheme while allowing us access to the
|
||||
/// raw container if we follow a few simple rules. One of the more
|
||||
/// important of those rules is that any iterator becomes invalid
|
||||
/// on element erasure. So pay attention.
|
||||
///
|
||||
/// If LLCORE_HTTP_READY_QUEUE_IGNORES_PRIORITY tests true, the class
|
||||
/// implements a std::priority_queue interface but on std::deque
|
||||
/// behavior to eliminate sensitivity to priority. In the future,
|
||||
/// this will likely become the only behavior or it may become
|
||||
/// a run-time election.
|
||||
///
|
||||
/// Threading: not thread-safe. Expected to be used entirely by
|
||||
/// a single thread, typically a worker thread of some sort.
|
||||
|
||||
#if LLCORE_HTTP_READY_QUEUE_IGNORES_PRIORITY
|
||||
|
||||
typedef std::deque<HttpOpRequest *> HttpReadyQueueBase;
|
||||
|
||||
#else
|
||||
|
||||
typedef std::priority_queue<HttpOpRequest *,
|
||||
std::deque<HttpOpRequest *>,
|
||||
LLCore::HttpOpRequestCompare> HttpReadyQueueBase;
|
||||
|
||||
#endif // LLCORE_HTTP_READY_QUEUE_IGNORES_PRIORITY
|
||||
|
||||
class HttpReadyQueue : public HttpReadyQueueBase
|
||||
{
|
||||
public:
|
||||
HttpReadyQueue()
|
||||
: HttpReadyQueueBase()
|
||||
{}
|
||||
|
||||
~HttpReadyQueue()
|
||||
{}
|
||||
|
||||
protected:
|
||||
HttpReadyQueue(const HttpReadyQueue &); // Not defined
|
||||
void operator=(const HttpReadyQueue &); // Not defined
|
||||
|
||||
public:
|
||||
|
||||
#if LLCORE_HTTP_READY_QUEUE_IGNORES_PRIORITY
|
||||
// Types and methods needed to make a std::deque look
|
||||
// more like a std::priority_queue, at least for our
|
||||
// purposes.
|
||||
typedef HttpReadyQueueBase container_type;
|
||||
|
||||
const_reference top() const
|
||||
{
|
||||
return front();
|
||||
}
|
||||
|
||||
void pop()
|
||||
{
|
||||
pop_front();
|
||||
}
|
||||
|
||||
void push(const value_type & v)
|
||||
{
|
||||
push_back(v);
|
||||
}
|
||||
|
||||
#endif // LLCORE_HTTP_READY_QUEUE_IGNORES_PRIORITY
|
||||
|
||||
const container_type & get_container() const
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
container_type & get_container()
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
}; // end class HttpReadyQueue
|
||||
|
||||
|
||||
} // end namespace LLCore
|
||||
|
||||
|
||||
#endif // _LLCORE_HTTP_READY_QUEUE_H_
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
/**
|
||||
* @file _httpreplyqueue.cpp
|
||||
* @brief Internal definitions for the operation reply queue
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include "_httpreplyqueue.h"
|
||||
|
||||
|
||||
#include "_mutex.h"
|
||||
#include "_thread.h"
|
||||
#include "_httpoperation.h"
|
||||
|
||||
using namespace LLCoreInt;
|
||||
|
||||
|
||||
namespace LLCore
|
||||
{
|
||||
|
||||
|
||||
HttpReplyQueue::HttpReplyQueue()
|
||||
: RefCounted(true)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
HttpReplyQueue::~HttpReplyQueue()
|
||||
{
|
||||
while (! mQueue.empty())
|
||||
{
|
||||
HttpOperation * op = mQueue.back();
|
||||
mQueue.pop_back();
|
||||
op->release();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void HttpReplyQueue::addOp(HttpOperation * op)
|
||||
{
|
||||
{
|
||||
HttpScopedLock lock(mQueueMutex);
|
||||
|
||||
mQueue.push_back(op);
|
||||
}
|
||||
mQueueCV.notify_all();
|
||||
}
|
||||
|
||||
|
||||
HttpOperation * HttpReplyQueue::fetchOp()
|
||||
{
|
||||
HttpOperation * result(NULL);
|
||||
|
||||
{
|
||||
HttpScopedLock lock(mQueueMutex);
|
||||
|
||||
if (mQueue.empty())
|
||||
return NULL;
|
||||
|
||||
result = mQueue.front();
|
||||
mQueue.erase(mQueue.begin());
|
||||
}
|
||||
|
||||
// Caller also acquires the reference count
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void HttpReplyQueue::fetchAll(OpContainer & ops)
|
||||
{
|
||||
// Not valid putting something back on the queue...
|
||||
llassert_always(ops.empty());
|
||||
|
||||
{
|
||||
HttpScopedLock lock(mQueueMutex);
|
||||
|
||||
if (! mQueue.empty())
|
||||
{
|
||||
mQueue.swap(ops);
|
||||
}
|
||||
}
|
||||
|
||||
// Caller also acquires the reference counts on each op.
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
} // end namespace LLCore
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
/**
|
||||
* @file _httpreplyqueue.h
|
||||
* @brief Internal declarations for the operation reply queue.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, 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 _LLCORE_HTTP_REPLY_QUEUE_H_
|
||||
#define _LLCORE_HTTP_REPLY_QUEUE_H_
|
||||
|
||||
|
||||
#include "_refcounted.h"
|
||||
#include "_mutex.h"
|
||||
|
||||
|
||||
namespace LLCore
|
||||
{
|
||||
|
||||
|
||||
class HttpOperation;
|
||||
|
||||
|
||||
/// Almost identical to the HttpRequestQueue class but
|
||||
/// whereas that class is a singleton and is known to the
|
||||
/// HttpService object, this queue is 1:1 with HttpRequest
|
||||
/// instances and isn't explicitly referenced by the
|
||||
/// service object. Instead, HttpOperation objects that
|
||||
/// want to generate replies back to their creators also
|
||||
/// keep references to the corresponding HttpReplyQueue.
|
||||
/// The HttpService plumbing then simply delivers replies
|
||||
/// to the requested reply queue.
|
||||
///
|
||||
/// One result of that is that the fetch operations do
|
||||
/// not have a wait forever option. The service object
|
||||
/// doesn't keep handles on everything it would need to
|
||||
/// notify so it can't wake up sleepers should it need to
|
||||
/// shutdown. So only non-blocking or timed-blocking modes
|
||||
/// are anticipated. These are how most application consumers
|
||||
/// will be coded anyway so it shouldn't be too much of a
|
||||
/// burden.
|
||||
|
||||
class HttpReplyQueue : public LLCoreInt::RefCounted
|
||||
{
|
||||
public:
|
||||
/// Caller acquires a Refcount on construction
|
||||
HttpReplyQueue();
|
||||
|
||||
protected:
|
||||
virtual ~HttpReplyQueue(); // Use release()
|
||||
|
||||
private:
|
||||
HttpReplyQueue(const HttpReplyQueue &); // Not defined
|
||||
void operator=(const HttpReplyQueue &); // Not defined
|
||||
|
||||
public:
|
||||
typedef std::vector<HttpOperation *> OpContainer;
|
||||
|
||||
/// Insert an object at the back of the reply queue.
|
||||
///
|
||||
/// Library also takes possession of one reference count to pass
|
||||
/// through the queue.
|
||||
///
|
||||
/// Threading: callable by any thread.
|
||||
void addOp(HttpOperation * op);
|
||||
|
||||
/// Fetch an operation from the head of the queue. Returns
|
||||
/// NULL if none exists.
|
||||
///
|
||||
/// Caller acquires reference count on returned operation.
|
||||
///
|
||||
/// Threading: callable by any thread.
|
||||
HttpOperation * fetchOp();
|
||||
|
||||
/// Caller acquires reference count on each returned operation
|
||||
///
|
||||
/// Threading: callable by any thread.
|
||||
void fetchAll(OpContainer & ops);
|
||||
|
||||
protected:
|
||||
OpContainer mQueue;
|
||||
LLCoreInt::HttpMutex mQueueMutex;
|
||||
LLCoreInt::HttpConditionVariable mQueueCV;
|
||||
|
||||
}; // end class HttpReplyQueue
|
||||
|
||||
} // end namespace LLCore
|
||||
|
||||
|
||||
#endif // _LLCORE_HTTP_REPLY_QUEUE_H_
|
||||
|
|
@ -0,0 +1,161 @@
|
|||
/**
|
||||
* @file _httprequestqueue.cpp
|
||||
* @brief
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include "_httprequestqueue.h"
|
||||
|
||||
#include "_httpoperation.h"
|
||||
#include "_mutex.h"
|
||||
|
||||
|
||||
using namespace LLCoreInt;
|
||||
|
||||
namespace LLCore
|
||||
{
|
||||
|
||||
HttpRequestQueue * HttpRequestQueue::sInstance(NULL);
|
||||
|
||||
|
||||
HttpRequestQueue::HttpRequestQueue()
|
||||
: RefCounted(true),
|
||||
mQueueStopped(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
HttpRequestQueue::~HttpRequestQueue()
|
||||
{
|
||||
while (! mQueue.empty())
|
||||
{
|
||||
HttpOperation * op = mQueue.back();
|
||||
mQueue.pop_back();
|
||||
op->release();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void HttpRequestQueue::init()
|
||||
{
|
||||
llassert_always(! sInstance);
|
||||
sInstance = new HttpRequestQueue();
|
||||
}
|
||||
|
||||
|
||||
void HttpRequestQueue::term()
|
||||
{
|
||||
if (sInstance)
|
||||
{
|
||||
sInstance->release();
|
||||
sInstance = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
HttpStatus HttpRequestQueue::addOp(HttpOperation * op)
|
||||
{
|
||||
bool wake(false);
|
||||
{
|
||||
HttpScopedLock lock(mQueueMutex);
|
||||
|
||||
if (mQueueStopped)
|
||||
{
|
||||
// Return op and error to caller
|
||||
return HttpStatus(HttpStatus::LLCORE, HE_SHUTTING_DOWN);
|
||||
}
|
||||
wake = mQueue.empty();
|
||||
mQueue.push_back(op);
|
||||
}
|
||||
if (wake)
|
||||
{
|
||||
mQueueCV.notify_all();
|
||||
}
|
||||
return HttpStatus();
|
||||
}
|
||||
|
||||
|
||||
HttpOperation * HttpRequestQueue::fetchOp(bool wait)
|
||||
{
|
||||
HttpOperation * result(NULL);
|
||||
|
||||
{
|
||||
HttpScopedLock lock(mQueueMutex);
|
||||
|
||||
while (mQueue.empty())
|
||||
{
|
||||
if (! wait || mQueueStopped)
|
||||
return NULL;
|
||||
mQueueCV.wait(lock);
|
||||
}
|
||||
|
||||
result = mQueue.front();
|
||||
mQueue.erase(mQueue.begin());
|
||||
}
|
||||
|
||||
// Caller also acquires the reference count
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void HttpRequestQueue::fetchAll(bool wait, OpContainer & ops)
|
||||
{
|
||||
// Not valid putting something back on the queue...
|
||||
llassert_always(ops.empty());
|
||||
|
||||
{
|
||||
HttpScopedLock lock(mQueueMutex);
|
||||
|
||||
while (mQueue.empty())
|
||||
{
|
||||
if (! wait || mQueueStopped)
|
||||
return;
|
||||
mQueueCV.wait(lock);
|
||||
}
|
||||
|
||||
mQueue.swap(ops);
|
||||
}
|
||||
|
||||
// Caller also acquires the reference counts on each op.
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
void HttpRequestQueue::wakeAll()
|
||||
{
|
||||
mQueueCV.notify_all();
|
||||
}
|
||||
|
||||
|
||||
void HttpRequestQueue::stopQueue()
|
||||
{
|
||||
{
|
||||
HttpScopedLock lock(mQueueMutex);
|
||||
|
||||
mQueueStopped = true;
|
||||
wakeAll();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // end namespace LLCore
|
||||
|
|
@ -0,0 +1,141 @@
|
|||
/**
|
||||
* @file _httprequestqueue.h
|
||||
* @brief Internal declaration for the operation request queue
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, 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 _LLCORE_HTTP_REQUEST_QUEUE_H_
|
||||
#define _LLCORE_HTTP_REQUEST_QUEUE_H_
|
||||
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "httpcommon.h"
|
||||
#include "_refcounted.h"
|
||||
#include "_mutex.h"
|
||||
|
||||
|
||||
namespace LLCore
|
||||
{
|
||||
|
||||
|
||||
class HttpOperation;
|
||||
|
||||
|
||||
/// Thread-safe queue of HttpOperation objects. Just
|
||||
/// a simple queue that handles the transfer of operation
|
||||
/// requests from all HttpRequest instances into the
|
||||
/// singleton HttpService instance.
|
||||
|
||||
class HttpRequestQueue : public LLCoreInt::RefCounted
|
||||
{
|
||||
protected:
|
||||
/// Caller acquires a Refcount on construction
|
||||
HttpRequestQueue();
|
||||
|
||||
protected:
|
||||
virtual ~HttpRequestQueue(); // Use release()
|
||||
|
||||
private:
|
||||
HttpRequestQueue(const HttpRequestQueue &); // Not defined
|
||||
void operator=(const HttpRequestQueue &); // Not defined
|
||||
|
||||
public:
|
||||
static void init();
|
||||
static void term();
|
||||
|
||||
/// Threading: callable by any thread once inited.
|
||||
inline static HttpRequestQueue * instanceOf()
|
||||
{
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
public:
|
||||
typedef std::vector<HttpOperation *> OpContainer;
|
||||
|
||||
/// Insert an object at the back of the request queue.
|
||||
///
|
||||
/// Caller must provide one refcount to the queue which takes
|
||||
/// possession of the count on success.
|
||||
///
|
||||
/// @return Standard status. On failure, caller
|
||||
/// must dispose of the operation with
|
||||
/// an explicit release() call.
|
||||
///
|
||||
/// Threading: callable by any thread.
|
||||
HttpStatus addOp(HttpOperation * op);
|
||||
|
||||
/// Return the operation on the front of the queue. If
|
||||
/// the queue is empty and @wait is false, call returns
|
||||
/// immediately and a NULL pointer is returned. If true,
|
||||
/// caller will sleep until explicitly woken. Wakeups
|
||||
/// can be spurious and callers must expect NULL pointers
|
||||
/// even if waiting is indicated.
|
||||
///
|
||||
/// Caller acquires reference count any returned operation
|
||||
///
|
||||
/// Threading: callable by any thread.
|
||||
HttpOperation * fetchOp(bool wait);
|
||||
|
||||
/// Return all queued requests to caller. The @ops argument
|
||||
/// should be empty when called and will be swap()'d with
|
||||
/// current contents. Handling of the @wait argument is
|
||||
/// identical to @fetchOp.
|
||||
///
|
||||
/// Caller acquires reference count on each returned operation
|
||||
///
|
||||
/// Threading: callable by any thread.
|
||||
void fetchAll(bool wait, OpContainer & ops);
|
||||
|
||||
/// Wake any sleeping threads. Normal queuing operations
|
||||
/// won't require this but it may be necessary for termination
|
||||
/// requests.
|
||||
///
|
||||
/// Threading: callable by any thread.
|
||||
void wakeAll();
|
||||
|
||||
/// Disallow further request queuing. Callers to @addOp will
|
||||
/// get a failure status (LLCORE, HE_SHUTTING_DOWN). Callers
|
||||
/// to @fetchAll or @fetchOp will get requests that are on the
|
||||
/// queue but the calls will no longer wait. Instead they'll
|
||||
/// return immediately. Also wakes up all sleepers to send
|
||||
/// them on their way.
|
||||
///
|
||||
/// Threading: callable by any thread.
|
||||
void stopQueue();
|
||||
|
||||
protected:
|
||||
static HttpRequestQueue * sInstance;
|
||||
|
||||
protected:
|
||||
OpContainer mQueue;
|
||||
LLCoreInt::HttpMutex mQueueMutex;
|
||||
LLCoreInt::HttpConditionVariable mQueueCV;
|
||||
bool mQueueStopped;
|
||||
|
||||
}; // end class HttpRequestQueue
|
||||
|
||||
} // end namespace LLCore
|
||||
|
||||
|
||||
#endif // _LLCORE_HTTP_REQUEST_QUEUE_H_
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
/**
|
||||
* @file _httpretryqueue.h
|
||||
* @brief Internal declaration for the operation retry queue
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, 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 _LLCORE_HTTP_RETRY_QUEUE_H_
|
||||
#define _LLCORE_HTTP_RETRY_QUEUE_H_
|
||||
|
||||
|
||||
#include <queue>
|
||||
|
||||
#include "_httpoprequest.h"
|
||||
|
||||
|
||||
namespace LLCore
|
||||
{
|
||||
|
||||
/// HttpRetryQueue provides a simple priority queue for HttpOpRequest objects.
|
||||
///
|
||||
/// This uses the priority_queue adaptor class to provide the queue
|
||||
/// as well as the ordering scheme while allowing us access to the
|
||||
/// raw container if we follow a few simple rules. One of the more
|
||||
/// important of those rules is that any iterator becomes invalid
|
||||
/// on element erasure. So pay attention.
|
||||
///
|
||||
/// Threading: not thread-safe. Expected to be used entirely by
|
||||
/// a single thread, typically a worker thread of some sort.
|
||||
|
||||
struct HttpOpRetryCompare
|
||||
{
|
||||
bool operator()(const HttpOpRequest * lhs, const HttpOpRequest * rhs)
|
||||
{
|
||||
return lhs->mPolicyRetryAt < rhs->mPolicyRetryAt;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
typedef std::priority_queue<HttpOpRequest *,
|
||||
std::deque<HttpOpRequest *>,
|
||||
LLCore::HttpOpRetryCompare> HttpRetryQueueBase;
|
||||
|
||||
class HttpRetryQueue : public HttpRetryQueueBase
|
||||
{
|
||||
public:
|
||||
HttpRetryQueue()
|
||||
: HttpRetryQueueBase()
|
||||
{}
|
||||
|
||||
~HttpRetryQueue()
|
||||
{}
|
||||
|
||||
protected:
|
||||
HttpRetryQueue(const HttpRetryQueue &); // Not defined
|
||||
void operator=(const HttpRetryQueue &); // Not defined
|
||||
|
||||
public:
|
||||
const container_type & get_container() const
|
||||
{
|
||||
return c;
|
||||
}
|
||||
|
||||
container_type & get_container()
|
||||
{
|
||||
return c;
|
||||
}
|
||||
|
||||
}; // end class HttpRetryQueue
|
||||
|
||||
|
||||
} // end namespace LLCore
|
||||
|
||||
|
||||
#endif // _LLCORE_HTTP_RETRY_QUEUE_H_
|
||||
|
|
@ -0,0 +1,348 @@
|
|||
/**
|
||||
* @file _httpservice.cpp
|
||||
* @brief Internal definitions of the Http service thread
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include "_httpservice.h"
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/function.hpp>
|
||||
|
||||
#include "_httpoperation.h"
|
||||
#include "_httprequestqueue.h"
|
||||
#include "_httppolicy.h"
|
||||
#include "_httplibcurl.h"
|
||||
#include "_thread.h"
|
||||
#include "_httpinternal.h"
|
||||
|
||||
#include "lltimer.h"
|
||||
#include "llthread.h"
|
||||
|
||||
|
||||
namespace LLCore
|
||||
{
|
||||
|
||||
HttpService * HttpService::sInstance(NULL);
|
||||
volatile HttpService::EState HttpService::sState(NOT_INITIALIZED);
|
||||
|
||||
HttpService::HttpService()
|
||||
: mRequestQueue(NULL),
|
||||
mExitRequested(0U),
|
||||
mThread(NULL),
|
||||
mPolicy(NULL),
|
||||
mTransport(NULL)
|
||||
{
|
||||
// Create the default policy class
|
||||
HttpPolicyClass pol_class;
|
||||
pol_class.set(HttpRequest::CP_CONNECTION_LIMIT, HTTP_CONNECTION_LIMIT_DEFAULT);
|
||||
pol_class.set(HttpRequest::CP_PER_HOST_CONNECTION_LIMIT, HTTP_CONNECTION_LIMIT_DEFAULT);
|
||||
pol_class.set(HttpRequest::CP_ENABLE_PIPELINING, 0L);
|
||||
mPolicyClasses.push_back(pol_class);
|
||||
}
|
||||
|
||||
|
||||
HttpService::~HttpService()
|
||||
{
|
||||
mExitRequested = 1U;
|
||||
if (RUNNING == sState)
|
||||
{
|
||||
// Trying to kill the service object with a running thread
|
||||
// is a bit tricky.
|
||||
if (mRequestQueue)
|
||||
{
|
||||
mRequestQueue->stopQueue();
|
||||
}
|
||||
|
||||
if (mThread)
|
||||
{
|
||||
if (! mThread->timedJoin(250))
|
||||
{
|
||||
// Failed to join, expect problems ahead so do a hard termination.
|
||||
mThread->cancel();
|
||||
|
||||
LL_WARNS("CoreHttp") << "Destroying HttpService with running thread. Expect problems."
|
||||
<< LL_ENDL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mRequestQueue)
|
||||
{
|
||||
mRequestQueue->release();
|
||||
mRequestQueue = NULL;
|
||||
}
|
||||
|
||||
delete mTransport;
|
||||
mTransport = NULL;
|
||||
|
||||
delete mPolicy;
|
||||
mPolicy = NULL;
|
||||
|
||||
if (mThread)
|
||||
{
|
||||
mThread->release();
|
||||
mThread = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void HttpService::init(HttpRequestQueue * queue)
|
||||
{
|
||||
llassert_always(! sInstance);
|
||||
llassert_always(NOT_INITIALIZED == sState);
|
||||
sInstance = new HttpService();
|
||||
|
||||
queue->addRef();
|
||||
sInstance->mRequestQueue = queue;
|
||||
sInstance->mPolicy = new HttpPolicy(sInstance);
|
||||
sInstance->mTransport = new HttpLibcurl(sInstance);
|
||||
sState = INITIALIZED;
|
||||
}
|
||||
|
||||
|
||||
void HttpService::term()
|
||||
{
|
||||
if (sInstance)
|
||||
{
|
||||
if (RUNNING == sState && sInstance->mThread)
|
||||
{
|
||||
// Unclean termination. Thread appears to be running. We'll
|
||||
// try to give the worker thread a chance to cancel using the
|
||||
// exit flag...
|
||||
sInstance->mExitRequested = 1U;
|
||||
sInstance->mRequestQueue->stopQueue();
|
||||
|
||||
// And a little sleep
|
||||
for (int i(0); i < 10 && RUNNING == sState; ++i)
|
||||
{
|
||||
ms_sleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
delete sInstance;
|
||||
sInstance = NULL;
|
||||
}
|
||||
sState = NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
|
||||
HttpRequest::policy_t HttpService::createPolicyClass()
|
||||
{
|
||||
const HttpRequest::policy_t policy_class(mPolicyClasses.size());
|
||||
if (policy_class >= HTTP_POLICY_CLASS_LIMIT)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
mPolicyClasses.push_back(HttpPolicyClass());
|
||||
return policy_class;
|
||||
}
|
||||
|
||||
|
||||
bool HttpService::isStopped()
|
||||
{
|
||||
// What is really wanted here is something like:
|
||||
//
|
||||
// HttpService * service = instanceOf();
|
||||
// return STOPPED == sState && (! service || ! service->mThread || ! service->mThread->joinable());
|
||||
//
|
||||
// But boost::thread is not giving me a consistent story on joinability
|
||||
// of a thread after it returns. Debug and non-debug builds are showing
|
||||
// different behavior on Linux/Etch so we do a weaker test that may
|
||||
// not be globally correct (i.e. thread *is* stopping, may not have
|
||||
// stopped but will very soon):
|
||||
|
||||
return STOPPED == sState;
|
||||
}
|
||||
|
||||
|
||||
/// Threading: callable by consumer thread *once*.
|
||||
void HttpService::startThread()
|
||||
{
|
||||
llassert_always(! mThread || STOPPED == sState);
|
||||
llassert_always(INITIALIZED == sState || STOPPED == sState);
|
||||
|
||||
if (mThread)
|
||||
{
|
||||
mThread->release();
|
||||
}
|
||||
|
||||
// Push current policy definitions, enable policy & transport components
|
||||
mPolicy->start(mPolicyGlobal, mPolicyClasses);
|
||||
mTransport->start(mPolicyClasses.size());
|
||||
|
||||
mThread = new LLCoreInt::HttpThread(boost::bind(&HttpService::threadRun, this, _1));
|
||||
sState = RUNNING;
|
||||
}
|
||||
|
||||
|
||||
/// Threading: callable by worker thread.
|
||||
void HttpService::stopRequested()
|
||||
{
|
||||
mExitRequested = 1U;
|
||||
}
|
||||
|
||||
|
||||
/// Threading: callable by worker thread.
|
||||
bool HttpService::changePriority(HttpHandle handle, HttpRequest::priority_t priority)
|
||||
{
|
||||
bool found(false);
|
||||
|
||||
// Skip the request queue as we currently don't leave earlier
|
||||
// requests sitting there. Start with the ready queue...
|
||||
found = mPolicy->changePriority(handle, priority);
|
||||
|
||||
// If not there, we could try the transport/active queue but priority
|
||||
// doesn't really have much effect there so we don't waste cycles.
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
|
||||
/// Try to find the given request handle on any of the request
|
||||
/// queues and cancel the operation.
|
||||
///
|
||||
/// @return True if the request was canceled.
|
||||
///
|
||||
/// Threading: callable by worker thread.
|
||||
bool HttpService::cancel(HttpHandle handle)
|
||||
{
|
||||
bool canceled(false);
|
||||
|
||||
// Request can't be on request queue so skip that.
|
||||
|
||||
// Check the policy component's queues first
|
||||
canceled = mPolicy->cancel(handle);
|
||||
|
||||
if (! canceled)
|
||||
{
|
||||
// If that didn't work, check transport's.
|
||||
canceled = mTransport->cancel(handle);
|
||||
}
|
||||
|
||||
return canceled;
|
||||
}
|
||||
|
||||
|
||||
/// Threading: callable by worker thread.
|
||||
void HttpService::shutdown()
|
||||
{
|
||||
// Disallow future enqueue of requests
|
||||
mRequestQueue->stopQueue();
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
// Shutdown transport canceling requests, freeing resources
|
||||
mTransport->shutdown();
|
||||
|
||||
// And now policy
|
||||
mPolicy->shutdown();
|
||||
}
|
||||
|
||||
|
||||
// Working thread loop-forever method. Gives time to
|
||||
// each of the request queue, policy layer and transport
|
||||
// layer pieces and then either sleeps for a small time
|
||||
// or waits for a request to come in. Repeats until
|
||||
// requested to stop.
|
||||
void HttpService::threadRun(LLCoreInt::HttpThread * thread)
|
||||
{
|
||||
boost::this_thread::disable_interruption di;
|
||||
|
||||
LLThread::registerThreadID();
|
||||
|
||||
ELoopSpeed loop(REQUEST_SLEEP);
|
||||
while (! mExitRequested)
|
||||
{
|
||||
loop = processRequestQueue(loop);
|
||||
|
||||
// Process ready queue issuing new requests as needed
|
||||
ELoopSpeed new_loop = mPolicy->processReadyQueue();
|
||||
loop = (std::min)(loop, new_loop);
|
||||
|
||||
// Give libcurl some cycles
|
||||
new_loop = mTransport->processTransport();
|
||||
loop = (std::min)(loop, new_loop);
|
||||
|
||||
// Determine whether to spin, sleep briefly or sleep for next request
|
||||
if (REQUEST_SLEEP != loop)
|
||||
{
|
||||
ms_sleep(HTTP_SERVICE_LOOP_SLEEP_NORMAL_MS);
|
||||
}
|
||||
}
|
||||
|
||||
shutdown();
|
||||
sState = STOPPED;
|
||||
}
|
||||
|
||||
|
||||
HttpService::ELoopSpeed HttpService::processRequestQueue(ELoopSpeed loop)
|
||||
{
|
||||
HttpRequestQueue::OpContainer ops;
|
||||
const bool wait_for_req(REQUEST_SLEEP == loop);
|
||||
|
||||
mRequestQueue->fetchAll(wait_for_req, ops);
|
||||
while (! ops.empty())
|
||||
{
|
||||
HttpOperation * op(ops.front());
|
||||
ops.erase(ops.begin());
|
||||
|
||||
// Process operation
|
||||
if (! mExitRequested)
|
||||
{
|
||||
// Setup for subsequent tracing
|
||||
long tracing(HTTP_TRACE_OFF);
|
||||
mPolicy->getGlobalOptions().get(HttpRequest::GP_TRACE, &tracing);
|
||||
op->mTracing = (std::max)(op->mTracing, int(tracing));
|
||||
|
||||
if (op->mTracing > HTTP_TRACE_OFF)
|
||||
{
|
||||
LL_INFOS("CoreHttp") << "TRACE, FromRequestQueue, Handle: "
|
||||
<< static_cast<HttpHandle>(op)
|
||||
<< LL_ENDL;
|
||||
}
|
||||
|
||||
// Stage
|
||||
op->stageFromRequest(this);
|
||||
}
|
||||
|
||||
// Done with operation
|
||||
op->release();
|
||||
}
|
||||
|
||||
// Queue emptied, allow polling loop to sleep
|
||||
return REQUEST_SLEEP;
|
||||
}
|
||||
|
||||
|
||||
} // end namespace LLCore
|
||||
|
|
@ -0,0 +1,224 @@
|
|||
/**
|
||||
* @file _httpservice.h
|
||||
* @brief Declarations for internal class providing HTTP service.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, 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 _LLCORE_HTTP_SERVICE_H_
|
||||
#define _LLCORE_HTTP_SERVICE_H_
|
||||
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "linden_common.h"
|
||||
#include "llapr.h"
|
||||
#include "httpcommon.h"
|
||||
#include "httprequest.h"
|
||||
#include "_httppolicyglobal.h"
|
||||
#include "_httppolicyclass.h"
|
||||
|
||||
|
||||
namespace LLCoreInt
|
||||
{
|
||||
|
||||
class HttpThread;
|
||||
|
||||
}
|
||||
|
||||
|
||||
namespace LLCore
|
||||
{
|
||||
|
||||
|
||||
class HttpRequestQueue;
|
||||
class HttpPolicy;
|
||||
class HttpLibcurl;
|
||||
|
||||
|
||||
/// The HttpService class does the work behind the request queue. It
|
||||
/// oversees the HTTP workflow carrying out a number of tasks:
|
||||
/// - Pulling requests from the global request queue
|
||||
/// - Executing 'immediate' requests directly
|
||||
/// - Prioritizing and re-queuing on internal queues the slower requests
|
||||
/// - Providing cpu cycles to the libcurl plumbing
|
||||
/// - Overseeing retry operations
|
||||
///
|
||||
/// Note that the service object doesn't have a pointer to any
|
||||
/// reply queue. These are kept by HttpRequest and HttpOperation
|
||||
/// only.
|
||||
///
|
||||
/// Service, Policy and Transport
|
||||
///
|
||||
/// HttpService could have been a monolithic class combining a request
|
||||
/// queue servicer, request policy manager and network transport.
|
||||
/// Instead, to prevent monolithic growth and allow for easier
|
||||
/// replacement, it was developed as three separate classes: HttpService,
|
||||
/// HttpPolicy and HttpLibcurl (transport). These always exist in a
|
||||
/// 1:1:1 relationship with HttpService managing instances of the other
|
||||
/// two. So, these classes do not use reference counting to refer
|
||||
/// to one another, their lifecycles are always managed together.
|
||||
|
||||
class HttpService
|
||||
{
|
||||
protected:
|
||||
HttpService();
|
||||
virtual ~HttpService();
|
||||
|
||||
private:
|
||||
HttpService(const HttpService &); // Not defined
|
||||
void operator=(const HttpService &); // Not defined
|
||||
|
||||
public:
|
||||
enum EState
|
||||
{
|
||||
NOT_INITIALIZED = -1,
|
||||
INITIALIZED, ///< init() has been called
|
||||
RUNNING, ///< thread created and running
|
||||
STOPPED ///< thread has committed to exiting
|
||||
};
|
||||
|
||||
// Ordered enumeration of idling strategies available to
|
||||
// threadRun's loop. Ordered so that std::min on values
|
||||
// produces the most conservative result of multiple
|
||||
// requests.
|
||||
enum ELoopSpeed
|
||||
{
|
||||
NORMAL, ///< continuous polling of request, ready, active queues
|
||||
REQUEST_SLEEP ///< can sleep indefinitely waiting for request queue write
|
||||
};
|
||||
|
||||
static void init(HttpRequestQueue *);
|
||||
static void term();
|
||||
|
||||
/// Threading: callable by any thread once inited.
|
||||
inline static HttpService * instanceOf()
|
||||
{
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
/// Return the state of the worker thread. Note that the
|
||||
/// transition from RUNNING to STOPPED is performed by the
|
||||
/// worker thread itself. This has two weaknesses:
|
||||
/// - race where the thread hasn't really stopped but will
|
||||
/// - data ordering between threads where a non-worker thread
|
||||
/// may see a stale RUNNING status.
|
||||
///
|
||||
/// This transition is generally of interest only to unit tests
|
||||
/// and these weaknesses shouldn't be any real burden.
|
||||
///
|
||||
/// Threading: callable by any thread with above exceptions.
|
||||
static EState getState()
|
||||
{
|
||||
return sState;
|
||||
}
|
||||
|
||||
/// Threading: callable by any thread but uses @see getState() and
|
||||
/// acquires its weaknesses.
|
||||
static bool isStopped();
|
||||
|
||||
/// Threading: callable by consumer thread *once*.
|
||||
void startThread();
|
||||
|
||||
/// Threading: callable by worker thread.
|
||||
void stopRequested();
|
||||
|
||||
/// Threading: callable by worker thread.
|
||||
void shutdown();
|
||||
|
||||
/// Try to find the given request handle on any of the request
|
||||
/// queues and reset the priority (and queue position) of the
|
||||
/// request if found.
|
||||
///
|
||||
/// @return True if the request was found somewhere.
|
||||
///
|
||||
/// Threading: callable by worker thread.
|
||||
bool changePriority(HttpHandle handle, HttpRequest::priority_t priority);
|
||||
|
||||
/// Try to find the given request handle on any of the request
|
||||
/// queues and cancel the operation.
|
||||
///
|
||||
/// @return True if the request was found and canceled.
|
||||
///
|
||||
/// Threading: callable by worker thread.
|
||||
bool cancel(HttpHandle handle);
|
||||
|
||||
/// Threading: callable by worker thread.
|
||||
HttpPolicy & getPolicy()
|
||||
{
|
||||
return *mPolicy;
|
||||
}
|
||||
|
||||
/// Threading: callable by worker thread.
|
||||
HttpLibcurl & getTransport()
|
||||
{
|
||||
return *mTransport;
|
||||
}
|
||||
|
||||
/// Threading: callable by worker thread.
|
||||
HttpRequestQueue & getRequestQueue()
|
||||
{
|
||||
return *mRequestQueue;
|
||||
}
|
||||
|
||||
/// Threading: callable by consumer thread.
|
||||
HttpPolicyGlobal & getGlobalOptions()
|
||||
{
|
||||
return mPolicyGlobal;
|
||||
}
|
||||
|
||||
/// Threading: callable by consumer thread.
|
||||
HttpRequest::policy_t createPolicyClass();
|
||||
|
||||
/// Threading: callable by consumer thread.
|
||||
HttpPolicyClass & getClassOptions(HttpRequest::policy_t policy_class)
|
||||
{
|
||||
llassert(policy_class >= 0 && policy_class < mPolicyClasses.size());
|
||||
return mPolicyClasses[policy_class];
|
||||
}
|
||||
|
||||
protected:
|
||||
void threadRun(LLCoreInt::HttpThread * thread);
|
||||
|
||||
ELoopSpeed processRequestQueue(ELoopSpeed loop);
|
||||
|
||||
protected:
|
||||
static HttpService * sInstance;
|
||||
|
||||
// === shared data ===
|
||||
static volatile EState sState;
|
||||
HttpRequestQueue * mRequestQueue; // Refcounted
|
||||
LLAtomicU32 mExitRequested;
|
||||
LLCoreInt::HttpThread * mThread;
|
||||
|
||||
// === consumer-thread-only data ===
|
||||
HttpPolicyGlobal mPolicyGlobal;
|
||||
std::vector<HttpPolicyClass> mPolicyClasses;
|
||||
|
||||
// === working-thread-only data ===
|
||||
HttpPolicy * mPolicy; // Simple pointer, has ownership
|
||||
HttpLibcurl * mTransport; // Simple pointer, has ownership
|
||||
}; // end class HttpService
|
||||
|
||||
} // end namespace LLCore
|
||||
|
||||
#endif // _LLCORE_HTTP_SERVICE_H_
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
/**
|
||||
* @file _mutex.hpp
|
||||
* @brief mutex type abstraction
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, 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 LLCOREINT_MUTEX_H_
|
||||
#define LLCOREINT_MUTEX_H_
|
||||
|
||||
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
|
||||
namespace LLCoreInt
|
||||
{
|
||||
|
||||
// MUTEX TYPES
|
||||
|
||||
// unique mutex type
|
||||
typedef boost::mutex HttpMutex;
|
||||
|
||||
// CONDITION VARIABLES
|
||||
|
||||
// standard condition variable
|
||||
typedef boost::condition_variable HttpConditionVariable;
|
||||
|
||||
// LOCKS AND FENCES
|
||||
|
||||
// scoped unique lock
|
||||
typedef boost::unique_lock<HttpMutex> HttpScopedLock;
|
||||
|
||||
}
|
||||
|
||||
#endif // LLCOREINT_MUTEX_H
|
||||
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
/**
|
||||
* @file _refcounted.cpp
|
||||
* @brief Atomic, thread-safe ref counting and destruction mixin class
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include "_refcounted.h"
|
||||
|
||||
|
||||
namespace LLCoreInt
|
||||
{
|
||||
|
||||
#if ! LL_WINDOWS
|
||||
|
||||
const S32 RefCounted::NOT_REF_COUNTED;
|
||||
|
||||
#endif // ! LL_WINDOWS
|
||||
|
||||
RefCounted::~RefCounted()
|
||||
{}
|
||||
|
||||
|
||||
} // end namespace LLCoreInt
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,126 @@
|
|||
/**
|
||||
* @file _refcounted.h
|
||||
* @brief Atomic, thread-safe ref counting and destruction mixin class
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, 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 LLCOREINT__REFCOUNTED_H_
|
||||
#define LLCOREINT__REFCOUNTED_H_
|
||||
|
||||
|
||||
#include "linden_common.h"
|
||||
|
||||
#include "fix_macros.h"
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
#include "llapr.h"
|
||||
|
||||
|
||||
namespace LLCoreInt
|
||||
{
|
||||
|
||||
|
||||
class RefCounted
|
||||
{
|
||||
private:
|
||||
RefCounted(); // Not defined - may not be default constructed
|
||||
void operator=(const RefCounted &); // Not defined
|
||||
|
||||
public:
|
||||
explicit RefCounted(bool const implicit)
|
||||
: mRefCount(implicit)
|
||||
{}
|
||||
|
||||
// ref-count interface
|
||||
void addRef() const;
|
||||
void release() const;
|
||||
bool isLastRef() const;
|
||||
S32 getRefCount() const;
|
||||
void noRef() const;
|
||||
|
||||
static const S32 NOT_REF_COUNTED = -1;
|
||||
|
||||
protected:
|
||||
virtual ~RefCounted();
|
||||
virtual void destroySelf();
|
||||
|
||||
private:
|
||||
mutable LLAtomicS32 mRefCount;
|
||||
|
||||
}; // end class RefCounted
|
||||
|
||||
|
||||
inline void RefCounted::addRef() const
|
||||
{
|
||||
S32 count(mRefCount++);
|
||||
llassert_always(count >= 0);
|
||||
}
|
||||
|
||||
|
||||
inline void RefCounted::release() const
|
||||
{
|
||||
S32 count(mRefCount);
|
||||
llassert_always(count != NOT_REF_COUNTED);
|
||||
llassert_always(count > 0);
|
||||
count = mRefCount--;
|
||||
|
||||
// clean ourselves up if that was the last reference
|
||||
if (0 == count)
|
||||
{
|
||||
const_cast<RefCounted *>(this)->destroySelf();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
inline bool RefCounted::isLastRef() const
|
||||
{
|
||||
const S32 count(mRefCount);
|
||||
llassert_always(count != NOT_REF_COUNTED);
|
||||
llassert_always(count >= 1);
|
||||
return (1 == count);
|
||||
}
|
||||
|
||||
|
||||
inline S32 RefCounted::getRefCount() const
|
||||
{
|
||||
const S32 result(mRefCount);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
inline void RefCounted::noRef() const
|
||||
{
|
||||
llassert_always(mRefCount <= 1);
|
||||
mRefCount = NOT_REF_COUNTED;
|
||||
}
|
||||
|
||||
|
||||
inline void RefCounted::destroySelf()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
} // end namespace LLCoreInt
|
||||
|
||||
#endif // LLCOREINT__REFCOUNTED_H_
|
||||
|
||||
|
|
@ -0,0 +1,123 @@
|
|||
/**
|
||||
* @file _thread.h
|
||||
* @brief thread type abstraction
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, 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 LLCOREINT_THREAD_H_
|
||||
#define LLCOREINT_THREAD_H_
|
||||
|
||||
#include "linden_common.h"
|
||||
|
||||
#include <boost/thread.hpp>
|
||||
#include <boost/function.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time_types.hpp>
|
||||
|
||||
#include "_refcounted.h"
|
||||
|
||||
namespace LLCoreInt
|
||||
{
|
||||
|
||||
class HttpThread : public RefCounted
|
||||
{
|
||||
private:
|
||||
HttpThread(); // Not defined
|
||||
void operator=(const HttpThread &); // Not defined
|
||||
|
||||
void at_exit()
|
||||
{
|
||||
// the thread function has exited so we need to release our reference
|
||||
// to ourself so that we will be automagically cleaned up.
|
||||
release();
|
||||
}
|
||||
|
||||
void run()
|
||||
{ // THREAD CONTEXT
|
||||
|
||||
// Take out additional reference for the at_exit handler
|
||||
addRef();
|
||||
boost::this_thread::at_thread_exit(boost::bind(&HttpThread::at_exit, this));
|
||||
|
||||
// run the thread function
|
||||
mThreadFunc(this);
|
||||
|
||||
} // THREAD CONTEXT
|
||||
|
||||
protected:
|
||||
virtual ~HttpThread()
|
||||
{
|
||||
delete mThread;
|
||||
}
|
||||
|
||||
public:
|
||||
/// Constructs a thread object for concurrent execution but does
|
||||
/// not start running. Caller receives on refcount on the thread
|
||||
/// instance. If the thread is started, another will be taken
|
||||
/// out for the exit handler.
|
||||
explicit HttpThread(boost::function<void (HttpThread *)> threadFunc)
|
||||
: RefCounted(true), // implicit reference
|
||||
mThreadFunc(threadFunc)
|
||||
{
|
||||
// this creates a boost thread that will call HttpThread::run on this instance
|
||||
// and pass it the threadfunc callable...
|
||||
boost::function<void()> f = boost::bind(&HttpThread::run, this);
|
||||
|
||||
mThread = new boost::thread(f);
|
||||
}
|
||||
|
||||
inline void join()
|
||||
{
|
||||
mThread->join();
|
||||
}
|
||||
|
||||
inline bool timedJoin(S32 millis)
|
||||
{
|
||||
return mThread->timed_join(boost::posix_time::milliseconds(millis));
|
||||
}
|
||||
|
||||
inline bool joinable() const
|
||||
{
|
||||
return mThread->joinable();
|
||||
}
|
||||
|
||||
// A very hostile method to force a thread to quit
|
||||
inline void cancel()
|
||||
{
|
||||
boost::thread::native_handle_type thread(mThread->native_handle());
|
||||
#if LL_WINDOWS
|
||||
TerminateThread(thread, 0);
|
||||
#else
|
||||
pthread_cancel(thread);
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
boost::function<void(HttpThread *)> mThreadFunc;
|
||||
boost::thread * mThread;
|
||||
}; // end class HttpThread
|
||||
|
||||
} // end namespace LLCoreInt
|
||||
|
||||
#endif // LLCOREINT_THREAD_H_
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,352 @@
|
|||
/**
|
||||
* @file bufferarray.cpp
|
||||
* @brief Implements the BufferArray scatter/gather buffer
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include "bufferarray.h"
|
||||
|
||||
|
||||
// BufferArray is a list of chunks, each a BufferArray::Block, of contiguous
|
||||
// data presented as a single array. Chunks are at least BufferArray::BLOCK_ALLOC_SIZE
|
||||
// in length and can be larger. Any chunk may be partially filled or even
|
||||
// empty.
|
||||
//
|
||||
// The BufferArray itself is sharable as a RefCounted entity. As shared
|
||||
// reads don't work with the concept of a current position/seek value,
|
||||
// none is kept with the object. Instead, the read and write operations
|
||||
// all take position arguments. Single write/shared read isn't supported
|
||||
// directly and any such attempts have to be serialized outside of this
|
||||
// implementation.
|
||||
|
||||
namespace LLCore
|
||||
{
|
||||
|
||||
|
||||
// ==================================
|
||||
// BufferArray::Block Declaration
|
||||
// ==================================
|
||||
|
||||
class BufferArray::Block
|
||||
{
|
||||
public:
|
||||
~Block();
|
||||
|
||||
void operator delete(void *);
|
||||
void operator delete(void *, size_t len);
|
||||
|
||||
protected:
|
||||
Block(size_t len);
|
||||
|
||||
Block(const Block &); // Not defined
|
||||
void operator=(const Block &); // Not defined
|
||||
|
||||
// Allocate the block with the additional space for the
|
||||
// buffered data at the end of the object.
|
||||
void * operator new(size_t len, size_t addl_len);
|
||||
|
||||
public:
|
||||
// Only public entry to get a block.
|
||||
static Block * alloc(size_t len);
|
||||
|
||||
public:
|
||||
size_t mUsed;
|
||||
size_t mAlloced;
|
||||
|
||||
// *NOTE: Must be last member of the object. We'll
|
||||
// overallocate as requested via operator new and index
|
||||
// into the array at will.
|
||||
char mData[1];
|
||||
};
|
||||
|
||||
|
||||
// ==================================
|
||||
// BufferArray Definitions
|
||||
// ==================================
|
||||
|
||||
|
||||
#if ! LL_WINDOWS
|
||||
const size_t BufferArray::BLOCK_ALLOC_SIZE;
|
||||
#endif // ! LL_WINDOWS
|
||||
|
||||
BufferArray::BufferArray()
|
||||
: LLCoreInt::RefCounted(true),
|
||||
mLen(0)
|
||||
{}
|
||||
|
||||
|
||||
BufferArray::~BufferArray()
|
||||
{
|
||||
for (container_t::iterator it(mBlocks.begin());
|
||||
it != mBlocks.end();
|
||||
++it)
|
||||
{
|
||||
delete *it;
|
||||
*it = NULL;
|
||||
}
|
||||
mBlocks.clear();
|
||||
}
|
||||
|
||||
|
||||
size_t BufferArray::append(const void * src, size_t len)
|
||||
{
|
||||
const size_t ret(len);
|
||||
const char * c_src(static_cast<const char *>(src));
|
||||
|
||||
// First, try to copy into the last block
|
||||
if (len && ! mBlocks.empty())
|
||||
{
|
||||
Block & last(*mBlocks.back());
|
||||
if (last.mUsed < last.mAlloced)
|
||||
{
|
||||
// Some will fit...
|
||||
const size_t copy_len((std::min)(len, (last.mAlloced - last.mUsed)));
|
||||
|
||||
memcpy(&last.mData[last.mUsed], c_src, copy_len);
|
||||
last.mUsed += copy_len;
|
||||
llassert_always(last.mUsed <= last.mAlloced);
|
||||
mLen += copy_len;
|
||||
c_src += copy_len;
|
||||
len -= copy_len;
|
||||
}
|
||||
}
|
||||
|
||||
// Then get new blocks as needed
|
||||
while (len)
|
||||
{
|
||||
const size_t copy_len((std::min)(len, BLOCK_ALLOC_SIZE));
|
||||
|
||||
if (mBlocks.size() >= mBlocks.capacity())
|
||||
{
|
||||
mBlocks.reserve(mBlocks.size() + 5);
|
||||
}
|
||||
Block * block = Block::alloc(BLOCK_ALLOC_SIZE);
|
||||
memcpy(block->mData, c_src, copy_len);
|
||||
block->mUsed = copy_len;
|
||||
llassert_always(block->mUsed <= block->mAlloced);
|
||||
mBlocks.push_back(block);
|
||||
mLen += copy_len;
|
||||
c_src += copy_len;
|
||||
len -= copy_len;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void * BufferArray::appendBufferAlloc(size_t len)
|
||||
{
|
||||
// If someone asks for zero-length, we give them a valid pointer.
|
||||
if (mBlocks.size() >= mBlocks.capacity())
|
||||
{
|
||||
mBlocks.reserve(mBlocks.size() + 5);
|
||||
}
|
||||
Block * block = Block::alloc((std::max)(BLOCK_ALLOC_SIZE, len));
|
||||
block->mUsed = len;
|
||||
mBlocks.push_back(block);
|
||||
mLen += len;
|
||||
return block->mData;
|
||||
}
|
||||
|
||||
|
||||
size_t BufferArray::read(size_t pos, void * dst, size_t len)
|
||||
{
|
||||
char * c_dst(static_cast<char *>(dst));
|
||||
|
||||
if (pos >= mLen)
|
||||
return 0;
|
||||
size_t len_limit(mLen - pos);
|
||||
len = (std::min)(len, len_limit);
|
||||
if (0 == len)
|
||||
return 0;
|
||||
|
||||
size_t result(0), offset(0);
|
||||
const int block_limit(mBlocks.size());
|
||||
int block_start(findBlock(pos, &offset));
|
||||
if (block_start < 0)
|
||||
return 0;
|
||||
|
||||
do
|
||||
{
|
||||
Block & block(*mBlocks[block_start]);
|
||||
size_t block_limit(block.mUsed - offset);
|
||||
size_t block_len((std::min)(block_limit, len));
|
||||
|
||||
memcpy(c_dst, &block.mData[offset], block_len);
|
||||
result += block_len;
|
||||
len -= block_len;
|
||||
c_dst += block_len;
|
||||
offset = 0;
|
||||
++block_start;
|
||||
}
|
||||
while (len && block_start < block_limit);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
size_t BufferArray::write(size_t pos, const void * src, size_t len)
|
||||
{
|
||||
const char * c_src(static_cast<const char *>(src));
|
||||
|
||||
if (pos > mLen || 0 == len)
|
||||
return 0;
|
||||
|
||||
size_t result(0), offset(0);
|
||||
const int block_limit(mBlocks.size());
|
||||
int block_start(findBlock(pos, &offset));
|
||||
|
||||
if (block_start >= 0)
|
||||
{
|
||||
// Some or all of the write will be on top of
|
||||
// existing data.
|
||||
do
|
||||
{
|
||||
Block & block(*mBlocks[block_start]);
|
||||
size_t block_limit(block.mUsed - offset);
|
||||
size_t block_len((std::min)(block_limit, len));
|
||||
|
||||
memcpy(&block.mData[offset], c_src, block_len);
|
||||
result += block_len;
|
||||
c_src += block_len;
|
||||
len -= block_len;
|
||||
offset = 0;
|
||||
++block_start;
|
||||
}
|
||||
while (len && block_start < block_limit);
|
||||
}
|
||||
|
||||
// Something left, see if it will fit in the free
|
||||
// space of the last block.
|
||||
if (len && ! mBlocks.empty())
|
||||
{
|
||||
Block & last(*mBlocks.back());
|
||||
if (last.mUsed < last.mAlloced)
|
||||
{
|
||||
// Some will fit...
|
||||
const size_t copy_len((std::min)(len, (last.mAlloced - last.mUsed)));
|
||||
|
||||
memcpy(&last.mData[last.mUsed], c_src, copy_len);
|
||||
last.mUsed += copy_len;
|
||||
result += copy_len;
|
||||
llassert_always(last.mUsed <= last.mAlloced);
|
||||
mLen += copy_len;
|
||||
c_src += copy_len;
|
||||
len -= copy_len;
|
||||
}
|
||||
}
|
||||
|
||||
if (len)
|
||||
{
|
||||
// Some or all of the remaining write data will
|
||||
// be an append.
|
||||
result += append(c_src, len);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
int BufferArray::findBlock(size_t pos, size_t * ret_offset)
|
||||
{
|
||||
*ret_offset = 0;
|
||||
if (pos >= mLen)
|
||||
return -1; // Doesn't exist
|
||||
|
||||
const int block_limit(mBlocks.size());
|
||||
for (int i(0); i < block_limit; ++i)
|
||||
{
|
||||
if (pos < mBlocks[i]->mUsed)
|
||||
{
|
||||
*ret_offset = pos;
|
||||
return i;
|
||||
}
|
||||
pos -= mBlocks[i]->mUsed;
|
||||
}
|
||||
|
||||
// Shouldn't get here but...
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
bool BufferArray::getBlockStartEnd(int block, const char ** start, const char ** end)
|
||||
{
|
||||
if (block < 0 || block >= mBlocks.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const Block & b(*mBlocks[block]);
|
||||
*start = &b.mData[0];
|
||||
*end = &b.mData[b.mUsed];
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// ==================================
|
||||
// BufferArray::Block Definitions
|
||||
// ==================================
|
||||
|
||||
|
||||
BufferArray::Block::Block(size_t len)
|
||||
: mUsed(0),
|
||||
mAlloced(len)
|
||||
{
|
||||
memset(mData, 0, len);
|
||||
}
|
||||
|
||||
|
||||
BufferArray::Block::~Block()
|
||||
{
|
||||
mUsed = 0;
|
||||
mAlloced = 0;
|
||||
}
|
||||
|
||||
|
||||
void * BufferArray::Block::operator new(size_t len, size_t addl_len)
|
||||
{
|
||||
void * mem = new char[len + addl_len + sizeof(void *)];
|
||||
return mem;
|
||||
}
|
||||
|
||||
|
||||
void BufferArray::Block::operator delete(void * mem)
|
||||
{
|
||||
char * cmem = static_cast<char *>(mem);
|
||||
delete [] cmem;
|
||||
}
|
||||
|
||||
|
||||
void BufferArray::Block::operator delete(void * mem, size_t)
|
||||
{
|
||||
operator delete(mem);
|
||||
}
|
||||
|
||||
|
||||
BufferArray::Block * BufferArray::Block::alloc(size_t len)
|
||||
{
|
||||
Block * block = new (len) Block(len);
|
||||
return block;
|
||||
}
|
||||
|
||||
|
||||
} // end namespace LLCore
|
||||
|
|
@ -0,0 +1,137 @@
|
|||
/**
|
||||
* @file bufferarray.h
|
||||
* @brief Public-facing declaration for the BufferArray scatter/gather class
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, 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 _LLCORE_BUFFER_ARRAY_H_
|
||||
#define _LLCORE_BUFFER_ARRAY_H_
|
||||
|
||||
|
||||
#include <cstdlib>
|
||||
#include <vector>
|
||||
|
||||
#include "_refcounted.h"
|
||||
|
||||
|
||||
namespace LLCore
|
||||
{
|
||||
|
||||
class BufferArrayStreamBuf;
|
||||
|
||||
/// A very simple scatter/gather type map for bulk data. The motivation
|
||||
/// for this class is the writedata callback used by libcurl. Response
|
||||
/// bodies are delivered to the caller in a sequence of sequential write
|
||||
/// operations and this class captures them without having to reallocate
|
||||
/// and move data.
|
||||
///
|
||||
/// The interface looks a little like a unix file descriptor but only
|
||||
/// just. There is a notion of a current position, starting from 0,
|
||||
/// which is used as the position in the data when performing read and
|
||||
/// write operations. The position also moves after various operations:
|
||||
/// - seek(...)
|
||||
/// - read(...)
|
||||
/// - write(...)
|
||||
/// - append(...)
|
||||
/// - appendBufferAlloc(...)
|
||||
/// The object also keeps a total length value which is updated after
|
||||
/// write and append operations and beyond which the current position
|
||||
/// cannot be set.
|
||||
///
|
||||
/// Threading: not thread-safe
|
||||
///
|
||||
/// Allocation: Refcounted, heap only. Caller of the constructor
|
||||
/// is given a single refcount.
|
||||
///
|
||||
class BufferArray : public LLCoreInt::RefCounted
|
||||
{
|
||||
public:
|
||||
// BufferArrayStreamBuf has intimate knowledge of this
|
||||
// implementation to implement a buffer-free adapter.
|
||||
// Changes here will likely need to be reflected there.
|
||||
friend class BufferArrayStreamBuf;
|
||||
|
||||
BufferArray();
|
||||
|
||||
protected:
|
||||
virtual ~BufferArray(); // Use release()
|
||||
|
||||
private:
|
||||
BufferArray(const BufferArray &); // Not defined
|
||||
void operator=(const BufferArray &); // Not defined
|
||||
|
||||
public:
|
||||
// Internal magic number, may be used by unit tests.
|
||||
static const size_t BLOCK_ALLOC_SIZE = 65540;
|
||||
|
||||
/// Appends the indicated data to the BufferArray
|
||||
/// modifying current position and total size. New
|
||||
/// position is one beyond the final byte of the buffer.
|
||||
///
|
||||
/// @return Count of bytes copied to BufferArray
|
||||
size_t append(const void * src, size_t len);
|
||||
|
||||
/// Similar to @see append(), this call guarantees a
|
||||
/// contiguous block of memory of requested size placed
|
||||
/// at the current end of the BufferArray. On return,
|
||||
/// the data in the memory is considered valid whether
|
||||
/// the caller writes to it or not.
|
||||
///
|
||||
/// @return Pointer to contiguous region at end
|
||||
/// of BufferArray of 'len' size.
|
||||
void * appendBufferAlloc(size_t len);
|
||||
|
||||
/// Current count of bytes in BufferArray instance.
|
||||
size_t size() const
|
||||
{
|
||||
return mLen;
|
||||
}
|
||||
|
||||
/// Copies data from the given position in the instance
|
||||
/// to the caller's buffer. Will return a short count of
|
||||
/// bytes copied if the 'len' extends beyond the data.
|
||||
size_t read(size_t pos, void * dst, size_t len);
|
||||
|
||||
/// Copies data from the caller's buffer to the instance
|
||||
/// at the current position. May overwrite existing data,
|
||||
/// append data when current position is equal to the
|
||||
/// size of the instance or do a mix of both.
|
||||
size_t write(size_t pos, const void * src, size_t len);
|
||||
|
||||
protected:
|
||||
int findBlock(size_t pos, size_t * ret_offset);
|
||||
|
||||
bool getBlockStartEnd(int block, const char ** start, const char ** end);
|
||||
|
||||
protected:
|
||||
class Block;
|
||||
typedef std::vector<Block *> container_t;
|
||||
|
||||
container_t mBlocks;
|
||||
size_t mLen;
|
||||
}; // end class BufferArray
|
||||
|
||||
|
||||
} // end namespace LLCore
|
||||
|
||||
#endif // _LLCORE_BUFFER_ARRAY_H_
|
||||
|
|
@ -0,0 +1,285 @@
|
|||
/**
|
||||
* @file bufferstream.cpp
|
||||
* @brief Implements the BufferStream adapter class
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include "bufferstream.h"
|
||||
|
||||
#include "bufferarray.h"
|
||||
|
||||
|
||||
namespace LLCore
|
||||
{
|
||||
|
||||
BufferArrayStreamBuf::BufferArrayStreamBuf(BufferArray * array)
|
||||
: mBufferArray(array),
|
||||
mReadCurPos(0),
|
||||
mReadCurBlock(-1),
|
||||
mReadBegin(NULL),
|
||||
mReadCur(NULL),
|
||||
mReadEnd(NULL),
|
||||
mWriteCurPos(0)
|
||||
{
|
||||
if (array)
|
||||
{
|
||||
array->addRef();
|
||||
mWriteCurPos = array->mLen;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BufferArrayStreamBuf::~BufferArrayStreamBuf()
|
||||
{
|
||||
if (mBufferArray)
|
||||
{
|
||||
mBufferArray->release();
|
||||
mBufferArray = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BufferArrayStreamBuf::int_type BufferArrayStreamBuf::underflow()
|
||||
{
|
||||
if (! mBufferArray)
|
||||
{
|
||||
return traits_type::eof();
|
||||
}
|
||||
|
||||
if (mReadCur == mReadEnd)
|
||||
{
|
||||
// Find the next block with actual data or leave
|
||||
// mCurBlock/mCur/mEnd unchanged if we're at the end
|
||||
// of any block chain.
|
||||
const char * new_begin(NULL), * new_end(NULL);
|
||||
int new_cur_block(mReadCurBlock + 1);
|
||||
|
||||
while (mBufferArray->getBlockStartEnd(new_cur_block, &new_begin, &new_end))
|
||||
{
|
||||
if (new_begin != new_end)
|
||||
{
|
||||
break;
|
||||
}
|
||||
++new_cur_block;
|
||||
}
|
||||
if (new_begin == new_end)
|
||||
{
|
||||
return traits_type::eof();
|
||||
}
|
||||
|
||||
mReadCurBlock = new_cur_block;
|
||||
mReadBegin = mReadCur = new_begin;
|
||||
mReadEnd = new_end;
|
||||
}
|
||||
|
||||
return traits_type::to_int_type(*mReadCur);
|
||||
}
|
||||
|
||||
|
||||
BufferArrayStreamBuf::int_type BufferArrayStreamBuf::uflow()
|
||||
{
|
||||
const int_type ret(underflow());
|
||||
|
||||
if (traits_type::eof() != ret)
|
||||
{
|
||||
++mReadCur;
|
||||
++mReadCurPos;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
BufferArrayStreamBuf::int_type BufferArrayStreamBuf::pbackfail(int_type ch)
|
||||
{
|
||||
if (! mBufferArray)
|
||||
{
|
||||
return traits_type::eof();
|
||||
}
|
||||
|
||||
if (mReadCur == mReadBegin)
|
||||
{
|
||||
// Find the previous block with actual data or leave
|
||||
// mCurBlock/mBegin/mCur/mEnd unchanged if we're at the
|
||||
// beginning of any block chain.
|
||||
const char * new_begin(NULL), * new_end(NULL);
|
||||
int new_cur_block(mReadCurBlock - 1);
|
||||
|
||||
while (mBufferArray->getBlockStartEnd(new_cur_block, &new_begin, &new_end))
|
||||
{
|
||||
if (new_begin != new_end)
|
||||
{
|
||||
break;
|
||||
}
|
||||
--new_cur_block;
|
||||
}
|
||||
if (new_begin == new_end)
|
||||
{
|
||||
return traits_type::eof();
|
||||
}
|
||||
|
||||
mReadCurBlock = new_cur_block;
|
||||
mReadBegin = new_begin;
|
||||
mReadEnd = mReadCur = new_end;
|
||||
}
|
||||
|
||||
if (traits_type::eof() != ch && mReadCur[-1] != ch)
|
||||
{
|
||||
return traits_type::eof();
|
||||
}
|
||||
--mReadCurPos;
|
||||
return traits_type::to_int_type(*--mReadCur);
|
||||
}
|
||||
|
||||
|
||||
std::streamsize BufferArrayStreamBuf::showmanyc()
|
||||
{
|
||||
if (! mBufferArray)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return mBufferArray->mLen - mReadCurPos;
|
||||
}
|
||||
|
||||
|
||||
BufferArrayStreamBuf::int_type BufferArrayStreamBuf::overflow(int c)
|
||||
{
|
||||
if (! mBufferArray || mWriteCurPos > mBufferArray->mLen)
|
||||
{
|
||||
return traits_type::eof();
|
||||
}
|
||||
const size_t wrote(mBufferArray->write(mWriteCurPos, &c, 1));
|
||||
mWriteCurPos += wrote;
|
||||
return wrote ? c : traits_type::eof();
|
||||
}
|
||||
|
||||
|
||||
std::streamsize BufferArrayStreamBuf::xsputn(const char * src, std::streamsize count)
|
||||
{
|
||||
if (! mBufferArray || mWriteCurPos > mBufferArray->mLen)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
const size_t wrote(mBufferArray->write(mWriteCurPos, src, count));
|
||||
mWriteCurPos += wrote;
|
||||
return wrote;
|
||||
}
|
||||
|
||||
|
||||
std::streampos BufferArrayStreamBuf::seekoff(std::streamoff off,
|
||||
std::ios_base::seekdir way,
|
||||
std::ios_base::openmode which)
|
||||
{
|
||||
std::streampos ret(-1);
|
||||
|
||||
if (! mBufferArray)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (std::ios_base::in == which)
|
||||
{
|
||||
size_t pos(0);
|
||||
|
||||
switch (way)
|
||||
{
|
||||
case std::ios_base::beg:
|
||||
pos = off;
|
||||
break;
|
||||
|
||||
case std::ios_base::cur:
|
||||
pos = mReadCurPos += off;
|
||||
break;
|
||||
|
||||
case std::ios_base::end:
|
||||
pos = mBufferArray->mLen - off;
|
||||
break;
|
||||
|
||||
default:
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (pos >= mBufferArray->size())
|
||||
{
|
||||
pos = (std::max)(size_t(0), mBufferArray->size() - 1);
|
||||
}
|
||||
size_t ba_offset(0);
|
||||
int block(mBufferArray->findBlock(pos, &ba_offset));
|
||||
if (block < 0)
|
||||
return ret;
|
||||
const char * start(NULL), * end(NULL);
|
||||
if (! mBufferArray->getBlockStartEnd(block, &start, &end))
|
||||
return ret;
|
||||
mReadCurBlock = block;
|
||||
mReadBegin = start;
|
||||
mReadCur = start + ba_offset;
|
||||
mReadEnd = end;
|
||||
ret = mReadCurPos = pos;
|
||||
}
|
||||
else if (std::ios_base::out == which)
|
||||
{
|
||||
size_t pos(0);
|
||||
|
||||
switch (way)
|
||||
{
|
||||
case std::ios_base::beg:
|
||||
pos = off;
|
||||
break;
|
||||
|
||||
case std::ios_base::cur:
|
||||
pos = mWriteCurPos += off;
|
||||
break;
|
||||
|
||||
case std::ios_base::end:
|
||||
pos = mBufferArray->mLen - off;
|
||||
break;
|
||||
|
||||
default:
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (pos < 0)
|
||||
return ret;
|
||||
if (pos > mBufferArray->size())
|
||||
{
|
||||
pos = mBufferArray->size();
|
||||
}
|
||||
ret = mWriteCurPos = pos;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
BufferArrayStream::BufferArrayStream(BufferArray * ba)
|
||||
: std::iostream(&mStreamBuf),
|
||||
mStreamBuf(ba)
|
||||
{}
|
||||
|
||||
|
||||
BufferArrayStream::~BufferArrayStream()
|
||||
{}
|
||||
|
||||
|
||||
} // end namespace LLCore
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,153 @@
|
|||
/**
|
||||
* @file bufferstream.h
|
||||
* @brief Public-facing declaration for the BufferStream adapter class
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, 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 _LLCORE_BUFFER_STREAM_H_
|
||||
#define _LLCORE_BUFFER_STREAM_H_
|
||||
|
||||
|
||||
#include <sstream>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "bufferarray.h"
|
||||
|
||||
|
||||
/// @file bufferstream.h
|
||||
///
|
||||
/// std::streambuf and std::iostream adapters for BufferArray
|
||||
/// objects.
|
||||
///
|
||||
/// BufferArrayStreamBuf inherits std::streambuf and implements
|
||||
/// an unbuffered interface for streambuf. This may or may not
|
||||
/// be the most time efficient implementation and it is a little
|
||||
/// challenging.
|
||||
///
|
||||
/// BufferArrayStream inherits std::iostream and will be the
|
||||
/// adapter object most callers will be interested in (though
|
||||
/// it uses BufferArrayStreamBuf internally). Instances allow
|
||||
/// for the usual streaming operators ('<<', '>>') and serialization
|
||||
/// methods.
|
||||
///
|
||||
/// Example of LLSD serialization to a BufferArray:
|
||||
///
|
||||
/// BufferArray * ba = new BufferArray;
|
||||
/// BufferArrayStream bas(ba);
|
||||
/// LLSDSerialize::toXML(llsd, bas);
|
||||
/// operationOnBufferArray(ba);
|
||||
/// ba->release();
|
||||
/// ba = NULL;
|
||||
/// // operationOnBufferArray and bas are each holding
|
||||
/// // references to the ba instance at this point.
|
||||
///
|
||||
|
||||
namespace LLCore
|
||||
{
|
||||
|
||||
|
||||
// =====================================================
|
||||
// BufferArrayStreamBuf
|
||||
// =====================================================
|
||||
|
||||
/// Adapter class to put a std::streambuf interface on a BufferArray
|
||||
///
|
||||
/// Application developers will rarely be interested in anything
|
||||
/// other than the constructor and even that will rarely be used
|
||||
/// except indirectly via the @BufferArrayStream class. The
|
||||
/// choice of interfaces implemented yields a bufferless adapter
|
||||
/// that doesn't used either the input or output pointer triplets
|
||||
/// of the more common buffered implementations. This may or may
|
||||
/// not be faster and that question could stand to be looked at
|
||||
/// sometime.
|
||||
///
|
||||
|
||||
class BufferArrayStreamBuf : public std::streambuf
|
||||
{
|
||||
public:
|
||||
/// Constructor increments the reference count on the
|
||||
/// BufferArray argument and calls release() on destruction.
|
||||
BufferArrayStreamBuf(BufferArray * array);
|
||||
virtual ~BufferArrayStreamBuf();
|
||||
|
||||
private:
|
||||
BufferArrayStreamBuf(const BufferArrayStreamBuf &); // Not defined
|
||||
void operator=(const BufferArrayStreamBuf &); // Not defined
|
||||
|
||||
public:
|
||||
// Input interfaces from std::streambuf
|
||||
int_type underflow();
|
||||
int_type uflow();
|
||||
int_type pbackfail(int_type ch);
|
||||
std::streamsize showmanyc();
|
||||
|
||||
// Output interfaces from std::streambuf
|
||||
int_type overflow(int c);
|
||||
std::streamsize xsputn(const char * src, std::streamsize count);
|
||||
|
||||
// Common/misc interfaces from std::streambuf
|
||||
std::streampos seekoff(std::streamoff off, std::ios_base::seekdir way, std::ios_base::openmode which);
|
||||
|
||||
protected:
|
||||
BufferArray * mBufferArray; // Ref counted
|
||||
size_t mReadCurPos;
|
||||
int mReadCurBlock;
|
||||
const char * mReadBegin;
|
||||
const char * mReadCur;
|
||||
const char * mReadEnd;
|
||||
size_t mWriteCurPos;
|
||||
|
||||
}; // end class BufferArrayStreamBuf
|
||||
|
||||
|
||||
// =====================================================
|
||||
// BufferArrayStream
|
||||
// =====================================================
|
||||
|
||||
/// Adapter class that supplies streaming operators to BufferArray
|
||||
///
|
||||
/// Provides a streaming adapter to an existing BufferArray
|
||||
/// instance so that the convenient '<<' and '>>' conversions
|
||||
/// can be applied to a BufferArray. Very convenient for LLSD
|
||||
/// serialization and parsing as well.
|
||||
|
||||
class BufferArrayStream : public std::iostream
|
||||
{
|
||||
public:
|
||||
/// Constructor increments the reference count on the
|
||||
/// BufferArray argument and calls release() on destruction.
|
||||
BufferArrayStream(BufferArray * ba);
|
||||
~BufferArrayStream();
|
||||
|
||||
protected:
|
||||
BufferArrayStream(const BufferArrayStream &);
|
||||
void operator=(const BufferArrayStream &);
|
||||
|
||||
protected:
|
||||
BufferArrayStreamBuf mStreamBuf;
|
||||
}; // end class BufferArrayStream
|
||||
|
||||
|
||||
} // end namespace LLCore
|
||||
|
||||
#endif // _LLCORE_BUFFER_STREAM_H_
|
||||
|
|
@ -0,0 +1,943 @@
|
|||
/**
|
||||
* @file http_texture_load.cpp
|
||||
* @brief Texture download example for core-http library
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <set>
|
||||
#include <map>
|
||||
#if !defined(WIN32)
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
#include "linden_common.h"
|
||||
|
||||
#include "httpcommon.h"
|
||||
#include "httprequest.h"
|
||||
#include "httphandler.h"
|
||||
#include "httpresponse.h"
|
||||
#include "httpheaders.h"
|
||||
#include "bufferarray.h"
|
||||
#include "_mutex.h"
|
||||
|
||||
#include <curl/curl.h>
|
||||
#include <openssl/crypto.h>
|
||||
|
||||
#include "lltimer.h"
|
||||
|
||||
|
||||
void init_curl();
|
||||
void term_curl();
|
||||
unsigned long ssl_thread_id_callback(void);
|
||||
void ssl_locking_callback(int mode, int type, const char * file, int line);
|
||||
void usage(std::ostream & out);
|
||||
|
||||
// Default command line settings
|
||||
static int concurrency_limit(40);
|
||||
static char url_format[1024] = "http://example.com/some/path?texture_id=%s.texture";
|
||||
|
||||
#if defined(WIN32)
|
||||
|
||||
#define strncpy(_a, _b, _c) strncpy_s(_a, _b, _c)
|
||||
#define strtok_r(_a, _b, _c) strtok_s(_a, _b, _c)
|
||||
|
||||
int getopt(int argc, char * const argv[], const char *optstring);
|
||||
char *optarg(NULL);
|
||||
int optind(1);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
// Mostly just a container for the texture IDs and fetch
|
||||
// parameters....
|
||||
class WorkingSet : public LLCore::HttpHandler
|
||||
{
|
||||
public:
|
||||
WorkingSet();
|
||||
~WorkingSet();
|
||||
|
||||
bool reload(LLCore::HttpRequest *);
|
||||
|
||||
virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response);
|
||||
|
||||
void loadTextureUuids(FILE * in);
|
||||
|
||||
public:
|
||||
struct Spec
|
||||
{
|
||||
std::string mUuid;
|
||||
int mOffset;
|
||||
int mLength;
|
||||
};
|
||||
typedef std::set<LLCore::HttpHandle> handle_set_t;
|
||||
typedef std::vector<Spec> texture_list_t;
|
||||
|
||||
public:
|
||||
bool mVerbose;
|
||||
bool mRandomRange;
|
||||
int mMaxConcurrency;
|
||||
handle_set_t mHandles;
|
||||
int mRemaining;
|
||||
int mLimit;
|
||||
int mAt;
|
||||
std::string mUrl;
|
||||
texture_list_t mTextures;
|
||||
int mErrorsApi;
|
||||
int mErrorsHttp;
|
||||
int mErrorsHttp404;
|
||||
int mErrorsHttp416;
|
||||
int mErrorsHttp500;
|
||||
int mErrorsHttp503;
|
||||
int mSuccesses;
|
||||
long mByteCount;
|
||||
LLCore::HttpHeaders * mHeaders;
|
||||
};
|
||||
|
||||
|
||||
// Gather process information while we run. Process
|
||||
// size, cpu consumed, wallclock time.
|
||||
|
||||
class Metrics
|
||||
{
|
||||
public:
|
||||
class MetricsImpl;
|
||||
|
||||
public:
|
||||
Metrics();
|
||||
~Metrics();
|
||||
|
||||
void init();
|
||||
void sample();
|
||||
void term();
|
||||
|
||||
protected:
|
||||
MetricsImpl * mImpl;
|
||||
|
||||
public:
|
||||
U64 mMaxVSZ;
|
||||
U64 mMinVSZ;
|
||||
U64 mStartWallTime;
|
||||
U64 mEndWallTime;
|
||||
U64 mStartUTime;
|
||||
U64 mEndUTime;
|
||||
U64 mStartSTime;
|
||||
U64 mEndSTime;
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
bool do_random(false);
|
||||
bool do_verbose(false);
|
||||
|
||||
int option(-1);
|
||||
while (-1 != (option = getopt(argc, argv, "u:c:h?Rv")))
|
||||
{
|
||||
switch (option)
|
||||
{
|
||||
case 'u':
|
||||
strncpy(url_format, optarg, sizeof(url_format));
|
||||
url_format[sizeof(url_format) - 1] = '\0';
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
{
|
||||
unsigned long value;
|
||||
char * end;
|
||||
|
||||
value = strtoul(optarg, &end, 10);
|
||||
if (value < 1 || value > 100 || *end != '\0')
|
||||
{
|
||||
usage(std::cerr);
|
||||
return 1;
|
||||
}
|
||||
concurrency_limit = value;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'R':
|
||||
do_random = true;
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
do_verbose = true;
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
case '?':
|
||||
usage(std::cout);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if ((optind + 1) != argc)
|
||||
{
|
||||
usage(std::cerr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
FILE * uuids(fopen(argv[optind], "r"));
|
||||
if (! uuids)
|
||||
{
|
||||
const char * errstr(strerror(errno));
|
||||
|
||||
std::cerr << "Couldn't open UUID file '" << argv[optind] << "'. Reason: "
|
||||
<< errstr << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Initialization
|
||||
init_curl();
|
||||
LLCore::HttpRequest::createService();
|
||||
LLCore::HttpRequest::startThread();
|
||||
|
||||
// Get service point
|
||||
LLCore::HttpRequest * hr = new LLCore::HttpRequest();
|
||||
|
||||
// Get a handler/working set
|
||||
WorkingSet ws;
|
||||
|
||||
// Fill the working set with work
|
||||
ws.mUrl = url_format;
|
||||
ws.loadTextureUuids(uuids);
|
||||
ws.mRandomRange = do_random;
|
||||
ws.mVerbose = do_verbose;
|
||||
ws.mMaxConcurrency = concurrency_limit;
|
||||
|
||||
if (! ws.mTextures.size())
|
||||
{
|
||||
std::cerr << "No UUIDs found in file '" << argv[optind] << "'." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Setup metrics
|
||||
Metrics metrics;
|
||||
metrics.init();
|
||||
|
||||
// Run it
|
||||
int passes(0);
|
||||
while (! ws.reload(hr))
|
||||
{
|
||||
hr->update(5000000);
|
||||
ms_sleep(2);
|
||||
if (0 == (++passes % 200))
|
||||
{
|
||||
metrics.sample();
|
||||
}
|
||||
}
|
||||
metrics.sample();
|
||||
metrics.term();
|
||||
|
||||
// Report
|
||||
std::cout << "HTTP errors: " << ws.mErrorsHttp << " API errors: " << ws.mErrorsApi
|
||||
<< " Successes: " << ws.mSuccesses << " Byte count: " << ws.mByteCount
|
||||
<< std::endl;
|
||||
std::cout << "HTTP 404 errors: " << ws.mErrorsHttp404 << " HTTP 416 errors: " << ws.mErrorsHttp416
|
||||
<< " HTTP 500 errors: " << ws.mErrorsHttp500 << " HTTP 503 errors: " << ws.mErrorsHttp503
|
||||
<< std::endl;
|
||||
std::cout << "User CPU: " << (metrics.mEndUTime - metrics.mStartUTime)
|
||||
<< " uS System CPU: " << (metrics.mEndSTime - metrics.mStartSTime)
|
||||
<< " uS Wall Time: " << (metrics.mEndWallTime - metrics.mStartWallTime)
|
||||
<< " uS Maximum VSZ: " << metrics.mMaxVSZ
|
||||
<< " Bytes Minimum VSZ: " << metrics.mMinVSZ << " Bytes"
|
||||
<< std::endl;
|
||||
|
||||
// Clean up
|
||||
hr->requestStopThread(NULL);
|
||||
ms_sleep(1000);
|
||||
delete hr;
|
||||
LLCore::HttpRequest::destroyService();
|
||||
term_curl();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void usage(std::ostream & out)
|
||||
{
|
||||
out << "\n"
|
||||
"usage:\thttp_texture_load [options] uuid_file\n"
|
||||
"\n"
|
||||
"This is a standalone program to drive the New Platform HTTP Library.\n"
|
||||
"The program is supplied with a file of texture UUIDs, one per line\n"
|
||||
"These are fetched sequentially using a pool of concurrent connection\n"
|
||||
"until all are fetched. The default URL format is only useful from\n"
|
||||
"within Linden Lab but this can be overriden with a printf-style\n"
|
||||
"URL formatting string on the command line.\n"
|
||||
"\n"
|
||||
"Options:\n"
|
||||
"\n"
|
||||
" -u <url_format> printf-style format string for URL generation\n"
|
||||
" Default: " << url_format << "\n"
|
||||
" -R Issue GETs with random Range: headers\n"
|
||||
" -c <limit> Maximum request concurrency. Range: [1..100]\n"
|
||||
" Default: " << concurrency_limit << "\n"
|
||||
" -v Verbose mode. Issue some chatter while running\n"
|
||||
" -h print this help\n"
|
||||
"\n"
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
|
||||
WorkingSet::WorkingSet()
|
||||
: LLCore::HttpHandler(),
|
||||
mVerbose(false),
|
||||
mRandomRange(false),
|
||||
mRemaining(200),
|
||||
mLimit(200),
|
||||
mAt(0),
|
||||
mErrorsApi(0),
|
||||
mErrorsHttp(0),
|
||||
mErrorsHttp404(0),
|
||||
mErrorsHttp416(0),
|
||||
mErrorsHttp500(0),
|
||||
mErrorsHttp503(0),
|
||||
mSuccesses(0),
|
||||
mByteCount(0L)
|
||||
{
|
||||
mTextures.reserve(30000);
|
||||
|
||||
mHeaders = new LLCore::HttpHeaders;
|
||||
mHeaders->mHeaders.push_back("Accept: image/x-j2c");
|
||||
}
|
||||
|
||||
|
||||
WorkingSet::~WorkingSet()
|
||||
{
|
||||
if (mHeaders)
|
||||
{
|
||||
mHeaders->release();
|
||||
mHeaders = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool WorkingSet::reload(LLCore::HttpRequest * hr)
|
||||
{
|
||||
int to_do((std::min)(mRemaining, mMaxConcurrency - int(mHandles.size())));
|
||||
|
||||
for (int i(0); i < to_do; ++i)
|
||||
{
|
||||
char buffer[1024];
|
||||
#if defined(WIN32)
|
||||
_snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, mUrl.c_str(), mTextures[mAt].mUuid.c_str());
|
||||
#else
|
||||
snprintf(buffer, sizeof(buffer), mUrl.c_str(), mTextures[mAt].mUuid.c_str());
|
||||
#endif
|
||||
int offset(mRandomRange ? ((unsigned long) rand()) % 1000000UL : mTextures[mAt].mOffset);
|
||||
int length(mRandomRange ? ((unsigned long) rand()) % 1000000UL : mTextures[mAt].mLength);
|
||||
|
||||
LLCore::HttpHandle handle;
|
||||
if (offset || length)
|
||||
{
|
||||
handle = hr->requestGetByteRange(0, 0, buffer, offset, length, NULL, mHeaders, this);
|
||||
}
|
||||
else
|
||||
{
|
||||
handle = hr->requestGet(0, 0, buffer, NULL, mHeaders, this);
|
||||
}
|
||||
if (! handle)
|
||||
{
|
||||
// Fatal. Couldn't queue up something.
|
||||
std::cerr << "Failed to queue work to HTTP Service. Reason: "
|
||||
<< hr->getStatus().toString() << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
mHandles.insert(handle);
|
||||
}
|
||||
mAt++;
|
||||
mRemaining--;
|
||||
|
||||
if (mVerbose)
|
||||
{
|
||||
static int count(0);
|
||||
++count;
|
||||
if (0 == (count %5))
|
||||
std::cout << "Queued " << count << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
// Are we done?
|
||||
return (! mRemaining) && mHandles.empty();
|
||||
}
|
||||
|
||||
|
||||
void WorkingSet::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response)
|
||||
{
|
||||
handle_set_t::iterator it(mHandles.find(handle));
|
||||
if (mHandles.end() == it)
|
||||
{
|
||||
// Wha?
|
||||
std::cerr << "Failed to find handle in request list. Fatal." << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
LLCore::HttpStatus status(response->getStatus());
|
||||
if (status)
|
||||
{
|
||||
// More success
|
||||
LLCore::BufferArray * data(response->getBody());
|
||||
mByteCount += data->size();
|
||||
++mSuccesses;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Something in this library or libcurl
|
||||
if (status.isHttpStatus())
|
||||
{
|
||||
static const LLCore::HttpStatus hs404(404);
|
||||
static const LLCore::HttpStatus hs416(416);
|
||||
static const LLCore::HttpStatus hs500(500);
|
||||
static const LLCore::HttpStatus hs503(503);
|
||||
|
||||
++mErrorsHttp;
|
||||
if (hs404 == status)
|
||||
{
|
||||
++mErrorsHttp404;
|
||||
}
|
||||
else if (hs416 == status)
|
||||
{
|
||||
++mErrorsHttp416;
|
||||
}
|
||||
else if (hs500 == status)
|
||||
{
|
||||
++mErrorsHttp500;
|
||||
}
|
||||
else if (hs503 == status)
|
||||
{
|
||||
++mErrorsHttp503;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
++mErrorsApi;
|
||||
}
|
||||
}
|
||||
mHandles.erase(it);
|
||||
}
|
||||
|
||||
if (mVerbose)
|
||||
{
|
||||
static int count(0);
|
||||
++count;
|
||||
if (0 == (count %5))
|
||||
std::cout << "Handled " << count << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void WorkingSet::loadTextureUuids(FILE * in)
|
||||
{
|
||||
char buffer[1024];
|
||||
|
||||
while (fgets(buffer, sizeof(buffer), in))
|
||||
{
|
||||
WorkingSet::Spec texture;
|
||||
char * state(NULL);
|
||||
char * token = strtok_r(buffer, " \t\n,", &state);
|
||||
if (token && 36 == strlen(token))
|
||||
{
|
||||
// Close enough for this function
|
||||
texture.mUuid = token;
|
||||
texture.mOffset = 0;
|
||||
texture.mLength = 0;
|
||||
token = strtok_r(buffer, " \t\n,", &state);
|
||||
if (token)
|
||||
{
|
||||
int offset(atoi(token));
|
||||
token = strtok_r(buffer, " \t\n,", &state);
|
||||
if (token)
|
||||
{
|
||||
int length(atoi(token));
|
||||
texture.mOffset = offset;
|
||||
texture.mLength = length;
|
||||
}
|
||||
}
|
||||
mTextures.push_back(texture);
|
||||
}
|
||||
}
|
||||
mRemaining = mLimit = mTextures.size();
|
||||
}
|
||||
|
||||
|
||||
int ssl_mutex_count(0);
|
||||
LLCoreInt::HttpMutex ** ssl_mutex_list = NULL;
|
||||
|
||||
void 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 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 ssl_thread_id_callback(void)
|
||||
{
|
||||
#if defined(WIN32)
|
||||
return (unsigned long) GetCurrentThread();
|
||||
#else
|
||||
return (unsigned long) pthread_self();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if defined(WIN32)
|
||||
|
||||
// Very much a subset of posix functionality. Don't push
|
||||
// it too hard...
|
||||
int getopt(int argc, char * const argv[], const char *optstring)
|
||||
{
|
||||
static int pos(0);
|
||||
while (optind < argc)
|
||||
{
|
||||
if (pos == 0)
|
||||
{
|
||||
if (argv[optind][0] != '-')
|
||||
return -1;
|
||||
pos = 1;
|
||||
}
|
||||
if (! argv[optind][pos])
|
||||
{
|
||||
++optind;
|
||||
pos = 0;
|
||||
continue;
|
||||
}
|
||||
const char * thing(strchr(optstring, argv[optind][pos]));
|
||||
if (! thing)
|
||||
{
|
||||
++optind;
|
||||
return -1;
|
||||
}
|
||||
if (thing[1] == ':')
|
||||
{
|
||||
optarg = argv[++optind];
|
||||
++optind;
|
||||
pos = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
optarg = NULL;
|
||||
++pos;
|
||||
}
|
||||
return *thing;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#if LL_WINDOWS
|
||||
|
||||
#define PSAPI_VERSION 1
|
||||
#include "windows.h"
|
||||
#include "psapi.h"
|
||||
|
||||
class Metrics::MetricsImpl
|
||||
{
|
||||
public:
|
||||
MetricsImpl()
|
||||
{}
|
||||
|
||||
~MetricsImpl()
|
||||
{}
|
||||
|
||||
void init(Metrics * metrics)
|
||||
{
|
||||
HANDLE self(GetCurrentProcess()); // Does not have to be closed
|
||||
FILETIME ft_dummy, ft_system, ft_user;
|
||||
GetProcessTimes(self, &ft_dummy, &ft_dummy, &ft_system, &ft_user);
|
||||
ULARGE_INTEGER uli;
|
||||
uli.u.LowPart = ft_system.dwLowDateTime;
|
||||
uli.u.HighPart = ft_system.dwHighDateTime;
|
||||
metrics->mStartSTime = uli.QuadPart / U64L(10); // Convert to uS
|
||||
uli.u.LowPart = ft_user.dwLowDateTime;
|
||||
uli.u.HighPart = ft_user.dwHighDateTime;
|
||||
metrics->mStartUTime = uli.QuadPart / U64L(10);
|
||||
metrics->mStartWallTime = totalTime();
|
||||
}
|
||||
|
||||
void sample(Metrics * metrics)
|
||||
{
|
||||
PROCESS_MEMORY_COUNTERS_EX counters;
|
||||
|
||||
GetProcessMemoryInfo(GetCurrentProcess(),
|
||||
(PROCESS_MEMORY_COUNTERS *) &counters,
|
||||
sizeof(counters));
|
||||
// Okay, PrivateUsage isn't truly VSZ but it will be
|
||||
// a good tracker for leaks and fragmentation. Work on
|
||||
// a better estimator later...
|
||||
SIZE_T vsz(counters.PrivateUsage);
|
||||
metrics->mMaxVSZ = (std::max)(metrics->mMaxVSZ, U64(vsz));
|
||||
metrics->mMinVSZ = (std::min)(metrics->mMinVSZ, U64(vsz));
|
||||
}
|
||||
|
||||
void term(Metrics * metrics)
|
||||
{
|
||||
HANDLE self(GetCurrentProcess()); // Does not have to be closed
|
||||
FILETIME ft_dummy, ft_system, ft_user;
|
||||
GetProcessTimes(self, &ft_dummy, &ft_dummy, &ft_system, &ft_user);
|
||||
ULARGE_INTEGER uli;
|
||||
uli.u.LowPart = ft_system.dwLowDateTime;
|
||||
uli.u.HighPart = ft_system.dwHighDateTime;
|
||||
metrics->mEndSTime = uli.QuadPart / U64L(10);
|
||||
uli.u.LowPart = ft_user.dwLowDateTime;
|
||||
uli.u.HighPart = ft_user.dwHighDateTime;
|
||||
metrics->mEndUTime = uli.QuadPart / U64L(10);
|
||||
metrics->mEndWallTime = totalTime();
|
||||
}
|
||||
|
||||
protected:
|
||||
};
|
||||
|
||||
#elif LL_DARWIN
|
||||
|
||||
#include <sys/resource.h>
|
||||
#include <mach/mach.h>
|
||||
|
||||
class Metrics::MetricsImpl
|
||||
{
|
||||
public:
|
||||
MetricsImpl()
|
||||
{}
|
||||
|
||||
~MetricsImpl()
|
||||
{}
|
||||
|
||||
void init(Metrics * metrics)
|
||||
{
|
||||
U64 utime, stime;
|
||||
|
||||
if (getTimes(&utime, &stime))
|
||||
{
|
||||
metrics->mStartSTime = stime;
|
||||
metrics->mStartUTime = utime;
|
||||
}
|
||||
metrics->mStartWallTime = totalTime();
|
||||
sample(metrics);
|
||||
}
|
||||
|
||||
void sample(Metrics * metrics)
|
||||
{
|
||||
U64 vsz;
|
||||
|
||||
if (getVM(&vsz))
|
||||
{
|
||||
metrics->mMaxVSZ = (std::max)(metrics->mMaxVSZ, vsz);
|
||||
metrics->mMinVSZ = (std::min)(metrics->mMinVSZ, vsz);
|
||||
}
|
||||
}
|
||||
|
||||
void term(Metrics * metrics)
|
||||
{
|
||||
U64 utime, stime;
|
||||
|
||||
if (getTimes(&utime, &stime))
|
||||
{
|
||||
metrics->mEndSTime = stime;
|
||||
metrics->mEndUTime = utime;
|
||||
}
|
||||
metrics->mEndWallTime = totalTime();
|
||||
}
|
||||
|
||||
protected:
|
||||
bool getVM(U64 * vsz)
|
||||
{
|
||||
task_basic_info task_info_block;
|
||||
mach_msg_type_number_t task_info_count(TASK_BASIC_INFO_COUNT);
|
||||
|
||||
if (KERN_SUCCESS != task_info(mach_task_self(),
|
||||
TASK_BASIC_INFO,
|
||||
(task_info_t) &task_info_block,
|
||||
&task_info_count))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
* vsz = task_info_block.virtual_size;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool getTimes(U64 * utime, U64 * stime)
|
||||
{
|
||||
struct rusage usage;
|
||||
|
||||
if (getrusage(RUSAGE_SELF, &usage))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
* utime = U64(usage.ru_utime.tv_sec) * U64L(1000000) + usage.ru_utime.tv_usec;
|
||||
* stime = U64(usage.ru_stime.tv_sec) * U64L(1000000) + usage.ru_stime.tv_usec;
|
||||
return true;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
class Metrics::MetricsImpl
|
||||
{
|
||||
public:
|
||||
MetricsImpl()
|
||||
: mProcFS(NULL),
|
||||
mUsecsPerTick(U64L(0))
|
||||
{}
|
||||
|
||||
|
||||
~MetricsImpl()
|
||||
{
|
||||
if (mProcFS)
|
||||
{
|
||||
fclose(mProcFS);
|
||||
mProcFS = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void init(Metrics * metrics)
|
||||
{
|
||||
if (! mProcFS)
|
||||
{
|
||||
mProcFS = fopen("/proc/self/stat", "r");
|
||||
if (! mProcFS)
|
||||
{
|
||||
const int errnum(errno);
|
||||
LL_ERRS("Main") << "Error opening proc fs: " << strerror(errnum) << LL_ENDL;
|
||||
}
|
||||
}
|
||||
|
||||
long ticks_per_sec(sysconf(_SC_CLK_TCK));
|
||||
mUsecsPerTick = U64L(1000000) / ticks_per_sec;
|
||||
U64 usecs_per_sec(mUsecsPerTick * ticks_per_sec);
|
||||
if (900000 > usecs_per_sec || 1100000 < usecs_per_sec)
|
||||
{
|
||||
LL_ERRS("Main") << "Resolution problems using uSecs for ticks" << LL_ENDL;
|
||||
}
|
||||
|
||||
U64 utime, stime;
|
||||
if (scanProcFS(&utime, &stime, NULL))
|
||||
{
|
||||
metrics->mStartSTime = stime;
|
||||
metrics->mStartUTime = utime;
|
||||
}
|
||||
metrics->mStartWallTime = totalTime();
|
||||
|
||||
sample(metrics);
|
||||
}
|
||||
|
||||
|
||||
void sample(Metrics * metrics)
|
||||
{
|
||||
U64 vsz;
|
||||
if (scanProcFS(NULL, NULL, &vsz))
|
||||
{
|
||||
metrics->mMaxVSZ = (std::max)(metrics->mMaxVSZ, vsz);
|
||||
metrics->mMinVSZ = (std::min)(metrics->mMinVSZ, vsz);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void term(Metrics * metrics)
|
||||
{
|
||||
U64 utime, stime;
|
||||
if (scanProcFS(&utime, &stime, NULL))
|
||||
{
|
||||
metrics->mEndSTime = stime;
|
||||
metrics->mEndUTime = utime;
|
||||
}
|
||||
metrics->mEndWallTime = totalTime();
|
||||
|
||||
sample(metrics);
|
||||
|
||||
if (mProcFS)
|
||||
{
|
||||
fclose(mProcFS);
|
||||
mProcFS = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
bool scanProcFS(U64 * utime, U64 * stime, U64 * vsz)
|
||||
{
|
||||
if (mProcFS)
|
||||
{
|
||||
int i_dummy;
|
||||
unsigned int ui_dummy;
|
||||
unsigned long ul_dummy, user_ticks, sys_ticks, vsize;
|
||||
long l_dummy, rss;
|
||||
unsigned long long ull_dummy;
|
||||
char c_dummy;
|
||||
|
||||
char buffer[256];
|
||||
|
||||
static const char * format("%d %*s %c %d %d %d %d %d %u %lu %lu %lu %lu %lu %lu %ld %ld %ld %ld %ld %ld %llu %lu %ld");
|
||||
|
||||
fseek(mProcFS, 0L, SEEK_SET);
|
||||
size_t len = fread(buffer, 1, sizeof(buffer) - 1, mProcFS);
|
||||
if (! len)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
buffer[len] = '\0';
|
||||
if (23 == sscanf(buffer, format,
|
||||
&i_dummy, // pid
|
||||
// &s_dummy, // command name
|
||||
&c_dummy, // state
|
||||
&i_dummy, // ppid
|
||||
&i_dummy, // pgrp
|
||||
&i_dummy, // session
|
||||
&i_dummy, // terminal
|
||||
&i_dummy, // terminal group id
|
||||
&ui_dummy, // flags
|
||||
&ul_dummy, // minor faults
|
||||
&ul_dummy, // minor faults in children
|
||||
&ul_dummy, // major faults
|
||||
&ul_dummy, // major faults in children
|
||||
&user_ticks,
|
||||
&sys_ticks,
|
||||
&l_dummy, // cutime
|
||||
&l_dummy, // cstime
|
||||
&l_dummy, // process priority
|
||||
&l_dummy, // nice value
|
||||
&l_dummy, // thread count
|
||||
&l_dummy, // time to SIGALRM
|
||||
&ull_dummy, // start time
|
||||
&vsize,
|
||||
&rss))
|
||||
{
|
||||
// Looks like we understand the line
|
||||
if (utime)
|
||||
{
|
||||
*utime = user_ticks * mUsecsPerTick;
|
||||
}
|
||||
|
||||
if (stime)
|
||||
{
|
||||
*stime = sys_ticks * mUsecsPerTick;
|
||||
}
|
||||
|
||||
if (vsz)
|
||||
{
|
||||
*vsz = vsize;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected:
|
||||
FILE * mProcFS;
|
||||
U64 mUsecsPerTick;
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif // LL_WINDOWS
|
||||
|
||||
Metrics::Metrics()
|
||||
: mMaxVSZ(U64(0)),
|
||||
mMinVSZ(U64L(0xffffffffffffffff)),
|
||||
mStartWallTime(U64(0)),
|
||||
mEndWallTime(U64(0)),
|
||||
mStartUTime(U64(0)),
|
||||
mEndUTime(U64(0)),
|
||||
mStartSTime(U64(0)),
|
||||
mEndSTime(U64(0))
|
||||
{
|
||||
mImpl = new MetricsImpl();
|
||||
}
|
||||
|
||||
|
||||
Metrics::~Metrics()
|
||||
{
|
||||
delete mImpl;
|
||||
mImpl = NULL;
|
||||
}
|
||||
|
||||
|
||||
void Metrics::init()
|
||||
{
|
||||
mImpl->init(this);
|
||||
}
|
||||
|
||||
|
||||
void Metrics::sample()
|
||||
{
|
||||
mImpl->sample(this);
|
||||
}
|
||||
|
||||
|
||||
void Metrics::term()
|
||||
{
|
||||
mImpl->term(this);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,179 @@
|
|||
/**
|
||||
* @file httpcommon.cpp
|
||||
* @brief
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include "httpcommon.h"
|
||||
|
||||
#include <curl/curl.h>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
|
||||
namespace LLCore
|
||||
{
|
||||
|
||||
HttpStatus::type_enum_t EXT_CURL_EASY;
|
||||
HttpStatus::type_enum_t EXT_CURL_MULTI;
|
||||
HttpStatus::type_enum_t LLCORE;
|
||||
|
||||
HttpStatus::operator unsigned long() const
|
||||
{
|
||||
static const int shift(sizeof(unsigned long) * 4);
|
||||
|
||||
unsigned long result(((unsigned long) mType) << shift | (unsigned long) (int) mStatus);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
std::string HttpStatus::toHex() const
|
||||
{
|
||||
std::ostringstream result;
|
||||
result.width(8);
|
||||
result.fill('0');
|
||||
result << std::hex << operator unsigned long();
|
||||
return result.str();
|
||||
}
|
||||
|
||||
|
||||
std::string HttpStatus::toString() const
|
||||
{
|
||||
static const char * llcore_errors[] =
|
||||
{
|
||||
"",
|
||||
"HTTP error reply status",
|
||||
"Services shutting down",
|
||||
"Operation canceled",
|
||||
"Invalid Content-Range header encountered",
|
||||
"Request handle not found",
|
||||
"Invalid datatype for argument or option",
|
||||
"Option has not been explicitly set",
|
||||
"Option is not dynamic and must be set early",
|
||||
"Invalid HTTP status code received from server"
|
||||
};
|
||||
static const int llcore_errors_count(sizeof(llcore_errors) / sizeof(llcore_errors[0]));
|
||||
|
||||
static const struct
|
||||
{
|
||||
type_enum_t mCode;
|
||||
const char * mText;
|
||||
}
|
||||
http_errors[] =
|
||||
{
|
||||
// Keep sorted by mCode, we binary search this list.
|
||||
{ 100, "Continue" },
|
||||
{ 101, "Switching Protocols" },
|
||||
{ 200, "OK" },
|
||||
{ 201, "Created" },
|
||||
{ 202, "Accepted" },
|
||||
{ 203, "Non-Authoritative Information" },
|
||||
{ 204, "No Content" },
|
||||
{ 205, "Reset Content" },
|
||||
{ 206, "Partial Content" },
|
||||
{ 300, "Multiple Choices" },
|
||||
{ 301, "Moved Permanently" },
|
||||
{ 302, "Found" },
|
||||
{ 303, "See Other" },
|
||||
{ 304, "Not Modified" },
|
||||
{ 305, "Use Proxy" },
|
||||
{ 307, "Temporary Redirect" },
|
||||
{ 400, "Bad Request" },
|
||||
{ 401, "Unauthorized" },
|
||||
{ 402, "Payment Required" },
|
||||
{ 403, "Forbidden" },
|
||||
{ 404, "Not Found" },
|
||||
{ 405, "Method Not Allowed" },
|
||||
{ 406, "Not Acceptable" },
|
||||
{ 407, "Proxy Authentication Required" },
|
||||
{ 408, "Request Time-out" },
|
||||
{ 409, "Conflict" },
|
||||
{ 410, "Gone" },
|
||||
{ 411, "Length Required" },
|
||||
{ 412, "Precondition Failed" },
|
||||
{ 413, "Request Entity Too Large" },
|
||||
{ 414, "Request-URI Too Large" },
|
||||
{ 415, "Unsupported Media Type" },
|
||||
{ 416, "Requested range not satisfiable" },
|
||||
{ 417, "Expectation Failed" },
|
||||
{ 500, "Internal Server Error" },
|
||||
{ 501, "Not Implemented" },
|
||||
{ 502, "Bad Gateway" },
|
||||
{ 503, "Service Unavailable" },
|
||||
{ 504, "Gateway Time-out" },
|
||||
{ 505, "HTTP Version not supported" }
|
||||
};
|
||||
static const int http_errors_count(sizeof(http_errors) / sizeof(http_errors[0]));
|
||||
|
||||
if (*this)
|
||||
{
|
||||
return std::string("");
|
||||
}
|
||||
switch (mType)
|
||||
{
|
||||
case EXT_CURL_EASY:
|
||||
return std::string(curl_easy_strerror(CURLcode(mStatus)));
|
||||
|
||||
case EXT_CURL_MULTI:
|
||||
return std::string(curl_multi_strerror(CURLMcode(mStatus)));
|
||||
|
||||
case LLCORE:
|
||||
if (mStatus >= 0 && mStatus < llcore_errors_count)
|
||||
{
|
||||
return std::string(llcore_errors[mStatus]);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (isHttpStatus())
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
return std::string(http_errors[at].mText);
|
||||
}
|
||||
if (at == bottom)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if (mType < http_errors[at].mCode)
|
||||
{
|
||||
top = at;
|
||||
}
|
||||
else
|
||||
{
|
||||
bottom = at;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return std::string("Unknown error");
|
||||
}
|
||||
|
||||
} // end namespace LLCore
|
||||
|
||||
|
|
@ -0,0 +1,311 @@
|
|||
/**
|
||||
* @file httpcommon.h
|
||||
* @brief Public-facing declarations and definitions of common types
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, 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 _LLCORE_HTTP_COMMON_H_
|
||||
#define _LLCORE_HTTP_COMMON_H_
|
||||
|
||||
/// @package LLCore::HTTP
|
||||
///
|
||||
/// This library implements a high-level, Indra-code-free client interface to
|
||||
/// HTTP services based on actual patterns found in the viewer and simulator.
|
||||
/// Interfaces are similar to those supplied by the legacy classes
|
||||
/// LLCurlRequest and LLHTTPClient. To that is added a policy scheme that
|
||||
/// allows an application to specify connection behaviors: limits on
|
||||
/// connections, HTTP keepalive, HTTP pipelining, retry-on-error limits, etc.
|
||||
///
|
||||
/// Features of the library include:
|
||||
/// - Single, private working thread where all transport and processing occurs.
|
||||
/// - Support for multiple consumers running in multiple threads.
|
||||
/// - Scatter/gather (a.k.a. buffer array) model for bulk data movement.
|
||||
/// - Reference counting used for many object instance lifetimes.
|
||||
/// - Minimal data sharing across threads for correctness and low latency.
|
||||
///
|
||||
/// The public interface is declared in a few key header files:
|
||||
/// - "llcorehttp/bufferarray.h"
|
||||
/// - "llcorehttp/httpcommon.h"
|
||||
/// - "llcorehttp/httphandler.h"
|
||||
/// - "llcorehttp/httpheaders.h"
|
||||
/// - "llcorehttp/httpoptions.h"
|
||||
/// - "llcorehttp/httprequest.h"
|
||||
/// - "llcorehttp/httpresponse.h"
|
||||
///
|
||||
/// The library is still under early development and particular users
|
||||
/// may need access to internal implementation details that are found
|
||||
/// in the _*.h header files. But this is a crutch to be avoided if at
|
||||
/// all possible and probably indicates some interface work is neeeded.
|
||||
///
|
||||
/// Using the library is fairly easy. Global setup needs a few
|
||||
/// steps:
|
||||
///
|
||||
/// - libcurl initialization including thread-safely callbacks for SSL:
|
||||
/// . curl_global_init(...)
|
||||
/// . CRYPTO_set_locking_callback(...)
|
||||
/// . CRYPTO_set_id_callback(...)
|
||||
/// - HttpRequest::createService() called to instantiate singletons
|
||||
/// and support objects.
|
||||
///
|
||||
/// An HTTP consumer in an application, and an application may have many
|
||||
/// consumers, does a few things:
|
||||
///
|
||||
/// - Instantiate and retain an object based on HttpRequest. This
|
||||
/// object becomes the portal into runtime services for the consumer.
|
||||
/// - Derive or mixin the HttpHandler class if you want notification
|
||||
/// when requests succeed or fail. This object's onCompleted()
|
||||
/// method is invoked and an instance can be shared across
|
||||
/// requests.
|
||||
///
|
||||
/// Issuing a request is straightforward:
|
||||
/// - Construct a suitable URL.
|
||||
/// - Configure HTTP options for the request. (optional)
|
||||
/// - Build a list of additional headers. (optional)
|
||||
/// - Invoke one of the requestXXXX() methods (requestGetByteRange,
|
||||
/// requestPost, etc.) on the HttpRequest instance supplying the
|
||||
/// above along with a policy class, a priority and an optional
|
||||
/// pointer to an HttpHandler instance. Work is then queued to
|
||||
/// the worker thread and occurs asynchronously.
|
||||
/// - Periodically invoke the update() method on the HttpRequest
|
||||
/// instance which performs completion notification to HttpHandler
|
||||
/// objects.
|
||||
/// - Do completion processing in your onCompletion() method.
|
||||
///
|
||||
/// Code fragments:
|
||||
/// Rather than a poorly-maintained example in comments, look in the
|
||||
/// example subdirectory which is a minimal yet functional tool to do
|
||||
/// GET request performance testing. With four calls:
|
||||
///
|
||||
/// init_curl();
|
||||
/// LLCore::HttpRequest::createService();
|
||||
/// LLCore::HttpRequest::startThread();
|
||||
/// LLCore::HttpRequest * hr = new LLCore::HttpRequest();
|
||||
///
|
||||
/// the program is basically ready to issue requests.
|
||||
///
|
||||
|
||||
|
||||
#include "linden_common.h" // Modifies curl/curl.h interfaces
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
namespace LLCore
|
||||
{
|
||||
|
||||
|
||||
/// All queued requests are represented by an HttpHandle value.
|
||||
/// The invalid value is returned when a request failed to queue.
|
||||
/// The actual status for these failures is then fetched with
|
||||
/// HttpRequest::getStatus().
|
||||
///
|
||||
/// The handle is valid only for the life of a request. On
|
||||
/// return from any HttpHandler notification, the handle immediately
|
||||
/// 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
|
||||
/// timebase compatible with the environment.
|
||||
typedef U64 HttpTime;
|
||||
|
||||
/// Error codes defined by the library itself as distinct from
|
||||
/// libcurl (or any other transport provider).
|
||||
enum HttpError
|
||||
{
|
||||
// Successful value compatible with the libcurl codes.
|
||||
HE_SUCCESS = 0,
|
||||
|
||||
// Intended for HTTP reply codes 100-999, indicates that
|
||||
// the reply should be considered an error by the application.
|
||||
HE_REPLY_ERROR = 1,
|
||||
|
||||
// Service is shutting down and requested operation will
|
||||
// not be queued or performed.
|
||||
HE_SHUTTING_DOWN = 2,
|
||||
|
||||
// Operation was canceled by request.
|
||||
HE_OP_CANCELED = 3,
|
||||
|
||||
// Invalid content range header received.
|
||||
HE_INV_CONTENT_RANGE_HDR = 4,
|
||||
|
||||
// Request handle not found
|
||||
HE_HANDLE_NOT_FOUND = 5,
|
||||
|
||||
// Invalid datatype for option/setting
|
||||
HE_INVALID_ARG = 6,
|
||||
|
||||
// Option hasn't been explicitly set
|
||||
HE_OPT_NOT_SET = 7,
|
||||
|
||||
// Option not dynamic, must be set during init phase
|
||||
HE_OPT_NOT_DYNAMIC = 8,
|
||||
|
||||
// Invalid HTTP status code returned by server
|
||||
HE_INVALID_HTTP_STATUS = 9
|
||||
|
||||
}; // end enum HttpError
|
||||
|
||||
|
||||
/// HttpStatus encapsulates errors from libcurl (easy, multi), HTTP
|
||||
/// reply status codes and internal errors as well. The encapsulation
|
||||
/// isn't expected to completely isolate the caller from libcurl but
|
||||
/// basic operational tests (success or failure) are provided.
|
||||
///
|
||||
/// Non-HTTP status are encoded as (type, status) with type being
|
||||
/// one of: EXT_CURL_EASY, EXT_CURL_MULTI or LLCORE and status
|
||||
/// being the success/error code from that domain. HTTP status
|
||||
/// is encoded as (status, error_flag). Status should be in the
|
||||
/// range [100, 999] and error_flag is either HE_SUCCESS or
|
||||
/// HE_REPLY_ERROR to indicate whether this should be treated as
|
||||
/// a successful status or an error. The application is responsible
|
||||
/// for making that determination and a range like [200, 299] isn't
|
||||
/// automatically assumed to be definitive.
|
||||
///
|
||||
/// Examples:
|
||||
///
|
||||
/// 1. Construct a default, successful status code:
|
||||
/// HttpStatus();
|
||||
///
|
||||
/// 2. Construct a successful, HTTP 200 status code:
|
||||
/// HttpStatus(200);
|
||||
///
|
||||
/// 3. Construct a failed, HTTP 404 not-found status code:
|
||||
/// HttpStatus(404);
|
||||
///
|
||||
/// 4. Construct a failed libcurl couldn't connect status code:
|
||||
/// HttpStatus(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT);
|
||||
///
|
||||
/// 5. Construct an HTTP 301 status code to be treated as success:
|
||||
/// HttpStatus(301, HE_SUCCESS);
|
||||
///
|
||||
|
||||
struct HttpStatus
|
||||
{
|
||||
typedef unsigned short type_enum_t;
|
||||
|
||||
HttpStatus()
|
||||
: mType(LLCORE),
|
||||
mStatus(HE_SUCCESS)
|
||||
{}
|
||||
|
||||
HttpStatus(type_enum_t type, short status)
|
||||
: mType(type),
|
||||
mStatus(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);
|
||||
}
|
||||
|
||||
HttpStatus(const HttpStatus & rhs)
|
||||
: mType(rhs.mType),
|
||||
mStatus(rhs.mStatus)
|
||||
{}
|
||||
|
||||
HttpStatus & operator=(const HttpStatus & rhs)
|
||||
{
|
||||
// Don't care if lhs & rhs are the same object
|
||||
|
||||
mType = rhs.mType;
|
||||
mStatus = rhs.mStatus;
|
||||
return *this;
|
||||
}
|
||||
|
||||
static const type_enum_t EXT_CURL_EASY = 0;
|
||||
static const type_enum_t EXT_CURL_MULTI = 1;
|
||||
static const type_enum_t LLCORE = 2;
|
||||
|
||||
type_enum_t mType;
|
||||
short mStatus;
|
||||
|
||||
/// Test for successful status in the code regardless
|
||||
/// of error source (internal, libcurl).
|
||||
///
|
||||
/// @return 'true' when status is successful.
|
||||
///
|
||||
operator bool() const
|
||||
{
|
||||
return 0 == mStatus;
|
||||
}
|
||||
|
||||
/// Inverse of previous operator.
|
||||
///
|
||||
/// @return 'true' on any error condition
|
||||
bool operator !() const
|
||||
{
|
||||
return 0 != 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;
|
||||
}
|
||||
|
||||
bool operator!=(const HttpStatus & rhs) const
|
||||
{
|
||||
return ! operator==(rhs);
|
||||
}
|
||||
|
||||
/// Convert to single numeric representation. Mainly
|
||||
/// for logging or other informal purposes. Also
|
||||
/// creates an ambiguous second path to integer conversion
|
||||
/// which tends to find programming errors such as formatting
|
||||
/// the status to a stream (operator<<).
|
||||
operator unsigned long() const;
|
||||
unsigned long toULong() const
|
||||
{
|
||||
return operator unsigned long();
|
||||
}
|
||||
|
||||
/// And to convert to a hex string.
|
||||
std::string toHex() const;
|
||||
|
||||
/// Convert status to a string representation. For
|
||||
/// success, returns an empty string. For failure
|
||||
/// statuses, a string as appropriate for the source of
|
||||
/// the error code (libcurl easy, libcurl multi, or
|
||||
/// LLCore itself).
|
||||
std::string toString() const;
|
||||
|
||||
/// Returns true if the status value represents an
|
||||
/// HTTP response status (100 - 999).
|
||||
bool isHttpStatus() const
|
||||
{
|
||||
return mType >= type_enum_t(100) && mType <= type_enum_t(999);
|
||||
}
|
||||
|
||||
}; // end struct HttpStatus
|
||||
|
||||
} // end namespace LLCore
|
||||
|
||||
#endif // _LLCORE_HTTP_COMMON_H_
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
/**
|
||||
* @file httphandler.h
|
||||
* @brief Public-facing declarations for the HttpHandler class
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, 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 _LLCORE_HTTP_HANDLER_H_
|
||||
#define _LLCORE_HTTP_HANDLER_H_
|
||||
|
||||
|
||||
#include "httpcommon.h"
|
||||
|
||||
|
||||
namespace LLCore
|
||||
{
|
||||
|
||||
class HttpResponse;
|
||||
|
||||
|
||||
/// HttpHandler defines an interface used by the library to
|
||||
/// notify library callers of significant events, currently
|
||||
/// request completion. Callers must derive or mixin this class
|
||||
/// then provide an implementation of the @see onCompleted
|
||||
/// method to receive such notifications. An instance may
|
||||
/// 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
|
||||
/// tread-compatible. Most derivations, however, will have
|
||||
/// different constraints.
|
||||
///
|
||||
/// Allocation: Not refcounted, may be stack allocated though
|
||||
/// 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
|
||||
{
|
||||
public:
|
||||
virtual ~HttpHandler()
|
||||
{}
|
||||
|
||||
/// Method invoked during calls to @see update(). Each invocation
|
||||
/// represents the completion of some requested operation. Caller
|
||||
/// can identify the request from the handle and interrogate the
|
||||
/// response argument for success/failure, data and other information.
|
||||
///
|
||||
/// @param handle Identifier of the request generating
|
||||
/// the notification.
|
||||
/// @param response Supplies detailed information about
|
||||
/// the request including status codes
|
||||
/// (both programming and HTTP), HTTP body
|
||||
/// data and encodings, headers, etc.
|
||||
/// The response object is refcounted and
|
||||
/// the called code may retain the object
|
||||
/// by invoking @see addRef() on it. The
|
||||
/// library itself drops all references to
|
||||
/// to object on return and never touches
|
||||
/// it again.
|
||||
///
|
||||
virtual void onCompleted(HttpHandle handle, HttpResponse * response) = 0;
|
||||
|
||||
}; // end class HttpHandler
|
||||
|
||||
|
||||
} // end namespace LLCore
|
||||
|
||||
#endif // _LLCORE_HTTP_HANDLER_H_
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
/**
|
||||
* @file httpheaders.cpp
|
||||
* @brief Implementation of the HTTPHeaders class
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include "httpheaders.h"
|
||||
|
||||
|
||||
namespace LLCore
|
||||
{
|
||||
|
||||
|
||||
HttpHeaders::HttpHeaders()
|
||||
: RefCounted(true)
|
||||
{}
|
||||
|
||||
|
||||
HttpHeaders::~HttpHeaders()
|
||||
{}
|
||||
|
||||
|
||||
} // end namespace LLCore
|
||||
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
/**
|
||||
* @file httpheaders.h
|
||||
* @brief Public-facing declarations for the HttpHeaders class
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, 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 _LLCORE_HTTP_HEADERS_H_
|
||||
#define _LLCORE_HTTP_HEADERS_H_
|
||||
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "_refcounted.h"
|
||||
|
||||
|
||||
namespace LLCore
|
||||
{
|
||||
|
||||
///
|
||||
/// Maintains an ordered list of name/value pairs representing
|
||||
/// HTTP header lines. This is used both to provide additional
|
||||
/// headers when making HTTP requests and in responses when the
|
||||
/// caller has asked that headers be returned (not the default
|
||||
/// option).
|
||||
///
|
||||
/// @note
|
||||
/// This is a minimally-functional placeholder at the moment
|
||||
/// to fill out the class hierarchy. The final class will be
|
||||
/// something else, probably more pair-oriented. It's also
|
||||
/// an area where shared values are desirable so refcounting is
|
||||
/// already specced and a copy-on-write scheme imagined.
|
||||
/// Expect changes here.
|
||||
///
|
||||
/// Threading: Not intrinsically thread-safe. It *is* expected
|
||||
/// that callers will build these objects and then share them
|
||||
/// via reference counting with the worker thread. The implication
|
||||
/// is that once an HttpHeader instance is handed to a request,
|
||||
/// the object must be treated as read-only.
|
||||
///
|
||||
/// Allocation: Refcounted, heap only. Caller of the
|
||||
/// constructor is given a refcount.
|
||||
///
|
||||
|
||||
class HttpHeaders : public LLCoreInt::RefCounted
|
||||
{
|
||||
public:
|
||||
/// @post In addition to the instance, caller has a refcount
|
||||
/// to the instance. A call to @see release() will destroy
|
||||
/// the instance.
|
||||
HttpHeaders();
|
||||
|
||||
protected:
|
||||
virtual ~HttpHeaders(); // Use release()
|
||||
|
||||
HttpHeaders(const HttpHeaders &); // Not defined
|
||||
void operator=(const HttpHeaders &); // Not defined
|
||||
|
||||
public:
|
||||
typedef std::vector<std::string> container_t;
|
||||
container_t mHeaders;
|
||||
|
||||
}; // end class HttpHeaders
|
||||
|
||||
} // end namespace LLCore
|
||||
|
||||
|
||||
#endif // _LLCORE_HTTP_HEADERS_H_
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
/**
|
||||
* @file httpoptions.cpp
|
||||
* @brief Implementation of the HTTPOptions class
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include "httpoptions.h"
|
||||
|
||||
#include "_httpinternal.h"
|
||||
|
||||
|
||||
namespace LLCore
|
||||
{
|
||||
|
||||
|
||||
HttpOptions::HttpOptions()
|
||||
: RefCounted(true),
|
||||
mWantHeaders(false),
|
||||
mTracing(HTTP_TRACE_OFF),
|
||||
mTimeout(HTTP_REQUEST_TIMEOUT_DEFAULT),
|
||||
mRetries(HTTP_RETRY_COUNT_DEFAULT)
|
||||
{}
|
||||
|
||||
|
||||
HttpOptions::~HttpOptions()
|
||||
{}
|
||||
|
||||
|
||||
void HttpOptions::setWantHeaders(bool wanted)
|
||||
{
|
||||
mWantHeaders = wanted;
|
||||
}
|
||||
|
||||
|
||||
void HttpOptions::setTrace(long level)
|
||||
{
|
||||
mTracing = int(level);
|
||||
}
|
||||
|
||||
|
||||
void HttpOptions::setTimeout(unsigned int timeout)
|
||||
{
|
||||
mTimeout = timeout;
|
||||
}
|
||||
|
||||
|
||||
void HttpOptions::setRetries(unsigned int retries)
|
||||
{
|
||||
mRetries = retries;
|
||||
}
|
||||
|
||||
|
||||
} // end namespace LLCore
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
/**
|
||||
* @file httpoptions.h
|
||||
* @brief Public-facing declarations for the HTTPOptions class
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, 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 _LLCORE_HTTP_OPTIONS_H_
|
||||
#define _LLCORE_HTTP_OPTIONS_H_
|
||||
|
||||
|
||||
#include "httpcommon.h"
|
||||
|
||||
#include "_refcounted.h"
|
||||
|
||||
|
||||
namespace LLCore
|
||||
{
|
||||
|
||||
|
||||
/// Really a struct in spirit, it provides options that
|
||||
/// modify HTTP requests.
|
||||
///
|
||||
/// Sharing instances across requests. It's intended that
|
||||
/// these be shared across requests: caller can create one
|
||||
/// of these, set it up as needed and then reference it
|
||||
/// repeatedly in HTTP operations. But see the Threading
|
||||
/// note about references.
|
||||
///
|
||||
/// Threading: While this class does nothing to ensure thread
|
||||
/// safety, it *is* intended to be shared between the application
|
||||
/// thread and the worker thread. This means that once an instance
|
||||
/// is delivered to the library in request operations, the
|
||||
/// option data must not be written until all such requests
|
||||
/// complete and relinquish their references.
|
||||
///
|
||||
/// Allocation: Refcounted, heap only. Caller of the constructor
|
||||
/// is given a refcount.
|
||||
///
|
||||
class HttpOptions : public LLCoreInt::RefCounted
|
||||
{
|
||||
public:
|
||||
HttpOptions();
|
||||
|
||||
protected:
|
||||
virtual ~HttpOptions(); // Use release()
|
||||
|
||||
HttpOptions(const HttpOptions &); // Not defined
|
||||
void operator=(const HttpOptions &); // Not defined
|
||||
|
||||
public:
|
||||
void setWantHeaders(bool wanted);
|
||||
bool getWantHeaders() const
|
||||
{
|
||||
return mWantHeaders;
|
||||
}
|
||||
|
||||
void setTrace(int long);
|
||||
int getTrace() const
|
||||
{
|
||||
return mTracing;
|
||||
}
|
||||
|
||||
void setTimeout(unsigned int timeout);
|
||||
unsigned int getTimeout() const
|
||||
{
|
||||
return mTimeout;
|
||||
}
|
||||
|
||||
void setRetries(unsigned int retries);
|
||||
unsigned int getRetries() const
|
||||
{
|
||||
return mRetries;
|
||||
}
|
||||
|
||||
protected:
|
||||
bool mWantHeaders;
|
||||
int mTracing;
|
||||
unsigned int mTimeout;
|
||||
unsigned int mRetries;
|
||||
|
||||
}; // end class HttpOptions
|
||||
|
||||
|
||||
} // end namespace HttpOptions
|
||||
|
||||
#endif // _LLCORE_HTTP_OPTIONS_H_
|
||||
|
|
@ -0,0 +1,504 @@
|
|||
/**
|
||||
* @file httprequest.cpp
|
||||
* @brief Implementation of the HTTPRequest class
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include "httprequest.h"
|
||||
|
||||
#include "_httprequestqueue.h"
|
||||
#include "_httpreplyqueue.h"
|
||||
#include "_httpservice.h"
|
||||
#include "_httppolicy.h"
|
||||
#include "_httpoperation.h"
|
||||
#include "_httpoprequest.h"
|
||||
#include "_httpopsetpriority.h"
|
||||
#include "_httpopcancel.h"
|
||||
#include "_httpopsetget.h"
|
||||
|
||||
#include "lltimer.h"
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
bool has_inited(false);
|
||||
|
||||
}
|
||||
|
||||
namespace LLCore
|
||||
{
|
||||
|
||||
// ====================================
|
||||
// HttpRequest Implementation
|
||||
// ====================================
|
||||
|
||||
|
||||
HttpRequest::policy_t HttpRequest::sNextPolicyID(1);
|
||||
|
||||
|
||||
HttpRequest::HttpRequest()
|
||||
: //HttpHandler(),
|
||||
mReplyQueue(NULL),
|
||||
mRequestQueue(NULL)
|
||||
{
|
||||
mRequestQueue = HttpRequestQueue::instanceOf();
|
||||
mRequestQueue->addRef();
|
||||
|
||||
mReplyQueue = new HttpReplyQueue();
|
||||
}
|
||||
|
||||
|
||||
HttpRequest::~HttpRequest()
|
||||
{
|
||||
if (mRequestQueue)
|
||||
{
|
||||
mRequestQueue->release();
|
||||
mRequestQueue = NULL;
|
||||
}
|
||||
|
||||
if (mReplyQueue)
|
||||
{
|
||||
mReplyQueue->release();
|
||||
mReplyQueue = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ====================================
|
||||
// Policy Methods
|
||||
// ====================================
|
||||
|
||||
|
||||
HttpStatus HttpRequest::setPolicyGlobalOption(EGlobalPolicy opt, long value)
|
||||
{
|
||||
if (HttpService::RUNNING == HttpService::instanceOf()->getState())
|
||||
{
|
||||
return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC);
|
||||
}
|
||||
return HttpService::instanceOf()->getGlobalOptions().set(opt, value);
|
||||
}
|
||||
|
||||
|
||||
HttpStatus HttpRequest::setPolicyGlobalOption(EGlobalPolicy opt, const std::string & value)
|
||||
{
|
||||
if (HttpService::RUNNING == HttpService::instanceOf()->getState())
|
||||
{
|
||||
return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC);
|
||||
}
|
||||
return HttpService::instanceOf()->getGlobalOptions().set(opt, value);
|
||||
}
|
||||
|
||||
|
||||
HttpRequest::policy_t HttpRequest::createPolicyClass()
|
||||
{
|
||||
if (HttpService::RUNNING == HttpService::instanceOf()->getState())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return HttpService::instanceOf()->createPolicyClass();
|
||||
}
|
||||
|
||||
|
||||
HttpStatus HttpRequest::setPolicyClassOption(policy_t policy_id,
|
||||
EClassPolicy opt,
|
||||
long value)
|
||||
{
|
||||
if (HttpService::RUNNING == HttpService::instanceOf()->getState())
|
||||
{
|
||||
return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC);
|
||||
}
|
||||
return HttpService::instanceOf()->getClassOptions(policy_id).set(opt, value);
|
||||
}
|
||||
|
||||
|
||||
// ====================================
|
||||
// Request Methods
|
||||
// ====================================
|
||||
|
||||
|
||||
HttpStatus HttpRequest::getStatus() const
|
||||
{
|
||||
return mLastReqStatus;
|
||||
}
|
||||
|
||||
|
||||
HttpHandle HttpRequest::requestGet(policy_t policy_id,
|
||||
priority_t priority,
|
||||
const std::string & url,
|
||||
HttpOptions * options,
|
||||
HttpHeaders * headers,
|
||||
HttpHandler * user_handler)
|
||||
{
|
||||
HttpStatus status;
|
||||
HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID);
|
||||
|
||||
HttpOpRequest * op = new HttpOpRequest();
|
||||
if (! (status = op->setupGet(policy_id, priority, url, options, headers)))
|
||||
{
|
||||
op->release();
|
||||
mLastReqStatus = status;
|
||||
return handle;
|
||||
}
|
||||
op->setReplyPath(mReplyQueue, user_handler);
|
||||
if (! (status = mRequestQueue->addOp(op))) // transfers refcount
|
||||
{
|
||||
op->release();
|
||||
mLastReqStatus = status;
|
||||
return handle;
|
||||
}
|
||||
|
||||
mLastReqStatus = status;
|
||||
handle = static_cast<HttpHandle>(op);
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
|
||||
HttpHandle HttpRequest::requestGetByteRange(policy_t policy_id,
|
||||
priority_t priority,
|
||||
const std::string & url,
|
||||
size_t offset,
|
||||
size_t len,
|
||||
HttpOptions * options,
|
||||
HttpHeaders * headers,
|
||||
HttpHandler * user_handler)
|
||||
{
|
||||
HttpStatus status;
|
||||
HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID);
|
||||
|
||||
HttpOpRequest * op = new HttpOpRequest();
|
||||
if (! (status = op->setupGetByteRange(policy_id, priority, url, offset, len, options, headers)))
|
||||
{
|
||||
op->release();
|
||||
mLastReqStatus = status;
|
||||
return handle;
|
||||
}
|
||||
op->setReplyPath(mReplyQueue, user_handler);
|
||||
if (! (status = mRequestQueue->addOp(op))) // transfers refcount
|
||||
{
|
||||
op->release();
|
||||
mLastReqStatus = status;
|
||||
return handle;
|
||||
}
|
||||
|
||||
mLastReqStatus = status;
|
||||
handle = static_cast<HttpHandle>(op);
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
|
||||
HttpHandle HttpRequest::requestPost(policy_t policy_id,
|
||||
priority_t priority,
|
||||
const std::string & url,
|
||||
BufferArray * body,
|
||||
HttpOptions * options,
|
||||
HttpHeaders * headers,
|
||||
HttpHandler * user_handler)
|
||||
{
|
||||
HttpStatus status;
|
||||
HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID);
|
||||
|
||||
HttpOpRequest * op = new HttpOpRequest();
|
||||
if (! (status = op->setupPost(policy_id, priority, url, body, options, headers)))
|
||||
{
|
||||
op->release();
|
||||
mLastReqStatus = status;
|
||||
return handle;
|
||||
}
|
||||
op->setReplyPath(mReplyQueue, user_handler);
|
||||
if (! (status = mRequestQueue->addOp(op))) // transfers refcount
|
||||
{
|
||||
op->release();
|
||||
mLastReqStatus = status;
|
||||
return handle;
|
||||
}
|
||||
|
||||
mLastReqStatus = status;
|
||||
handle = static_cast<HttpHandle>(op);
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
|
||||
HttpHandle HttpRequest::requestPut(policy_t policy_id,
|
||||
priority_t priority,
|
||||
const std::string & url,
|
||||
BufferArray * body,
|
||||
HttpOptions * options,
|
||||
HttpHeaders * headers,
|
||||
HttpHandler * user_handler)
|
||||
{
|
||||
HttpStatus status;
|
||||
HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID);
|
||||
|
||||
HttpOpRequest * op = new HttpOpRequest();
|
||||
if (! (status = op->setupPut(policy_id, priority, url, body, options, headers)))
|
||||
{
|
||||
op->release();
|
||||
mLastReqStatus = status;
|
||||
return handle;
|
||||
}
|
||||
op->setReplyPath(mReplyQueue, user_handler);
|
||||
if (! (status = mRequestQueue->addOp(op))) // transfers refcount
|
||||
{
|
||||
op->release();
|
||||
mLastReqStatus = status;
|
||||
return handle;
|
||||
}
|
||||
|
||||
mLastReqStatus = status;
|
||||
handle = static_cast<HttpHandle>(op);
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
|
||||
HttpHandle HttpRequest::requestNoOp(HttpHandler * user_handler)
|
||||
{
|
||||
HttpStatus status;
|
||||
HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID);
|
||||
|
||||
HttpOpNull * op = new HttpOpNull();
|
||||
op->setReplyPath(mReplyQueue, user_handler);
|
||||
if (! (status = mRequestQueue->addOp(op))) // transfers refcount
|
||||
{
|
||||
op->release();
|
||||
mLastReqStatus = status;
|
||||
return handle;
|
||||
}
|
||||
|
||||
mLastReqStatus = status;
|
||||
handle = static_cast<HttpHandle>(op);
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
|
||||
HttpStatus HttpRequest::update(long usecs)
|
||||
{
|
||||
HttpOperation * op(NULL);
|
||||
|
||||
if (usecs)
|
||||
{
|
||||
const HttpTime limit(totalTime() + HttpTime(usecs));
|
||||
while (limit >= totalTime() && (op = mReplyQueue->fetchOp()))
|
||||
{
|
||||
// Process operation
|
||||
op->visitNotifier(this);
|
||||
|
||||
// We're done with the operation
|
||||
op->release();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Same as above, just no time limit
|
||||
HttpReplyQueue::OpContainer replies;
|
||||
mReplyQueue->fetchAll(replies);
|
||||
if (! replies.empty())
|
||||
{
|
||||
for (HttpReplyQueue::OpContainer::iterator iter(replies.begin());
|
||||
replies.end() != iter;
|
||||
++iter)
|
||||
{
|
||||
// Swap op pointer for NULL;
|
||||
op = *iter; *iter = NULL;
|
||||
|
||||
// Process operation
|
||||
op->visitNotifier(this);
|
||||
|
||||
// We're done with the operation
|
||||
op->release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return HttpStatus();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// ====================================
|
||||
// Request Management Methods
|
||||
// ====================================
|
||||
|
||||
HttpHandle HttpRequest::requestCancel(HttpHandle request, HttpHandler * user_handler)
|
||||
{
|
||||
HttpStatus status;
|
||||
HttpHandle ret_handle(LLCORE_HTTP_HANDLE_INVALID);
|
||||
|
||||
HttpOpCancel * op = new HttpOpCancel(request);
|
||||
op->setReplyPath(mReplyQueue, user_handler);
|
||||
if (! (status = mRequestQueue->addOp(op))) // transfers refcount
|
||||
{
|
||||
op->release();
|
||||
mLastReqStatus = status;
|
||||
return ret_handle;
|
||||
}
|
||||
|
||||
mLastReqStatus = status;
|
||||
ret_handle = static_cast<HttpHandle>(op);
|
||||
|
||||
return ret_handle;
|
||||
}
|
||||
|
||||
|
||||
HttpHandle HttpRequest::requestSetPriority(HttpHandle request, priority_t priority,
|
||||
HttpHandler * handler)
|
||||
{
|
||||
HttpStatus status;
|
||||
HttpHandle ret_handle(LLCORE_HTTP_HANDLE_INVALID);
|
||||
|
||||
HttpOpSetPriority * op = new HttpOpSetPriority(request, priority);
|
||||
op->setReplyPath(mReplyQueue, handler);
|
||||
if (! (status = mRequestQueue->addOp(op))) // transfers refcount
|
||||
{
|
||||
op->release();
|
||||
mLastReqStatus = status;
|
||||
return ret_handle;
|
||||
}
|
||||
|
||||
mLastReqStatus = status;
|
||||
ret_handle = static_cast<HttpHandle>(op);
|
||||
|
||||
return ret_handle;
|
||||
}
|
||||
|
||||
|
||||
// ====================================
|
||||
// Utility Methods
|
||||
// ====================================
|
||||
|
||||
HttpStatus HttpRequest::createService()
|
||||
{
|
||||
HttpStatus status;
|
||||
|
||||
if (! has_inited)
|
||||
{
|
||||
HttpRequestQueue::init();
|
||||
HttpRequestQueue * rq = HttpRequestQueue::instanceOf();
|
||||
HttpService::init(rq);
|
||||
has_inited = true;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
HttpStatus HttpRequest::destroyService()
|
||||
{
|
||||
HttpStatus status;
|
||||
|
||||
if (has_inited)
|
||||
{
|
||||
HttpService::term();
|
||||
HttpRequestQueue::term();
|
||||
has_inited = false;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
HttpStatus HttpRequest::startThread()
|
||||
{
|
||||
HttpStatus status;
|
||||
|
||||
HttpService::instanceOf()->startThread();
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
HttpHandle HttpRequest::requestStopThread(HttpHandler * user_handler)
|
||||
{
|
||||
HttpStatus status;
|
||||
HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID);
|
||||
|
||||
HttpOpStop * op = new HttpOpStop();
|
||||
op->setReplyPath(mReplyQueue, user_handler);
|
||||
if (! (status = mRequestQueue->addOp(op))) // transfers refcount
|
||||
{
|
||||
op->release();
|
||||
mLastReqStatus = status;
|
||||
return handle;
|
||||
}
|
||||
|
||||
mLastReqStatus = status;
|
||||
handle = static_cast<HttpHandle>(op);
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
|
||||
HttpHandle HttpRequest::requestSpin(int mode)
|
||||
{
|
||||
HttpStatus status;
|
||||
HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID);
|
||||
|
||||
HttpOpSpin * op = new HttpOpSpin(mode);
|
||||
op->setReplyPath(mReplyQueue, NULL);
|
||||
if (! (status = mRequestQueue->addOp(op))) // transfers refcount
|
||||
{
|
||||
op->release();
|
||||
mLastReqStatus = status;
|
||||
return handle;
|
||||
}
|
||||
|
||||
mLastReqStatus = status;
|
||||
handle = static_cast<HttpHandle>(op);
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
// ====================================
|
||||
// Dynamic Policy Methods
|
||||
// ====================================
|
||||
|
||||
HttpHandle HttpRequest::requestSetHttpProxy(const std::string & proxy, HttpHandler * handler)
|
||||
{
|
||||
HttpStatus status;
|
||||
HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID);
|
||||
|
||||
HttpOpSetGet * op = new HttpOpSetGet();
|
||||
op->setupSet(GP_HTTP_PROXY, proxy);
|
||||
op->setReplyPath(mReplyQueue, handler);
|
||||
if (! (status = mRequestQueue->addOp(op))) // transfers refcount
|
||||
{
|
||||
op->release();
|
||||
mLastReqStatus = status;
|
||||
return handle;
|
||||
}
|
||||
|
||||
mLastReqStatus = status;
|
||||
handle = static_cast<HttpHandle>(op);
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
|
||||
} // end namespace LLCore
|
||||
|
||||
|
|
@ -0,0 +1,535 @@
|
|||
/**
|
||||
* @file httprequest.h
|
||||
* @brief Public-facing declarations for HttpRequest class
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, 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 _LLCORE_HTTP_REQUEST_H_
|
||||
#define _LLCORE_HTTP_REQUEST_H_
|
||||
|
||||
|
||||
#include "httpcommon.h"
|
||||
#include "httphandler.h"
|
||||
|
||||
|
||||
namespace LLCore
|
||||
{
|
||||
|
||||
class HttpRequestQueue;
|
||||
class HttpReplyQueue;
|
||||
class HttpService;
|
||||
class HttpOptions;
|
||||
class HttpHeaders;
|
||||
class HttpOperation;
|
||||
class BufferArray;
|
||||
|
||||
/// HttpRequest supplies the entry into the HTTP transport
|
||||
/// services in the LLCore libraries. Services provided include:
|
||||
///
|
||||
/// - Some, but not all, global initialization of libcurl.
|
||||
/// - Starting asynchronous, threaded HTTP requests.
|
||||
/// - Definition of policy classes affect request handling.
|
||||
/// - Utilities to control request options and headers
|
||||
///
|
||||
/// Requests
|
||||
///
|
||||
/// The class supports the current HTTP request operations:
|
||||
///
|
||||
/// - requestGetByteRange: GET with Range header for a single range of bytes
|
||||
///
|
||||
/// Policy Classes
|
||||
///
|
||||
/// <TBD>
|
||||
///
|
||||
/// Usage
|
||||
///
|
||||
/// <TBD>
|
||||
///
|
||||
/// Threading: An instance may only be used by one application/
|
||||
/// consumer thread. But a thread may have as many instances of
|
||||
/// this as it likes.
|
||||
///
|
||||
/// Allocation: Not refcounted, may be stack allocated though that
|
||||
/// hasn't been tested. Queued requests can still run and any
|
||||
/// queued replies will keep refcounts to the reply queue leading
|
||||
/// to memory leaks.
|
||||
///
|
||||
/// @pre Before using this class (static or instances), some global
|
||||
/// initialization is required. See @see httpcommon.h for more information.
|
||||
///
|
||||
/// @nosubgrouping
|
||||
///
|
||||
|
||||
class HttpRequest
|
||||
{
|
||||
public:
|
||||
HttpRequest();
|
||||
virtual ~HttpRequest();
|
||||
|
||||
private:
|
||||
HttpRequest(const HttpRequest &); // Disallowed
|
||||
void operator=(const HttpRequest &); // Disallowed
|
||||
|
||||
public:
|
||||
typedef unsigned int policy_t;
|
||||
typedef unsigned int priority_t;
|
||||
|
||||
public:
|
||||
/// @name PolicyMethods
|
||||
/// @{
|
||||
|
||||
/// Represents a default, catch-all policy class that guarantees
|
||||
/// eventual service for any HTTP request.
|
||||
static const int DEFAULT_POLICY_ID = 0;
|
||||
|
||||
enum EGlobalPolicy
|
||||
{
|
||||
/// Maximum number of connections the library will use to
|
||||
/// perform operations. This is somewhat soft as the underlying
|
||||
/// transport will cache some connections (up to 5).
|
||||
|
||||
/// A long value setting the maximum number of connections
|
||||
/// allowed over all policy classes. Note that this will be
|
||||
/// a somewhat soft value. There may be an additional five
|
||||
/// connections per policy class depending upon runtime
|
||||
/// behavior.
|
||||
GP_CONNECTION_LIMIT,
|
||||
|
||||
/// String containing a system-appropriate directory name
|
||||
/// where SSL certs are stored.
|
||||
GP_CA_PATH,
|
||||
|
||||
/// String giving a full path to a file containing SSL certs.
|
||||
GP_CA_FILE,
|
||||
|
||||
/// String of host/port to use as simple HTTP proxy. This is
|
||||
/// going to change in the future into something more elaborate
|
||||
/// that may support richer schemes.
|
||||
GP_HTTP_PROXY,
|
||||
|
||||
/// 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.
|
||||
GP_LLPROXY,
|
||||
|
||||
/// Long value setting the logging trace level for the
|
||||
/// library. Possible values are:
|
||||
/// 0 - No tracing (default)
|
||||
/// 1 - Basic tracing of request start, stop and major events.
|
||||
/// 2 - Connection, header and payload size information from
|
||||
/// HTTP transactions.
|
||||
/// 3 - Partial logging of payload itself.
|
||||
///
|
||||
/// These values are also used in the trace modes for
|
||||
/// individual requests in HttpOptions. Also be aware that
|
||||
/// tracing tends to impact performance of the viewer.
|
||||
GP_TRACE
|
||||
};
|
||||
|
||||
/// Set a parameter on a global policy option. Calls
|
||||
/// made after the start of the servicing thread are
|
||||
/// not honored and return an error status.
|
||||
///
|
||||
/// @param opt Enum of option to be set.
|
||||
/// @param value Desired value of option.
|
||||
/// @return Standard status code.
|
||||
static HttpStatus setPolicyGlobalOption(EGlobalPolicy opt, long value);
|
||||
static HttpStatus setPolicyGlobalOption(EGlobalPolicy opt, const std::string & value);
|
||||
|
||||
/// Create a new policy class into which requests can be made.
|
||||
///
|
||||
/// @return If positive, the policy_id used to reference
|
||||
/// the class in other methods. If 0, an error
|
||||
/// occurred and @see getStatus() may provide more
|
||||
/// detail on the reason.
|
||||
static policy_t createPolicyClass();
|
||||
|
||||
enum EClassPolicy
|
||||
{
|
||||
/// Limits the number of connections used for the class.
|
||||
CP_CONNECTION_LIMIT,
|
||||
|
||||
/// Limits the number of connections used for a single
|
||||
/// literal address/port pair within the class.
|
||||
CP_PER_HOST_CONNECTION_LIMIT,
|
||||
|
||||
/// Suitable requests are allowed to pipeline on their
|
||||
/// connections when they ask for it.
|
||||
CP_ENABLE_PIPELINING
|
||||
};
|
||||
|
||||
/// Set a parameter on a class-based policy option. Calls
|
||||
/// made after the start of the servicing thread are
|
||||
/// not honored and return an error status.
|
||||
///
|
||||
/// @param policy_id ID of class as returned by @see createPolicyClass().
|
||||
/// @param opt Enum of option to be set.
|
||||
/// @param value Desired value of option.
|
||||
/// @return Standard status code.
|
||||
static HttpStatus setPolicyClassOption(policy_t policy_id, EClassPolicy opt, long value);
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name RequestMethods
|
||||
///
|
||||
/// @{
|
||||
|
||||
/// Some calls expect to succeed as the normal part of operation and so
|
||||
/// return a useful value rather than a status. When they do fail, the
|
||||
/// status is saved and can be fetched with this method.
|
||||
///
|
||||
/// @return Status of the failing method invocation. If the
|
||||
/// preceding call succeeded or other HttpStatus
|
||||
/// returning calls immediately preceded this method,
|
||||
/// the returned value may not be reliable.
|
||||
///
|
||||
HttpStatus getStatus() const;
|
||||
|
||||
/// Queue a full HTTP GET request to be issued for entire entity.
|
||||
/// The request is queued and serviced by the working thread and
|
||||
/// notification of completion delivered to the optional HttpHandler
|
||||
/// argument during @see update() calls.
|
||||
///
|
||||
/// With a valid handle returned, it can be used to reference the
|
||||
/// request in other requests (like cancellation) and will be an
|
||||
/// argument when any HttpHandler object is invoked.
|
||||
///
|
||||
/// Headers supplied by default:
|
||||
/// - Connection: keep-alive
|
||||
/// - Accept: */*
|
||||
/// - Accept-Encoding: deflate, gzip
|
||||
/// - Keep-alive: 300
|
||||
/// - Host: <stuff>
|
||||
///
|
||||
/// Some headers excluded by default:
|
||||
/// - Pragma:
|
||||
/// - Cache-control:
|
||||
/// - Range:
|
||||
/// - Transfer-Encoding:
|
||||
/// - Referer:
|
||||
///
|
||||
/// @param policy_id Default or user-defined policy class under
|
||||
/// which this request is to be serviced.
|
||||
/// @param priority Standard priority scheme inherited from
|
||||
/// Indra code base (U32-type scheme).
|
||||
/// @param url URL with any encoded query parameters to
|
||||
/// be accessed.
|
||||
/// @param options Optional instance of an HttpOptions object
|
||||
/// to provide additional controls over the request
|
||||
/// function for this request only. Any such
|
||||
/// object then becomes shared-read across threads
|
||||
/// and no code should modify the HttpOptions
|
||||
/// instance.
|
||||
/// @param headers Optional instance of an HttpHeaders object
|
||||
/// to provide additional and/or overridden
|
||||
/// headers for the request. As with options,
|
||||
/// the instance becomes shared-read across threads
|
||||
/// and no code should modify the HttpHeaders
|
||||
/// instance.
|
||||
/// @param handler Optional pointer to an HttpHandler instance
|
||||
/// whose onCompleted() method will be invoked
|
||||
/// during calls to update(). This is a non-
|
||||
/// reference-counted object which would be a
|
||||
/// problem for shutdown and other edge cases but
|
||||
/// the pointer is only dereferenced during
|
||||
/// calls to update().
|
||||
///
|
||||
/// @return The handle of the request if successfully
|
||||
/// queued or LLCORE_HTTP_HANDLE_INVALID if the
|
||||
/// request could not be queued. In the latter
|
||||
/// case, @see getStatus() will return more info.
|
||||
///
|
||||
HttpHandle requestGet(policy_t policy_id,
|
||||
priority_t priority,
|
||||
const std::string & url,
|
||||
HttpOptions * options,
|
||||
HttpHeaders * headers,
|
||||
HttpHandler * handler);
|
||||
|
||||
|
||||
/// Queue a full HTTP GET request to be issued with a 'Range' header.
|
||||
/// The request is queued and serviced by the working thread and
|
||||
/// notification of completion delivered to the optional HttpHandler
|
||||
/// argument during @see update() calls.
|
||||
///
|
||||
/// With a valid handle returned, it can be used to reference the
|
||||
/// request in other requests (like cancellation) and will be an
|
||||
/// argument when any HttpHandler object is invoked.
|
||||
///
|
||||
/// Headers supplied by default:
|
||||
/// - Connection: keep-alive
|
||||
/// - Accept: */*
|
||||
/// - Accept-Encoding: deflate, gzip
|
||||
/// - Keep-alive: 300
|
||||
/// - Host: <stuff>
|
||||
/// - Range: <stuff> (will be omitted if offset == 0 and len == 0)
|
||||
///
|
||||
/// Some headers excluded by default:
|
||||
/// - Pragma:
|
||||
/// - Cache-control:
|
||||
/// - Transfer-Encoding:
|
||||
/// - Referer:
|
||||
///
|
||||
/// @param policy_id @see requestGet()
|
||||
/// @param priority "
|
||||
/// @param url "
|
||||
/// @param offset Offset of first byte into resource to be returned.
|
||||
/// @param len Count of bytes to be returned
|
||||
/// @param options @see requestGet()
|
||||
/// @param headers "
|
||||
/// @param handler "
|
||||
/// @return "
|
||||
///
|
||||
HttpHandle requestGetByteRange(policy_t policy_id,
|
||||
priority_t priority,
|
||||
const std::string & url,
|
||||
size_t offset,
|
||||
size_t len,
|
||||
HttpOptions * options,
|
||||
HttpHeaders * headers,
|
||||
HttpHandler * handler);
|
||||
|
||||
|
||||
/// Queue a full HTTP POST. Query arguments and body may
|
||||
/// be provided. Caller is responsible for escaping and
|
||||
/// encoding and communicating the content types.
|
||||
///
|
||||
/// Headers supplied by default:
|
||||
/// - Connection: keep-alive
|
||||
/// - Accept: */*
|
||||
/// - Accept-Encoding: deflate, gzip
|
||||
/// - Keep-Alive: 300
|
||||
/// - Host: <stuff>
|
||||
/// - Content-Length: <digits>
|
||||
/// - Content-Type: application/x-www-form-urlencoded
|
||||
///
|
||||
/// Some headers excluded by default:
|
||||
/// - Pragma:
|
||||
/// - Cache-Control:
|
||||
/// - Transfer-Encoding: ... chunked ...
|
||||
/// - Referer:
|
||||
/// - Content-Encoding:
|
||||
/// - Expect:
|
||||
///
|
||||
/// @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 requestPost(policy_t policy_id,
|
||||
priority_t priority,
|
||||
const std::string & url,
|
||||
BufferArray * body,
|
||||
HttpOptions * options,
|
||||
HttpHeaders * headers,
|
||||
HttpHandler * handler);
|
||||
|
||||
|
||||
/// Queue a full HTTP PUT. Query arguments and body may
|
||||
/// be provided. Caller is responsible for escaping and
|
||||
/// encoding and communicating the content types.
|
||||
///
|
||||
/// Headers supplied by default:
|
||||
/// - Connection: keep-alive
|
||||
/// - Accept: */*
|
||||
/// - Accept-Encoding: deflate, gzip
|
||||
/// - Keep-Alive: 300
|
||||
/// - Host: <stuff>
|
||||
/// - Content-Length: <digits>
|
||||
///
|
||||
/// Some headers excluded by default:
|
||||
/// - Pragma:
|
||||
/// - Cache-Control:
|
||||
/// - Transfer-Encoding: ... chunked ...
|
||||
/// - Referer:
|
||||
/// - Content-Encoding:
|
||||
/// - Expect:
|
||||
/// - Content-Type:
|
||||
///
|
||||
/// @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 requestPut(policy_t policy_id,
|
||||
priority_t priority,
|
||||
const std::string & url,
|
||||
BufferArray * body,
|
||||
HttpOptions * options,
|
||||
HttpHeaders * headers,
|
||||
HttpHandler * 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.
|
||||
///
|
||||
/// @param handler @see requestGet()
|
||||
/// @return "
|
||||
///
|
||||
HttpHandle requestNoOp(HttpHandler * handler);
|
||||
|
||||
/// While all the heavy work is done by the worker thread, notifications
|
||||
/// must be performed in the context of the application thread. These
|
||||
/// are done synchronously during calls to this method which gives the
|
||||
/// library control so notification can be performed. Application handlers
|
||||
/// are expected to return 'quickly' and do any significant processing
|
||||
/// outside of the notification callback to onCompleted().
|
||||
///
|
||||
/// @param usecs Maximum number of wallclock microseconds to
|
||||
/// spend in the call. As hinted at above, this
|
||||
/// is partly a function of application code so it's
|
||||
/// a soft limit. A '0' value will run without
|
||||
/// time limit until everything queued has been
|
||||
/// delivered.
|
||||
///
|
||||
/// @return Standard status code.
|
||||
HttpStatus update(long usecs);
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name RequestMgmtMethods
|
||||
///
|
||||
/// @{
|
||||
|
||||
HttpHandle requestCancel(HttpHandle request, HttpHandler *);
|
||||
|
||||
/// Request that a previously-issued request be reprioritized.
|
||||
/// The status of whether the change itself succeeded arrives
|
||||
/// via notification.
|
||||
///
|
||||
/// @param request Handle of previously-issued request to
|
||||
/// be changed.
|
||||
/// @param priority New priority value.
|
||||
/// @param handler @see requestGet()
|
||||
/// @return "
|
||||
///
|
||||
HttpHandle requestSetPriority(HttpHandle request, priority_t priority, HttpHandler * handler);
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name UtilityMethods
|
||||
///
|
||||
/// @{
|
||||
|
||||
/// Initialization method that needs to be called before queueing any
|
||||
/// requests. Doesn't start the worker thread and may be called befoer
|
||||
/// or after policy setup.
|
||||
static HttpStatus createService();
|
||||
|
||||
/// Mostly clean shutdown of services prior to exit. Caller is expected
|
||||
/// to have stopped a running worker thread before calling this.
|
||||
static HttpStatus destroyService();
|
||||
|
||||
/// Called once after @see createService() to start the worker thread.
|
||||
/// Stopping the thread is achieved by requesting it via @see requestStopThread().
|
||||
/// May be called before or after requests are issued.
|
||||
static HttpStatus startThread();
|
||||
|
||||
/// Queues a request to the worker thread to have it stop processing
|
||||
/// and exit (without exiting the program). When the operation is
|
||||
/// picked up by the worker thread, it immediately processes it and
|
||||
/// begins detaching from refcounted resources like request and
|
||||
/// reply queues and then returns to the host OS. It *does* queue a
|
||||
/// reply to give the calling application thread a notification that
|
||||
/// the operation has been performed.
|
||||
///
|
||||
/// @param handler (optional)
|
||||
/// @return The handle of the request if successfully
|
||||
/// queued or LLCORE_HTTP_HANDLE_INVALID if the
|
||||
/// request could not be queued. In the latter
|
||||
/// case, @see getStatus() will return more info.
|
||||
/// As the request cannot be cancelled, the handle
|
||||
/// is generally not useful.
|
||||
///
|
||||
HttpHandle requestStopThread(HttpHandler * handler);
|
||||
|
||||
/// Queue a Spin request.
|
||||
/// DEBUG/TESTING ONLY. This puts the worker into a CPU spin for
|
||||
/// test purposes.
|
||||
///
|
||||
/// @param mode 0 for hard spin, 1 for soft spin
|
||||
/// @return Standard handle return cases.
|
||||
///
|
||||
HttpHandle requestSpin(int mode);
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name DynamicPolicyMethods
|
||||
///
|
||||
/// @{
|
||||
|
||||
/// Request that a running transport pick up a new proxy setting.
|
||||
/// An empty string will indicate no proxy is to be used.
|
||||
HttpHandle requestSetHttpProxy(const std::string & proxy, HttpHandler * handler);
|
||||
|
||||
/// @}
|
||||
|
||||
protected:
|
||||
void generateNotification(HttpOperation * op);
|
||||
|
||||
private:
|
||||
/// @name InstanceData
|
||||
///
|
||||
/// @{
|
||||
HttpStatus mLastReqStatus;
|
||||
HttpReplyQueue * mReplyQueue;
|
||||
HttpRequestQueue * mRequestQueue;
|
||||
|
||||
/// @}
|
||||
|
||||
// ====================================
|
||||
/// @name GlobalState
|
||||
///
|
||||
/// @{
|
||||
///
|
||||
/// Must be established before any threading is allowed to
|
||||
/// start.
|
||||
///
|
||||
static policy_t sNextPolicyID;
|
||||
|
||||
/// @}
|
||||
// End Global State
|
||||
// ====================================
|
||||
|
||||
}; // end class HttpRequest
|
||||
|
||||
|
||||
} // end namespace LLCore
|
||||
|
||||
|
||||
|
||||
#endif // _LLCORE_HTTP_REQUEST_H_
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
/**
|
||||
* @file httpresponse.cpp
|
||||
* @brief
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include "httpresponse.h"
|
||||
#include "bufferarray.h"
|
||||
#include "httpheaders.h"
|
||||
|
||||
|
||||
namespace LLCore
|
||||
{
|
||||
|
||||
|
||||
HttpResponse::HttpResponse()
|
||||
: LLCoreInt::RefCounted(true),
|
||||
mReplyOffset(0U),
|
||||
mReplyLength(0U),
|
||||
mReplyFullLength(0U),
|
||||
mBufferArray(NULL),
|
||||
mHeaders(NULL)
|
||||
{}
|
||||
|
||||
|
||||
HttpResponse::~HttpResponse()
|
||||
{
|
||||
setBody(NULL);
|
||||
setHeaders(NULL);
|
||||
}
|
||||
|
||||
|
||||
void HttpResponse::setBody(BufferArray * ba)
|
||||
{
|
||||
if (mBufferArray == ba)
|
||||
return;
|
||||
|
||||
if (mBufferArray)
|
||||
{
|
||||
mBufferArray->release();
|
||||
}
|
||||
|
||||
if (ba)
|
||||
{
|
||||
ba->addRef();
|
||||
}
|
||||
|
||||
mBufferArray = ba;
|
||||
}
|
||||
|
||||
|
||||
void HttpResponse::setHeaders(HttpHeaders * headers)
|
||||
{
|
||||
if (mHeaders == headers)
|
||||
return;
|
||||
|
||||
if (mHeaders)
|
||||
{
|
||||
mHeaders->release();
|
||||
}
|
||||
|
||||
if (headers)
|
||||
{
|
||||
headers->addRef();
|
||||
}
|
||||
|
||||
mHeaders = headers;
|
||||
}
|
||||
|
||||
|
||||
} // end namespace LLCore
|
||||
|
|
@ -0,0 +1,161 @@
|
|||
/**
|
||||
* @file httpresponse.h
|
||||
* @brief Public-facing declarations for the HttpResponse class
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, 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 _LLCORE_HTTP_RESPONSE_H_
|
||||
#define _LLCORE_HTTP_RESPONSE_H_
|
||||
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "httpcommon.h"
|
||||
|
||||
#include "_refcounted.h"
|
||||
|
||||
|
||||
namespace LLCore
|
||||
{
|
||||
|
||||
class BufferArray;
|
||||
class HttpHeaders;
|
||||
|
||||
/// HttpResponse is instantiated by the library and handed to
|
||||
/// the caller during callbacks to the handler. It supplies
|
||||
/// all the status, header and HTTP body data the caller is
|
||||
/// interested in. Methods provide simple getters to return
|
||||
/// individual pieces of the response.
|
||||
///
|
||||
/// Typical usage will have the caller interrogate the object
|
||||
/// and return from the handler callback. Instances are refcounted
|
||||
/// and callers can bump the count and retain the object as needed.
|
||||
///
|
||||
/// Threading: Not intrinsically thread-safe.
|
||||
///
|
||||
/// Allocation: Refcounted, heap only. Caller of the constructor
|
||||
/// is given a refcount.
|
||||
///
|
||||
class HttpResponse : public LLCoreInt::RefCounted
|
||||
{
|
||||
public:
|
||||
HttpResponse();
|
||||
|
||||
protected:
|
||||
virtual ~HttpResponse(); // Use release()
|
||||
|
||||
HttpResponse(const HttpResponse &); // Not defined
|
||||
void operator=(const HttpResponse &); // Not defined
|
||||
|
||||
public:
|
||||
/// Returns the final status of the requested operation.
|
||||
///
|
||||
HttpStatus getStatus() const
|
||||
{
|
||||
return mStatus;
|
||||
}
|
||||
|
||||
void setStatus(const HttpStatus & status)
|
||||
{
|
||||
mStatus = status;
|
||||
}
|
||||
|
||||
/// Simple getter for the response body returned as a scatter/gather
|
||||
/// buffer. If the operation doesn't produce data (such as the Null
|
||||
/// or StopThread operations), this may be NULL.
|
||||
///
|
||||
/// Caller can hold onto the response by incrementing the reference
|
||||
/// count of the returned object.
|
||||
BufferArray * getBody() const
|
||||
{
|
||||
return mBufferArray;
|
||||
}
|
||||
|
||||
/// 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.
|
||||
void setBody(BufferArray * ba);
|
||||
|
||||
/// And a getter for the headers. And as with @see getResponse(),
|
||||
/// if headers aren't available because the operation doesn't produce
|
||||
/// any or delivery of headers wasn't requested in the options, this
|
||||
/// will be NULL.
|
||||
///
|
||||
/// Caller can hold onto the headers by incrementing the reference
|
||||
/// count of the returned object.
|
||||
HttpHeaders * getHeaders() const
|
||||
{
|
||||
return mHeaders;
|
||||
}
|
||||
|
||||
/// Behaves like @see setResponse() but for header data.
|
||||
void setHeaders(HttpHeaders * headers);
|
||||
|
||||
/// If a 'Range:' header was used, these methods are involved
|
||||
/// in setting and returning data about the actual response.
|
||||
/// If both @offset and @length are returned as 0, we probably
|
||||
/// didn't get a Content-Range header in the response. This
|
||||
/// occurs with various Capabilities-based services and the
|
||||
/// caller is going to have to make assumptions on receipt of
|
||||
/// a 206 status. The @full value may also be zero in cases of
|
||||
/// parsing problems or a wild-carded length response.
|
||||
void getRange(unsigned int * offset, unsigned int * length, unsigned int * full) const
|
||||
{
|
||||
*offset = mReplyOffset;
|
||||
*length = mReplyLength;
|
||||
*full = mReplyFullLength;
|
||||
}
|
||||
|
||||
void setRange(unsigned int offset, unsigned int length, unsigned int full_length)
|
||||
{
|
||||
mReplyOffset = offset;
|
||||
mReplyLength = length;
|
||||
mReplyFullLength = full_length;
|
||||
}
|
||||
|
||||
///
|
||||
const std::string & getContentType() const
|
||||
{
|
||||
return mContentType;
|
||||
}
|
||||
|
||||
void setContentType(const std::string & con_type)
|
||||
{
|
||||
mContentType = con_type;
|
||||
}
|
||||
|
||||
protected:
|
||||
// Response data here
|
||||
HttpStatus mStatus;
|
||||
unsigned int mReplyOffset;
|
||||
unsigned int mReplyLength;
|
||||
unsigned int mReplyFullLength;
|
||||
BufferArray * mBufferArray;
|
||||
HttpHeaders * mHeaders;
|
||||
std::string mContentType;
|
||||
};
|
||||
|
||||
|
||||
} // end namespace LLCore
|
||||
|
||||
#endif // _LLCORE_HTTP_RESPONSE_H_
|
||||
|
|
@ -0,0 +1,175 @@
|
|||
/**
|
||||
* @file llcorehttp_test
|
||||
* @brief Main test runner
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include "llcorehttp_test.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
// These are not the right way in viewer for some reason:
|
||||
// #include <tut/tut.hpp>
|
||||
// #include <tut/tut_reporter.hpp>
|
||||
// This works:
|
||||
#include "../test/lltut.h"
|
||||
|
||||
// Pull in each of the test sets
|
||||
#include "test_bufferarray.hpp"
|
||||
#include "test_bufferstream.hpp"
|
||||
#include "test_httpstatus.hpp"
|
||||
#include "test_refcounted.hpp"
|
||||
#include "test_httpoperation.hpp"
|
||||
#include "test_httprequest.hpp"
|
||||
#include "test_httpheaders.hpp"
|
||||
#include "test_httprequestqueue.hpp"
|
||||
|
||||
#include "llproxy.h"
|
||||
|
||||
unsigned long ssl_thread_id_callback(void);
|
||||
void ssl_locking_callback(int mode, int type, const char * file, int line);
|
||||
|
||||
#if 0 // lltut provides main and runner
|
||||
|
||||
namespace tut
|
||||
{
|
||||
test_runner_singleton runner;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
curl_global_init(CURL_GLOBAL_ALL);
|
||||
|
||||
// *FIXME: Need threaded/SSL curl setup here.
|
||||
|
||||
tut::reporter reporter;
|
||||
|
||||
tut::runner.get().set_callback(&reporter);
|
||||
tut::runner.get().run_tests();
|
||||
return !reporter.all_ok();
|
||||
|
||||
curl_global_cleanup();
|
||||
}
|
||||
|
||||
#endif // 0
|
||||
|
||||
int ssl_mutex_count(0);
|
||||
LLCoreInt::HttpMutex ** ssl_mutex_list = NULL;
|
||||
|
||||
void 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);
|
||||
}
|
||||
|
||||
LLProxy::getInstance();
|
||||
}
|
||||
|
||||
|
||||
void term_curl()
|
||||
{
|
||||
LLProxy::cleanupClass();
|
||||
|
||||
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 ssl_thread_id_callback(void)
|
||||
{
|
||||
#if defined(WIN32)
|
||||
return (unsigned long) GetCurrentThread();
|
||||
#else
|
||||
return (unsigned long) pthread_self();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::string get_base_url()
|
||||
{
|
||||
const char * env(getenv("LL_TEST_PORT"));
|
||||
|
||||
if (! env)
|
||||
{
|
||||
std::cerr << "LL_TEST_PORT environment variable missing." << std::endl;
|
||||
std::cerr << "Test expects to run in test_llcorehttp_peer.py script." << std::endl;
|
||||
tut::ensure("LL_TEST_PORT set in environment", NULL != env);
|
||||
}
|
||||
|
||||
int port(atoi(env));
|
||||
std::ostringstream out;
|
||||
out << "http://localhost:" << port << "/";
|
||||
return out.str();
|
||||
}
|
||||
|
||||
|
||||
void stop_thread(LLCore::HttpRequest * req)
|
||||
{
|
||||
if (req)
|
||||
{
|
||||
req->requestStopThread(NULL);
|
||||
|
||||
int count = 0;
|
||||
int limit = 10;
|
||||
while (count++ < limit && ! HttpService::isStopped())
|
||||
{
|
||||
req->update(1000);
|
||||
usleep(100000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
/**
|
||||
* @file llcorehttp_test.h
|
||||
* @brief Main test runner
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, 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 _LLCOREHTTP_TEST_H_
|
||||
#define _LLCOREHTTP_TEST_H_
|
||||
|
||||
#include "linden_common.h" // Modifies curl interfaces
|
||||
|
||||
#include <curl/curl.h>
|
||||
#include <openssl/crypto.h>
|
||||
#include <string>
|
||||
|
||||
#include "httprequest.h"
|
||||
|
||||
// Initialization and cleanup for libcurl. Mainly provides
|
||||
// a mutex callback for SSL and a thread ID hash for libcurl.
|
||||
// If you don't use these (or equivalent) and do use libcurl,
|
||||
// you'll see stalls and other anomalies when performing curl
|
||||
// operations.
|
||||
extern void init_curl();
|
||||
extern void term_curl();
|
||||
extern std::string get_base_url();
|
||||
extern void stop_thread(LLCore::HttpRequest * req);
|
||||
|
||||
class ScopedCurlInit
|
||||
{
|
||||
public:
|
||||
ScopedCurlInit()
|
||||
{
|
||||
init_curl();
|
||||
}
|
||||
|
||||
~ScopedCurlInit()
|
||||
{
|
||||
term_curl();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif // _LLCOREHTTP_TEST_H_
|
||||
|
|
@ -0,0 +1,184 @@
|
|||
/**
|
||||
* @file test_allocator.cpp
|
||||
* @brief quick and dirty allocator for tracking memory allocations
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include "test_allocator.h"
|
||||
|
||||
#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1050
|
||||
#include <libkern/OSAtomic.h>
|
||||
#elif defined(_MSC_VER)
|
||||
#include <Windows.h>
|
||||
#elif (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__ ) > 40100
|
||||
// atomic extensions are built into GCC on posix platforms
|
||||
#endif
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <new>
|
||||
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
|
||||
#if defined(WIN32)
|
||||
#define THROW_BAD_ALLOC() _THROW1(std::bad_alloc)
|
||||
#define THROW_NOTHING() _THROW0()
|
||||
#else
|
||||
#define THROW_BAD_ALLOC() throw(std::bad_alloc)
|
||||
#define THROW_NOTHING() throw()
|
||||
#endif
|
||||
|
||||
|
||||
struct BlockHeader
|
||||
{
|
||||
struct Block * next;
|
||||
std::size_t size;
|
||||
bool in_use;
|
||||
};
|
||||
|
||||
struct Block
|
||||
{
|
||||
BlockHeader hdr;
|
||||
unsigned char data[1];
|
||||
};
|
||||
|
||||
#define TRACE_MSG(val) std::cout << __FUNCTION__ << "(" << val << ") [" << __FILE__ << ":" << __LINE__ << "]" << std::endl;
|
||||
|
||||
static unsigned char MemBuf[ 4096 * 1024 ];
|
||||
Block * pNext = static_cast<Block *>(static_cast<void *>(MemBuf));
|
||||
volatile std::size_t MemTotal = 0;
|
||||
|
||||
// cross-platform compare and swap operation
|
||||
static bool CAS(void * volatile * ptr, void * expected, void * new_value)
|
||||
{
|
||||
#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1050
|
||||
return OSAtomicCompareAndSwapPtr( expected, new_value, ptr );
|
||||
#elif defined(_MSC_VER)
|
||||
return expected == InterlockedCompareExchangePointer( ptr, new_value, expected );
|
||||
#elif (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__ ) > 40100
|
||||
return __sync_bool_compare_and_swap( ptr, expected, new_value );
|
||||
#endif
|
||||
}
|
||||
|
||||
static void * GetMem(std::size_t size)
|
||||
{
|
||||
// TRACE_MSG(size);
|
||||
volatile Block * pBlock = NULL;
|
||||
volatile Block * pNewNext = NULL;
|
||||
|
||||
// do a lock-free update of the global next pointer
|
||||
do
|
||||
{
|
||||
pBlock = pNext;
|
||||
pNewNext = (volatile Block *)(pBlock->data + size);
|
||||
|
||||
} while(! CAS((void * volatile *) &pNext, (void *) pBlock, (void *) pNewNext));
|
||||
|
||||
// if we get here, we safely carved out a block of memory in the
|
||||
// memory pool...
|
||||
|
||||
// initialize our block
|
||||
pBlock->hdr.next = (Block *)(pBlock->data + size);
|
||||
pBlock->hdr.size = size;
|
||||
pBlock->hdr.in_use = true;
|
||||
memset((void *) pBlock->data, 0, pBlock->hdr.size);
|
||||
|
||||
// do a lock-free update of the global memory total
|
||||
volatile size_t total = 0;
|
||||
volatile size_t new_total = 0;
|
||||
do
|
||||
{
|
||||
total = MemTotal;
|
||||
new_total = total + size;
|
||||
|
||||
} while (! CAS((void * volatile *) &MemTotal, (void *) total, (void *) new_total));
|
||||
|
||||
return (void *) pBlock->data;
|
||||
}
|
||||
|
||||
|
||||
static void FreeMem(void * p)
|
||||
{
|
||||
// get the pointer to the block record
|
||||
Block * pBlock = (Block *)((unsigned char *) p - sizeof(BlockHeader));
|
||||
|
||||
// TRACE_MSG(pBlock->hdr.size);
|
||||
bool * cur_in_use = &(pBlock->hdr.in_use);
|
||||
volatile bool in_use = false;
|
||||
bool new_in_use = false;
|
||||
do
|
||||
{
|
||||
in_use = pBlock->hdr.in_use;
|
||||
} while (! CAS((void * volatile *) cur_in_use, (void *) in_use, (void *) new_in_use));
|
||||
|
||||
// do a lock-free update of the global memory total
|
||||
volatile size_t total = 0;
|
||||
volatile size_t new_total = 0;
|
||||
do
|
||||
{
|
||||
total = MemTotal;
|
||||
new_total = total - pBlock->hdr.size;
|
||||
} while (! CAS((void * volatile *)&MemTotal, (void *) total, (void *) new_total));
|
||||
}
|
||||
|
||||
|
||||
std::size_t GetMemTotal()
|
||||
{
|
||||
return MemTotal;
|
||||
}
|
||||
|
||||
|
||||
void * operator new(std::size_t size) THROW_BAD_ALLOC()
|
||||
{
|
||||
return GetMem( size );
|
||||
}
|
||||
|
||||
|
||||
void * operator new[](std::size_t size) THROW_BAD_ALLOC()
|
||||
{
|
||||
return GetMem( size );
|
||||
}
|
||||
|
||||
|
||||
void operator delete(void * p) THROW_NOTHING()
|
||||
{
|
||||
if (p)
|
||||
{
|
||||
FreeMem( p );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void operator delete[](void * p) THROW_NOTHING()
|
||||
{
|
||||
if (p)
|
||||
{
|
||||
FreeMem( p );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
/**
|
||||
* @file test_allocator.h
|
||||
* @brief quick and dirty allocator for tracking memory allocations
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, 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 TEST_ALLOCATOR_H
|
||||
#define TEST_ALLOCATOR_H
|
||||
|
||||
#include <cstdlib>
|
||||
#include <new>
|
||||
|
||||
size_t GetMemTotal();
|
||||
#if defined(WIN32)
|
||||
void * operator new(std::size_t size) _THROW1(std::bad_alloc);
|
||||
void * operator new[](std::size_t size) _THROW1(std::bad_alloc);
|
||||
void operator delete(void * p) _THROW0();
|
||||
void operator delete[](void * p) _THROW0();
|
||||
#else
|
||||
void * operator new(std::size_t size) throw (std::bad_alloc);
|
||||
void * operator new[](std::size_t size) throw (std::bad_alloc);
|
||||
void operator delete(void * p) throw ();
|
||||
void operator delete[](void * p) throw ();
|
||||
#endif
|
||||
|
||||
#endif // TEST_ALLOCATOR_H
|
||||
|
||||
|
|
@ -0,0 +1,432 @@
|
|||
/**
|
||||
* @file test_bufferarray.hpp
|
||||
* @brief unit tests for the LLCore::BufferArray class
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, 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 TEST_LLCORE_BUFFER_ARRAY_H_
|
||||
#define TEST_LLCORE_BUFFER_ARRAY_H_
|
||||
|
||||
#include "bufferarray.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "test_allocator.h"
|
||||
|
||||
|
||||
using namespace LLCore;
|
||||
|
||||
|
||||
|
||||
namespace tut
|
||||
{
|
||||
|
||||
struct BufferArrayTestData
|
||||
{
|
||||
// the test objects inherit from this so the member functions and variables
|
||||
// can be referenced directly inside of the test functions.
|
||||
size_t mMemTotal;
|
||||
};
|
||||
|
||||
typedef test_group<BufferArrayTestData> BufferArrayTestGroupType;
|
||||
typedef BufferArrayTestGroupType::object BufferArrayTestObjectType;
|
||||
BufferArrayTestGroupType BufferArrayTestGroup("BufferArray Tests");
|
||||
|
||||
template <> template <>
|
||||
void BufferArrayTestObjectType::test<1>()
|
||||
{
|
||||
set_test_name("BufferArray construction");
|
||||
|
||||
// record the total amount of dynamically allocated memory
|
||||
mMemTotal = GetMemTotal();
|
||||
|
||||
// create a new ref counted object with an implicit reference
|
||||
BufferArray * ba = new BufferArray();
|
||||
ensure("One ref on construction of BufferArray", ba->getRefCount() == 1);
|
||||
ensure("Memory being used", mMemTotal < GetMemTotal());
|
||||
ensure("Nothing in BA", 0 == ba->size());
|
||||
|
||||
// Try to read
|
||||
char buffer[20];
|
||||
size_t read_len(ba->read(0, buffer, sizeof(buffer)));
|
||||
ensure("Read returns empty", 0 == read_len);
|
||||
|
||||
// release the implicit reference, causing the object to be released
|
||||
ba->release();
|
||||
|
||||
// make sure we didn't leak any memory
|
||||
ensure(mMemTotal == GetMemTotal());
|
||||
}
|
||||
|
||||
template <> template <>
|
||||
void BufferArrayTestObjectType::test<2>()
|
||||
{
|
||||
set_test_name("BufferArray single write");
|
||||
|
||||
// record the total amount of dynamically allocated memory
|
||||
mMemTotal = GetMemTotal();
|
||||
|
||||
// create a new ref counted object with an implicit reference
|
||||
BufferArray * ba = new BufferArray();
|
||||
|
||||
// write some data to the buffer
|
||||
char str1[] = "abcdefghij";
|
||||
char buffer[256];
|
||||
|
||||
size_t len = ba->write(0, str1, strlen(str1));
|
||||
ensure("Wrote length correct", strlen(str1) == len);
|
||||
ensure("Recorded size correct", strlen(str1) == ba->size());
|
||||
|
||||
// read some data back
|
||||
memset(buffer, 'X', sizeof(buffer));
|
||||
len = ba->read(2, buffer, 2);
|
||||
ensure("Read length correct", 2 == len);
|
||||
ensure("Read content correct", 'c' == buffer[0] && 'd' == buffer[1]);
|
||||
ensure("Read didn't overwrite", 'X' == buffer[2]);
|
||||
|
||||
// release the implicit reference, causing the object to be released
|
||||
ba->release();
|
||||
|
||||
// make sure we didn't leak any memory
|
||||
ensure(mMemTotal == GetMemTotal());
|
||||
}
|
||||
|
||||
|
||||
template <> template <>
|
||||
void BufferArrayTestObjectType::test<3>()
|
||||
{
|
||||
set_test_name("BufferArray multiple writes");
|
||||
|
||||
// record the total amount of dynamically allocated memory
|
||||
mMemTotal = GetMemTotal();
|
||||
|
||||
// create a new ref counted object with an implicit reference
|
||||
BufferArray * ba = new BufferArray();
|
||||
|
||||
// write some data to the buffer
|
||||
char str1[] = "abcdefghij";
|
||||
size_t str1_len(strlen(str1));
|
||||
char buffer[256];
|
||||
|
||||
size_t len = ba->write(0, str1, str1_len);
|
||||
ensure("Wrote length correct", str1_len == len);
|
||||
ensure("Recorded size correct", str1_len == ba->size());
|
||||
|
||||
// again...
|
||||
len = ba->write(str1_len, str1, strlen(str1));
|
||||
ensure("Wrote length correct", str1_len == len);
|
||||
ensure("Recorded size correct", (2 * str1_len) == ba->size());
|
||||
|
||||
// read some data back
|
||||
memset(buffer, 'X', sizeof(buffer));
|
||||
len = ba->read(8, buffer, 4);
|
||||
ensure("Read length correct", 4 == len);
|
||||
ensure("Read content correct", 'i' == buffer[0] && 'j' == buffer[1]);
|
||||
ensure("Read content correct", 'a' == buffer[2] && 'b' == buffer[3]);
|
||||
ensure("Read didn't overwrite", 'X' == buffer[4]);
|
||||
|
||||
// Read whole thing
|
||||
memset(buffer, 'X', sizeof(buffer));
|
||||
len = ba->read(0, buffer, sizeof(buffer));
|
||||
ensure("Read length correct", (2 * str1_len) == len);
|
||||
ensure("Read content correct (3)", 0 == strncmp(buffer, str1, str1_len));
|
||||
ensure("Read content correct (4)", 0 == strncmp(&buffer[str1_len], str1, str1_len));
|
||||
ensure("Read didn't overwrite (5)", 'X' == buffer[2 * str1_len]);
|
||||
|
||||
// release the implicit reference, causing the object to be released
|
||||
ba->release();
|
||||
|
||||
// make sure we didn't leak any memory
|
||||
ensure(mMemTotal == GetMemTotal());
|
||||
}
|
||||
|
||||
template <> template <>
|
||||
void BufferArrayTestObjectType::test<4>()
|
||||
{
|
||||
set_test_name("BufferArray overwriting");
|
||||
|
||||
// record the total amount of dynamically allocated memory
|
||||
mMemTotal = GetMemTotal();
|
||||
|
||||
// create a new ref counted object with an implicit reference
|
||||
BufferArray * ba = new BufferArray();
|
||||
|
||||
// write some data to the buffer
|
||||
char str1[] = "abcdefghij";
|
||||
size_t str1_len(strlen(str1));
|
||||
char str2[] = "ABCDEFGHIJ";
|
||||
char buffer[256];
|
||||
|
||||
size_t len = ba->write(0, str1, str1_len);
|
||||
ensure("Wrote length correct", str1_len == len);
|
||||
ensure("Recorded size correct", str1_len == ba->size());
|
||||
|
||||
// again...
|
||||
len = ba->write(str1_len, str1, strlen(str1));
|
||||
ensure("Wrote length correct", str1_len == len);
|
||||
ensure("Recorded size correct", (2 * str1_len) == ba->size());
|
||||
|
||||
// reposition and overwrite
|
||||
len = ba->write(8, str2, 4);
|
||||
ensure("Overwrite length correct", 4 == len);
|
||||
|
||||
// Leave position and read verifying content (stale really from seek() days)
|
||||
memset(buffer, 'X', sizeof(buffer));
|
||||
len = ba->read(12, buffer, 4);
|
||||
ensure("Read length correct", 4 == len);
|
||||
ensure("Read content correct", 'c' == buffer[0] && 'd' == buffer[1]);
|
||||
ensure("Read content correct.2", 'e' == buffer[2] && 'f' == buffer[3]);
|
||||
ensure("Read didn't overwrite", 'X' == buffer[4]);
|
||||
|
||||
// reposition and check
|
||||
len = ba->read(6, buffer, 8);
|
||||
ensure("Read length correct.2", 8 == len);
|
||||
ensure("Read content correct.3", 'g' == buffer[0] && 'h' == buffer[1]);
|
||||
ensure("Read content correct.4", 'A' == buffer[2] && 'B' == buffer[3]);
|
||||
ensure("Read content correct.5", 'C' == buffer[4] && 'D' == buffer[5]);
|
||||
ensure("Read content correct.6", 'c' == buffer[6] && 'd' == buffer[7]);
|
||||
ensure("Read didn't overwrite.7", 'X' == buffer[8]);
|
||||
|
||||
// release the implicit reference, causing the object to be released
|
||||
ba->release();
|
||||
|
||||
// make sure we didn't leak any memory
|
||||
ensure(mMemTotal == GetMemTotal());
|
||||
}
|
||||
|
||||
template <> template <>
|
||||
void BufferArrayTestObjectType::test<5>()
|
||||
{
|
||||
set_test_name("BufferArray multiple writes - sequential reads");
|
||||
|
||||
// record the total amount of dynamically allocated memory
|
||||
mMemTotal = GetMemTotal();
|
||||
|
||||
// create a new ref counted object with an implicit reference
|
||||
BufferArray * ba = new BufferArray();
|
||||
|
||||
// write some data to the buffer
|
||||
char str1[] = "abcdefghij";
|
||||
size_t str1_len(strlen(str1));
|
||||
char buffer[256];
|
||||
|
||||
size_t len = ba->write(0, str1, str1_len);
|
||||
ensure("Wrote length correct", str1_len == len);
|
||||
ensure("Recorded size correct", str1_len == ba->size());
|
||||
|
||||
// again...
|
||||
len = ba->write(str1_len, str1, str1_len);
|
||||
ensure("Wrote length correct", str1_len == len);
|
||||
ensure("Recorded size correct", (2 * str1_len) == ba->size());
|
||||
|
||||
// read some data back
|
||||
memset(buffer, 'X', sizeof(buffer));
|
||||
len = ba->read(8, buffer, 4);
|
||||
ensure("Read length correct", 4 == len);
|
||||
ensure("Read content correct", 'i' == buffer[0] && 'j' == buffer[1]);
|
||||
ensure("Read content correct.2", 'a' == buffer[2] && 'b' == buffer[3]);
|
||||
ensure("Read didn't overwrite", 'X' == buffer[4]);
|
||||
|
||||
// Read some more without repositioning
|
||||
memset(buffer, 'X', sizeof(buffer));
|
||||
len = ba->read(12, buffer, sizeof(buffer));
|
||||
ensure("Read length correct", (str1_len - 2) == len);
|
||||
ensure("Read content correct.3", 0 == strncmp(buffer, str1+2, str1_len-2));
|
||||
ensure("Read didn't overwrite.2", 'X' == buffer[str1_len-1]);
|
||||
|
||||
// release the implicit reference, causing the object to be released
|
||||
ba->release();
|
||||
|
||||
// make sure we didn't leak any memory
|
||||
ensure(mMemTotal == GetMemTotal());
|
||||
}
|
||||
|
||||
template <> template <>
|
||||
void BufferArrayTestObjectType::test<6>()
|
||||
{
|
||||
set_test_name("BufferArray overwrite spanning blocks and appending");
|
||||
|
||||
// record the total amount of dynamically allocated memory
|
||||
mMemTotal = GetMemTotal();
|
||||
|
||||
// create a new ref counted object with an implicit reference
|
||||
BufferArray * ba = new BufferArray();
|
||||
|
||||
// write some data to the buffer
|
||||
char str1[] = "abcdefghij";
|
||||
size_t str1_len(strlen(str1));
|
||||
char str2[] = "ABCDEFGHIJKLMNOPQRST";
|
||||
size_t str2_len(strlen(str2));
|
||||
char buffer[256];
|
||||
|
||||
size_t len = ba->write(0, str1, str1_len);
|
||||
ensure("Wrote length correct", str1_len == len);
|
||||
ensure("Recorded size correct", str1_len == ba->size());
|
||||
|
||||
// again...
|
||||
len = ba->write(str1_len, str1, strlen(str1));
|
||||
ensure("Wrote length correct", str1_len == len);
|
||||
ensure("Recorded size correct", (2 * str1_len) == ba->size());
|
||||
|
||||
// reposition and overwrite
|
||||
len = ba->write(8, str2, str2_len);
|
||||
ensure("Overwrite length correct", str2_len == len);
|
||||
|
||||
// Leave position and read verifying content
|
||||
memset(buffer, 'X', sizeof(buffer));
|
||||
len = ba->read(8 + str2_len, buffer, 0);
|
||||
ensure("Read length correct", 0 == len);
|
||||
ensure("Read didn't overwrite", 'X' == buffer[0]);
|
||||
|
||||
// reposition and check
|
||||
len = ba->read(0, buffer, sizeof(buffer));
|
||||
ensure("Read length correct.2", (str1_len + str2_len - 2) == len);
|
||||
ensure("Read content correct", 0 == strncmp(buffer, str1, str1_len-2));
|
||||
ensure("Read content correct.2", 0 == strncmp(buffer+str1_len-2, str2, str2_len));
|
||||
ensure("Read didn't overwrite.2", 'X' == buffer[str1_len + str2_len - 2]);
|
||||
|
||||
// release the implicit reference, causing the object to be released
|
||||
ba->release();
|
||||
|
||||
// make sure we didn't leak any memory
|
||||
ensure("All memory released", mMemTotal == GetMemTotal());
|
||||
}
|
||||
|
||||
template <> template <>
|
||||
void BufferArrayTestObjectType::test<7>()
|
||||
{
|
||||
set_test_name("BufferArray overwrite spanning blocks and sequential writes");
|
||||
|
||||
// record the total amount of dynamically allocated memory
|
||||
mMemTotal = GetMemTotal();
|
||||
|
||||
// create a new ref counted object with an implicit reference
|
||||
BufferArray * ba = new BufferArray();
|
||||
|
||||
// write some data to the buffer
|
||||
char str1[] = "abcdefghij";
|
||||
size_t str1_len(strlen(str1));
|
||||
char str2[] = "ABCDEFGHIJKLMNOPQRST";
|
||||
size_t str2_len(strlen(str2));
|
||||
char buffer[256];
|
||||
|
||||
// 2x str1
|
||||
size_t len = ba->write(0, str1, str1_len);
|
||||
len = ba->write(str1_len, str1, str1_len);
|
||||
|
||||
// reposition and overwrite
|
||||
len = ba->write(6, str2, 2);
|
||||
ensure("Overwrite length correct", 2 == len);
|
||||
|
||||
len = ba->write(8, str2, 2);
|
||||
ensure("Overwrite length correct.2", 2 == len);
|
||||
|
||||
len = ba->write(10, str2, 2);
|
||||
ensure("Overwrite length correct.3", 2 == len);
|
||||
|
||||
// append some data
|
||||
len = ba->append(str2, str2_len);
|
||||
ensure("Append length correct", str2_len == len);
|
||||
|
||||
// append some more
|
||||
void * out_buf(ba->appendBufferAlloc(str1_len));
|
||||
memcpy(out_buf, str1, str1_len);
|
||||
|
||||
// And some final writes
|
||||
len = ba->write(3 * str1_len + str2_len, str2, 2);
|
||||
ensure("Write length correct.2", 2 == len);
|
||||
|
||||
// Check contents
|
||||
memset(buffer, 'X', sizeof(buffer));
|
||||
len = ba->read(0, buffer, sizeof(buffer));
|
||||
ensure("Final buffer length correct", (3 * str1_len + str2_len + 2) == len);
|
||||
ensure("Read content correct", 0 == strncmp(buffer, str1, 6));
|
||||
ensure("Read content correct.2", 0 == strncmp(buffer + 6, str2, 2));
|
||||
ensure("Read content correct.3", 0 == strncmp(buffer + 8, str2, 2));
|
||||
ensure("Read content correct.4", 0 == strncmp(buffer + 10, str2, 2));
|
||||
ensure("Read content correct.5", 0 == strncmp(buffer + str1_len + 2, str1 + 2, str1_len - 2));
|
||||
ensure("Read content correct.6", 0 == strncmp(buffer + str1_len + str1_len, str2, str2_len));
|
||||
ensure("Read content correct.7", 0 == strncmp(buffer + str1_len + str1_len + str2_len, str1, str1_len));
|
||||
ensure("Read content correct.8", 0 == strncmp(buffer + str1_len + str1_len + str2_len + str1_len, str2, 2));
|
||||
ensure("Read didn't overwrite", 'X' == buffer[str1_len + str1_len + str2_len + str1_len + 2]);
|
||||
|
||||
// release the implicit reference, causing the object to be released
|
||||
ba->release();
|
||||
|
||||
// make sure we didn't leak any memory
|
||||
ensure("All memory released", mMemTotal == GetMemTotal());
|
||||
}
|
||||
|
||||
template <> template <>
|
||||
void BufferArrayTestObjectType::test<8>()
|
||||
{
|
||||
set_test_name("BufferArray zero-length appendBufferAlloc");
|
||||
|
||||
// record the total amount of dynamically allocated memory
|
||||
mMemTotal = GetMemTotal();
|
||||
|
||||
// create a new ref counted object with an implicit reference
|
||||
BufferArray * ba = new BufferArray();
|
||||
|
||||
// write some data to the buffer
|
||||
char str1[] = "abcdefghij";
|
||||
size_t str1_len(strlen(str1));
|
||||
char str2[] = "ABCDEFGHIJKLMNOPQRST";
|
||||
size_t str2_len(strlen(str2));
|
||||
char buffer[256];
|
||||
|
||||
// 2x str1
|
||||
size_t len = ba->write(0, str1, str1_len);
|
||||
len = ba->write(str1_len, str1, str1_len);
|
||||
|
||||
// zero-length allocate (we allow this with a valid pointer returned)
|
||||
void * out_buf(ba->appendBufferAlloc(0));
|
||||
ensure("Buffer from zero-length appendBufferAlloc non-NULL", NULL != out_buf);
|
||||
|
||||
// Do it again
|
||||
void * out_buf2(ba->appendBufferAlloc(0));
|
||||
ensure("Buffer from zero-length appendBufferAlloc non-NULL.2", NULL != out_buf2);
|
||||
ensure("Two zero-length appendBufferAlloc buffers distinct", out_buf != out_buf2);
|
||||
|
||||
// And some final writes
|
||||
len = ba->write(2 * str1_len, str2, str2_len);
|
||||
|
||||
// Check contents
|
||||
memset(buffer, 'X', sizeof(buffer));
|
||||
len = ba->read(0, buffer, sizeof(buffer));
|
||||
ensure("Final buffer length correct", (2 * str1_len + str2_len) == len);
|
||||
ensure("Read content correct.1", 0 == strncmp(buffer, str1, str1_len));
|
||||
ensure("Read content correct.2", 0 == strncmp(buffer + str1_len, str1, str1_len));
|
||||
ensure("Read content correct.3", 0 == strncmp(buffer + str1_len + str1_len, str2, str2_len));
|
||||
ensure("Read didn't overwrite", 'X' == buffer[str1_len + str1_len + str2_len]);
|
||||
|
||||
// release the implicit reference, causing the object to be released
|
||||
ba->release();
|
||||
|
||||
// make sure we didn't leak any memory
|
||||
ensure("All memory released", mMemTotal == GetMemTotal());
|
||||
}
|
||||
|
||||
} // end namespace tut
|
||||
|
||||
|
||||
#endif // TEST_LLCORE_BUFFER_ARRAY_H_
|
||||
|
|
@ -0,0 +1,304 @@
|
|||
/**
|
||||
* @file test_bufferstream.hpp
|
||||
* @brief unit tests for the LLCore::BufferArrayStreamBuf/BufferArrayStream classes
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, 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 TEST_LLCORE_BUFFER_STREAM_H_
|
||||
#define TEST_LLCORE_BUFFER_STREAM_H_
|
||||
|
||||
#include "bufferstream.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "test_allocator.h"
|
||||
#include "llsd.h"
|
||||
#include "llsdserialize.h"
|
||||
|
||||
|
||||
using namespace LLCore;
|
||||
|
||||
|
||||
namespace tut
|
||||
{
|
||||
|
||||
struct BufferStreamTestData
|
||||
{
|
||||
// the test objects inherit from this so the member functions and variables
|
||||
// can be referenced directly inside of the test functions.
|
||||
size_t mMemTotal;
|
||||
};
|
||||
|
||||
typedef test_group<BufferStreamTestData> BufferStreamTestGroupType;
|
||||
typedef BufferStreamTestGroupType::object BufferStreamTestObjectType;
|
||||
BufferStreamTestGroupType BufferStreamTestGroup("BufferStream Tests");
|
||||
typedef BufferArrayStreamBuf::traits_type tst_traits_t;
|
||||
|
||||
|
||||
template <> template <>
|
||||
void BufferStreamTestObjectType::test<1>()
|
||||
{
|
||||
set_test_name("BufferArrayStreamBuf construction with NULL BufferArray");
|
||||
|
||||
// record the total amount of dynamically allocated memory
|
||||
mMemTotal = GetMemTotal();
|
||||
|
||||
// create a new ref counted object with an implicit reference
|
||||
BufferArrayStreamBuf * bsb = new BufferArrayStreamBuf(NULL);
|
||||
ensure("Memory being used", mMemTotal < GetMemTotal());
|
||||
|
||||
// Not much will work with a NULL
|
||||
ensure("underflow() on NULL fails", tst_traits_t::eof() == bsb->underflow());
|
||||
ensure("uflow() on NULL fails", tst_traits_t::eof() == bsb->uflow());
|
||||
ensure("pbackfail() on NULL fails", tst_traits_t::eof() == bsb->pbackfail('c'));
|
||||
ensure("showmanyc() on NULL fails", bsb->showmanyc() == -1);
|
||||
ensure("overflow() on NULL fails", tst_traits_t::eof() == bsb->overflow('c'));
|
||||
ensure("xsputn() on NULL fails", bsb->xsputn("blah", 4) == 0);
|
||||
ensure("seekoff() on NULL fails", bsb->seekoff(0, std::ios_base::beg, std::ios_base::in) == std::streampos(-1));
|
||||
|
||||
// release the implicit reference, causing the object to be released
|
||||
delete bsb;
|
||||
bsb = NULL;
|
||||
|
||||
// make sure we didn't leak any memory
|
||||
ensure("Allocated memory returned", mMemTotal == GetMemTotal());
|
||||
}
|
||||
|
||||
|
||||
template <> template <>
|
||||
void BufferStreamTestObjectType::test<2>()
|
||||
{
|
||||
set_test_name("BufferArrayStream construction with NULL BufferArray");
|
||||
|
||||
// record the total amount of dynamically allocated memory
|
||||
mMemTotal = GetMemTotal();
|
||||
|
||||
// create a new ref counted object with an implicit reference
|
||||
BufferArrayStream * bas = new BufferArrayStream(NULL);
|
||||
ensure("Memory being used", mMemTotal < GetMemTotal());
|
||||
|
||||
// Not much will work with a NULL here
|
||||
ensure("eof() is false on NULL", ! bas->eof());
|
||||
ensure("fail() is false on NULL", ! bas->fail());
|
||||
ensure("good() on NULL", bas->good());
|
||||
|
||||
// release the implicit reference, causing the object to be released
|
||||
delete bas;
|
||||
bas = NULL;
|
||||
|
||||
// make sure we didn't leak any memory
|
||||
ensure("Allocated memory returned", mMemTotal == GetMemTotal());
|
||||
}
|
||||
|
||||
|
||||
template <> template <>
|
||||
void BufferStreamTestObjectType::test<3>()
|
||||
{
|
||||
set_test_name("BufferArrayStreamBuf construction with empty BufferArray");
|
||||
|
||||
// record the total amount of dynamically allocated memory
|
||||
mMemTotal = GetMemTotal();
|
||||
|
||||
// create a new ref counted BufferArray with implicit reference
|
||||
BufferArray * ba = new BufferArray;
|
||||
BufferArrayStreamBuf * bsb = new BufferArrayStreamBuf(ba);
|
||||
ensure("Memory being used", mMemTotal < GetMemTotal());
|
||||
|
||||
// I can release my ref on the BA
|
||||
ba->release();
|
||||
ba = NULL;
|
||||
|
||||
// release the implicit reference, causing the object to be released
|
||||
delete bsb;
|
||||
bsb = NULL;
|
||||
|
||||
// make sure we didn't leak any memory
|
||||
ensure("Allocated memory returned", mMemTotal == GetMemTotal());
|
||||
}
|
||||
|
||||
|
||||
template <> template <>
|
||||
void BufferStreamTestObjectType::test<4>()
|
||||
{
|
||||
set_test_name("BufferArrayStream construction with empty BufferArray");
|
||||
|
||||
// record the total amount of dynamically allocated memory
|
||||
mMemTotal = GetMemTotal();
|
||||
|
||||
// create a new ref counted BufferArray with implicit reference
|
||||
BufferArray * ba = new BufferArray;
|
||||
|
||||
{
|
||||
// create a new ref counted object with an implicit reference
|
||||
BufferArrayStream bas(ba);
|
||||
ensure("Memory being used", mMemTotal < GetMemTotal());
|
||||
}
|
||||
|
||||
// release the implicit reference, causing the object to be released
|
||||
ba->release();
|
||||
ba = NULL;
|
||||
|
||||
// make sure we didn't leak any memory
|
||||
ensure("Allocated memory returned", mMemTotal == GetMemTotal());
|
||||
}
|
||||
|
||||
|
||||
template <> template <>
|
||||
void BufferStreamTestObjectType::test<5>()
|
||||
{
|
||||
set_test_name("BufferArrayStreamBuf construction with real BufferArray");
|
||||
|
||||
// record the total amount of dynamically allocated memory
|
||||
mMemTotal = GetMemTotal();
|
||||
|
||||
// create a new ref counted BufferArray with implicit reference
|
||||
BufferArray * ba = new BufferArray;
|
||||
const char * content("This is a string. A fragment.");
|
||||
const size_t c_len(strlen(content));
|
||||
ba->append(content, c_len);
|
||||
|
||||
// Creat an adapter for the BufferArray
|
||||
BufferArrayStreamBuf * bsb = new BufferArrayStreamBuf(ba);
|
||||
ensure("Memory being used", mMemTotal < GetMemTotal());
|
||||
|
||||
// I can release my ref on the BA
|
||||
ba->release();
|
||||
ba = NULL;
|
||||
|
||||
// Various static state
|
||||
ensure("underflow() returns 'T'", bsb->underflow() == 'T');
|
||||
ensure("underflow() returns 'T' again", bsb->underflow() == 'T');
|
||||
ensure("uflow() returns 'T'", bsb->uflow() == 'T');
|
||||
ensure("uflow() returns 'h'", bsb->uflow() == 'h');
|
||||
ensure("pbackfail('i') fails", tst_traits_t::eof() == bsb->pbackfail('i'));
|
||||
ensure("pbackfail('T') fails", tst_traits_t::eof() == bsb->pbackfail('T'));
|
||||
ensure("pbackfail('h') succeeds", bsb->pbackfail('h') == 'h');
|
||||
ensure("showmanyc() is everything but the 'T'", bsb->showmanyc() == (c_len - 1));
|
||||
ensure("overflow() appends", bsb->overflow('c') == 'c');
|
||||
ensure("showmanyc() reflects append", bsb->showmanyc() == (c_len - 1 + 1));
|
||||
ensure("xsputn() appends some more", bsb->xsputn("bla!", 4) == 4);
|
||||
ensure("showmanyc() reflects 2nd append", bsb->showmanyc() == (c_len - 1 + 5));
|
||||
ensure("seekoff() succeeds", bsb->seekoff(0, std::ios_base::beg, std::ios_base::in) == std::streampos(0));
|
||||
ensure("seekoff() succeeds 2", bsb->seekoff(4, std::ios_base::cur, std::ios_base::in) == std::streampos(4));
|
||||
ensure("showmanyc() picks up seekoff", bsb->showmanyc() == (c_len + 5 - 4));
|
||||
ensure("seekoff() succeeds 3", bsb->seekoff(0, std::ios_base::end, std::ios_base::in) == std::streampos(c_len + 4));
|
||||
ensure("pbackfail('!') succeeds", tst_traits_t::eof() == bsb->pbackfail('!'));
|
||||
|
||||
// release the implicit reference, causing the object to be released
|
||||
delete bsb;
|
||||
bsb = NULL;
|
||||
|
||||
// make sure we didn't leak any memory
|
||||
ensure("Allocated memory returned", mMemTotal == GetMemTotal());
|
||||
}
|
||||
|
||||
|
||||
template <> template <>
|
||||
void BufferStreamTestObjectType::test<6>()
|
||||
{
|
||||
set_test_name("BufferArrayStream construction with real BufferArray");
|
||||
|
||||
// record the total amount of dynamically allocated memory
|
||||
mMemTotal = GetMemTotal();
|
||||
|
||||
// create a new ref counted BufferArray with implicit reference
|
||||
BufferArray * ba = new BufferArray;
|
||||
//const char * content("This is a string. A fragment.");
|
||||
//const size_t c_len(strlen(content));
|
||||
//ba->append(content, strlen(content));
|
||||
|
||||
{
|
||||
// Creat an adapter for the BufferArray
|
||||
BufferArrayStream bas(ba);
|
||||
ensure("Memory being used", mMemTotal < GetMemTotal());
|
||||
|
||||
// Basic operations
|
||||
bas << "Hello" << 27 << ".";
|
||||
ensure("BA length 8", ba->size() == 8);
|
||||
|
||||
std::string str;
|
||||
bas >> str;
|
||||
ensure("reads correctly", str == "Hello27.");
|
||||
}
|
||||
|
||||
// release the implicit reference, causing the object to be released
|
||||
ba->release();
|
||||
ba = NULL;
|
||||
|
||||
// make sure we didn't leak any memory
|
||||
// ensure("Allocated memory returned", mMemTotal == GetMemTotal());
|
||||
// static U64 mem = GetMemTotal();
|
||||
}
|
||||
|
||||
|
||||
template <> template <>
|
||||
void BufferStreamTestObjectType::test<7>()
|
||||
{
|
||||
set_test_name("BufferArrayStream with LLSD serialization");
|
||||
|
||||
// record the total amount of dynamically allocated memory
|
||||
mMemTotal = GetMemTotal();
|
||||
|
||||
// create a new ref counted BufferArray with implicit reference
|
||||
BufferArray * ba = new BufferArray;
|
||||
|
||||
{
|
||||
// Creat an adapter for the BufferArray
|
||||
BufferArrayStream bas(ba);
|
||||
ensure("Memory being used", mMemTotal < GetMemTotal());
|
||||
|
||||
// LLSD
|
||||
LLSD llsd = LLSD::emptyMap();
|
||||
|
||||
llsd["int"] = LLSD::Integer(3);
|
||||
llsd["float"] = LLSD::Real(923289.28992);
|
||||
llsd["string"] = LLSD::String("aksjdl;ajsdgfjgfal;sdgjakl;sdfjkl;ajsdfkl;ajsdfkl;jaskl;dfj");
|
||||
|
||||
LLSD llsd_map = LLSD::emptyMap();
|
||||
llsd_map["int"] = LLSD::Integer(-2889);
|
||||
llsd_map["float"] = LLSD::Real(2.37829e32);
|
||||
llsd_map["string"] = LLSD::String("OHIGODHSPDGHOSDHGOPSHDGP");
|
||||
|
||||
llsd["map"] = llsd_map;
|
||||
|
||||
// Serialize it
|
||||
LLSDSerialize::toXML(llsd, bas);
|
||||
|
||||
std::string str;
|
||||
bas >> str;
|
||||
// std::cout << "SERIALIZED LLSD: " << str << std::endl;
|
||||
ensure("Extracted string has reasonable length", str.size() > 60);
|
||||
}
|
||||
|
||||
// release the implicit reference, causing the object to be released
|
||||
ba->release();
|
||||
ba = NULL;
|
||||
|
||||
// make sure we didn't leak any memory
|
||||
// ensure("Allocated memory returned", mMemTotal == GetMemTotal());
|
||||
}
|
||||
|
||||
|
||||
} // end namespace tut
|
||||
|
||||
|
||||
#endif // TEST_LLCORE_BUFFER_STREAM_H_
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
/**
|
||||
* @file test_httpheaders.hpp
|
||||
* @brief unit tests for the LLCore::HttpHeaders class
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, 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 TEST_LLCORE_HTTP_HEADERS_H_
|
||||
#define TEST_LLCORE_HTTP_HEADERS_H_
|
||||
|
||||
#include "httpheaders.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "test_allocator.h"
|
||||
|
||||
|
||||
using namespace LLCoreInt;
|
||||
|
||||
|
||||
|
||||
namespace tut
|
||||
{
|
||||
|
||||
struct HttpHeadersTestData
|
||||
{
|
||||
// the test objects inherit from this so the member functions and variables
|
||||
// can be referenced directly inside of the test functions.
|
||||
size_t mMemTotal;
|
||||
};
|
||||
|
||||
typedef test_group<HttpHeadersTestData> HttpHeadersTestGroupType;
|
||||
typedef HttpHeadersTestGroupType::object HttpHeadersTestObjectType;
|
||||
HttpHeadersTestGroupType HttpHeadersTestGroup("HttpHeaders Tests");
|
||||
|
||||
template <> template <>
|
||||
void HttpHeadersTestObjectType::test<1>()
|
||||
{
|
||||
set_test_name("HttpHeaders construction");
|
||||
|
||||
// record the total amount of dynamically allocated memory
|
||||
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);
|
||||
ensure("Memory being used", mMemTotal < GetMemTotal());
|
||||
ensure("Nothing in headers", 0 == headers->mHeaders.size());
|
||||
|
||||
// release the implicit reference, causing the object to be released
|
||||
headers->release();
|
||||
|
||||
// make sure we didn't leak any memory
|
||||
ensure(mMemTotal == GetMemTotal());
|
||||
}
|
||||
|
||||
template <> template <>
|
||||
void HttpHeadersTestObjectType::test<2>()
|
||||
{
|
||||
set_test_name("HttpHeaders construction");
|
||||
|
||||
// record the total amount of dynamically allocated memory
|
||||
mMemTotal = GetMemTotal();
|
||||
|
||||
// create a new ref counted object with an implicit reference
|
||||
HttpHeaders * headers = new HttpHeaders();
|
||||
|
||||
{
|
||||
// Append a few strings
|
||||
std::string str1("Pragma:");
|
||||
headers->mHeaders.push_back(str1);
|
||||
std::string str2("Accept: application/json");
|
||||
headers->mHeaders.push_back(str2);
|
||||
|
||||
ensure("Headers retained", 2 == headers->mHeaders.size());
|
||||
ensure("First is first", headers->mHeaders[0] == str1);
|
||||
ensure("Second is second", headers->mHeaders[1] == str2);
|
||||
}
|
||||
|
||||
// release the implicit reference, causing the object to be released
|
||||
headers->release();
|
||||
|
||||
// make sure we didn't leak any memory
|
||||
ensure(mMemTotal == GetMemTotal());
|
||||
}
|
||||
|
||||
} // end namespace tut
|
||||
|
||||
|
||||
#endif // TEST_LLCORE_HTTP_HEADERS_H_
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
/**
|
||||
* @file test_httpoperation.hpp
|
||||
* @brief unit tests for the LLCore::HttpOperation-derived classes
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, 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 TEST_LLCORE_HTTP_OPERATION_H_
|
||||
#define TEST_LLCORE_HTTP_OPERATION_H_
|
||||
|
||||
#include "_httpoperation.h"
|
||||
#include "httphandler.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "test_allocator.h"
|
||||
|
||||
|
||||
using namespace LLCoreInt;
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
class TestHandler : public LLCore::HttpHandler
|
||||
{
|
||||
public:
|
||||
virtual void onCompleted(HttpHandle, HttpResponse *)
|
||||
{
|
||||
std::cout << "TestHandler::onCompleted() invoked" << std::endl;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
} // end namespace anonymous
|
||||
|
||||
|
||||
namespace tut
|
||||
{
|
||||
struct HttpOperationTestData
|
||||
{
|
||||
// the test objects inherit from this so the member functions and variables
|
||||
// can be referenced directly inside of the test functions.
|
||||
size_t mMemTotal;
|
||||
};
|
||||
|
||||
typedef test_group<HttpOperationTestData> HttpOperationTestGroupType;
|
||||
typedef HttpOperationTestGroupType::object HttpOperationTestObjectType;
|
||||
HttpOperationTestGroupType HttpOperationTestGroup("HttpOperation Tests");
|
||||
|
||||
template <> template <>
|
||||
void HttpOperationTestObjectType::test<1>()
|
||||
{
|
||||
set_test_name("HttpOpNull construction");
|
||||
|
||||
// record the total amount of dynamically allocated memory
|
||||
mMemTotal = GetMemTotal();
|
||||
|
||||
// create a new ref counted object with an implicit reference
|
||||
HttpOpNull * op = new HttpOpNull();
|
||||
ensure(op->getRefCount() == 1);
|
||||
ensure(mMemTotal < GetMemTotal());
|
||||
|
||||
// release the implicit reference, causing the object to be released
|
||||
op->release();
|
||||
|
||||
// make sure we didn't leak any memory
|
||||
ensure(mMemTotal == GetMemTotal());
|
||||
}
|
||||
|
||||
template <> template <>
|
||||
void HttpOperationTestObjectType::test<2>()
|
||||
{
|
||||
set_test_name("HttpOpNull construction with handlers");
|
||||
|
||||
// record the total amount of dynamically allocated memory
|
||||
mMemTotal = GetMemTotal();
|
||||
|
||||
// Get some handlers
|
||||
TestHandler * h1 = new TestHandler();
|
||||
|
||||
// create a new ref counted object with an implicit reference
|
||||
HttpOpNull * op = new HttpOpNull();
|
||||
|
||||
// Add the handlers
|
||||
op->setReplyPath(NULL, h1);
|
||||
|
||||
// Check ref count
|
||||
ensure(op->getRefCount() == 1);
|
||||
|
||||
// release the reference, releasing the operation but
|
||||
// not the handlers.
|
||||
op->release();
|
||||
op = NULL;
|
||||
ensure(mMemTotal != GetMemTotal());
|
||||
|
||||
// release the handlers
|
||||
delete h1;
|
||||
h1 = NULL;
|
||||
|
||||
ensure(mMemTotal == GetMemTotal());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // TEST_LLCORE_HTTP_OPERATION_H_
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,186 @@
|
|||
/**
|
||||
* @file test_httprequestqueue.hpp
|
||||
* @brief unit tests for the LLCore::HttpRequestQueue class
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, 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 TEST_LLCORE_HTTP_REQUESTQUEUE_H_
|
||||
#define TEST_LLCORE_HTTP_REQUESTQUEUE_H_
|
||||
|
||||
#include "_httprequestqueue.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "test_allocator.h"
|
||||
#include "_httpoperation.h"
|
||||
|
||||
|
||||
using namespace LLCoreInt;
|
||||
|
||||
|
||||
|
||||
namespace tut
|
||||
{
|
||||
|
||||
struct HttpRequestqueueTestData
|
||||
{
|
||||
// the test objects inherit from this so the member functions and variables
|
||||
// can be referenced directly inside of the test functions.
|
||||
size_t mMemTotal;
|
||||
};
|
||||
|
||||
typedef test_group<HttpRequestqueueTestData> HttpRequestqueueTestGroupType;
|
||||
typedef HttpRequestqueueTestGroupType::object HttpRequestqueueTestObjectType;
|
||||
HttpRequestqueueTestGroupType HttpRequestqueueTestGroup("HttpRequestqueue Tests");
|
||||
|
||||
template <> template <>
|
||||
void HttpRequestqueueTestObjectType::test<1>()
|
||||
{
|
||||
set_test_name("HttpRequestQueue construction");
|
||||
|
||||
// record the total amount of dynamically allocated memory
|
||||
mMemTotal = GetMemTotal();
|
||||
|
||||
// create a new ref counted object with an implicit reference
|
||||
HttpRequestQueue::init();
|
||||
|
||||
ensure("One ref on construction of HttpRequestQueue", HttpRequestQueue::instanceOf()->getRefCount() == 1);
|
||||
ensure("Memory being used", mMemTotal < GetMemTotal());
|
||||
|
||||
// release the implicit reference, causing the object to be released
|
||||
HttpRequestQueue::term();
|
||||
|
||||
// make sure we didn't leak any memory
|
||||
ensure(mMemTotal == GetMemTotal());
|
||||
}
|
||||
|
||||
template <> template <>
|
||||
void HttpRequestqueueTestObjectType::test<2>()
|
||||
{
|
||||
set_test_name("HttpRequestQueue refcount works");
|
||||
|
||||
// record the total amount of dynamically allocated memory
|
||||
mMemTotal = GetMemTotal();
|
||||
|
||||
// create a new ref counted object with an implicit reference
|
||||
HttpRequestQueue::init();
|
||||
|
||||
HttpRequestQueue * rq = HttpRequestQueue::instanceOf();
|
||||
rq->addRef();
|
||||
|
||||
// release the singleton, hold on to the object
|
||||
HttpRequestQueue::term();
|
||||
|
||||
ensure("One ref after term() called", rq->getRefCount() == 1);
|
||||
ensure("Memory being used", mMemTotal < GetMemTotal());
|
||||
|
||||
// Drop ref
|
||||
rq->release();
|
||||
|
||||
// make sure we didn't leak any memory
|
||||
ensure(mMemTotal == GetMemTotal());
|
||||
}
|
||||
|
||||
template <> template <>
|
||||
void HttpRequestqueueTestObjectType::test<3>()
|
||||
{
|
||||
set_test_name("HttpRequestQueue addOp/fetchOp work");
|
||||
|
||||
// record the total amount of dynamically allocated memory
|
||||
mMemTotal = GetMemTotal();
|
||||
|
||||
// create a new ref counted object with an implicit reference
|
||||
HttpRequestQueue::init();
|
||||
|
||||
HttpRequestQueue * rq = HttpRequestQueue::instanceOf();
|
||||
|
||||
HttpOperation * 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();
|
||||
|
||||
op = rq->fetchOp(false);
|
||||
ensure("Better not be two of them", NULL == op);
|
||||
|
||||
// release the singleton, hold on to the object
|
||||
HttpRequestQueue::term();
|
||||
|
||||
// make sure we didn't leak any memory
|
||||
ensure(mMemTotal == GetMemTotal());
|
||||
}
|
||||
|
||||
template <> template <>
|
||||
void HttpRequestqueueTestObjectType::test<4>()
|
||||
{
|
||||
set_test_name("HttpRequestQueue addOp/fetchAll work");
|
||||
|
||||
// record the total amount of dynamically allocated memory
|
||||
mMemTotal = GetMemTotal();
|
||||
|
||||
// create a new ref counted object with an implicit reference
|
||||
HttpRequestQueue::init();
|
||||
|
||||
HttpRequestQueue * rq = HttpRequestQueue::instanceOf();
|
||||
|
||||
HttpOperation * op = new HttpOpNull();
|
||||
rq->addOp(op); // transfer my refcount
|
||||
|
||||
op = new HttpOpNull();
|
||||
rq->addOp(op); // transfer my refcount
|
||||
|
||||
op = new HttpOpNull();
|
||||
rq->addOp(op); // transfer my refcount
|
||||
|
||||
{
|
||||
HttpRequestQueue::OpContainer ops;
|
||||
rq->fetchAll(true, ops); // Potentially hangs the test on failure
|
||||
ensure("Three go in, three come out", 3 == ops.size());
|
||||
|
||||
op = rq->fetchOp(false);
|
||||
ensure("Better not be any more of them", NULL == op);
|
||||
|
||||
// release the singleton, hold on to the object
|
||||
HttpRequestQueue::term();
|
||||
|
||||
// We're still holding onto the ops.
|
||||
ensure(mMemTotal < GetMemTotal());
|
||||
|
||||
// Release them
|
||||
while (! ops.empty())
|
||||
{
|
||||
HttpOperation * op = ops.front();
|
||||
ops.erase(ops.begin());
|
||||
op->release();
|
||||
}
|
||||
}
|
||||
|
||||
// Should be clean
|
||||
ensure("All memory returned", mMemTotal == GetMemTotal());
|
||||
}
|
||||
|
||||
} // end namespace tut
|
||||
|
||||
|
||||
#endif // TEST_LLCORE_HTTP_REQUESTQUEUE_H_
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue