Pull merge from viewer-release

master
Merov Linden 2014-02-24 13:23:39 -08:00
commit 245de340d7
130 changed files with 8392 additions and 2253 deletions

View File

@ -471,3 +471,7 @@ d40c66e410741de7e90b1ed6dac28dd8a2d7e1f6 3.6.8-release
5b54b36862ff8bc3b6935673c9d1c1f22ee8d521 3.6.10-release
2feb70a4cfde43f2898d95ff8fcae3e67805c7c2 3.6.11-release
88bbfd7a6971033f3aa103f3a3500ceb4c73521b 3.6.12-release
0d9b9e50f1a8880e05f15688a9ec7d09e0e81013 3.6.13-release
5d746de933a98ca17887cde2fece80e9c7ab0b98 3.7.0-release
dcb4981ce255841b6083d8f65444b65d5a733a17 3.7.1-release
b842534cb4d76c9ef87676a62b1d2d19e79c015f 3.7.2-release

View File

@ -49,11 +49,13 @@ viewer_channel = "Second Life Test"
# Setup default packaging parameters.
sourceid = ""
additional_packages = "Amazon Desura B C"
additional_packages = "Amazon Desura A B C"
Amazon_sourceid = "1207v_Amazon"
Amazon_viewer_channel_suffix = "Amazon"
Desura_sourceid = "1208_desura"
Desura_viewer_channel_suffix = "Desura"
A_sourceid = "1300_A"
A_viewer_channel_suffix = "A"
B_sourceid = "1301_B"
B_viewer_channel_suffix = "B"
C_sourceid = "1302_C"

View File

@ -282,9 +282,9 @@
<key>archive</key>
<map>
<key>hash</key>
<string>aaea644191807f51051cefa2fac11069</string>
<string>f7d9b6a9c624364389b71209881f39de</string>
<key>url</key>
<string>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/curl-7.21.1-darwin-20110316.tar.bz2</string>
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-curl/rev/280289/arch/Darwin/installer/curl-7.24.0-darwin-20130826.tar.bz2</string>
</map>
<key>name</key>
<string>darwin</string>
@ -294,9 +294,9 @@
<key>archive</key>
<map>
<key>hash</key>
<string>2d9377951d99a1aa4735cea8d4b5aa71</string>
<string>58b7bf45383c1b1bc24afb303b1519c8</string>
<key>url</key>
<string>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/curl-7.21.1-linux-20110316.tar.bz2</string>
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-curl/rev/280289/arch/Linux/installer/curl-7.24.0-linux-20130826.tar.bz2</string>
</map>
<key>name</key>
<string>linux</string>
@ -306,9 +306,9 @@
<key>archive</key>
<map>
<key>hash</key>
<string>fea96aa2a7d513397317194f3d6c979b</string>
<string>8d9ccb0277a26bfe3f346c3c49ce4b58</string>
<key>url</key>
<string>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/curl-7.21.1-windows-20110211.tar.bz2</string>
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-curl/rev/280289/arch/CYGWIN/installer/curl-7.24.0-windows-20130826.tar.bz2</string>
</map>
<key>name</key>
<string>windows</string>

View File

@ -36,24 +36,59 @@ build_dir_CYGWIN()
echo build-vc100
}
viewer_channel_suffix()
{
local package_name="$1"
local suffix_var="${package_name}_viewer_channel_suffix"
local suffix=$(eval "echo \$${suffix_var}")
if [ "$suffix"x = ""x ]
then
echo ""
else
echo "_$suffix"
fi
}
installer_Darwin()
{
ls -1tr "$(build_dir_Darwin ${last_built_variant:-Release})/newview/"*"$additional_package_name"*.dmg 2>/dev/null | sed 1q
local package_name="$1"
local package_dir="$(build_dir_Darwin ${last_built_variant:-Release})/newview/"
local pattern=".*$(viewer_channel_suffix ${package_name})_[0-9]+_[0-9]+_[0-9]+_[0-9]+_i386\\.dmg\$"
# since the additional packages are built after the base package,
# sorting oldest first ensures that the unqualified package is returned
# even if someone makes a qualified name that duplicates the last word of the base name
local package=$(ls -1tr "$package_dir" 2>/dev/null | grep -E "$pattern" | head -n 1)
test "$package"x != ""x && echo "$package_dir/$package"
}
installer_Linux()
{
ls -1tr "$(build_dir_Linux ${last_built_variant:-Release})/newview/"*"$additional_package_name"*.tar.bz2 2>/dev/null | grep -v symbols | sed 1q
local package_name="$1"
local package_dir="$(build_dir_Linux ${last_built_variant:-Release})/newview/"
local pattern=".*$(viewer_channel_suffix ${package_name})_[0-9]+_[0-9]+_[0-9]+_[0-9]+_i686\\.tar\\.bz2\$"
# since the additional packages are built after the base package,
# sorting oldest first ensures that the unqualified package is returned
# even if someone makes a qualified name that duplicates the last word of the base name
package=$(ls -1tr "$package_dir" 2>/dev/null | grep -E "$pattern" | head -n 1)
test "$package"x != ""x && echo "$package_dir/$package"
}
installer_CYGWIN()
{
v=${last_built_variant:-Release}
d=$(build_dir_CYGWIN $v)
if [ -r "$d/newview/$additional_package_name$v/touched.bat" ]
local package_name="$1"
local variant=${last_built_variant:-Release}
local build_dir=$(build_dir_CYGWIN ${variant})
local package_dir
if [ "$package_name"x = ""x ]
then
p=$(sed 's:.*=::' "$d/newview/$additional_package_name$v/touched.bat")
echo "$d/newview/$additional_package_name$v/$p"
package_dir="${build_dir}/newview/${variant}"
else
package_dir="${build_dir}/newview/${package_name}/${variant}"
fi
if [ -r "${package_dir}/touched.bat" ]
then
local package_file=$(sed 's:.*=::' "${package_dir}/touched.bat")
echo "${package_dir}/${package_file}"
fi
}
@ -275,7 +310,7 @@ then
if $build_viewer_deb && [ "$last_built_variant" == "Release" ]
then
begin_section "Build Viewer Debian Package"
local have_private_repo=false
have_private_repo=false
# mangle the changelog
dch --force-bad-version \
--distribution unstable \
@ -362,19 +397,15 @@ then
# Upload additional packages.
for package_id in $additional_packages
do
case $arch in
CYGWIN) export additional_package_name="$package_id/" ;;
*) export additional_package_name=$package_id ;;
esac
package=$(installer_$arch)
package=$(installer_$arch "$package_id")
if [ x"$package" != x ]
then
upload_item installer "$package" binary/octet-stream
upload_item quicklink "$package" binary/octet-stream
else
record_failure "Failed to upload $package_id package ($package::$additional_package_name)."
record_failure "Failed to find additional package for '$package_id'."
fi
done
export additional_package_name=""
case "$last_built_variant" in
Release)
@ -384,6 +415,12 @@ then
upload_item symbolfile "$build_dir/$symbolfile" binary/octet-stream
done
# Upload the actual dependencies used
if [ -r "$build_dir/packages/installed-packages.xml" ]
then
upload_item installer "$build_dir/packages/installed-packages.xml" text/xml
fi
# Upload the llphysicsextensions_tpv package, if one was produced
# *TODO: Make this an upload-extension
if [ -r "$build_dir/llphysicsextensions_package" ]

View File

@ -180,6 +180,9 @@ Ansariel Hiller
MAINT-2368
STORM-1931
MAINT-2773
BUG-3764
STORM-1984
STORM-1979
Aralara Rajal
Arare Chantilly
CHUIBUG-191
@ -307,6 +310,7 @@ Christopher Organiser
Ciaran Laval
Cinder Roxley
BUG-2326
BUG-3863
OPEN-185
STORM-1703
STORM-1948
@ -674,6 +678,19 @@ Jonathan Yap
OPEN-161
STORM-1953
STORM-1957
STORM-1993
STORM-1980
OPEN-113
STORM-1975
STORM-1982
STORM-1975
STORM-1987
STORM-1982
STORM-1992
STORM-1989
STORM-1987
STORM-1986
STORM-1981
Kadah Coba
STORM-1060
STORM-1843
@ -1140,6 +1157,7 @@ snowy Sidran
Sovereign Engineer
MAINT-2334
OPEN-189
OPEN-195
SpacedOut Frye
VWR-34
VWR-45
@ -1259,6 +1277,8 @@ Tofu Buzzard
Tony Kembia
Tonya Souther
STORM-1905
BUG-3875
BUG-3968
Torben Trautman
TouchaHoney Perhaps
TraductoresAnonimos Alter

View File

@ -4,3 +4,4 @@ Wed Nov 7 00:25:19 UTC 2012

View File

@ -72,7 +72,7 @@ class UUID(object):
ip = ''
try:
ip = socket.gethostbyname(socket.gethostname())
except(socket.gaierror):
except(socket.gaierror, socket.error):
# no ip address, so just default to somewhere in 10.x.x.x
ip = '10'
for i in range(3):

View File

@ -135,7 +135,7 @@ int vfs_seek(void *datasource, ogg_int64_t offset, int whence)
origin = -1;
break;
default:
llerrs << "Invalid whence argument to vfs_seek" << llendl;
LL_ERRS("AudioEngine") << "Invalid whence argument to vfs_seek" << LL_ENDL;
return -1;
}
@ -197,12 +197,12 @@ BOOL LLVorbisDecodeState::initDecode()
vfs_callbacks.close_func = vfs_close;
vfs_callbacks.tell_func = vfs_tell;
//llinfos << "Initing decode from vfile: " << mUUID << llendl;
LL_DEBUGS("AudioEngine") << "Initing decode from vfile: " << mUUID << LL_ENDL;
mInFilep = new LLVFile(gVFS, mUUID, LLAssetType::AT_SOUND);
if (!mInFilep || !mInFilep->getSize())
{
llwarns << "unable to open vorbis source vfile for reading" << llendl;
LL_WARNS("AudioEngine") << "unable to open vorbis source vfile for reading" << LL_ENDL;
delete mInFilep;
mInFilep = NULL;
return FALSE;
@ -211,7 +211,7 @@ BOOL LLVorbisDecodeState::initDecode()
int r = ov_open_callbacks(mInFilep, &mVF, NULL, 0, vfs_callbacks);
if(r < 0)
{
llwarns << r << " Input to vorbis decode does not appear to be an Ogg bitstream: " << mUUID << llendl;
LL_WARNS("AudioEngine") << r << " Input to vorbis decode does not appear to be an Ogg bitstream: " << mUUID << LL_ENDL;
return(FALSE);
}
@ -229,36 +229,36 @@ BOOL LLVorbisDecodeState::initDecode()
if( vi->channels < 1 || vi->channels > LLVORBIS_CLIP_MAX_CHANNELS )
{
abort_decode = true;
llwarns << "Bad channel count: " << vi->channels << llendl;
LL_WARNS("AudioEngine") << "Bad channel count: " << vi->channels << LL_ENDL;
}
}
else // !vi
{
abort_decode = true;
llwarns << "No default bitstream found" << llendl;
LL_WARNS("AudioEngine") << "No default bitstream found" << LL_ENDL;
}
if( (size_t)sample_count > LLVORBIS_CLIP_REJECT_SAMPLES ||
(size_t)sample_count <= 0)
{
abort_decode = true;
llwarns << "Illegal sample count: " << sample_count << llendl;
LL_WARNS("AudioEngine") << "Illegal sample count: " << sample_count << LL_ENDL;
}
if( size_guess > LLVORBIS_CLIP_REJECT_SIZE ||
size_guess < 0)
{
abort_decode = true;
llwarns << "Illegal sample size: " << size_guess << llendl;
LL_WARNS("AudioEngine") << "Illegal sample size: " << size_guess << LL_ENDL;
}
if( abort_decode )
{
llwarns << "Canceling initDecode. Bad asset: " << mUUID << llendl;
LL_WARNS("AudioEngine") << "Canceling initDecode. Bad asset: " << mUUID << LL_ENDL;
vorbis_comment* comment = ov_comment(&mVF,-1);
if (comment && comment->vendor)
{
llwarns << "Bad asset encoded by: " << comment->vendor << llendl;
LL_WARNS("AudioEngine") << "Bad asset encoded by: " << comment->vendor << LL_ENDL;
}
delete mInFilep;
mInFilep = NULL;
@ -359,12 +359,12 @@ BOOL LLVorbisDecodeState::decodeSection()
{
if (!mInFilep)
{
llwarns << "No VFS file to decode in vorbis!" << llendl;
LL_WARNS("AudioEngine") << "No VFS file to decode in vorbis!" << LL_ENDL;
return TRUE;
}
if (mDone)
{
// llwarns << "Already done with decode, aborting!" << llendl;
// LL_WARNS("AudioEngine") << "Already done with decode, aborting!" << LL_ENDL;
return TRUE;
}
char pcmout[4096]; /*Flawfinder: ignore*/
@ -377,14 +377,14 @@ BOOL LLVorbisDecodeState::decodeSection()
eof = TRUE;
mDone = TRUE;
mValid = TRUE;
// llinfos << "Vorbis EOF" << llendl;
// LL_INFOS("AudioEngine") << "Vorbis EOF" << LL_ENDL;
}
else if (ret < 0)
{
/* error in the stream. Not a problem, just reporting it in
case we (the app) cares. In this case, we don't. */
llwarns << "BAD vorbis decode in decodeSection." << llendl;
LL_WARNS("AudioEngine") << "BAD vorbis decode in decodeSection." << LL_ENDL;
mValid = FALSE;
mDone = TRUE;
@ -393,7 +393,7 @@ BOOL LLVorbisDecodeState::decodeSection()
}
else
{
// llinfos << "Vorbis read " << ret << "bytes" << llendl;
// LL_INFOS("AudioEngine") << "Vorbis read " << ret << "bytes" << LL_ENDL;
/* we don't bother dealing with sample rate changes, etc, but.
you'll have to*/
std::copy(pcmout, pcmout+ret, std::back_inserter(mWAVBuffer));
@ -405,7 +405,7 @@ BOOL LLVorbisDecodeState::finishDecode()
{
if (!isValid())
{
llwarns << "Bogus vorbis decode state for " << getUUID() << ", aborting!" << llendl;
LL_WARNS("AudioEngine") << "Bogus vorbis decode state for " << getUUID() << ", aborting!" << LL_ENDL;
return TRUE; // We've finished
}
@ -480,7 +480,7 @@ BOOL LLVorbisDecodeState::finishDecode()
if (36 == data_length)
{
llwarns << "BAD Vorbis decode in finishDecode!" << llendl;
LL_WARNS("AudioEngine") << "BAD Vorbis decode in finishDecode!" << LL_ENDL;
mValid = FALSE;
return TRUE; // we've finished
}
@ -497,7 +497,7 @@ BOOL LLVorbisDecodeState::finishDecode()
{
if (mBytesRead == 0)
{
llwarns << "Unable to write file in LLVorbisDecodeState::finishDecode" << llendl;
LL_WARNS("AudioEngine") << "Unable to write file in LLVorbisDecodeState::finishDecode" << LL_ENDL;
mValid = FALSE;
return TRUE; // we've finished
}
@ -515,7 +515,7 @@ BOOL LLVorbisDecodeState::finishDecode()
LLVFile output(gVFS, mUUID, LLAssetType::AT_SOUND_WAV);
output.write(&mWAVBuffer[0], mWAVBuffer.size());
#endif
//llinfos << "Finished decode for " << getUUID() << llendl;
LL_DEBUGS("AudioEngine") << "Finished decode for " << getUUID() << LL_ENDL;
return TRUE;
}
@ -524,7 +524,7 @@ void LLVorbisDecodeState::flushBadFile()
{
if (mInFilep)
{
llwarns << "Flushing bad vorbis file from VFS for " << mUUID << llendl;
LL_WARNS("AudioEngine") << "Flushing bad vorbis file from VFS for " << mUUID << LL_ENDL;
mInFilep->remove();
}
}
@ -568,7 +568,7 @@ void LLAudioDecodeMgr::Impl::processQueue(const F32 num_secs)
if (mCurrentDecodep->isDone() && !mCurrentDecodep->isValid())
{
// We had an error when decoding, abort.
llwarns << mCurrentDecodep->getUUID() << " has invalid vorbis data, aborting decode" << llendl;
LL_WARNS("AudioEngine") << mCurrentDecodep->getUUID() << " has invalid vorbis data, aborting decode" << LL_ENDL;
mCurrentDecodep->flushBadFile();
LLAudioData *adp = gAudiop->getAudioData(mCurrentDecodep->getUUID());
adp->setHasValidData(false);
@ -590,7 +590,7 @@ void LLAudioDecodeMgr::Impl::processQueue(const F32 num_secs)
LLAudioData *adp = gAudiop->getAudioData(mCurrentDecodep->getUUID());
if (!adp)
{
llwarns << "Missing LLAudioData for decode of " << mCurrentDecodep->getUUID() << llendl;
LL_WARNS("AudioEngine") << "Missing LLAudioData for decode of " << mCurrentDecodep->getUUID() << LL_ENDL;
}
else if (mCurrentDecodep->isValid() && mCurrentDecodep->isDone())
{
@ -601,12 +601,12 @@ void LLAudioDecodeMgr::Impl::processQueue(const F32 num_secs)
// At this point, we could see if anyone needs this sound immediately, but
// I'm not sure that there's a reason to - we need to poll all of the playing
// sounds anyway.
//llinfos << "Finished the vorbis decode, now what?" << llendl;
//LL_INFOS("AudioEngine") << "Finished the vorbis decode, now what?" << LL_ENDL;
}
else
{
adp->setHasCompletedDecode(true);
llinfos << "Vorbis decode failed for " << mCurrentDecodep->getUUID() << llendl;
LL_INFOS("AudioEngine") << "Vorbis decode failed for " << mCurrentDecodep->getUUID() << LL_ENDL;
}
mCurrentDecodep = NULL;
}
@ -631,7 +631,7 @@ void LLAudioDecodeMgr::Impl::processQueue(const F32 num_secs)
continue;
}
lldebugs << "Decoding " << uuid << " from audio queue!" << llendl;
lldebugs << "Decoding " << uuid << " from audio queue!" << LL_ENDL;
std::string uuid_str;
std::string d_path;
@ -674,19 +674,19 @@ BOOL LLAudioDecodeMgr::addDecodeRequest(const LLUUID &uuid)
if (gAudiop->hasDecodedFile(uuid))
{
// Already have a decoded version, don't need to decode it.
//llinfos << "addDecodeRequest for " << uuid << " has decoded file already" << llendl;
LL_DEBUGS("AudioEngine") << "addDecodeRequest for " << uuid << " has decoded file already" << LL_ENDL;
return TRUE;
}
if (gAssetStorage->hasLocalAsset(uuid, LLAssetType::AT_SOUND))
{
// Just put it on the decode queue.
//llinfos << "addDecodeRequest for " << uuid << " has local asset file already" << llendl;
LL_DEBUGS("AudioEngine") << "addDecodeRequest for " << uuid << " has local asset file already" << LL_ENDL;
mImpl->mDecodeQueue.push(uuid);
return TRUE;
}
//llinfos << "addDecodeRequest for " << uuid << " no file available" << llendl;
LL_DEBUGS("AudioEngine") << "addDecodeRequest for " << uuid << " no file available" << LL_ENDL;
return FALSE;
}

View File

@ -123,7 +123,7 @@ bool LLAudioEngine::init(const S32 num_channels, void* userdata)
// Initialize the decode manager
gAudioDecodeMgrp = new LLAudioDecodeMgr;
llinfos << "LLAudioEngine::init() AudioEngine successfully initialized" << llendl;
LL_INFOS("AudioEngine") << "LLAudioEngine::init() AudioEngine successfully initialized" << LL_ENDL;
return true;
}
@ -308,7 +308,7 @@ void LLAudioEngine::idle(F32 max_decode_time)
LLAudioChannel *channelp = getFreeChannel(max_priority);
if (channelp)
{
//llinfos << "Replacing source in channel due to priority!" << llendl;
LL_DEBUGS("AudioEngine") << "Replacing source in channel due to priority!" << LL_ENDL;
max_sourcep->setChannel(channelp);
channelp->setSource(max_sourcep);
if (max_sourcep->isSyncSlave())
@ -479,7 +479,7 @@ void LLAudioEngine::idle(F32 max_decode_time)
{
if (!mBuffers[i]->mInUse && mBuffers[i]->mLastUseTimer.getElapsedTimeF32() > 30.f)
{
//llinfos << "Flushing unused buffer!" << llendl;
LL_DEBUGS("AudioEngine") << "Flushing unused buffer!" << LL_ENDL;
mBuffers[i]->mAudioDatap->mBufferp = NULL;
delete mBuffers[i];
mBuffers[i] = NULL;
@ -591,8 +591,8 @@ LLAudioBuffer * LLAudioEngine::getFreeBuffer()
if (buffer_id >= 0)
{
lldebugs << "Taking over unused buffer " << buffer_id << llendl;
//llinfos << "Flushing unused buffer!" << llendl;
lldebugs << "Taking over unused buffer " << buffer_id << LL_ENDL;
LL_DEBUGS("AudioEngine") << "Flushing unused buffer!" << LL_ENDL;
mBuffers[buffer_id]->mAudioDatap->mBufferp = NULL;
delete mBuffers[buffer_id];
mBuffers[buffer_id] = createBuffer();
@ -673,6 +673,8 @@ void LLAudioEngine::cleanupBuffer(LLAudioBuffer *bufferp)
bool LLAudioEngine::preloadSound(const LLUUID &uuid)
{
LL_DEBUGS("AudioEngine")<<"( "<<uuid<<" )"<<LL_ENDL;
gAudiop->getAudioData(uuid); // We don't care about the return value, this is just to make sure
// that we have an entry, which will mean that the audio engine knows about this
@ -684,7 +686,7 @@ bool LLAudioEngine::preloadSound(const LLUUID &uuid)
// At some point we need to have the audio/asset system check the static VFS
// before it goes off and fetches stuff from the server.
//llwarns << "Used internal preload for non-local sound" << llendl;
LL_DEBUGS("AudioEngine") << "Used internal preload for non-local sound "<< uuid << LL_ENDL;
return false;
}
@ -815,7 +817,7 @@ void LLAudioEngine::triggerSound(const LLUUID &audio_uuid, const LLUUID& owner_i
const S32 type, const LLVector3d &pos_global)
{
// Create a new source (since this can't be associated with an existing source.
//llinfos << "Localized: " << audio_uuid << llendl;
LL_DEBUGS("AudioEngine") << "Localized: " << audio_uuid << LL_ENDL;
if (mMuted)
{
@ -982,11 +984,14 @@ void LLAudioEngine::cleanupAudioSource(LLAudioSource *asp)
iter = mAllSources.find(asp->getID());
if (iter == mAllSources.end())
{
llwarns << "Cleaning up unknown audio source!" << llendl;
return;
LL_WARNS("AudioEngine") << "Cleaning up unknown audio source!" << LL_ENDL;
}
else
{
LL_DEBUGS("AudioEngine") << "Cleaning up audio sources for "<< asp->getID() <<LL_ENDL;
delete asp;
mAllSources.erase(iter);
}
delete asp;
mAllSources.erase(iter);
}
@ -1013,16 +1018,18 @@ bool LLAudioEngine::hasDecodedFile(const LLUUID &uuid)
bool LLAudioEngine::hasLocalFile(const LLUUID &uuid)
{
// See if it's in the VFS.
return gVFS->getExists(uuid, LLAssetType::AT_SOUND);
bool have_local = gVFS->getExists(uuid, LLAssetType::AT_SOUND);
LL_DEBUGS("AudioEngine") << "sound uuid "<<uuid<<" exists in VFS"<<LL_ENDL;
return have_local;
}
void LLAudioEngine::startNextTransfer()
{
//llinfos << "LLAudioEngine::startNextTransfer()" << llendl;
//LL_DEBUGS("AudioEngine") << "LLAudioEngine::startNextTransfer()" << LL_ENDL;
if (mCurrentTransfer.notNull() || getMuted())
{
//llinfos << "Transfer in progress, aborting" << llendl;
//LL_DEBUGS("AudioEngine") << "Transfer in progress, aborting" << LL_ENDL;
return;
}
@ -1203,7 +1210,7 @@ void LLAudioEngine::startNextTransfer()
if (asset_id.notNull())
{
llinfos << "Getting asset data for: " << asset_id << llendl;
LL_INFOS("AudioEngine") << "Getting audio asset data for: " << asset_id << LL_ENDL;
gAudiop->mCurrentTransfer = asset_id;
gAudiop->mCurrentTransferTimer.reset();
gAssetStorage->getAssetData(asset_id, LLAssetType::AT_SOUND,
@ -1211,7 +1218,7 @@ void LLAudioEngine::startNextTransfer()
}
else
{
//llinfos << "No pending transfers?" << llendl;
//LL_DEBUGS("AudioEngine") << "No pending transfers?" << LL_ENDL;
}
}
@ -1221,7 +1228,7 @@ void LLAudioEngine::assetCallback(LLVFS *vfs, const LLUUID &uuid, LLAssetType::E
{
if (result_code)
{
llinfos << "Boom, error in audio file transfer: " << LLAssetStorage::getErrorString( result_code ) << " (" << result_code << ")" << llendl;
LL_INFOS("AudioEngine") << "Boom, error in audio file transfer: " << LLAssetStorage::getErrorString( result_code ) << " (" << result_code << ")" << LL_ENDL;
// Need to mark data as bad to avoid constant rerequests.
LLAudioData *adp = gAudiop->getAudioData(uuid);
if (adp)
@ -1238,11 +1245,11 @@ void LLAudioEngine::assetCallback(LLVFS *vfs, const LLUUID &uuid, LLAssetType::E
if (!adp)
{
// Should never happen
llwarns << "Got asset callback without audio data for " << uuid << llendl;
LL_WARNS("AudioEngine") << "Got asset callback without audio data for " << uuid << LL_ENDL;
}
else
{
// llinfos << "Got asset callback with good audio data for " << uuid << ", making decode request" << llendl;
LL_DEBUGS("AudioEngine") << "Got asset callback with good audio data for " << uuid << ", making decode request" << LL_ENDL;
adp->setHasValidData(true);
adp->setHasLocalData(true);
gAudioDecodeMgrp->addDecodeRequest(uuid);
@ -1321,7 +1328,7 @@ void LLAudioSource::update()
}
else if (adp->hasCompletedDecode()) // Only mark corrupted after decode is done
{
llwarns << "Marking LLAudioSource corrupted for " << adp->getID() << llendl;
LL_WARNS("AudioEngine") << "Marking LLAudioSource corrupted for " << adp->getID() << LL_ENDL;
mCorrupted = true ;
}
}
@ -1357,7 +1364,6 @@ bool LLAudioSource::setupChannel()
if (!adp->getBuffer())
{
// We're not ready to play back the sound yet, so don't try and allocate a channel for it.
//llwarns << "Aborting, no buffer" << llendl;
return false;
}
@ -1375,7 +1381,7 @@ bool LLAudioSource::setupChannel()
// Ugh, we don't have any free channels.
// Now we have to reprioritize.
// For now, just don't play the sound.
//llwarns << "Aborting, no free channels" << llendl;
//llwarns << "Aborting, no free channels" << LL_ENDL;
return false;
}
@ -1474,7 +1480,7 @@ bool LLAudioSource::isDone() const
{
// We don't have a channel assigned, and it's been
// over 15 seconds since we tried to play it. Don't bother.
//llinfos << "No channel assigned, source is done" << llendl;
LL_DEBUGS("AudioEngine") << "No channel assigned, source is done" << LL_ENDL;
return true;
}
else
@ -1640,7 +1646,7 @@ LLAudioChannel::LLAudioChannel() :
LLAudioChannel::~LLAudioChannel()
{
// Need to disconnect any sources which are using this channel.
//llinfos << "Cleaning up audio channel" << llendl;
LL_DEBUGS("AudioEngine") << "Cleaning up audio channel" << LL_ENDL;
if (mCurrentSourcep)
{
mCurrentSourcep->setChannel(NULL);
@ -1651,29 +1657,29 @@ LLAudioChannel::~LLAudioChannel()
void LLAudioChannel::setSource(LLAudioSource *sourcep)
{
//llinfos << this << ": setSource(" << sourcep << ")" << llendl;
if (!sourcep)
{
// Clearing the source for this channel, don't need to do anything.
//llinfos << "Clearing source for channel" << llendl;
LL_DEBUGS("AudioEngine") << "Clearing source" << ( mCurrentSourcep ? mCurrentSourcep->getID() : LLUUID::null ) << LL_ENDL;
cleanup();
mCurrentSourcep = NULL;
mWaiting = false;
return;
}
if (sourcep == mCurrentSourcep)
else
{
// Don't reallocate the channel, this will make FMOD goofy.
//llinfos << "Calling setSource with same source!" << llendl;
LL_DEBUGS("AudioEngine") << "( id: " << sourcep->getID() << ")" << LL_ENDL;
if (sourcep == mCurrentSourcep)
{
// Don't reallocate the channel, this will make FMOD goofy.
//LL_DEBUGS("AudioEngine") << "Calling setSource with same source!" << LL_ENDL;
}
mCurrentSourcep = sourcep;
updateBuffer();
update3DPosition();
}
mCurrentSourcep = sourcep;
updateBuffer();
update3DPosition();
}
@ -1768,7 +1774,7 @@ bool LLAudioData::load()
if (mBufferp)
{
// We already have this sound in a buffer, don't do anything.
llinfos << "Already have a buffer for this sound, don't bother loading!" << llendl;
LL_INFOS("AudioEngine") << "Already have a buffer for this sound, don't bother loading!" << LL_ENDL;
return true;
}
@ -1776,7 +1782,7 @@ bool LLAudioData::load()
if (!mBufferp)
{
// No free buffers, abort.
llinfos << "Not able to allocate a new audio buffer, aborting." << llendl;
LL_INFOS("AudioEngine") << "Not able to allocate a new audio buffer, aborting." << LL_ENDL;
return true;
}

View File

@ -43,6 +43,7 @@ set(llcommon_SOURCE_FILES
llcriticaldamp.cpp
llcursortypes.cpp
lldate.cpp
lldeadmantimer.cpp
lldependencies.cpp
lldictionary.cpp
llerror.cpp
@ -79,6 +80,7 @@ set(llcommon_SOURCE_FILES
llptrto.cpp
llprocess.cpp
llprocessor.cpp
llprocinfo.cpp
llqueuedthread.cpp
llrand.cpp
llrefcount.cpp
@ -146,6 +148,7 @@ set(llcommon_HEADER_FILES
lldarray.h
lldarrayptr.h
lldate.h
lldeadmantimer.h
lldefs.h
lldependencies.h
lldeleteutils.h
@ -207,6 +210,7 @@ set(llcommon_HEADER_FILES
llpriqueuemap.h
llprocess.h
llprocessor.h
llprocinfo.h
llptrskiplist.h
llptrskipmap.h
llptrto.h
@ -324,12 +328,14 @@ if (LL_TESTS)
LL_ADD_INTEGRATION_TEST(bitpack "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llbase64 "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(lldate "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(lldeadmantimer "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(lldependencies "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llerror "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llframetimer "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llinstancetracker "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(lllazy "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llprocessor "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llprocinfo "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llrand "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llsdserialize "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llsingleton "" "${test_libs}")

View File

@ -0,0 +1,188 @@
/**
* @file lldeadmantimer.cpp
* @brief Simple deadman-switch timer.
* @author monty@lindenlab.com
*
* $LicenseInfo:firstyear=2013&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2013, 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 "lldeadmantimer.h"
// *TODO: Currently, this uses lltimer functions for its time
// aspects and this leaks into the apis in the U64s/F64s. Would
// like to perhaps switch this over to TSC register-based timers
// sometime and drop the overhead some more.
// Flag states and their meaning:
// mActive mDone Meaning
// false false Nothing running, no result available
// true false Timer running, no result available
// false true Timer finished, result can be read once
// true true Not allowed
//
LLDeadmanTimer::LLDeadmanTimer(F64 horizon, bool inc_cpu)
: mHorizon(time_type(llmax(horizon, F64(0.0)) * gClockFrequency)),
mActive(false), // If true, a timer is running.
mDone(false), // If true, timer has completed and can be read (once)
mStarted(U64L(0)),
mExpires(U64L(0)),
mStopped(U64L(0)),
mCount(U64L(0)),
mIncCPU(inc_cpu),
mUStartCPU(LLProcInfo::time_type(U64L(0))),
mUEndCPU(LLProcInfo::time_type(U64L(0))),
mSStartCPU(LLProcInfo::time_type(U64L(0))),
mSEndCPU(LLProcInfo::time_type(U64L(0)))
{}
// static
LLDeadmanTimer::time_type LLDeadmanTimer::getNow()
{
return LLTimer::getCurrentClockCount();
}
void LLDeadmanTimer::start(time_type now)
{
// *TODO: If active, let's complete an existing timer and save
// the result to the side. I think this will be useful later.
// For now, wipe out anything in progress, start fresh.
if (! now)
{
now = LLTimer::getCurrentClockCount();
}
mActive = true;
mDone = false;
mStarted = now;
mExpires = now + mHorizon;
mStopped = now;
mCount = U64L(0);
if (mIncCPU)
{
LLProcInfo::getCPUUsage(mUStartCPU, mSStartCPU);
}
}
void LLDeadmanTimer::stop(time_type now)
{
if (! mActive)
{
return;
}
if (! now)
{
now = getNow();
}
mStopped = now;
mActive = false;
mDone = true;
if (mIncCPU)
{
LLProcInfo::getCPUUsage(mUEndCPU, mSEndCPU);
}
}
bool LLDeadmanTimer::isExpired(time_type now, F64 & started, F64 & stopped, U64 & count,
U64 & user_cpu, U64 & sys_cpu)
{
const bool status(isExpired(now, started, stopped, count));
if (status)
{
user_cpu = U64(mUEndCPU - mUStartCPU);
sys_cpu = U64(mSEndCPU - mSStartCPU);
}
return status;
}
bool LLDeadmanTimer::isExpired(time_type now, F64 & started, F64 & stopped, U64 & count)
{
if (mActive && ! mDone)
{
if (! now)
{
now = getNow();
}
if (now >= mExpires)
{
// mStopped from ringBell() is the value we want
mActive = false;
mDone = true;
}
}
if (! mDone)
{
return false;
}
started = mStarted * gClockFrequencyInv;
stopped = mStopped * gClockFrequencyInv;
count = mCount;
mDone = false;
return true;
}
void LLDeadmanTimer::ringBell(time_type now, unsigned int count)
{
if (! mActive)
{
return;
}
if (! now)
{
now = getNow();
}
if (now >= mExpires)
{
// Timer has expired, this event will be dropped
mActive = false;
mDone = true;
}
else
{
// Timer renewed, keep going
mStopped = now;
mExpires = now + mHorizon;
mCount += count;
if (mIncCPU)
{
LLProcInfo::getCPUUsage(mUEndCPU, mSEndCPU);
}
}
return;
}

View File

@ -0,0 +1,214 @@
/**
* @file lldeadmantimer.h
* @brief Interface to a simple event timer with a deadman's switch
* @author monty@lindenlab.com
*
* $LicenseInfo:firstyear=2013&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2013, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LL_DEADMANTIMER_H
#define LL_DEADMANTIMER_H
#include "linden_common.h"
#include "lltimer.h"
#include "llprocinfo.h"
/// @file lldeadmantimer.h
///
/// There are interesting user-experienced events in the viewer that
/// would seem to have well-defined start and stop points but which
/// actually lack such milestones in the code. Such events (like
/// time to load meshes after logging in, initial inventory load,
/// display name fetch) can be defined somewhat after-the-fact by
/// noticing when we no longer perform operations towards their
/// completion. This class is intended to help in such applications.
///
/// What it implements is a deadman's switch (also known as a
/// keepalive switch and a doorbell switch). The basic operation is
/// as follows:
///
/// * LLDeadmanTimer is instantiated with a horizon value in seconds,
/// one for each event of interest.
/// * When an event starts, @see start() is invoked to begin a
/// timing operation.
/// * As operations are performed in service of the event (issuing
/// HTTP requests, receiving responses), @see ringBell() is invoked
/// to inform the timer that the operation is still active.
/// * If the operation is canceled or otherwise terminated, @see
/// stop() can be called to end the timing operation.
/// * Concurrent with the ringBell() calls, the program makes
/// periodic (shorter than the horizon but not too short) calls
/// to @see isExpired() to see if the event has expired due to
/// either a stop() call or lack of activity (defined as a ringBell()
/// call in the previous 'horizon' seconds). If it has expired,
/// the caller also receives start, stop and count values for the
/// event which the application can then report in whatever manner
/// it sees fit.
/// * The timer becomes passive after an isExpired() call that returns
/// true. It can then be restarted with a new start() call.
///
/// Threading: Instances are not thread-safe. They also use
/// timing code from lltimer.h which is also unsafe.
///
/// Allocation: Not refcounted, may be stack or heap allocated.
///
class LL_COMMON_API LLDeadmanTimer
{
public:
/// Public types
/// Low-level time type chosen for compatibility with
/// LLTimer::getCurrentClockCount() which is the basis
/// of time operations in this class. This is likely
/// to change in a future version in a move to TSC-based
/// timing.
typedef U64 time_type;
public:
/// Construct and initialize an LLDeadmanTimer
///
/// @param horizon Time, in seconds, after the last @see ringBell()
/// call at which point the timer will consider itself
/// expired.
///
/// @param inc_cpu If true, gather system and user cpu stats while
/// running the timer. This does require more syscalls
/// during updates. If false, cpu usage data isn't
/// collected and will be zero if queried.
LLDeadmanTimer(F64 horizon, bool inc_cpu);
~LLDeadmanTimer()
{}
private:
LLDeadmanTimer(const LLDeadmanTimer &); // Not defined
void operator=(const LLDeadmanTimer &); // Not defined
public:
/// Get the current time. Zero-basis for this time
/// representation is not defined and is different on
/// different platforms. Do not attempt to compute
/// negative times relative to the first value returned,
/// there may not be enough 'front porch' on the range
/// to prevent wraparound.
///
/// Note: Implementation is expected to change in a
/// future release as well.
///
static time_type getNow();
/// Begin timing. If the timer is already active, it is reset
/// and timing begins now.
///
/// @param now Current time as returned by @see
/// LLTimer::getCurrentClockCount(). If zero,
/// method will lookup current time.
///
void start(time_type now);
/// End timing. Actively declare the end of the event independent
/// of the deadman's switch operation. @see isExpired() will return
/// true and appropriate values will be returned.
///
/// @param now Current time as returned by @see
/// LLTimer::getCurrentClockCount(). If zero,
/// method will lookup current time.
///
void stop(time_type now);
/// Declare that something interesting happened. This has two
/// effects on an unexpired-timer. 1) The expiration time
/// is extended for 'horizon' seconds after the 'now' value.
/// 2) An internal counter associated with the event is incremented
/// by the @ref count parameter. This count is returned via the
/// @see isExpired() method.
///
/// @param now Current time as returned by @see
/// LLTimer::getCurrentClockCount(). If zero,
/// method will lookup current time.
///
/// @param count Count of events to be associated with
/// this bell ringing.
///
void ringBell(time_type now, unsigned int count);
/// Checks the status of the timer. If the timer has expired,
/// also returns various timer-related stats. Unlike ringBell(),
/// does not extend the horizon, it only checks for expiration.
///
/// @param now Current time as returned by @see
/// LLTimer::getCurrentClockCount(). If zero,
/// method will lookup current time.
///
/// @param started If expired, the starting time of the event is
/// returned to the caller via this reference.
///
/// @param stopped If expired, the ending time of the event is
/// returned to the caller via this reference.
/// Ending time will be that provided in the
/// stop() method or the last ringBell() call
/// leading to expiration, whichever (stop() call
/// or notice of expiration) happened first.
///
/// @param count If expired, the number of ringBell() calls
/// made prior to expiration.
///
/// @param user_cpu Amount of CPU spent in user mode by the process
/// during the event. Value in microseconds and will
/// read zero if not enabled by the constructor.
///
/// @param sys_cpu Amount of CPU spent in system mode by the process.
///
/// @return true if the timer has expired, false otherwise.
/// If true, it also returns the started,
/// stopped and count values otherwise these are
/// left unchanged.
///
bool isExpired(time_type now, F64 & started, F64 & stopped, U64 & count,
U64 & user_cpu, U64 & sys_cpu);
/// Identical to the six-arugment form except it does without the
/// CPU time return if the caller isn't interested in it.
bool isExpired(time_type now, F64 & started, F64 & stopped, U64 & count);
protected:
time_type mHorizon;
bool mActive;
bool mDone;
time_type mStarted;
time_type mExpires;
time_type mStopped;
time_type mCount;
const bool mIncCPU; // Include CPU metrics in timer
LLProcInfo::time_type mUStartCPU;
LLProcInfo::time_type mUEndCPU;
LLProcInfo::time_type mSStartCPU;
LLProcInfo::time_type mSEndCPU;
};
#endif // LL_DEADMANTIMER_H

View File

@ -0,0 +1,94 @@
/**
* @file llprocinfo.cpp
* @brief Process, cpu and resource usage information APIs.
* @author monty@lindenlab.com
*
* $LicenseInfo:firstyear=2013&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2013, 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 "llprocinfo.h"
#if LL_WINDOWS
#define PSAPI_VERSION 1
#include "windows.h"
#include "psapi.h"
#elif LL_DARWIN
#include <sys/resource.h>
#include <mach/mach.h>
#else
#include <sys/time.h>
#include <sys/resource.h>
#endif // LL_WINDOWS/LL_DARWIN
// static
void LLProcInfo::getCPUUsage(time_type & user_time, time_type & system_time)
{
#if LL_WINDOWS
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;
system_time = uli.QuadPart / U64L(10); // Convert to uS
uli.u.LowPart = ft_user.dwLowDateTime;
uli.u.HighPart = ft_user.dwHighDateTime;
user_time = uli.QuadPart / U64L(10);
#elif LL_DARWIN
struct rusage usage;
if (getrusage(RUSAGE_SELF, &usage))
{
user_time = system_time = time_type(0U);
return;
}
user_time = U64(usage.ru_utime.tv_sec) * U64L(1000000) + usage.ru_utime.tv_usec;
system_time = U64(usage.ru_stime.tv_sec) * U64L(1000000) + usage.ru_stime.tv_usec;
#else // Linux
struct rusage usage;
if (getrusage(RUSAGE_SELF, &usage))
{
user_time = system_time = time_type(0U);
return;
}
user_time = U64(usage.ru_utime.tv_sec) * U64L(1000000) + usage.ru_utime.tv_usec;
system_time = U64(usage.ru_stime.tv_sec) * U64L(1000000) + usage.ru_stime.tv_usec;
#endif // LL_WINDOWS/LL_DARWIN/Linux
}

View File

@ -0,0 +1,68 @@
/**
* @file llprocinfo.h
* @brief Interface to process/cpu/resource information services.
* @author monty@lindenlab.com
*
* $LicenseInfo:firstyear=2013&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2013, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LL_PROCINFO_H
#define LL_PROCINFO_H
#include "linden_common.h"
/// @file llprocinfo.h
///
/// Right now, this is really a namespace disguised as a class.
/// It wraps some types and functions to return information about
/// process resource consumption in a non-OS-specific manner.
///
/// Threading: No instances so that's thread-safe. Implementations
/// of static functions should be thread-safe, they mostly involve
/// direct syscall invocations.
///
/// Allocation: Not instantiatable.
class LL_COMMON_API LLProcInfo
{
public:
/// Public types
typedef U64 time_type; /// Relative microseconds
private:
LLProcInfo(); // Not defined
~LLProcInfo(); // Not defined
LLProcInfo(const LLProcInfo &); // Not defined
void operator=(const LLProcInfo &); // Not defined
public:
/// Get accumulated system and user CPU time in
/// microseconds. Syscalls involved in every invocation.
///
/// Threading: expected to be safe.
static void getCPUUsage(time_type & user_time, time_type & system_time);
};
#endif // LL_PROCINFO_H

View File

@ -3,7 +3,7 @@
*
* $LicenseInfo:firstyear=2004&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
* Copyright (C) 2010-2013, 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
@ -373,6 +373,36 @@ void LLMutex::lock()
#endif
}
bool LLMutex::trylock()
{
if(isSelfLocked())
{ //redundant lock
mCount++;
return true;
}
apr_status_t status(apr_thread_mutex_trylock(mAPRMutexp));
if (APR_STATUS_IS_EBUSY(status))
{
return false;
}
#if MUTEX_DEBUG
// Have to have the lock before we can access the debug info
U32 id = LLThread::currentID();
if (mIsLocked[id] != FALSE)
llerrs << "Already locked in Thread: " << id << llendl;
mIsLocked[id] = TRUE;
#endif
#if LL_DARWIN
mLockingThread = LLThread::currentID();
#else
mLockingThread = sThreadID;
#endif
return true;
}
void LLMutex::unlock()
{
if (mCount > 0)

View File

@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2004&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
* Copyright (C) 2010-2013, 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
@ -156,7 +156,8 @@ public:
virtual ~LLMutex();
void lock(); // blocks
void unlock();
bool trylock(); // non-blocking, returns true if lock held.
void unlock(); // undefined behavior when called on mutex not being held
bool isLocked(); // non-blocking, but does do a lock/unlock so not free
bool isSelfLocked(); //return true if locked in a same thread
U32 lockingThread() const; //get ID of locking thread
@ -174,6 +175,8 @@ protected:
#endif
};
//============================================================================
// Actually a condition/mutex pair (since each condition needs to be associated with a mutex).
class LL_COMMON_API LLCondition : public LLMutex
{
@ -189,6 +192,8 @@ protected:
apr_thread_cond_t *mAPRCondp;
};
//============================================================================
class LLMutexLock
{
public:
@ -210,6 +215,43 @@ private:
//============================================================================
// Scoped locking class similar in function to LLMutexLock but uses
// the trylock() method to conditionally acquire lock without
// blocking. Caller resolves the resulting condition by calling
// the isLocked() method and either punts or continues as indicated.
//
// Mostly of interest to callers needing to avoid stalls who can
// guarantee another attempt at a later time.
class LLMutexTrylock
{
public:
LLMutexTrylock(LLMutex* mutex)
: mMutex(mutex),
mLocked(false)
{
if (mMutex)
mLocked = mMutex->trylock();
}
~LLMutexTrylock()
{
if (mMutex && mLocked)
mMutex->unlock();
}
bool isLocked() const
{
return mLocked;
}
private:
LLMutex* mMutex;
bool mLocked;
};
//============================================================================
void LLThread::lockData()
{
mDataLock->lock();

View File

@ -146,6 +146,13 @@ static inline time_t time_max()
}
}
// These are really statics but they've been global for awhile
// and they're material to other timing classes. If you are
// not implementing a timer class, do not use these directly.
extern LL_COMMON_API F64 gClockFrequency;
extern LL_COMMON_API F64 gClockFrequencyInv;
extern LL_COMMON_API F64 gClocksToMicroseconds;
// Correction factor used by time_corrected() above.
extern LL_COMMON_API S32 gUTCOffset;

View File

@ -0,0 +1,628 @@
/**
* @file lldeadmantimer_test.cpp
* @brief Tests for the LLDeadmanTimer class.
*
* $LicenseInfo:firstyear=2013&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2013, 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 "../lldeadmantimer.h"
#include "../llsd.h"
#include "../lltimer.h"
#include "../test/lltut.h"
// Convert between floating point time deltas and U64 time deltas.
// Reflects an implementation detail inside lldeadmantimer.cpp
static LLDeadmanTimer::time_type float_time_to_u64(F64 delta)
{
return LLDeadmanTimer::time_type(delta * gClockFrequency);
}
static F64 u64_time_to_float(LLDeadmanTimer::time_type delta)
{
return delta * gClockFrequencyInv;
}
namespace tut
{
struct deadmantimer_test
{
deadmantimer_test()
{
// LLTimer internals updating
update_clock_frequencies();
}
};
typedef test_group<deadmantimer_test> deadmantimer_group_t;
typedef deadmantimer_group_t::object deadmantimer_object_t;
tut::deadmantimer_group_t deadmantimer_instance("LLDeadmanTimer");
// Basic construction test and isExpired() call
template<> template<>
void deadmantimer_object_t::test<1>()
{
{
// Without cpu metrics
F64 started(42.0), stopped(97.0);
U64 count(U64L(8));
LLDeadmanTimer timer(10.0, false);
ensure_equals("WOCM isExpired() returns false after ctor()", timer.isExpired(0, started, stopped, count), false);
ensure_approximately_equals("WOCM t1 - isExpired() does not modify started", started, F64(42.0), 2);
ensure_approximately_equals("WOCM t1 - isExpired() does not modify stopped", stopped, F64(97.0), 2);
ensure_equals("WOCM t1 - isExpired() does not modify count", count, U64L(8));
}
{
// With cpu metrics
F64 started(42.0), stopped(97.0);
U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
LLDeadmanTimer timer(10.0, true);
ensure_equals("WCM isExpired() returns false after ctor()", timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu), false);
ensure_approximately_equals("WCM t1 - isExpired() does not modify started", started, F64(42.0), 2);
ensure_approximately_equals("WCM t1 - isExpired() does not modify stopped", stopped, F64(97.0), 2);
ensure_equals("WCM t1 - isExpired() does not modify count", count, U64L(8));
ensure_equals("WCM t1 - isExpired() does not modify user_cpu", user_cpu, U64L(29000));
ensure_equals("WCM t1 - isExpired() does not modify sys_cpu", sys_cpu, U64L(57000));
}
}
// Construct with zero horizon - not useful generally but will be useful in testing
template<> template<>
void deadmantimer_object_t::test<2>()
{
{
// Without cpu metrics
F64 started(42.0), stopped(97.0);
U64 count(U64L(8));
LLDeadmanTimer timer(0.0, false); // Zero is pre-expired
ensure_equals("WOCM isExpired() still returns false with 0.0 time ctor()",
timer.isExpired(0, started, stopped, count), false);
}
{
// With cpu metrics
F64 started(42.0), stopped(97.0);
U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
LLDeadmanTimer timer(0.0, true); // Zero is pre-expired
ensure_equals("WCM isExpired() still returns false with 0.0 time ctor()",
timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu), false);
}
}
// "pre-expired" timer - starting a timer with a 0.0 horizon will result in
// expiration on first test.
template<> template<>
void deadmantimer_object_t::test<3>()
{
{
// Without cpu metrics
F64 started(42.0), stopped(97.0);
U64 count(U64L(8));
LLDeadmanTimer timer(0.0, false);
timer.start(0);
ensure_equals("WOCM isExpired() returns true with 0.0 horizon time",
timer.isExpired(0, started, stopped, count), true);
ensure_approximately_equals("WOCM expired timer with no bell ringing has stopped == started", started, stopped, 8);
}
{
// With cpu metrics
F64 started(42.0), stopped(97.0);
U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
LLDeadmanTimer timer(0.0, true);
timer.start(0);
ensure_equals("WCM isExpired() returns true with 0.0 horizon time",
timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu), true);
ensure_approximately_equals("WCM expired timer with no bell ringing has stopped == started", started, stopped, 8);
}
}
// "pre-expired" timer - bell rings are ignored as we're already expired.
template<> template<>
void deadmantimer_object_t::test<4>()
{
{
// Without cpu metrics
F64 started(42.0), stopped(97.0);
U64 count(U64L(8));
LLDeadmanTimer timer(0.0, false);
timer.start(0);
timer.ringBell(LLDeadmanTimer::getNow() + float_time_to_u64(1000.0), 1);
ensure_equals("WOCM isExpired() returns true with 0.0 horizon time after bell ring",
timer.isExpired(0, started, stopped, count), true);
ensure_approximately_equals("WOCM ringBell has no impact on expired timer leaving stopped == started", started, stopped, 8);
}
{
// With cpu metrics
F64 started(42.0), stopped(97.0);
U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
LLDeadmanTimer timer(0.0, true);
timer.start(0);
timer.ringBell(LLDeadmanTimer::getNow() + float_time_to_u64(1000.0), 1);
ensure_equals("WCM isExpired() returns true with 0.0 horizon time after bell ring",
timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu), true);
ensure_approximately_equals("WCM ringBell has no impact on expired timer leaving stopped == started", started, stopped, 8);
}
}
// start(0) test - unexpired timer reports unexpired
template<> template<>
void deadmantimer_object_t::test<5>()
{
{
// Without cpu metrics
F64 started(42.0), stopped(97.0);
U64 count(U64L(8));
LLDeadmanTimer timer(10.0, false);
timer.start(0);
ensure_equals("WOCM isExpired() returns false after starting with 10.0 horizon time",
timer.isExpired(0, started, stopped, count), false);
ensure_approximately_equals("WOCM t5 - isExpired() does not modify started", started, F64(42.0), 2);
ensure_approximately_equals("WOCM t5 - isExpired() does not modify stopped", stopped, F64(97.0), 2);
ensure_equals("WOCM t5 - isExpired() does not modify count", count, U64L(8));
}
{
// With cpu metrics
F64 started(42.0), stopped(97.0);
U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
LLDeadmanTimer timer(10.0, true);
timer.start(0);
ensure_equals("WCM isExpired() returns false after starting with 10.0 horizon time",
timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu), false);
ensure_approximately_equals("WCM t5 - isExpired() does not modify started", started, F64(42.0), 2);
ensure_approximately_equals("WCM t5 - isExpired() does not modify stopped", stopped, F64(97.0), 2);
ensure_equals("WCM t5 - isExpired() does not modify count", count, U64L(8));
ensure_equals("WCM t5 - isExpired() does not modify user_cpu", user_cpu, U64L(29000));
ensure_equals("WCM t5 - isExpired() does not modify sys_cpu", sys_cpu, U64L(57000));
}
}
// start() test - start in the past but not beyond 1 horizon
template<> template<>
void deadmantimer_object_t::test<6>()
{
{
// Without cpu metrics
F64 started(42.0), stopped(97.0);
U64 count(U64L(8));
LLDeadmanTimer timer(10.0, false);
// Would like to do subtraction on current time but can't because
// the implementation on Windows is zero-based. We wrap around
// the backside resulting in a large U64 number.
LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow());
LLDeadmanTimer::time_type now(the_past + float_time_to_u64(5.0));
timer.start(the_past);
ensure_equals("WOCM t6 - isExpired() returns false with 10.0 horizon time starting 5.0 in past",
timer.isExpired(now, started, stopped, count), false);
ensure_approximately_equals("WOCM t6 - isExpired() does not modify started", started, F64(42.0), 2);
ensure_approximately_equals("WOCM t6 - isExpired() does not modify stopped", stopped, F64(97.0), 2);
ensure_equals("WOCM t6 - isExpired() does not modify count", count, U64L(8));
}
{
// With cpu metrics
F64 started(42.0), stopped(97.0);
U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
LLDeadmanTimer timer(10.0, true);
// Would like to do subtraction on current time but can't because
// the implementation on Windows is zero-based. We wrap around
// the backside resulting in a large U64 number.
LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow());
LLDeadmanTimer::time_type now(the_past + float_time_to_u64(5.0));
timer.start(the_past);
ensure_equals("WCM t6 - isExpired() returns false with 10.0 horizon time starting 5.0 in past",
timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false);
ensure_approximately_equals("WCM t6 - isExpired() does not modify started", started, F64(42.0), 2);
ensure_approximately_equals("WCM t6 - isExpired() does not modify stopped", stopped, F64(97.0), 2);
ensure_equals("t6 - isExpired() does not modify count", count, U64L(8));
ensure_equals("WCM t6 - isExpired() does not modify user_cpu", user_cpu, U64L(29000));
ensure_equals("WCM t6 - isExpired() does not modify sys_cpu", sys_cpu, U64L(57000));
}
}
// start() test - start in the past but well beyond 1 horizon
template<> template<>
void deadmantimer_object_t::test<7>()
{
{
// Without cpu metrics
F64 started(42.0), stopped(97.0);
U64 count(U64L(8));
LLDeadmanTimer timer(10.0, false);
// Would like to do subtraction on current time but can't because
// the implementation on Windows is zero-based. We wrap around
// the backside resulting in a large U64 number.
LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow());
LLDeadmanTimer::time_type now(the_past + float_time_to_u64(20.0));
timer.start(the_past);
ensure_equals("WOCM t7 - isExpired() returns true with 10.0 horizon time starting 20.0 in past",
timer.isExpired(now,started, stopped, count), true);
ensure_approximately_equals("WOCM t7 - starting before horizon still gives equal started / stopped", started, stopped, 8);
}
{
// With cpu metrics
F64 started(42.0), stopped(97.0);
U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
LLDeadmanTimer timer(10.0, true);
// Would like to do subtraction on current time but can't because
// the implementation on Windows is zero-based. We wrap around
// the backside resulting in a large U64 number.
LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow());
LLDeadmanTimer::time_type now(the_past + float_time_to_u64(20.0));
timer.start(the_past);
ensure_equals("WCM t7 - isExpired() returns true with 10.0 horizon time starting 20.0 in past",
timer.isExpired(now,started, stopped, count, user_cpu, sys_cpu), true);
ensure_approximately_equals("WOCM t7 - starting before horizon still gives equal started / stopped", started, stopped, 8);
}
}
// isExpired() test - results are read-once. Probes after first true are false.
template<> template<>
void deadmantimer_object_t::test<8>()
{
{
// Without cpu metrics
F64 started(42.0), stopped(97.0);
U64 count(U64L(8));
LLDeadmanTimer timer(10.0, false);
// Would like to do subtraction on current time but can't because
// the implementation on Windows is zero-based. We wrap around
// the backside resulting in a large U64 number.
LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow());
LLDeadmanTimer::time_type now(the_past + float_time_to_u64(20.0));
timer.start(the_past);
ensure_equals("WOCM t8 - isExpired() returns true with 10.0 horizon time starting 20.0 in past",
timer.isExpired(now, started, stopped, count), true);
started = 42.0;
stopped = 97.0;
count = U64L(8);
ensure_equals("WOCM t8 - second isExpired() returns false after true",
timer.isExpired(now, started, stopped, count), false);
ensure_approximately_equals("WOCM t8 - 2nd isExpired() does not modify started", started, F64(42.0), 2);
ensure_approximately_equals("WOCM t8 - 2nd isExpired() does not modify stopped", stopped, F64(97.0), 2);
ensure_equals("WOCM t8 - 2nd isExpired() does not modify count", count, U64L(8));
}
{
// With cpu metrics
F64 started(42.0), stopped(97.0);
U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
LLDeadmanTimer timer(10.0, true);
// Would like to do subtraction on current time but can't because
// the implementation on Windows is zero-based. We wrap around
// the backside resulting in a large U64 number.
LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow());
LLDeadmanTimer::time_type now(the_past + float_time_to_u64(20.0));
timer.start(the_past);
ensure_equals("WCM t8 - isExpired() returns true with 10.0 horizon time starting 20.0 in past",
timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), true);
started = 42.0;
stopped = 97.0;
count = U64L(8);
user_cpu = 29000;
sys_cpu = 57000;
ensure_equals("WCM t8 - second isExpired() returns false after true",
timer.isExpired(now, started, stopped, count), false);
ensure_approximately_equals("WCM t8 - 2nd isExpired() does not modify started", started, F64(42.0), 2);
ensure_approximately_equals("WCM t8 - 2nd isExpired() does not modify stopped", stopped, F64(97.0), 2);
ensure_equals("WCM t8 - 2nd isExpired() does not modify count", count, U64L(8));
ensure_equals("WCM t8 - 2nd isExpired() does not modify user_cpu", user_cpu, U64L(29000));
ensure_equals("WCM t8 - 2nd isExpired() does not modify sys_cpu", sys_cpu, U64L(57000));
}
}
// ringBell() test - see that we can keep a timer from expiring
template<> template<>
void deadmantimer_object_t::test<9>()
{
{
// Without cpu metrics
F64 started(42.0), stopped(97.0);
U64 count(U64L(8));
LLDeadmanTimer timer(5.0, false);
LLDeadmanTimer::time_type now(LLDeadmanTimer::getNow());
F64 real_start(u64_time_to_float(now));
timer.start(0);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
ensure_equals("WOCM t9 - 5.0 horizon timer has not timed out after 10 1-second bell rings",
timer.isExpired(now, started, stopped, count), false);
F64 last_good_ring(u64_time_to_float(now));
// Jump forward and expire
now += float_time_to_u64(10.0);
ensure_equals("WOCM t9 - 5.0 horizon timer expires on 10-second jump",
timer.isExpired(now, started, stopped, count), true);
ensure_approximately_equals("WOCM t9 - started matches start() time", started, real_start, 4);
ensure_approximately_equals("WOCM t9 - stopped matches last ringBell() time", stopped, last_good_ring, 4);
ensure_equals("WOCM t9 - 10 good ringBell()s", count, U64L(10));
ensure_equals("WOCM t9 - single read only", timer.isExpired(now, started, stopped, count), false);
}
{
// With cpu metrics
F64 started(42.0), stopped(97.0);
U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
LLDeadmanTimer timer(5.0, true);
LLDeadmanTimer::time_type now(LLDeadmanTimer::getNow());
F64 real_start(u64_time_to_float(now));
timer.start(0);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
ensure_equals("WCM t9 - 5.0 horizon timer has not timed out after 10 1-second bell rings",
timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false);
F64 last_good_ring(u64_time_to_float(now));
// Jump forward and expire
now += float_time_to_u64(10.0);
ensure_equals("WCM t9 - 5.0 horizon timer expires on 10-second jump",
timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), true);
ensure_approximately_equals("WCM t9 - started matches start() time", started, real_start, 4);
ensure_approximately_equals("WCM t9 - stopped matches last ringBell() time", stopped, last_good_ring, 4);
ensure_equals("WCM t9 - 10 good ringBell()s", count, U64L(10));
ensure_equals("WCM t9 - single read only", timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false);
}
}
// restart after expiration test - verify that restarts behave well
template<> template<>
void deadmantimer_object_t::test<10>()
{
{
// Without cpu metrics
F64 started(42.0), stopped(97.0);
U64 count(U64L(8));
LLDeadmanTimer timer(5.0, false);
LLDeadmanTimer::time_type now(LLDeadmanTimer::getNow());
F64 real_start(u64_time_to_float(now));
timer.start(0);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
ensure_equals("WOCM t10 - 5.0 horizon timer has not timed out after 10 1-second bell rings",
timer.isExpired(now, started, stopped, count), false);
F64 last_good_ring(u64_time_to_float(now));
// Jump forward and expire
now += float_time_to_u64(10.0);
ensure_equals("WOCM t10 - 5.0 horizon timer expires on 10-second jump",
timer.isExpired(now, started, stopped, count), true);
ensure_approximately_equals("WOCM t10 - started matches start() time", started, real_start, 4);
ensure_approximately_equals("WOCM t10 - stopped matches last ringBell() time", stopped, last_good_ring, 4);
ensure_equals("WOCM t10 - 10 good ringBell()s", count, U64L(10));
ensure_equals("WOCM t10 - single read only", timer.isExpired(now, started, stopped, count), false);
// Jump forward and restart
now += float_time_to_u64(1.0);
real_start = u64_time_to_float(now);
timer.start(now);
// Run a modified bell ring sequence
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
ensure_equals("WOCM t10 - 5.0 horizon timer has not timed out after 8 1-second bell rings",
timer.isExpired(now, started, stopped, count), false);
last_good_ring = u64_time_to_float(now);
// Jump forward and expire
now += float_time_to_u64(10.0);
ensure_equals("WOCM t10 - 5.0 horizon timer expires on 8-second jump",
timer.isExpired(now, started, stopped, count), true);
ensure_approximately_equals("WOCM t10 - 2nd started matches start() time", started, real_start, 4);
ensure_approximately_equals("WOCM t10 - 2nd stopped matches last ringBell() time", stopped, last_good_ring, 4);
ensure_equals("WOCM t10 - 8 good ringBell()s", count, U64L(8));
ensure_equals("WOCM t10 - single read only - 2nd start",
timer.isExpired(now, started, stopped, count), false);
}
{
// With cpu metrics
F64 started(42.0), stopped(97.0);
U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
LLDeadmanTimer timer(5.0, true);
LLDeadmanTimer::time_type now(LLDeadmanTimer::getNow());
F64 real_start(u64_time_to_float(now));
timer.start(0);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
ensure_equals("WCM t10 - 5.0 horizon timer has not timed out after 10 1-second bell rings",
timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false);
F64 last_good_ring(u64_time_to_float(now));
// Jump forward and expire
now += float_time_to_u64(10.0);
ensure_equals("WCM t10 - 5.0 horizon timer expires on 10-second jump",
timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), true);
ensure_approximately_equals("WCM t10 - started matches start() time", started, real_start, 4);
ensure_approximately_equals("WCM t10 - stopped matches last ringBell() time", stopped, last_good_ring, 4);
ensure_equals("WCM t10 - 10 good ringBell()s", count, U64L(10));
ensure_equals("WCM t10 - single read only", timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false);
// Jump forward and restart
now += float_time_to_u64(1.0);
real_start = u64_time_to_float(now);
timer.start(now);
// Run a modified bell ring sequence
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
ensure_equals("WCM t10 - 5.0 horizon timer has not timed out after 8 1-second bell rings",
timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false);
last_good_ring = u64_time_to_float(now);
// Jump forward and expire
now += float_time_to_u64(10.0);
ensure_equals("WCM t10 - 5.0 horizon timer expires on 8-second jump",
timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), true);
ensure_approximately_equals("WCM t10 - 2nd started matches start() time", started, real_start, 4);
ensure_approximately_equals("WCM t10 - 2nd stopped matches last ringBell() time", stopped, last_good_ring, 4);
ensure_equals("WCM t10 - 8 good ringBell()s", count, U64L(8));
ensure_equals("WCM t10 - single read only - 2nd start",
timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false);
}
}
} // end namespace tut

View File

@ -0,0 +1,91 @@
/**
* @file llprocinfo_test.cpp
* @brief Tests for the LLProcInfo class.
*
* $LicenseInfo:firstyear=2013&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2013, 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 "../llprocinfo.h"
#include "../test/lltut.h"
#include "../lltimer.h"
static const LLProcInfo::time_type bad_user(289375U), bad_system(275U);
namespace tut
{
struct procinfo_test
{
procinfo_test()
{
}
};
typedef test_group<procinfo_test> procinfo_group_t;
typedef procinfo_group_t::object procinfo_object_t;
tut::procinfo_group_t procinfo_instance("LLProcInfo");
// Basic invocation works
template<> template<>
void procinfo_object_t::test<1>()
{
LLProcInfo::time_type user(bad_user), system(bad_system);
set_test_name("getCPUUsage() basic function");
LLProcInfo::getCPUUsage(user, system);
ensure_not_equals("getCPUUsage() writes to its user argument", user, bad_user);
ensure_not_equals("getCPUUsage() writes to its system argument", system, bad_system);
}
// Time increases
template<> template<>
void procinfo_object_t::test<2>()
{
LLProcInfo::time_type user(bad_user), system(bad_system);
LLProcInfo::time_type user2(bad_user), system2(bad_system);
set_test_name("getCPUUsage() increases over time");
LLProcInfo::getCPUUsage(user, system);
for (int i(0); i < 100000; ++i)
{
ms_sleep(0);
}
LLProcInfo::getCPUUsage(user2, system2);
ensure_equals("getCPUUsage() user value doesn't decrease over time", user2 >= user, true);
ensure_equals("getCPUUsage() system value doesn't decrease over time", system2 >= system, true);
}
} // end namespace tut

View File

@ -0,0 +1,671 @@
1. HTTP Fetching in 15 Minutes
Let's start with a trivial working example. You'll need a throwaway
build of the viewer. And we'll use indra/newview/llappviewer.cpp as
the host module for these hacks.
First, add some headers:
#include "httpcommon.h"
#include "httprequest.h"
#include "httphandler.h"
You'll need to derive a class from HttpHandler (not HttpHandle).
This is used to deliver notifications of HTTP completion to your
code. Place it near the top, before LLDeferredTaskList, say:
class MyHandler : public LLCore::HttpHandler
{
public:
MyHandler()
: LLCore::HttpHandler()
{}
virtual void onCompleted(LLCore::HttpHandle /* handle */,
LLCore::HttpResponse * /* response */)
{
LL_INFOS("Hack") << "It is happening again." << LL_ENDL;
delete this; // Last statement
}
};
Add some statics up there as well:
// Our request object. Allocate during initialiation.
static LLCore::HttpRequest * my_request(NULL);
// The policy class for HTTP traffic.
// Use HttpRequest::DEFAULT_POLICY_ID, but DO NOT SHIP WITH THIS VALUE!!
static LLCore::HttpRequest::policy_t my_policy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
// Priority for HTTP requests. Use 0U.
static LLCore::HttpRequest::priority_t my_priority(0U);
In LLAppViewer::init() after mAppCoreHttp.init(), create a request object:
my_request = new LLCore::HttpRequest();
In LLAppViewer::mainLoop(), just before entering the while loop,
we'll kick off one HTTP request:
// Construct a handler object (we'll use the heap this time):
MyHandler * my_handler = new MyHandler;
// Issue a GET request to 'http://www.example.com/' kicking off
// all the I/O, retry logic, etc.
LLCore::HttpHandle handle;
handle = my_request->requestGet(my_policy,
my_priority,
"http://www.example.com/",
NULL,
NULL,
my_handler);
if (LLCORE_HTTP_HANDLE_INVALID == handle)
{
LL_WARNS("Hack") << "Failed to launch HTTP request. Try again."
<< LL_ENDL;
}
Finally, arrange to periodically call update() on the request object
to find out when the request completes. This will be done by
calling the onCompleted() method with status information and
response data from the HTTP operation. Add this to the
LLAppViewer::idle() method after the ping:
my_request->update(0);
That's it. Build it, run it and watch the log file. You should get
the "It is happening again." message indicating that the HTTP
operation completed in some manner.
2. What Does All That Mean
MyHandler/HttpHandler. This class replaces the Responder-style in
legacy code. One method is currently defined. It is used for all
request completions, successful or failed:
void onCompleted(LLCore::HttpHandle /* handle */,
LLCore::HttpResponse * /* response */);
The onCompleted() method is invoked as a callback during calls to
HttpRequest::update(). All I/O is completed asynchronously in
another thread. But notifications are polled by calling update()
and invoking a handler for completed requests.
In this example, the invocation also deletes the handler (which is
never referenced by the llcorehttp code again). But other
allocation models are possible including handlers shared by many
requests, stack-based handlers and handlers mixed in with other,
unrelated classes.
LLCore::HttpRequest(). Instances of this class are used to request
all major functions of the library. Initialization, starting
requests, delivering final notification of completion and various
utility operations are all done via instances. There is one very
important rule for instances:
Request objects may NOT be shared between threads.
my_priority. The APIs support the idea of priority ordering of
requests but it hasn't been implemented and the hope is that this
will become useless and removed from the interface. Use 0U except
as noted.
my_policy. This is an important one. This library attempts to
manage TCP connection usage more rigorously than in the past. This
is done by issuing requests to a queue that has various settable
properties. These establish connection usage for the queue as well
as how queues compete with one another. (This is patterned after
class-based queueing used in various networking stacks.) Several
classes are pre-defined. Deciding when to use an existing class and
when to create a new one will determine what kind of experience
users have. We'll pick up this question in detail below.
requestGet(). Issues an ordinary HTTP GET request to a given URL
and associating the request with a policy class, a priority and an
response handler. Two additional arguments, not used here, allow
for additional headers on the request and for per-request options.
If successful, the call returns a handle whose value is other than
LLCORE_HTTP_HANDLE_INVALID. The HTTP operation is then performed
asynchronously by another thread without any additional work by the
caller. If the handle returned is invalid, you can get the status
code by calling my_request->getStatus().
update(). To get notification that the request has completed, a
call to update() will invoke onCompleted() methods.
3. Refinements, Necessary and Otherwise
MyHandler::onCompleted(). You'll want to do something useful with
your response. Distinguish errors from successes and getting the
response body back in some form.
Add a new header:
#include "bufferarray.h"
Replace the existing MyHandler::onCompleted() definition with:
virtual void onCompleted(LLCore::HttpHandle /* handle */,
LLCore::HttpResponse * response)
{
LLCore::HttpStatus status = response->getStatus();
if (status)
{
// Successful request. Try to fetch the data
LLCore::BufferArray * data = response->getBody();
if (data && data->size())
{
// There's some data. A BufferArray is a linked list
// of buckets. We'll create a linear buffer and copy
// the data into it.
size_t data_len = data->size();
char * data_blob = new char [data_len + 1];
data->read(0, data_blob, data_len);
data_blob[data_len] = '\0';
// Process the data now in NUL-terminated string.
// Needs more scrubbing but this will do.
LL_INFOS("Hack") << "Received: " << data_blob << LL_ENDL;
// Free the temporary data
delete [] data_blob;
}
}
else
{
// Something went wrong. Translate the status to
// a meaningful message.
LL_WARNS("Hack") << "HTTP GET failed. Status: "
<< status.toTerseString()
<< ", Reason: " << status.toString()
<< LL_ENDL;
}
delete this; // Last statement
}
HttpHeaders. The header file "httprequest.h" documents the expected
important headers that will go out with the request. You can add to
these by including an HttpHeaders object with the requestGet() call.
These are typically setup once as part of init rather than
dynamically created.
Add another header:
#include "httpheaders.h"
In LLAppViewer::mainLoop(), add this alongside the allocation of
my_handler:
// Additional headers for all requests
LLCore::HttpHeaders * my_headers = new LLCore::HttpHeaders();
my_headers->append("Accept", "text/html, application/llsd+xml");
HttpOptions. Options are similar and include a mix of value types.
One interesting per-request option is the trace setting. This
enables various debug-type messages in the log file that show the
progress of the request through the library. It takes values from
zero to three with higher values giving more verbose logging. We'll
use '2' and this will also give us a chance to verify that
HttpHeaders works as expected.
Same as above, a new header:
#include "httpoptions.h"
And in LLAppView::mainLoop():
// Special options for requests
LLCore::HttpOptions * my_options = new LLCore::HttpOptions();
my_options->setTrace(2);
Now let's put that all together into a more complete requesting
sequence. Replace the existing invocation of requestGet() with this
slightly more elaborate block:
LLCore::HttpHandle handle;
handle = my_request->requestGet(my_policy,
my_priority,
"http://www.example.com/",
my_options,
my_headers,
my_handler);
if (LLCORE_HTTP_HANDLE_INVALID == handle)
{
LLCore::HttpStatus status = my_request->getStatus();
LL_WARNS("Hack") << "Failed to request HTTP GET. Status: "
<< status.toTerseString()
<< ", Reason: " << status.toString()
<< LL_ENDL;
delete my_handler; // No longer needed.
my_handler = NULL;
}
Build, run and examine the log file. You'll get some new data with
this run. First, you should get the www.example.com home page
content:
----------------------------------------------------------------------------
2013-09-17T20:26:51Z INFO: MyHandler::onCompleted: Received: <!doctype html>
<html>
<head>
<title>Example Domain</title>
<meta charset="utf-8" />
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type="text/css">
body {
background-color: #f0f0f2;
margin: 0;
padding: 0;
font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
}
div {
width: 600px;
margin: 5em auto;
padding: 50px;
background-color: #fff;
border-radius: 1em;
}
a:link, a:visited {
color: #38488f;
text-decoration: none;
}
@media (max-width: 700px) {
body {
background-color: #fff;
}
div {
width: auto;
margin: 0 auto;
border-radius: 0;
padding: 1em;
}
}
</style>
</head>
<body>
<div>
<h1>Example Domain</h1>
<p>This domain is established to be used for illustrative examples in documents. You may use this
domain in examples without prior coordination or asking for permission.</p>
<p><a href="http://www.iana.org/domains/example">More information...</a></p>
</div>
</body>
</html>
----------------------------------------------------------------------------
You'll also get a detailed trace of the HTTP operation itself. Note
the HEADEROUT line which shows the additional header added to the
request.
----------------------------------------------------------------------------
HttpService::processRequestQueue: TRACE, FromRequestQueue, Handle: 086D3148
HttpLibcurl::addOp: TRACE, ToActiveQueue, Handle: 086D3148, Actives: 0, Readies: 0
HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle: 086D3148, Type: TEXT, Data: About to connect() to www.example.com port 80 (#0)
HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle: 086D3148, Type: TEXT, Data: Trying 93.184.216.119...
HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle: 086D3148, Type: TEXT, Data: Connected to www.example.com (93.184.216.119) port 80 (#0)
HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle: 086D3148, Type: TEXT, Data: Connected to www.example.com (93.184.216.119) port 80 (#0)
HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle: 086D3148, Type: HEADEROUT, Data: GET / HTTP/1.1 Host: www.example.com Accept-Encoding: deflate, gzip Connection: keep-alive Keep-alive: 300 Accept: text/html, application/llsd+xml
HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle: 086D3148, Type: HEADERIN, Data: HTTP/1.1 200 OK
HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle: 086D3148, Type: HEADERIN, Data: Accept-Ranges: bytes
HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle: 086D3148, Type: HEADERIN, Data: Cache-Control: max-age=604800
HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle: 086D3148, Type: HEADERIN, Data: Content-Type: text/html
HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle: 086D3148, Type: HEADERIN, Data: Date: Tue, 17 Sep 2013 20:26:56 GMT
HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle: 086D3148, Type: HEADERIN, Data: Etag: "3012602696"
HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle: 086D3148, Type: HEADERIN, Data: Expires: Tue, 24 Sep 2013 20:26:56 GMT
HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle: 086D3148, Type: HEADERIN, Data: Last-Modified: Fri, 09 Aug 2013 23:54:35 GMT
HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle: 086D3148, Type: HEADERIN, Data: Server: ECS (ewr/1590)
HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle: 086D3148, Type: HEADERIN, Data: X-Cache: HIT
HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle: 086D3148, Type: HEADERIN, Data: x-ec-custom-error: 1
HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle: 086D3148, Type: HEADERIN, Data: Content-Length: 1270
HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle: 086D3148, Type: HEADERIN, Data:
HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle: 086D3148, Type: DATAIN, Data: 256 Bytes
HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle: 086D3148, Type: TEXT, Data: Connection #0 to host www.example.com left intact
HttpLibcurl::completeRequest: TRACE, RequestComplete, Handle: 086D3148, Status: Http_200
HttpOperation::addAsReply: TRACE, ToReplyQueue, Handle: 086D3148
----------------------------------------------------------------------------
4. What Does All That Mean, Part 2
HttpStatus. The HttpStatus object encodes errors from libcurl, the
library itself and HTTP status values. It does this to avoid
collapsing all non-HTTP error into a single '499' HTTP status and to
make errors distinct.
To aid programming, the usual bool conversions are available so that
you can write 'if (status)' and the expected thing will happen
whether it's an HTTP, libcurl or library error. There's also
provision to override the treatment of HTTP errors (making 404 a
success, say).
Share data, don't copy it. The library was started with the goal of
avoiding data copies as much as possible. Instead, read-only data
sharing across threads with atomic reference counts is used for a
number of data types. These currently are:
* BufferArray. Linked list of data blocks/HTTP bodies.
* HttpHeaders. Shared headers for both requests and responses.
* HttpOptions. Request-only data modifying HTTP behavior.
* HttpResponse. HTTP response description given to onCompleted.
Using objects of these types requires a few rules:
* Constructor always gives a reference to caller.
* References are dropped with release() not delete.
* Additional references may be taken out with addRef().
* Unless otherwise stated, once an object is shared with another
thread it should be treated as read-only. There's no
synchronization on the objects themselves.
HttpResponse. You'll encounter this mainly in onCompleted() methods.
Commonly-used interfaces on this object:
* getStatus() to return the final status of the request.
* getBody() to retrieve the response body which may be NULL or
zero-length.
* getContentType() to return the value of the 'Content-Type'
header or an empty string if none was sent.
This is a reference-counted object so you can call addRef() on it
and hold onto the response for an arbitrary time. But you'll
usually just call a few methods and return from onCompleted() whose
caller will release the object.
BufferArray. The core data representation for request and response
bodies. In HTTP responses, it's fetched with the getBody() method
and may be NULL or non-NULL with zero length. All successful data
handling should check both conditions before attempting to fetch
data from the object. Data access model uses simple read/write
semantics:
* append()
* size()
* read()
* write()
(There is a more sophisticated stream adapter that extends these
methods and will be covered below.) So, one way to retrieve data
from a request is as follows:
LLCore::BufferArray * data = response->getBody();
if (data && data->size())
{
size_t data_len = data->size();
char * data_blob = new char [data_len + 1];
data->read(0, data_blob, data_len);
HttpOptions and HttpResponse. Really just simple containers of POD
and std::string pairs. But reference counted and the rule about not
modifying after sharing must be followed. You'll have the urge to
change options dynamically at some point. And you'll try to do that
by just writing new values to the shared object. And in tests
everything will appear to work. Then you ship and people in the
real world start hitting read/write races in strings and then crash.
Don't be lazy.
HttpHandle. Uniquely identifies a request and can be used to
identify it in an onCompleted() method or cancel it if it's still
queued. But as soon as a request's onCompleted() invocation
returns, the handle becomes invalid and may be reused immediately
for new requests. Don't hold on to handles after notification.
5. And Still More Refinements
(Note: The following refinements are just code fragments. They
don't directly fit into the working example above. But they
demonstrate several idioms you'll want to copy.)
LLSD, std::streambuf, std::iostream. The read(), write() and
append() methods may be adequate for your purposes. But we use a
lot of LLSD. Its interfaces aren't particularly compatible with
BufferArray. And so two adapters are available to give
stream-like behaviors: BufferArrayStreamBuf and BufferArrayStream,
which implement the std::streambuf and std::iostream interfaces,
respectively.
A std::streambuf interface isn't something you'll want to use
directly. Instead, you'll use the much friendlier std::iostream
interface found in BufferArrayStream. This adapter gives you all
the '>>' and '<<' operators you'll want as well as working
directly with the LLSD conversion operators.
Some new headers:
#include "bufferstream.h"
#include "llsdserialize.h"
And an updated fragment based on onCompleted() above:
// Successful request. Try to fetch the data
LLCore::BufferArray * data = response->getBody();
LLSD resp_llsd;
if (data && data->size())
{
// There's some data and we expect this to be
// LLSD. Checking of content type and validation
// during parsing would be admirable additions.
// But we'll forgo that now.
LLCore::BufferArrayStream data_stream(data);
LLSDSerialize::fromXML(resp_llsd, data_stream);
}
LL_INFOS("Hack") << "LLSD Received: " << resp_llsd << LL_ENDL;
}
else
{
Converting an LLSD object into an XML stream stored in a
BufferArray is just the reverse of the above:
BufferArray * data = new BufferArray();
LLCore::BufferArrayStream data_stream(data);
LLSD src_llsd;
src_llsd["foo"] = "bar";
LLSDSerialize::toXML(src_llsd, data_stream);
// 'data' now contains an XML payload and can be sent
// to a web service using the requestPut() or requestPost()
// methods.
... requestPost(...);
// And don't forget to release the BufferArray.
data->release();
data = NULL;
LLSD will often go hand-in-hand with BufferArray and data
transport. But you can also do all the streaming I/O you'd expect
of a std::iostream object:
BufferArray * data = new BufferArray();
LLCore::BufferArrayStream data_stream(data);
data_stream << "Hello, World!" << 29.4 << '\n';
std::string str;
data_stream >> str;
std::cout << str << std::endl;
data->release();
// Actual delete will occur when 'data_stream'
// falls out of scope and is destructed.
Scoping objects and cleaning up. The examples haven't bothered
with cleanup of objects that are no longer needed. Instead, most
objects have been allocated as if they were global and eternal.
You'll put the objects in more appropriate feature objects and
clean them up as a group. Here's a checklist for actions you may
need to take on cleanup:
* Call delete on:
o HttpHandlers created on the heap
o HttpRequest objects
* Call release() on:
o BufferArray objects
o HttpHeaders objects
o HttpOptions objects
o HttpResponse objects
On program exit, as threads wind down, the library continues to
operate safely. Threads don't interact via the library and even
dangling references to HttpHandler objects are safe. If you don't
call HttpRequest::update(), handler references are never
dereferenced.
You can take a more thorough approach to wind-down. Keep a list
of HttpHandles (not HttpHandlers) of outstanding requests. For
each of these, call HttpRequest::requestCancel() to cancel the
operation. (Don't add the cancel requests' handled to the list.)
This will cancel the outstanding requests that haven't completed.
Canceled or completed, all requests will queue notifications. You
can now cycle calling update() discarding responses. Continue
until all requests notify or a few seconds have passed.
Global startup and shutdown is handled in the viewer. But you can
learn about it in the code or in the documentation in the headers.
6. Choosing a Policy Class
Now it's time to get rid of the default policy class. Take a look
at the policy class definitions in newview/llappcorehttp.h.
Ideally, you'll find one that's compatible with what you're doing.
Some of the compatibility guidelines are:
* Destination: Pair of host and port. Mixing requests with
different destinations may cause more connection setup and tear
down.
* Method: http or https. Usually moot given destination. But
mixing these may also cause connection churn.
* Transfer size: If you're moving 100MB at a time and you make your
requests to the same policy class as a lot of small, fast event
information that fast traffic is going to get stuck behind you
and someone's experience is going to be miserable.
* Long poll requests: These are long-lived, must- do operations.
They have a special home called AP_LONG_POLL.
* Concurrency: High concurrency (5 or more) and large transfer
sizes are incompatible. Another head-of-the-line problem. High
concurrency is tolerated when it's desired to get maximal
throughput. Mesh and texture downloads, for example.
* Pipelined: If your requests are not idempotent, stay away from
anything marked 'soon' or 'yes'. Hidden retries may be a
problem for you. For now, would also recommend keeping PUT and
POST requests out of classes that may be pipelined. Support for
that is still a bit new.
If you haven't found a compatible match, you can either create a
new class (llappcorehttp.*) or just use AP_DEFAULT, the catchall
class when all else fails. Inventory query operations might be a
candidate for a new class that supported pipelining on https:.
Same with display name lookups and other bursty-at-login
operations. For other things, AP_DEFAULT will do what it can and
will, in some way or another, tolerate any usage. Whether the
users' experiences are good are for you to determine.
7. FAQ
Q1. What do these policy classes achieve?
A1. Previously, HTTP-using code in the viewer was written as if
it were some isolated, local operation that didn't have to
consider resources, contention or impact on services and the
larger environment. The result was an application with on the
order of 100 HTTP launch points in its codebase that could create
dozens or even 100's of TCP connections zeroing in on grid
services and disrupting networking equipment, web services and
innocent users. The use of policy classes (modeled on
http://en.wikipedia.org/wiki/Class-based_queueing) is a means to
restrict connection concurrency, good and necessary in itself. In
turn, that reduces demands on an expensive resource (connection
setup and concurrency) which relieves strain on network points.
That enables connection keepalive and opportunites for true
improvements in throughput and user experience.
Another aspect of the classes is that they give some control over
how competing demands for the network will be apportioned. If
mesh fetches, texture fetches and inventory queries are all being
made at once, the relative weights of their classes' concurrency
limits established that apportioning. We now have an opportunity
to balance the entire viewer system.
Q2. How's that data sharing with refcounts working for you?
A2. Meh. It does reduce memory churn and the frequency at which
free blocks must be moved between threads. But it's also a design
for static configuration and dynamic reconfiguration (not
requiring a restart) is favored. Creating new options for every
request isn't too bad, it a sequence of "new, fill, request,
release" for each requested operation. That in contrast to doing
the "new, fill, release" at startup. The bad comes in getting at
the source data. One rule in this work was "no new thread
problems." And one source for those is pulling setting values out
of gSettings in threads. None of that is thread safe though we
tend to get away with it.
Q3. What needs to be done?
A3. There's a To-Do list in _httpinternal.h. It has both large
and small projects here if someone would like to try changes.

View File

@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
* Copyright (C) 2012-2013, 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
@ -36,7 +36,8 @@
// General library to-do list
//
// - Implement policy classes. Structure is mostly there just didn't
// need it for the first consumer.
// need it for the first consumer. [Classes are there. More
// advanced features, like borrowing, aren't there yet.]
// - 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
@ -46,6 +47,7 @@
// 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.
// [Mostly fixed]
// - Move HttpOpRequest::prepareRequest() to HttpLibcurl for the
// pedantic.
// - Update downloader and other long-duration services are going to
@ -64,6 +66,12 @@
// 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.
// - Change the transfer timeout scheme. We're less interested in
// absolute time, in most cases, than in continuous progress.
// - Many of the policy class settings are currently applied to the
// entire class. Some, like connection limits, would be better
// applied to each destination target making multiple targets
// independent.
//
// Integration to-do list
// - LLTextureFetch still needs a major refactor. The use of
@ -73,7 +81,6 @@
// 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
@ -97,8 +104,8 @@ 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;
// *TODO: Currently limited to the default class + 1, extend.
const int HTTP_POLICY_CLASS_LIMIT = 8;
// Debug/informational tracing. Used both
// as a global option and in per-request traces.
@ -129,6 +136,7 @@ const int HTTP_REDIRECTS_DEFAULT = 10;
// Retries and time-on-queue are not included and aren't
// accounted for.
const long HTTP_REQUEST_TIMEOUT_DEFAULT = 30L;
const long HTTP_REQUEST_XFER_TIMEOUT_DEFAULT = 0L;
const long HTTP_REQUEST_TIMEOUT_MIN = 0L;
const long HTTP_REQUEST_TIMEOUT_MAX = 3600L;
@ -137,6 +145,11 @@ const int HTTP_CONNECTION_LIMIT_DEFAULT = 8;
const int HTTP_CONNECTION_LIMIT_MIN = 1;
const int HTTP_CONNECTION_LIMIT_MAX = 256;
// Miscellaneous defaults
const long HTTP_PIPELINING_DEFAULT = 0L;
const bool HTTP_USE_RETRY_AFTER_DEFAULT = true;
const long HTTP_THROTTLE_RATE_DEFAULT = 0L;
// Tuning parameters
// Time worker thread sleeps after a pass through the

View File

@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
* Copyright (C) 2012-2013, 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
@ -41,7 +41,8 @@ namespace LLCore
HttpLibcurl::HttpLibcurl(HttpService * service)
: mService(service),
mPolicyCount(0),
mMultiHandles(NULL)
mMultiHandles(NULL),
mActiveHandles(NULL)
{}
@ -77,6 +78,9 @@ void HttpLibcurl::shutdown()
delete [] mMultiHandles;
mMultiHandles = NULL;
delete [] mActiveHandles;
mActiveHandles = NULL;
}
mPolicyCount = 0;
@ -90,9 +94,12 @@ void HttpLibcurl::start(int policy_count)
mPolicyCount = policy_count;
mMultiHandles = new CURLM * [mPolicyCount];
mActiveHandles = new int [mPolicyCount];
for (int policy_class(0); policy_class < mPolicyCount; ++policy_class)
{
mMultiHandles[policy_class] = curl_multi_init();
mActiveHandles[policy_class] = 0;
}
}
@ -110,8 +117,10 @@ HttpService::ELoopSpeed HttpLibcurl::processTransport()
// Give libcurl some cycles to do I/O & callbacks
for (int policy_class(0); policy_class < mPolicyCount; ++policy_class)
{
if (! mMultiHandles[policy_class])
if (! mActiveHandles[policy_class] || ! mMultiHandles[policy_class])
{
continue;
}
int running(0);
CURLMcode status(CURLM_CALL_MULTI_PERFORM);
@ -132,12 +141,10 @@ HttpService::ELoopSpeed HttpLibcurl::processTransport()
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
completeRequest(mMultiHandles[policy_class], handle, result);
handle = NULL; // No longer valid on return
ret = HttpService::NORMAL; // If anything completes, we may have a free slot.
// Turning around quickly reduces connection gap by 7-10mS.
}
else if (CURLMSG_NONE == msg->msg)
{
@ -193,6 +200,7 @@ void HttpLibcurl::addOp(HttpOpRequest * op)
// On success, make operation active
mActiveOps.insert(op);
++mActiveHandles[op->mReqPolicy];
}
@ -214,6 +222,7 @@ bool HttpLibcurl::cancel(HttpHandle handle)
// Drop references
mActiveOps.erase(it);
--mActiveHandles[op->mReqPolicy];
op->release();
return true;
@ -240,7 +249,7 @@ void HttpLibcurl::cancelRequest(HttpOpRequest * op)
{
LL_INFOS("CoreHttp") << "TRACE, RequestCanceled, Handle: "
<< static_cast<HttpHandle>(op)
<< ", Status: " << op->mStatus.toHex()
<< ", Status: " << op->mStatus.toTerseString()
<< LL_ENDL;
}
@ -275,6 +284,7 @@ bool HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode
// Deactivate request
mActiveOps.erase(it);
--mActiveHandles[op->mReqPolicy];
op->mCurlActive = false;
// Set final status of request if it hasn't failed by other mechanisms yet
@ -316,7 +326,7 @@ bool HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode
{
LL_INFOS("CoreHttp") << "TRACE, RequestComplete, Handle: "
<< static_cast<HttpHandle>(op)
<< ", Status: " << op->mStatus.toHex()
<< ", Status: " << op->mStatus.toTerseString()
<< LL_ENDL;
}
@ -336,19 +346,9 @@ int HttpLibcurl::getActiveCount() const
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;
llassert_always(policy_class < mPolicyCount);
return mActiveHandles ? mActiveHandles[policy_class] : 0;
}
@ -359,12 +359,17 @@ int HttpLibcurl::getActiveCountInClass(int policy_class) const
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)
const HttpHeaders::const_iterator end(headers->end());
for (HttpHeaders::const_iterator it(headers->begin()); end != it; ++it)
{
slist = curl_slist_append(slist, (*it).c_str());
static const char sep[] = ": ";
std::string header;
header.reserve((*it).first.size() + (*it).second.size() + sizeof(sep));
header.append((*it).first);
header.append(sep);
header.append((*it).second);
slist = curl_slist_append(slist, header.c_str());
}
return slist;
}

View File

@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
* Copyright (C) 2012-2013, 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
@ -71,16 +71,22 @@ public:
///
/// @return Indication of how long this method is
/// willing to wait for next service call.
///
/// Threading: called by worker thread.
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.)
///
/// Threading: called by worker thread.
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.
///
/// Threading: called by init thread.
void start(int policy_count);
/// Synchronously stop libcurl operations. All active requests
@ -91,9 +97,13 @@ public:
/// respective reply queues.
///
/// Can be restarted with a start() call.
///
/// Threading: called by worker thread.
void shutdown();
/// Return global and per-class counts of active requests.
///
/// Threading: called by worker thread.
int getActiveCount() const;
int getActiveCountInClass(int policy_class) const;
@ -103,6 +113,7 @@ public:
///
/// @return True if handle was found and operation canceled.
///
/// Threading: called by worker thread.
bool cancel(HttpHandle handle);
protected:
@ -121,7 +132,8 @@ protected:
HttpService * mService; // Simple reference, not owner
active_set_t mActiveOps;
int mPolicyCount;
CURLM ** mMultiHandles;
CURLM ** mMultiHandles; // One handle per policy class
int * mActiveHandles; // Active count per policy class
}; // end class HttpLibcurl
} // end namespace LLCore

View File

@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
* Copyright (C) 2012-2013, 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
@ -53,7 +53,7 @@ HttpOperation::HttpOperation()
mUserHandler(NULL),
mReqPolicy(HttpRequest::DEFAULT_POLICY_ID),
mReqPriority(0U),
mTracing(0)
mTracing(HTTP_TRACE_OFF)
{
mMetricCreated = totalTime();
}
@ -94,7 +94,7 @@ 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_ERRS("CoreHttp") << "Default stageFromRequest method may not be called."
<< LL_ENDL;
}
@ -104,7 +104,7 @@ 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_ERRS("CoreHttp") << "Default stageFromReady method may not be called."
<< LL_ENDL;
}
@ -114,7 +114,7 @@ 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_ERRS("CoreHttp") << "Default stageFromActive method may not be called."
<< LL_ENDL;
}

View File

@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
* Copyright (C) 2012-2013, 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
@ -72,7 +72,7 @@ class HttpService;
class HttpOperation : public LLCoreInt::RefCounted
{
public:
/// Threading: called by a consumer/application thread.
/// Threading: called by consumer thread.
HttpOperation();
protected:
@ -108,7 +108,7 @@ public:
/// by the worker thread. This is passible data
/// until notification is performed.
///
/// Threading: called by application thread.
/// Threading: called by consumer thread.
///
void setReplyPath(HttpReplyQueue * reply_queue,
HttpHandler * handler);
@ -141,7 +141,7 @@ public:
/// call to HttpRequest::update(). This method does the necessary
/// dispatching.
///
/// Threading: called by application thread.
/// Threading: called by consumer thread.
///
virtual void visitNotifier(HttpRequest *);

View File

@ -64,6 +64,15 @@ int parse_content_range_header(char * buffer,
unsigned int * last,
unsigned int * length);
// Similar for Retry-After headers. Only parses the delta form
// of the header, HTTP time formats aren't interesting for client
// purposes.
//
// @return 0 if successfully parsed and seconds time delta
// returned in time argument.
//
int parse_retry_after_header(char * buffer, int * time);
// Take data from libcurl's CURLOPT_DEBUGFUNCTION callback and
// escape and format it for a tracing line in logging. Absolutely
@ -74,14 +83,16 @@ 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);
// OS-neutral string comparisons of various types.
int os_strcasecmp(const char * s1, const char * s2);
char * os_strtok_r(char * str, const char * delim, char ** saveptr);
char * os_strtrim(char * str);
char * os_strltrim(char * str);
void os_strlower(char * str);
static const char * const hdr_whitespace(" \t");
static const char * const hdr_separator(": \t");
// Error testing and reporting for libcurl status codes
void check_curl_easy_code(CURLcode code);
void check_curl_easy_code(CURLcode code, int curl_setopt_option);
} // end anonymous namespace
@ -104,12 +115,15 @@ HttpOpRequest::HttpOpRequest()
mCurlService(NULL),
mCurlHeaders(NULL),
mCurlBodyPos(0),
mCurlTemp(NULL),
mCurlTempLen(0),
mReplyBody(NULL),
mReplyOffset(0),
mReplyLength(0),
mReplyFullLength(0),
mReplyHeaders(NULL),
mPolicyRetries(0),
mPolicy503Retries(0),
mPolicyRetryAt(HttpTime(0)),
mPolicyRetryLimit(HTTP_RETRY_COUNT_DEFAULT)
{
@ -153,6 +167,10 @@ HttpOpRequest::~HttpOpRequest()
mCurlHeaders = NULL;
}
delete [] mCurlTemp;
mCurlTemp = NULL;
mCurlTempLen = 0;
if (mReplyBody)
{
mReplyBody->release();
@ -208,6 +226,11 @@ void HttpOpRequest::stageFromActive(HttpService * service)
mCurlHeaders = NULL;
}
// Also not needed on the other side
delete [] mCurlTemp;
mCurlTemp = NULL;
mCurlTempLen = 0;
addAsReply();
}
@ -226,6 +249,7 @@ void HttpOpRequest::visitNotifier(HttpRequest * request)
response->setRange(mReplyOffset, mReplyLength, mReplyFullLength);
}
response->setContentType(mReplyConType);
response->setRetries(mPolicyRetries, mPolicy503Retries);
mUserHandler->onCompleted(static_cast<HttpHandle>(this), response);
@ -335,6 +359,10 @@ void HttpOpRequest::setupCommon(HttpRequest::policy_t policy_id,
{
mProcFlags |= PF_SAVE_HEADERS;
}
if (options->getUseRetryAfter())
{
mProcFlags |= PF_USE_RETRY_AFTER;
}
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));
@ -350,6 +378,8 @@ void HttpOpRequest::setupCommon(HttpRequest::policy_t policy_id,
//
HttpStatus HttpOpRequest::prepareRequest(HttpService * service)
{
CURLcode code;
// Scrub transport and result data for retried op case
mCurlActive = false;
mCurlHandle = NULL;
@ -379,64 +409,108 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service)
// *FIXME: better error handling later
HttpStatus status;
// Get policy options
// Get global policy options
HttpPolicyGlobal & policy(service->getPolicy().getGlobalOptions());
mCurlHandle = LLCurl::createStandardCurlHandle();
if (! mCurlHandle)
{
// We're in trouble. We'll continue but it won't go well.
LL_WARNS("CoreHttp") << "Failed to allocate libcurl easy handle. Continuing."
<< LL_ENDL;
return HttpStatus(HttpStatus::LLCORE, HE_BAD_ALLOC);
}
code = curl_easy_setopt(mCurlHandle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
check_curl_easy_code(code, CURLOPT_IPRESOLVE);
code = curl_easy_setopt(mCurlHandle, CURLOPT_NOSIGNAL, 1);
check_curl_easy_code(code, CURLOPT_NOSIGNAL);
code = curl_easy_setopt(mCurlHandle, CURLOPT_NOPROGRESS, 1);
check_curl_easy_code(code, CURLOPT_NOPROGRESS);
code = curl_easy_setopt(mCurlHandle, CURLOPT_URL, mReqURL.c_str());
check_curl_easy_code(code, CURLOPT_URL);
code = curl_easy_setopt(mCurlHandle, CURLOPT_PRIVATE, this);
check_curl_easy_code(code, CURLOPT_PRIVATE);
code = curl_easy_setopt(mCurlHandle, CURLOPT_ENCODING, "");
check_curl_easy_code(code, CURLOPT_ENCODING);
curl_easy_setopt(mCurlHandle, CURLOPT_WRITEFUNCTION, writeCallback);
curl_easy_setopt(mCurlHandle, CURLOPT_READFUNCTION, readCallback);
curl_easy_setopt(mCurlHandle, CURLOPT_READDATA, this);
curl_easy_setopt(mCurlHandle, CURLOPT_WRITEDATA, this);
curl_easy_setopt(mCurlHandle, CURLOPT_URL, mReqURL.c_str());
curl_easy_setopt(mCurlHandle, CURLOPT_PRIVATE, this);
curl_easy_setopt(mCurlHandle, CURLOPT_MAXREDIRS, HTTP_REDIRECTS_DEFAULT);
// The Linksys WRT54G V5 router has an issue with frequent
// DNS lookups from LAN machines. If they happen too often,
// like for every HTTP request, the router gets annoyed after
// about 700 or so requests and starts issuing TCP RSTs to
// new connections. Reuse the DNS lookups for even a few
// seconds and no RSTs.
code = curl_easy_setopt(mCurlHandle, CURLOPT_DNS_CACHE_TIMEOUT, 15);
check_curl_easy_code(code, CURLOPT_DNS_CACHE_TIMEOUT);
code = curl_easy_setopt(mCurlHandle, CURLOPT_AUTOREFERER, 1);
check_curl_easy_code(code, CURLOPT_AUTOREFERER);
code = curl_easy_setopt(mCurlHandle, CURLOPT_FOLLOWLOCATION, 1);
check_curl_easy_code(code, CURLOPT_FOLLOWLOCATION);
code = curl_easy_setopt(mCurlHandle, CURLOPT_MAXREDIRS, HTTP_REDIRECTS_DEFAULT);
check_curl_easy_code(code, CURLOPT_MAXREDIRS);
code = curl_easy_setopt(mCurlHandle, CURLOPT_WRITEFUNCTION, writeCallback);
check_curl_easy_code(code, CURLOPT_WRITEFUNCTION);
code = curl_easy_setopt(mCurlHandle, CURLOPT_WRITEDATA, this);
check_curl_easy_code(code, CURLOPT_WRITEDATA);
code = curl_easy_setopt(mCurlHandle, CURLOPT_READFUNCTION, readCallback);
check_curl_easy_code(code, CURLOPT_READFUNCTION);
code = curl_easy_setopt(mCurlHandle, CURLOPT_READDATA, this);
check_curl_easy_code(code, CURLOPT_READDATA);
code = curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYPEER, 1);
check_curl_easy_code(code, CURLOPT_SSL_VERIFYPEER);
code = curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYHOST, 0);
check_curl_easy_code(code, CURLOPT_SSL_VERIFYHOST);
const std::string * opt_value(NULL);
long opt_long(0L);
policy.get(HttpRequest::GP_LLPROXY, &opt_long);
if (opt_long)
if (policy.mUseLLProxy)
{
// 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))
else if (policy.mHttpProxy.size())
{
// *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);
code = curl_easy_setopt(mCurlHandle, CURLOPT_PROXY, policy.mHttpProxy.c_str());
check_curl_easy_code(code, CURLOPT_PROXY);
code = curl_easy_setopt(mCurlHandle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
check_curl_easy_code(code, CURLOPT_PROXYTYPE);
}
if (policy.get(HttpRequest::GP_CA_PATH, &opt_value))
if (policy.mCAPath.size())
{
curl_easy_setopt(mCurlHandle, CURLOPT_CAPATH, opt_value->c_str());
code = curl_easy_setopt(mCurlHandle, CURLOPT_CAPATH, policy.mCAPath.c_str());
check_curl_easy_code(code, CURLOPT_CAPATH);
}
if (policy.get(HttpRequest::GP_CA_FILE, &opt_value))
if (policy.mCAFile.size())
{
curl_easy_setopt(mCurlHandle, CURLOPT_CAINFO, opt_value->c_str());
code = curl_easy_setopt(mCurlHandle, CURLOPT_CAINFO, policy.mCAFile.c_str());
check_curl_easy_code(code, CURLOPT_CAINFO);
}
switch (mReqMethod)
{
case HOR_GET:
curl_easy_setopt(mCurlHandle, CURLOPT_HTTPGET, 1);
code = curl_easy_setopt(mCurlHandle, CURLOPT_HTTPGET, 1);
check_curl_easy_code(code, CURLOPT_HTTPGET);
mCurlHeaders = curl_slist_append(mCurlHeaders, "Connection: keep-alive");
mCurlHeaders = curl_slist_append(mCurlHeaders, "Keep-alive: 300");
break;
case HOR_POST:
{
curl_easy_setopt(mCurlHandle, CURLOPT_POST, 1);
curl_easy_setopt(mCurlHandle, CURLOPT_ENCODING, "");
code = curl_easy_setopt(mCurlHandle, CURLOPT_POST, 1);
check_curl_easy_code(code, CURLOPT_POST);
code = curl_easy_setopt(mCurlHandle, CURLOPT_ENCODING, "");
check_curl_easy_code(code, 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);
code = curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDS, static_cast<void *>(NULL));
check_curl_easy_code(code, CURLOPT_POSTFIELDS);
code = curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDSIZE, data_size);
check_curl_easy_code(code, CURLOPT_POSTFIELDSIZE);
mCurlHeaders = curl_slist_append(mCurlHeaders, "Expect:");
mCurlHeaders = curl_slist_append(mCurlHeaders, "Connection: keep-alive");
mCurlHeaders = curl_slist_append(mCurlHeaders, "Keep-alive: 300");
@ -445,14 +519,17 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service)
case HOR_PUT:
{
curl_easy_setopt(mCurlHandle, CURLOPT_UPLOAD, 1);
code = curl_easy_setopt(mCurlHandle, CURLOPT_UPLOAD, 1);
check_curl_easy_code(code, CURLOPT_UPLOAD);
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);
code = curl_easy_setopt(mCurlHandle, CURLOPT_INFILESIZE, data_size);
check_curl_easy_code(code, CURLOPT_INFILESIZE);
code = curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDS, (void *) NULL);
check_curl_easy_code(code, CURLOPT_POSTFIELDS);
mCurlHeaders = curl_slist_append(mCurlHeaders, "Expect:");
mCurlHeaders = curl_slist_append(mCurlHeaders, "Connection: keep-alive");
mCurlHeaders = curl_slist_append(mCurlHeaders, "Keep-alive: 300");
@ -469,9 +546,12 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service)
// 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);
code = curl_easy_setopt(mCurlHandle, CURLOPT_VERBOSE, 1);
check_curl_easy_code(code, CURLOPT_VERBOSE);
code = curl_easy_setopt(mCurlHandle, CURLOPT_DEBUGDATA, this);
check_curl_easy_code(code, CURLOPT_DEBUGDATA);
code = curl_easy_setopt(mCurlHandle, CURLOPT_DEBUGFUNCTION, debugCallback);
check_curl_easy_code(code, CURLOPT_DEBUGFUNCTION);
}
// There's a CURLOPT for this now...
@ -499,13 +579,22 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service)
// Request options
long timeout(HTTP_REQUEST_TIMEOUT_DEFAULT);
long xfer_timeout(HTTP_REQUEST_XFER_TIMEOUT_DEFAULT);
if (mReqOptions)
{
{
timeout = mReqOptions->getTimeout();
timeout = llclamp(timeout, HTTP_REQUEST_TIMEOUT_MIN, HTTP_REQUEST_TIMEOUT_MAX);
xfer_timeout = mReqOptions->getTransferTimeout();
xfer_timeout = llclamp(xfer_timeout, HTTP_REQUEST_TIMEOUT_MIN, HTTP_REQUEST_TIMEOUT_MAX);
}
curl_easy_setopt(mCurlHandle, CURLOPT_TIMEOUT, timeout);
curl_easy_setopt(mCurlHandle, CURLOPT_CONNECTTIMEOUT, timeout);
if (xfer_timeout == 0L)
{
xfer_timeout = timeout;
}
code = curl_easy_setopt(mCurlHandle, CURLOPT_TIMEOUT, xfer_timeout);
check_curl_easy_code(code, CURLOPT_TIMEOUT);
code = curl_easy_setopt(mCurlHandle, CURLOPT_CONNECTTIMEOUT, timeout);
check_curl_easy_code(code, CURLOPT_CONNECTTIMEOUT);
// Request headers
if (mReqHeaders)
@ -513,12 +602,15 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service)
// Caller's headers last to override
mCurlHeaders = append_headers_to_slist(mReqHeaders, mCurlHeaders);
}
curl_easy_setopt(mCurlHandle, CURLOPT_HTTPHEADER, mCurlHeaders);
code = curl_easy_setopt(mCurlHandle, CURLOPT_HTTPHEADER, mCurlHeaders);
check_curl_easy_code(code, CURLOPT_HTTPHEADER);
if (mProcFlags & (PF_SCAN_RANGE_HEADER | PF_SAVE_HEADERS))
if (mProcFlags & (PF_SCAN_RANGE_HEADER | PF_SAVE_HEADERS | PF_USE_RETRY_AFTER))
{
curl_easy_setopt(mCurlHandle, CURLOPT_HEADERFUNCTION, headerCallback);
curl_easy_setopt(mCurlHandle, CURLOPT_HEADERDATA, this);
code = curl_easy_setopt(mCurlHandle, CURLOPT_HEADERFUNCTION, headerCallback);
check_curl_easy_code(code, CURLOPT_HEADERFUNCTION);
code = curl_easy_setopt(mCurlHandle, CURLOPT_HEADERDATA, this);
check_curl_easy_code(code, CURLOPT_HEADERDATA);
}
if (status)
@ -559,7 +651,7 @@ size_t HttpOpRequest::readCallback(void * data, size_t size, size_t nmemb, void
{
// Warn but continue if the read position moves beyond end-of-body
// for some reason.
LL_WARNS("HttpCore") << "Request body position beyond body size. Truncating request body."
LL_WARNS("CoreHttp") << "Request body position beyond body size. Truncating request body."
<< LL_ENDL;
}
return 0;
@ -576,15 +668,15 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi
{
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;
static const char con_ran_line[] = "content-range";
static const char con_retry_line[] = "retry-after";
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
bool is_header(true);
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
@ -592,11 +684,13 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi
op->mReplyOffset = 0;
op->mReplyLength = 0;
op->mReplyFullLength = 0;
op->mReplyRetryAfter = 0;
op->mStatus = HttpStatus();
if (op->mReplyHeaders)
{
op->mReplyHeaders->mHeaders.clear();
op->mReplyHeaders->clear();
}
is_header = false;
}
// Nothing in here wants a final CR/LF combination. Remove
@ -609,52 +703,109 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi
--wanted_hdr_size;
}
}
// Copy and normalize header fragments for the following
// stages. Would like to modify the data in-place but that
// may not be allowed and we need one byte extra for NUL.
// At the end of this we will have:
//
// If ':' present in header:
// 1. name points to text to left of colon which
// will be ascii lower-cased and left and right
// trimmed of whitespace.
// 2. value points to text to right of colon which
// will be left trimmed of whitespace.
// Otherwise:
// 1. name points to header which will be left
// trimmed of whitespace.
// 2. value is NULL
// Any non-NULL pointer may point to a zero-length string.
//
if (wanted_hdr_size >= op->mCurlTempLen)
{
delete [] op->mCurlTemp;
op->mCurlTempLen = 2 * wanted_hdr_size + 1;
op->mCurlTemp = new char [op->mCurlTempLen];
}
memcpy(op->mCurlTemp, hdr_data, wanted_hdr_size);
op->mCurlTemp[wanted_hdr_size] = '\0';
char * name(op->mCurlTemp);
char * value(strchr(name, ':'));
if (value)
{
*value++ = '\0';
os_strlower(name);
name = os_strtrim(name);
value = os_strltrim(value);
}
else
{
// Doesn't look well-formed, do minimal normalization on it
name = os_strltrim(name);
}
// Normalized, now reject headers with empty names.
if (! *name)
{
// No use continuing
return hdr_size;
}
// Save header if caller wants them in the response
if (op->mProcFlags & PF_SAVE_HEADERS)
if (is_header && 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));
op->mReplyHeaders->append(name, value ? value : "");
}
// From this point, header-specific processors are free to
// modify the header value.
// Detect and parse 'Content-Range' headers
if (op->mProcFlags & PF_SCAN_RANGE_HEADER)
if (is_header
&& op->mProcFlags & PF_SCAN_RANGE_HEADER
&& value && *value
&& ! strcmp(name, con_ran_line))
{
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;
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;
}
if (! (status = parse_content_range_header(value, &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, wanted_hdr_size)
<< "'. Ignoring."
<< LL_ENDL;
}
}
// Detect and parse 'Retry-After' headers
if (is_header
&& op->mProcFlags & PF_USE_RETRY_AFTER
&& value && *value
&& ! strcmp(name, con_retry_line))
{
int time(0);
if (! parse_retry_after_header(value, &time))
{
op->mReplyRetryAfter = time;
}
}
@ -769,14 +920,16 @@ int parse_content_range_header(char * buffer,
unsigned int * last,
unsigned int * length)
{
static const char * const hdr_whitespace(" \t");
char * tok_state(NULL), * tok(NULL);
bool match(true);
if (! os_strtok_r(buffer, hdr_separator, &tok_state))
if (! (tok = os_strtok_r(buffer, hdr_whitespace, &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)))
else
match = (0 == os_strcasecmp("bytes", tok));
if (match && ! (tok = os_strtok_r(NULL, hdr_whitespace, &tok_state)))
match = false;
if (match)
{
@ -815,6 +968,25 @@ int parse_content_range_header(char * buffer,
}
int parse_retry_after_header(char * buffer, int * time)
{
char * endptr(buffer);
long lcl_time(strtol(buffer, &endptr, 10));
if (*endptr == '\0' && endptr != buffer && lcl_time > 0)
{
*time = lcl_time;
return 0;
}
// Could attempt to parse HTTP time here but we're not really
// interested in it. Scheduling based on wallclock time on
// user hardware will lead to tears.
// 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;
@ -851,15 +1023,6 @@ void escape_libcurl_debug_data(char * buffer, size_t len, bool scrub, std::strin
}
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)
{
@ -881,6 +1044,73 @@ char * os_strtok_r(char *str, const char *delim, char ** savestate)
}
} // end anonymous namespace
void os_strlower(char * str)
{
for (char c(0); (c = *str); ++str)
{
*str = tolower(c);
}
}
char * os_strtrim(char * lstr)
{
while (' ' == *lstr || '\t' == *lstr)
{
++lstr;
}
if (*lstr)
{
char * rstr(lstr + strlen(lstr));
while (lstr < rstr && *--rstr)
{
if (' ' == *rstr || '\t' == *rstr)
{
*rstr = '\0';
}
}
llassert(lstr <= rstr);
}
return lstr;
}
char * os_strltrim(char * lstr)
{
while (' ' == *lstr || '\t' == *lstr)
{
++lstr;
}
return lstr;
}
void check_curl_easy_code(CURLcode code, int curl_setopt_option)
{
if (CURLE_OK != code)
{
// Comment from old llcurl code which may no longer apply:
//
// linux appears to throw a curl error once per session for a bad initialization
// at a pretty random time (when enabling cookies).
LL_WARNS("CoreHttp") << "libcurl error detected: " << curl_easy_strerror(code)
<< ", curl_easy_setopt option: " << curl_setopt_option
<< LL_ENDL;
}
}
void check_curl_easy_code(CURLcode code)
{
if (CURLE_OK != code)
{
// Comment from old llcurl code which may no longer apply:
//
// linux appears to throw a curl error once per session for a bad initialization
// at a pretty random time (when enabling cookies).
LL_WARNS("CoreHttp") << "libcurl error detected: " << curl_easy_strerror(code)
<< LL_ENDL;
}
}
} // end anonymous namespace

View File

@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
* Copyright (C) 2012-2013, 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
@ -157,6 +157,7 @@ protected:
unsigned int mProcFlags;
static const unsigned int PF_SCAN_RANGE_HEADER = 0x00000001U;
static const unsigned int PF_SAVE_HEADERS = 0x00000002U;
static const unsigned int PF_USE_RETRY_AFTER = 0x00000004U;
public:
// Request data
@ -174,6 +175,8 @@ public:
HttpService * mCurlService;
curl_slist * mCurlHeaders;
size_t mCurlBodyPos;
char * mCurlTemp; // Scratch buffer for header processing
size_t mCurlTempLen;
// Result data
HttpStatus mStatus;
@ -183,9 +186,11 @@ public:
size_t mReplyFullLength;
HttpHeaders * mReplyHeaders;
std::string mReplyConType;
int mReplyRetryAfter;
// Policy data
int mPolicyRetries;
int mPolicy503Retries;
HttpTime mPolicyRetryAt;
int mPolicyRetryLimit;
}; // end class HttpOpRequest

View File

@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
* Copyright (C) 2012-2013, 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
@ -27,6 +27,7 @@
#include "_httpopsetget.h"
#include "httpcommon.h"
#include "httprequest.h"
#include "_httpservice.h"
#include "_httppolicy.h"
@ -43,10 +44,11 @@ namespace LLCore
HttpOpSetGet::HttpOpSetGet()
: HttpOperation(),
mIsGlobal(false),
mDoSet(false),
mSetting(-1), // Nothing requested
mLongValue(0L)
mReqOption(HttpRequest::PO_CONNECTION_LIMIT),
mReqClass(HttpRequest::INVALID_POLICY_ID),
mReqDoSet(false),
mReqLongValue(0L),
mReplyLongValue(0L)
{}
@ -54,37 +56,84 @@ HttpOpSetGet::~HttpOpSetGet()
{}
void HttpOpSetGet::setupGet(HttpRequest::EGlobalPolicy setting)
HttpStatus HttpOpSetGet::setupGet(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass)
{
mIsGlobal = true;
mSetting = setting;
HttpStatus status;
mReqOption = opt;
mReqClass = pclass;
return status;
}
void HttpOpSetGet::setupSet(HttpRequest::EGlobalPolicy setting, const std::string & value)
HttpStatus HttpOpSetGet::setupSet(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass, long value)
{
mIsGlobal = true;
mDoSet = true;
mSetting = setting;
mStrValue = value;
HttpStatus status;
if (! HttpService::sOptionDesc[opt].mIsLong)
{
return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
}
if (! HttpService::sOptionDesc[opt].mIsDynamic)
{
return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC);
}
mReqOption = opt;
mReqClass = pclass;
mReqDoSet = true;
mReqLongValue = value;
return status;
}
HttpStatus HttpOpSetGet::setupSet(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass, const std::string & value)
{
HttpStatus status;
if (HttpService::sOptionDesc[opt].mIsLong)
{
return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
}
if (! HttpService::sOptionDesc[opt].mIsDynamic)
{
return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC);
}
mReqOption = opt;
mReqClass = pclass;
mReqDoSet = true;
mReqStrValue = value;
return status;
}
void HttpOpSetGet::stageFromRequest(HttpService * service)
{
HttpPolicyGlobal & pol_opt(service->getPolicy().getGlobalOptions());
HttpRequest::EGlobalPolicy setting(static_cast<HttpRequest::EGlobalPolicy>(mSetting));
if (mDoSet)
if (mReqDoSet)
{
mStatus = pol_opt.set(setting, mStrValue);
}
if (mStatus)
{
const std::string * value(NULL);
if ((mStatus = pol_opt.get(setting, &value)))
if (HttpService::sOptionDesc[mReqOption].mIsLong)
{
mStrValue = *value;
mStatus = service->setPolicyOption(mReqOption, mReqClass,
mReqLongValue, &mReplyLongValue);
}
else
{
mStatus = service->setPolicyOption(mReqOption, mReqClass,
mReqStrValue, &mReplyStrValue);
}
}
else
{
if (HttpService::sOptionDesc[mReqOption].mIsLong)
{
mStatus = service->getPolicyOption(mReqOption, mReqClass, &mReplyLongValue);
}
else
{
mStatus = service->getPolicyOption(mReqOption, mReqClass, &mReplyStrValue);
}
}

View File

@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
* Copyright (C) 2012-2013, 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
@ -46,7 +46,10 @@ namespace LLCore
/// configuration settings.
///
/// *NOTE: Expect this to change. Don't really like it yet.
///
/// *TODO: Can't return values to caller yet. Need to do
/// something better with HttpResponse and visitNotifier().
///
class HttpOpSetGet : public HttpOperation
{
public:
@ -61,19 +64,23 @@ private:
public:
/// Threading: called by application thread
void setupGet(HttpRequest::EGlobalPolicy setting);
void setupSet(HttpRequest::EGlobalPolicy setting, const std::string & value);
HttpStatus setupGet(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass);
HttpStatus setupSet(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass, long value);
HttpStatus setupSet(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass, const std::string & value);
virtual void stageFromRequest(HttpService *);
public:
// Request data
bool mIsGlobal;
bool mDoSet;
int mSetting;
long mLongValue;
std::string mStrValue;
HttpRequest::EPolicyOption mReqOption;
HttpRequest::policy_t mReqClass;
bool mReqDoSet;
long mReqLongValue;
std::string mReqStrValue;
// Reply Data
long mReplyLongValue;
std::string mReplyStrValue;
}; // end class HttpOpSetGet

View File

@ -41,57 +41,70 @@ namespace LLCore
// Per-policy-class data for a running system.
// Collection of queues, parameters, history, metrics, etc.
// Collection of queues, options and other data
// for a single policy class.
//
// Threading: accessed only by worker thread
struct HttpPolicy::State
struct HttpPolicy::ClassState
{
public:
State()
: mConnMax(HTTP_CONNECTION_LIMIT_DEFAULT),
mConnAt(HTTP_CONNECTION_LIMIT_DEFAULT),
mConnMin(1),
mNextSample(0),
mErrorCount(0),
mErrorFactor(0)
ClassState()
: mThrottleEnd(0),
mThrottleLeft(0L),
mRequestCount(0L)
{}
HttpReadyQueue mReadyQueue;
HttpRetryQueue mRetryQueue;
HttpPolicyClass mOptions;
long mConnMax;
long mConnAt;
long mConnMin;
HttpTime mNextSample;
unsigned long mErrorCount;
unsigned long mErrorFactor;
HttpTime mThrottleEnd;
long mThrottleLeft;
long mRequestCount;
};
HttpPolicy::HttpPolicy(HttpService * service)
: mActiveClasses(0),
mState(NULL),
mService(service)
{}
: mService(service)
{
// Create default class
mClasses.push_back(new ClassState());
}
HttpPolicy::~HttpPolicy()
{
shutdown();
for (class_list_t::iterator it(mClasses.begin()); it != mClasses.end(); ++it)
{
delete (*it);
}
mClasses.clear();
mService = NULL;
}
HttpRequest::policy_t HttpPolicy::createPolicyClass()
{
const HttpRequest::policy_t policy_class(mClasses.size());
if (policy_class >= HTTP_POLICY_CLASS_LIMIT)
{
return HttpRequest::INVALID_POLICY_ID;
}
mClasses.push_back(new ClassState());
return policy_class;
}
void HttpPolicy::shutdown()
{
for (int policy_class(0); policy_class < mActiveClasses; ++policy_class)
for (int policy_class(0); policy_class < mClasses.size(); ++policy_class)
{
HttpRetryQueue & retryq(mState[policy_class].mRetryQueue);
ClassState & state(*mClasses[policy_class]);
HttpRetryQueue & retryq(state.mRetryQueue);
while (! retryq.empty())
{
HttpOpRequest * op(retryq.top());
@ -101,7 +114,7 @@ void HttpPolicy::shutdown()
op->release();
}
HttpReadyQueue & readyq(mState[policy_class].mReadyQueue);
HttpReadyQueue & readyq(state.mReadyQueue);
while (! readyq.empty())
{
HttpOpRequest * op(readyq.top());
@ -111,28 +124,11 @@ void HttpPolicy::shutdown()
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::start()
{}
void HttpPolicy::addOp(HttpOpRequest * op)
@ -140,7 +136,8 @@ void HttpPolicy::addOp(HttpOpRequest * op)
const int policy_class(op->mReqPolicy);
op->mPolicyRetries = 0;
mState[policy_class].mReadyQueue.push(op);
op->mPolicy503Retries = 0;
mClasses[policy_class]->mReadyQueue.push(op);
}
@ -155,25 +152,39 @@ void HttpPolicy::retryOp(HttpOpRequest * op)
5000000 // ... to every 5.0 S.
};
static const int delta_max(int(LL_ARRAY_SIZE(retry_deltas)) - 1);
static const HttpStatus error_503(503);
const HttpTime now(totalTime());
const int policy_class(op->mReqPolicy);
const HttpTime delta(retry_deltas[llclamp(op->mPolicyRetries, 0, delta_max)]);
HttpTime delta(retry_deltas[llclamp(op->mPolicyRetries, 0, delta_max)]);
bool external_delta(false);
if (op->mReplyRetryAfter > 0 && op->mReplyRetryAfter < 30)
{
delta = op->mReplyRetryAfter * U64L(1000000);
external_delta = true;
}
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)
if (error_503 == op->mStatus)
{
++op->mPolicy503Retries;
}
LL_DEBUGS("CoreHttp") << "HTTP request " << static_cast<HttpHandle>(op)
<< " retry " << op->mPolicyRetries
<< " scheduled in " << (delta / HttpTime(1000))
<< " mS (" << (external_delta ? "external" : "internal")
<< "). Status: " << op->mStatus.toTerseString()
<< LL_ENDL;
if (op->mTracing > HTTP_TRACE_OFF)
{
LL_INFOS("CoreHttp") << "TRACE, ToRetryQueue, Handle: "
<< static_cast<HttpHandle>(op)
<< ", Delta: " << (delta / HttpTime(1000))
<< ", Retries: " << op->mPolicyRetries
<< LL_ENDL;
}
mState[policy_class].mRetryQueue.push(op);
mClasses[policy_class]->mRetryQueue.push(op);
}
@ -188,21 +199,43 @@ void HttpPolicy::retryOp(HttpOpRequest * op)
// the worker thread may sleep hard otherwise will ask for
// normal polling frequency.
//
// Implements a client-side request rate throttle as well.
// This is intended to mimic and predict throttling behavior
// of grid services but that is difficult to do with different
// time bases. This also represents a rigid coupling between
// viewer and server that makes it hard to change parameters
// and I hope we can make this go away with pipelining.
//
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)
for (int policy_class(0); policy_class < mClasses.size(); ++policy_class)
{
State & state(mState[policy_class]);
int active(transport.getActiveCountInClass(policy_class));
int needed(state.mConnAt - active); // Expect negatives here
ClassState & state(*mClasses[policy_class]);
HttpRetryQueue & retryq(state.mRetryQueue);
HttpReadyQueue & readyq(state.mReadyQueue);
if (retryq.empty() && readyq.empty())
{
continue;
}
const bool throttle_enabled(state.mOptions.mThrottleRate > 0L);
const bool throttle_current(throttle_enabled && now < state.mThrottleEnd);
if (throttle_current && state.mThrottleLeft <= 0)
{
// Throttled condition, don't serve this class but don't sleep hard.
result = HttpService::NORMAL;
continue;
}
int active(transport.getActiveCountInClass(policy_class));
int needed(state.mOptions.mConnectionLimit - active); // Expect negatives here
if (needed > 0)
{
// First see if we have any retries...
@ -216,10 +249,27 @@ HttpService::ELoopSpeed HttpPolicy::processReadyQueue()
op->stageFromReady(mService);
op->release();
++state.mRequestCount;
--needed;
if (throttle_enabled)
{
if (now >= state.mThrottleEnd)
{
// Throttle expired, move to next window
LL_DEBUGS("CoreHttp") << "Throttle expired with " << state.mThrottleLeft
<< " requests to go and " << state.mRequestCount
<< " requests issued." << LL_ENDL;
state.mThrottleLeft = state.mOptions.mThrottleRate;
state.mThrottleEnd = now + HttpTime(1000000);
}
if (--state.mThrottleLeft <= 0)
{
goto throttle_on;
}
}
}
// Now go on to the new requests...
while (needed > 0 && ! readyq.empty())
{
@ -229,10 +279,29 @@ HttpService::ELoopSpeed HttpPolicy::processReadyQueue()
op->stageFromReady(mService);
op->release();
++state.mRequestCount;
--needed;
if (throttle_enabled)
{
if (now >= state.mThrottleEnd)
{
// Throttle expired, move to next window
LL_DEBUGS("CoreHttp") << "Throttle expired with " << state.mThrottleLeft
<< " requests to go and " << state.mRequestCount
<< " requests issued." << LL_ENDL;
state.mThrottleLeft = state.mOptions.mThrottleRate;
state.mThrottleEnd = now + HttpTime(1000000);
}
if (--state.mThrottleLeft <= 0)
{
goto throttle_on;
}
}
}
}
throttle_on:
if (! readyq.empty() || ! retryq.empty())
{
// If anything is ready, continue looping...
@ -246,9 +315,9 @@ HttpService::ELoopSpeed HttpPolicy::processReadyQueue()
bool HttpPolicy::changePriority(HttpHandle handle, HttpRequest::priority_t priority)
{
for (int policy_class(0); policy_class < mActiveClasses; ++policy_class)
for (int policy_class(0); policy_class < mClasses.size(); ++policy_class)
{
State & state(mState[policy_class]);
ClassState & state(*mClasses[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.
@ -276,9 +345,9 @@ bool HttpPolicy::changePriority(HttpHandle handle, HttpRequest::priority_t prior
bool HttpPolicy::cancel(HttpHandle handle)
{
for (int policy_class(0); policy_class < mActiveClasses; ++policy_class)
for (int policy_class(0); policy_class < mClasses.size(); ++policy_class)
{
State & state(mState[policy_class]);
ClassState & state(*mClasses[policy_class]);
// Scan retry queue
HttpRetryQueue::container_type & c1(state.mRetryQueue.get_container());
@ -337,14 +406,14 @@ bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op)
LL_WARNS("CoreHttp") << "HTTP request " << static_cast<HttpHandle>(op)
<< " failed after " << op->mPolicyRetries
<< " retries. Reason: " << op->mStatus.toString()
<< " (" << op->mStatus.toHex() << ")"
<< " (" << op->mStatus.toTerseString() << ")"
<< LL_ENDL;
}
else if (op->mPolicyRetries)
{
LL_WARNS("CoreHttp") << "HTTP request " << static_cast<HttpHandle>(op)
<< " succeeded on retry " << op->mPolicyRetries << "."
<< LL_ENDL;
LL_DEBUGS("CoreHttp") << "HTTP request " << static_cast<HttpHandle>(op)
<< " succeeded on retry " << op->mPolicyRetries << "."
<< LL_ENDL;
}
op->stageFromActive(mService);
@ -352,13 +421,21 @@ bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op)
return false; // not active
}
HttpPolicyClass & HttpPolicy::getClassOptions(HttpRequest::policy_t pclass)
{
llassert_always(pclass >= 0 && pclass < mClasses.size());
return mClasses[pclass]->mOptions;
}
int HttpPolicy::getReadyCount(HttpRequest::policy_t policy_class) const
{
if (policy_class < mActiveClasses)
if (policy_class < mClasses.size())
{
return (mState[policy_class].mReadyQueue.size()
+ mState[policy_class].mRetryQueue.size());
return (mClasses[policy_class]->mReadyQueue.size()
+ mClasses[policy_class]->mRetryQueue.size());
}
return 0;
}

View File

@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
* Copyright (C) 2012-2013, 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
@ -60,6 +60,9 @@ private:
void operator=(const HttpPolicy &); // Not defined
public:
/// Threading: called by init thread.
HttpRequest::policy_t createPolicyClass();
/// Cancel all ready and retry requests sending them to
/// their notification queues. Release state resources
/// making further request handling impossible.
@ -71,9 +74,8 @@ public:
/// 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);
/// Threading: called by init thread
void start();
/// Give the policy layer some cycles to scan the ready
/// queue promoting higher-priority requests to active
@ -93,7 +95,7 @@ public:
/// and should not be modified by anyone until retrieved
/// from queue.
///
/// Threading: called by any thread
/// Threading: called by worker thread
void addOp(HttpOpRequest *);
/// Similar to addOp, used when a caller wants to retry a
@ -130,30 +132,39 @@ public:
/// 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.
/// Get a reference to global policy options. Caller is expected
/// to do context checks like no setting once running. These
/// are done, for example, in @see HttpService interfaces.
///
/// Threading: called by any thread *but* the object may
/// only be modified by the worker thread once running.
///
HttpPolicyGlobal & getGlobalOptions()
{
return mGlobalOptions;
}
/// Get a reference to class policy options. Caller is expected
/// to do context checks like no setting once running. These
/// are done, for example, in @see HttpService interfaces.
///
/// Threading: called by any thread *but* the object may
/// only be modified by the worker thread once running and
/// read accesses by other threads are exposed to races at
/// that point.
HttpPolicyClass & getClassOptions(HttpRequest::policy_t pclass);
/// 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;
struct ClassState;
typedef std::vector<ClassState *> class_list_t;
HttpPolicyGlobal mGlobalOptions;
class_list_t mClasses;
HttpService * mService; // Naked pointer, not refcounted, not owner
}; // end class HttpPolicy
} // end namespace LLCore

View File

@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
* Copyright (C) 2012-2013, 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
@ -34,10 +34,10 @@ namespace LLCore
HttpPolicyClass::HttpPolicyClass()
: mSetMask(0UL),
mConnectionLimit(HTTP_CONNECTION_LIMIT_DEFAULT),
: mConnectionLimit(HTTP_CONNECTION_LIMIT_DEFAULT),
mPerHostConnectionLimit(HTTP_CONNECTION_LIMIT_DEFAULT),
mPipelining(0)
mPipelining(HTTP_PIPELINING_DEFAULT),
mThrottleRate(HTTP_THROTTLE_RATE_DEFAULT)
{}
@ -49,75 +49,75 @@ HttpPolicyClass & HttpPolicyClass::operator=(const HttpPolicyClass & other)
{
if (this != &other)
{
mSetMask = other.mSetMask;
mConnectionLimit = other.mConnectionLimit;
mPerHostConnectionLimit = other.mPerHostConnectionLimit;
mPipelining = other.mPipelining;
mThrottleRate = other.mThrottleRate;
}
return *this;
}
HttpPolicyClass::HttpPolicyClass(const HttpPolicyClass & other)
: mSetMask(other.mSetMask),
mConnectionLimit(other.mConnectionLimit),
: mConnectionLimit(other.mConnectionLimit),
mPerHostConnectionLimit(other.mPerHostConnectionLimit),
mPipelining(other.mPipelining)
mPipelining(other.mPipelining),
mThrottleRate(other.mThrottleRate)
{}
HttpStatus HttpPolicyClass::set(HttpRequest::EClassPolicy opt, long value)
HttpStatus HttpPolicyClass::set(HttpRequest::EPolicyOption opt, long value)
{
switch (opt)
{
case HttpRequest::CP_CONNECTION_LIMIT:
case HttpRequest::PO_CONNECTION_LIMIT:
mConnectionLimit = llclamp(value, long(HTTP_CONNECTION_LIMIT_MIN), long(HTTP_CONNECTION_LIMIT_MAX));
break;
case HttpRequest::CP_PER_HOST_CONNECTION_LIMIT:
case HttpRequest::PO_PER_HOST_CONNECTION_LIMIT:
mPerHostConnectionLimit = llclamp(value, long(HTTP_CONNECTION_LIMIT_MIN), mConnectionLimit);
break;
case HttpRequest::CP_ENABLE_PIPELINING:
case HttpRequest::PO_ENABLE_PIPELINING:
mPipelining = llclamp(value, 0L, 1L);
break;
case HttpRequest::PO_THROTTLE_RATE:
mThrottleRate = llclamp(value, 0L, 1000000L);
break;
default:
return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
}
mSetMask |= 1UL << int(opt);
return HttpStatus();
}
HttpStatus HttpPolicyClass::get(HttpRequest::EClassPolicy opt, long * value)
HttpStatus HttpPolicyClass::get(HttpRequest::EPolicyOption opt, long * value) const
{
static const HttpStatus not_set(HttpStatus::LLCORE, HE_OPT_NOT_SET);
long * src(NULL);
switch (opt)
{
case HttpRequest::CP_CONNECTION_LIMIT:
src = &mConnectionLimit;
case HttpRequest::PO_CONNECTION_LIMIT:
*value = mConnectionLimit;
break;
case HttpRequest::CP_PER_HOST_CONNECTION_LIMIT:
src = &mPerHostConnectionLimit;
case HttpRequest::PO_PER_HOST_CONNECTION_LIMIT:
*value = mPerHostConnectionLimit;
break;
case HttpRequest::CP_ENABLE_PIPELINING:
src = &mPipelining;
case HttpRequest::PO_ENABLE_PIPELINING:
*value = mPipelining;
break;
case HttpRequest::PO_THROTTLE_RATE:
*value = mThrottleRate;
break;
default:
return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
}
if (! (mSetMask & (1UL << int(opt))))
return not_set;
*value = *src;
return HttpStatus();
}

View File

@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
* Copyright (C) 2012-2013, 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
@ -34,6 +34,18 @@
namespace LLCore
{
/// Options struct for per-class policy options.
///
/// Combines both raw blob data access with semantics-enforcing
/// set/get interfaces. For internal operations by the worker
/// thread, just grab the setting directly from instance and test/use
/// as needed. When attached to external APIs (the public API
/// options interfaces) the set/get methods are available to
/// enforce correct ranges, data types, contexts, etc. and suitable
/// status values are returned.
///
/// Threading: Single-threaded. In practice, init thread before
/// worker starts, worker thread after.
class HttpPolicyClass
{
public:
@ -44,14 +56,14 @@ public:
HttpPolicyClass(const HttpPolicyClass &); // Not defined
public:
HttpStatus set(HttpRequest::EClassPolicy opt, long value);
HttpStatus get(HttpRequest::EClassPolicy opt, long * value);
HttpStatus set(HttpRequest::EPolicyOption opt, long value);
HttpStatus get(HttpRequest::EPolicyOption opt, long * value) const;
public:
unsigned long mSetMask;
long mConnectionLimit;
long mPerHostConnectionLimit;
long mPipelining;
long mThrottleRate;
}; // end class HttpPolicyClass
} // end namespace LLCore

View File

@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
* Copyright (C) 2012-2013, 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
@ -34,8 +34,7 @@ namespace LLCore
HttpPolicyGlobal::HttpPolicyGlobal()
: mSetMask(0UL),
mConnectionLimit(HTTP_CONNECTION_LIMIT_DEFAULT),
: mConnectionLimit(HTTP_CONNECTION_LIMIT_DEFAULT),
mTrace(HTTP_TRACE_OFF),
mUseLLProxy(0)
{}
@ -49,7 +48,6 @@ HttpPolicyGlobal & HttpPolicyGlobal::operator=(const HttpPolicyGlobal & other)
{
if (this != &other)
{
mSetMask = other.mSetMask;
mConnectionLimit = other.mConnectionLimit;
mCAPath = other.mCAPath;
mCAFile = other.mCAFile;
@ -61,19 +59,19 @@ HttpPolicyGlobal & HttpPolicyGlobal::operator=(const HttpPolicyGlobal & other)
}
HttpStatus HttpPolicyGlobal::set(HttpRequest::EGlobalPolicy opt, long value)
HttpStatus HttpPolicyGlobal::set(HttpRequest::EPolicyOption opt, long value)
{
switch (opt)
{
case HttpRequest::GP_CONNECTION_LIMIT:
case HttpRequest::PO_CONNECTION_LIMIT:
mConnectionLimit = llclamp(value, long(HTTP_CONNECTION_LIMIT_MIN), long(HTTP_CONNECTION_LIMIT_MAX));
break;
case HttpRequest::GP_TRACE:
case HttpRequest::PO_TRACE:
mTrace = llclamp(value, long(HTTP_TRACE_MIN), long(HTTP_TRACE_MAX));
break;
case HttpRequest::GP_LLPROXY:
case HttpRequest::PO_LLPROXY:
mUseLLProxy = llclamp(value, 0L, 1L);
break;
@ -81,24 +79,23 @@ HttpStatus HttpPolicyGlobal::set(HttpRequest::EGlobalPolicy opt, long value)
return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
}
mSetMask |= 1UL << int(opt);
return HttpStatus();
}
HttpStatus HttpPolicyGlobal::set(HttpRequest::EGlobalPolicy opt, const std::string & value)
HttpStatus HttpPolicyGlobal::set(HttpRequest::EPolicyOption opt, const std::string & value)
{
switch (opt)
{
case HttpRequest::GP_CA_PATH:
case HttpRequest::PO_CA_PATH:
mCAPath = value;
break;
case HttpRequest::GP_CA_FILE:
case HttpRequest::PO_CA_FILE:
mCAFile = value;
break;
case HttpRequest::GP_HTTP_PROXY:
case HttpRequest::PO_HTTP_PROXY:
mCAFile = value;
break;
@ -106,69 +103,54 @@ HttpStatus HttpPolicyGlobal::set(HttpRequest::EGlobalPolicy opt, const std::stri
return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
}
mSetMask |= 1UL << int(opt);
return HttpStatus();
}
HttpStatus HttpPolicyGlobal::get(HttpRequest::EGlobalPolicy opt, long * value)
HttpStatus HttpPolicyGlobal::get(HttpRequest::EPolicyOption opt, long * value) const
{
static const HttpStatus not_set(HttpStatus::LLCORE, HE_OPT_NOT_SET);
long * src(NULL);
switch (opt)
{
case HttpRequest::GP_CONNECTION_LIMIT:
src = &mConnectionLimit;
case HttpRequest::PO_CONNECTION_LIMIT:
*value = mConnectionLimit;
break;
case HttpRequest::GP_TRACE:
src = &mTrace;
case HttpRequest::PO_TRACE:
*value = mTrace;
break;
case HttpRequest::GP_LLPROXY:
src = &mUseLLProxy;
case HttpRequest::PO_LLPROXY:
*value = 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)
HttpStatus HttpPolicyGlobal::get(HttpRequest::EPolicyOption opt, std::string * value) const
{
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;
case HttpRequest::PO_CA_PATH:
*value = mCAPath;
break;
case HttpRequest::GP_CA_FILE:
src = &mCAFile;
case HttpRequest::PO_CA_FILE:
*value = mCAFile;
break;
case HttpRequest::GP_HTTP_PROXY:
src = &mHttpProxy;
case HttpRequest::PO_HTTP_PROXY:
*value = mHttpProxy;
break;
default:
return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
}
if (! (mSetMask & (1UL << int(opt))))
return not_set;
*value = src;
return HttpStatus();
}

View File

@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
* Copyright (C) 2012-2013, 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
@ -34,6 +34,18 @@
namespace LLCore
{
/// Options struct for global policy options.
///
/// Combines both raw blob data access with semantics-enforcing
/// set/get interfaces. For internal operations by the worker
/// thread, just grab the setting directly from instance and test/use
/// as needed. When attached to external APIs (the public API
/// options interfaces) the set/get methods are available to
/// enforce correct ranges, data types, contexts, etc. and suitable
/// status values are returned.
///
/// Threading: Single-threaded. In practice, init thread before
/// worker starts, worker thread after.
class HttpPolicyGlobal
{
public:
@ -46,13 +58,12 @@ 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);
HttpStatus set(HttpRequest::EPolicyOption opt, long value);
HttpStatus set(HttpRequest::EPolicyOption opt, const std::string & value);
HttpStatus get(HttpRequest::EPolicyOption opt, long * value) const;
HttpStatus get(HttpRequest::EPolicyOption opt, std::string * value) const;
public:
unsigned long mSetMask;
long mConnectionLimit;
std::string mCAPath;
std::string mCAFile;

View File

@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
* Copyright (C) 2012-2013, 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
@ -43,6 +43,18 @@
namespace LLCore
{
const HttpService::OptionDescriptor HttpService::sOptionDesc[] =
{ // isLong isDynamic isGlobal isClass
{ true, true, true, true }, // PO_CONNECTION_LIMIT
{ true, true, false, true }, // PO_PER_HOST_CONNECTION_LIMIT
{ false, false, true, false }, // PO_CA_PATH
{ false, false, true, false }, // PO_CA_FILE
{ false, true, true, false }, // PO_HTTP_PROXY
{ true, true, true, false }, // PO_LLPROXY
{ true, true, true, false }, // PO_TRACE
{ true, true, false, true }, // PO_ENABLE_PIPELINING
{ true, true, false, true } // PO_THROTTLE_RATE
};
HttpService * HttpService::sInstance(NULL);
volatile HttpService::EState HttpService::sState(NOT_INITIALIZED);
@ -51,15 +63,9 @@ HttpService::HttpService()
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);
}
mTransport(NULL),
mLastPolicy(0)
{}
HttpService::~HttpService()
@ -149,13 +155,8 @@ void HttpService::term()
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;
mLastPolicy = mPolicy->createPolicyClass();
return mLastPolicy;
}
@ -188,8 +189,8 @@ void HttpService::startThread()
}
// Push current policy definitions, enable policy & transport components
mPolicy->start(mPolicyGlobal, mPolicyClasses);
mTransport->start(mPolicyClasses.size());
mPolicy->start();
mTransport->start(mLastPolicy + 1);
mThread = new LLCoreInt::HttpThread(boost::bind(&HttpService::threadRun, this, _1));
sState = RUNNING;
@ -322,7 +323,7 @@ HttpService::ELoopSpeed HttpService::processRequestQueue(ELoopSpeed loop)
{
// Setup for subsequent tracing
long tracing(HTTP_TRACE_OFF);
mPolicy->getGlobalOptions().get(HttpRequest::GP_TRACE, &tracing);
mPolicy->getGlobalOptions().get(HttpRequest::PO_TRACE, &tracing);
op->mTracing = (std::max)(op->mTracing, int(tracing));
if (op->mTracing > HTTP_TRACE_OFF)
@ -345,4 +346,137 @@ HttpService::ELoopSpeed HttpService::processRequestQueue(ELoopSpeed loop)
}
HttpStatus HttpService::getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass,
long * ret_value)
{
if (opt < HttpRequest::PO_CONNECTION_LIMIT // option must be in range
|| opt >= HttpRequest::PO_LAST // ditto
|| (! sOptionDesc[opt].mIsLong) // datatype is long
|| (pclass != HttpRequest::GLOBAL_POLICY_ID && pclass > mLastPolicy) // pclass in valid range
|| (pclass == HttpRequest::GLOBAL_POLICY_ID && ! sOptionDesc[opt].mIsGlobal) // global setting permitted
|| (pclass != HttpRequest::GLOBAL_POLICY_ID && ! sOptionDesc[opt].mIsClass)) // class setting permitted
// can always get, no dynamic check
{
return HttpStatus(HttpStatus::LLCORE, LLCore::HE_INVALID_ARG);
}
HttpStatus status;
if (pclass == HttpRequest::GLOBAL_POLICY_ID)
{
HttpPolicyGlobal & opts(mPolicy->getGlobalOptions());
status = opts.get(opt, ret_value);
}
else
{
HttpPolicyClass & opts(mPolicy->getClassOptions(pclass));
status = opts.get(opt, ret_value);
}
return status;
}
HttpStatus HttpService::getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass,
std::string * ret_value)
{
HttpStatus status(HttpStatus::LLCORE, LLCore::HE_INVALID_ARG);
if (opt < HttpRequest::PO_CONNECTION_LIMIT // option must be in range
|| opt >= HttpRequest::PO_LAST // ditto
|| (sOptionDesc[opt].mIsLong) // datatype is string
|| (pclass != HttpRequest::GLOBAL_POLICY_ID && pclass > mLastPolicy) // pclass in valid range
|| (pclass == HttpRequest::GLOBAL_POLICY_ID && ! sOptionDesc[opt].mIsGlobal) // global setting permitted
|| (pclass != HttpRequest::GLOBAL_POLICY_ID && ! sOptionDesc[opt].mIsClass)) // class setting permitted
// can always get, no dynamic check
{
return status;
}
// Only global has string values
if (pclass == HttpRequest::GLOBAL_POLICY_ID)
{
HttpPolicyGlobal & opts(mPolicy->getGlobalOptions());
status = opts.get(opt, ret_value);
}
return status;
}
HttpStatus HttpService::setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass,
long value, long * ret_value)
{
HttpStatus status(HttpStatus::LLCORE, LLCore::HE_INVALID_ARG);
if (opt < HttpRequest::PO_CONNECTION_LIMIT // option must be in range
|| opt >= HttpRequest::PO_LAST // ditto
|| (! sOptionDesc[opt].mIsLong) // datatype is long
|| (pclass != HttpRequest::GLOBAL_POLICY_ID && pclass > mLastPolicy) // pclass in valid range
|| (pclass == HttpRequest::GLOBAL_POLICY_ID && ! sOptionDesc[opt].mIsGlobal) // global setting permitted
|| (pclass != HttpRequest::GLOBAL_POLICY_ID && ! sOptionDesc[opt].mIsClass) // class setting permitted
|| (RUNNING == sState && ! sOptionDesc[opt].mIsDynamic)) // dynamic setting permitted
{
return status;
}
if (pclass == HttpRequest::GLOBAL_POLICY_ID)
{
HttpPolicyGlobal & opts(mPolicy->getGlobalOptions());
status = opts.set(opt, value);
if (status && ret_value)
{
status = opts.get(opt, ret_value);
}
}
else
{
HttpPolicyClass & opts(mPolicy->getClassOptions(pclass));
status = opts.set(opt, value);
if (status && ret_value)
{
status = opts.get(opt, ret_value);
}
}
return status;
}
HttpStatus HttpService::setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass,
const std::string & value, std::string * ret_value)
{
HttpStatus status(HttpStatus::LLCORE, LLCore::HE_INVALID_ARG);
if (opt < HttpRequest::PO_CONNECTION_LIMIT // option must be in range
|| opt >= HttpRequest::PO_LAST // ditto
|| (sOptionDesc[opt].mIsLong) // datatype is string
|| (pclass != HttpRequest::GLOBAL_POLICY_ID && pclass > mLastPolicy) // pclass in valid range
|| (pclass == HttpRequest::GLOBAL_POLICY_ID && ! sOptionDesc[opt].mIsGlobal) // global setting permitted
|| (pclass != HttpRequest::GLOBAL_POLICY_ID && ! sOptionDesc[opt].mIsClass) // class setting permitted
|| (RUNNING == sState && ! sOptionDesc[opt].mIsDynamic)) // dynamic setting permitted
{
return status;
}
// Only string values are global at this time
if (pclass == HttpRequest::GLOBAL_POLICY_ID)
{
HttpPolicyGlobal & opts(mPolicy->getGlobalOptions());
status = opts.set(opt, value);
if (status && ret_value)
{
status = opts.get(opt, ret_value);
}
}
return status;
}
} // end namespace LLCore

View File

@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
* Copyright (C) 2012-2013, 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
@ -53,6 +53,7 @@ namespace LLCore
class HttpRequestQueue;
class HttpPolicy;
class HttpLibcurl;
class HttpOpSetGet;
/// The HttpService class does the work behind the request queue. It
@ -106,7 +107,7 @@ public:
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();
@ -136,7 +137,7 @@ public:
/// acquires its weaknesses.
static bool isStopped();
/// Threading: callable by consumer thread *once*.
/// Threading: callable by init thread *once*.
void startThread();
/// Threading: callable by worker thread.
@ -180,28 +181,39 @@ public:
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:
friend class HttpOpSetGet;
friend class HttpRequest;
// Used internally to describe what operations are allowed
// on each policy option.
struct OptionDescriptor
{
bool mIsLong;
bool mIsDynamic;
bool mIsGlobal;
bool mIsClass;
};
HttpStatus getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t,
long * ret_value);
HttpStatus getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t,
std::string * ret_value);
HttpStatus setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t,
long value, long * ret_value);
HttpStatus setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t,
const std::string & value, std::string * ret_value);
protected:
static const OptionDescriptor sOptionDesc[HttpRequest::PO_LAST];
static HttpService * sInstance;
// === shared data ===
@ -210,13 +222,13 @@ protected:
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
// === main-thread-only data ===
HttpRequest::policy_t mLastPolicy;
}; // end class HttpService
} // end namespace LLCore

View File

@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
* Copyright (C) 2012-2013, 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
@ -39,6 +39,7 @@
#include "httprequest.h"
#include "httphandler.h"
#include "httpresponse.h"
#include "httpoptions.h"
#include "httpheaders.h"
#include "bufferarray.h"
#include "_mutex.h"
@ -57,6 +58,7 @@ void usage(std::ostream & out);
// Default command line settings
static int concurrency_limit(40);
static int highwater(100);
static char url_format[1024] = "http://example.com/some/path?texture_id=%s.texture";
#if defined(WIN32)
@ -79,11 +81,11 @@ public:
WorkingSet();
~WorkingSet();
bool reload(LLCore::HttpRequest *);
bool reload(LLCore::HttpRequest *, LLCore::HttpOptions *);
virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response);
void loadTextureUuids(FILE * in);
void loadAssetUuids(FILE * in);
public:
struct Spec
@ -93,24 +95,27 @@ public:
int mLength;
};
typedef std::set<LLCore::HttpHandle> handle_set_t;
typedef std::vector<Spec> texture_list_t;
typedef std::vector<Spec> asset_list_t;
public:
bool mVerbose;
bool mRandomRange;
int mMaxConcurrency;
int mRequestLowWater;
int mRequestHighWater;
handle_set_t mHandles;
int mRemaining;
int mLimit;
int mAt;
std::string mUrl;
texture_list_t mTextures;
asset_list_t mAssets;
int mErrorsApi;
int mErrorsHttp;
int mErrorsHttp404;
int mErrorsHttp416;
int mErrorsHttp500;
int mErrorsHttp503;
int mRetries;
int mRetriesHttp503;
int mSuccesses;
long mByteCount;
LLCore::HttpHeaders * mHeaders;
@ -158,7 +163,7 @@ int main(int argc, char** argv)
bool do_verbose(false);
int option(-1);
while (-1 != (option = getopt(argc, argv, "u:c:h?Rv")))
while (-1 != (option = getopt(argc, argv, "u:c:h?RvH:")))
{
switch (option)
{
@ -182,6 +187,21 @@ int main(int argc, char** argv)
}
break;
case 'H':
{
unsigned long value;
char * end;
value = strtoul(optarg, &end, 10);
if (value < 1 || value > 100 || *end != '\0')
{
usage(std::cerr);
return 1;
}
highwater = value;
}
break;
case 'R':
do_random = true;
break;
@ -216,25 +236,32 @@ int main(int argc, char** argv)
// Initialization
init_curl();
LLCore::HttpRequest::createService();
LLCore::HttpRequest::setPolicyClassOption(LLCore::HttpRequest::DEFAULT_POLICY_ID,
LLCore::HttpRequest::CP_CONNECTION_LIMIT,
concurrency_limit);
LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_CONNECTION_LIMIT,
LLCore::HttpRequest::DEFAULT_POLICY_ID,
concurrency_limit,
NULL);
LLCore::HttpRequest::startThread();
// Get service point
LLCore::HttpRequest * hr = new LLCore::HttpRequest();
// Get request options
LLCore::HttpOptions * opt = new LLCore::HttpOptions();
opt->setRetries(12);
opt->setUseRetryAfter(true);
// Get a handler/working set
WorkingSet ws;
// Fill the working set with work
ws.mUrl = url_format;
ws.loadTextureUuids(uuids);
ws.loadAssetUuids(uuids);
ws.mRandomRange = do_random;
ws.mVerbose = do_verbose;
ws.mMaxConcurrency = 100;
ws.mRequestHighWater = highwater;
ws.mRequestLowWater = ws.mRequestHighWater / 2;
if (! ws.mTextures.size())
if (! ws.mAssets.size())
{
std::cerr << "No UUIDs found in file '" << argv[optind] << "'." << std::endl;
return 1;
@ -246,9 +273,9 @@ int main(int argc, char** argv)
// Run it
int passes(0);
while (! ws.reload(hr))
while (! ws.reload(hr, opt))
{
hr->update(5000000);
hr->update(0);
ms_sleep(2);
if (0 == (++passes % 200))
{
@ -265,6 +292,8 @@ int main(int argc, char** argv)
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 << "Retries: " << ws.mRetries << " Retries on 503: " << ws.mRetriesHttp503
<< std::endl;
std::cout << "User CPU: " << (metrics.mEndUTime - metrics.mStartUTime)
<< " uS System CPU: " << (metrics.mEndSTime - metrics.mStartSTime)
<< " uS Wall Time: " << (metrics.mEndWallTime - metrics.mStartWallTime)
@ -275,6 +304,8 @@ int main(int argc, char** argv)
// Clean up
hr->requestStopThread(NULL);
ms_sleep(1000);
opt->release();
opt = NULL;
delete hr;
LLCore::HttpRequest::destroyService();
term_curl();
@ -300,8 +331,10 @@ void usage(std::ostream & out)
" -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"
" -c <limit> Maximum connection concurrency. Range: [1..100]\n"
" Default: " << concurrency_limit << "\n"
" -H <limit> HTTP request highwater (requests fed to llcorehttp).\n"
" Range: [1..100] Default: " << highwater << "\n"
" -v Verbose mode. Issue some chatter while running\n"
" -h print this help\n"
"\n"
@ -322,13 +355,15 @@ WorkingSet::WorkingSet()
mErrorsHttp416(0),
mErrorsHttp500(0),
mErrorsHttp503(0),
mRetries(0),
mRetriesHttp503(0),
mSuccesses(0),
mByteCount(0L)
{
mTextures.reserve(30000);
mAssets.reserve(30000);
mHeaders = new LLCore::HttpHeaders;
mHeaders->mHeaders.push_back("Accept: image/x-j2c");
mHeaders->append("Accept", "image/x-j2c");
}
@ -342,29 +377,35 @@ WorkingSet::~WorkingSet()
}
bool WorkingSet::reload(LLCore::HttpRequest * hr)
bool WorkingSet::reload(LLCore::HttpRequest * hr, LLCore::HttpOptions * opt)
{
int to_do((std::min)(mRemaining, mMaxConcurrency - int(mHandles.size())));
if (mRequestLowWater <= mHandles.size())
{
// Haven't fallen below low-water level yet.
return false;
}
int to_do((std::min)(mRemaining, mRequestHighWater - 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());
_snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, mUrl.c_str(), mAssets[mAt].mUuid.c_str());
#else
snprintf(buffer, sizeof(buffer), mUrl.c_str(), mTextures[mAt].mUuid.c_str());
snprintf(buffer, sizeof(buffer), mUrl.c_str(), mAssets[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);
int offset(mRandomRange ? ((unsigned long) rand()) % 1000000UL : mAssets[mAt].mOffset);
int length(mRandomRange ? ((unsigned long) rand()) % 1000000UL : mAssets[mAt].mLength);
LLCore::HttpHandle handle;
if (offset || length)
{
handle = hr->requestGetByteRange(0, 0, buffer, offset, length, NULL, mHeaders, this);
handle = hr->requestGetByteRange(0, 0, buffer, offset, length, opt, mHeaders, this);
}
else
{
handle = hr->requestGet(0, 0, buffer, NULL, mHeaders, this);
handle = hr->requestGet(0, 0, buffer, opt, mHeaders, this);
}
if (! handle)
{
@ -410,7 +451,7 @@ void WorkingSet::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * r
{
// More success
LLCore::BufferArray * data(response->getBody());
mByteCount += data->size();
mByteCount += data ? data->size() : 0;
++mSuccesses;
}
else
@ -446,6 +487,10 @@ void WorkingSet::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * r
++mErrorsApi;
}
}
unsigned int retry(0U), retry_503(0U);
response->getRetries(&retry, &retry_503);
mRetries += int(retry);
mRetriesHttp503 += int(retry_503);
mHandles.erase(it);
}
@ -459,21 +504,21 @@ void WorkingSet::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * r
}
void WorkingSet::loadTextureUuids(FILE * in)
void WorkingSet::loadAssetUuids(FILE * in)
{
char buffer[1024];
while (fgets(buffer, sizeof(buffer), in))
{
WorkingSet::Spec texture;
WorkingSet::Spec asset;
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;
asset.mUuid = token;
asset.mOffset = 0;
asset.mLength = 0;
token = strtok_r(buffer, " \t\n,", &state);
if (token)
{
@ -482,14 +527,14 @@ void WorkingSet::loadTextureUuids(FILE * in)
if (token)
{
int length(atoi(token));
texture.mOffset = offset;
texture.mLength = length;
asset.mOffset = offset;
asset.mLength = length;
}
}
mTextures.push_back(texture);
mAssets.push_back(asset);
}
}
mRemaining = mLimit = mTextures.size();
mRemaining = mLimit = mAssets.size();
}

View File

@ -70,7 +70,8 @@ std::string HttpStatus::toString() const
"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"
"Invalid HTTP status code received from server",
"Could not allocate required resource"
};
static const int llcore_errors_count(sizeof(llcore_errors) / sizeof(llcore_errors[0]));
@ -177,6 +178,44 @@ std::string HttpStatus::toString() const
}
std::string HttpStatus::toTerseString() const
{
std::ostringstream result;
unsigned int error_value((unsigned short) mStatus);
switch (mType)
{
case EXT_CURL_EASY:
result << "Easy_";
break;
case EXT_CURL_MULTI:
result << "Multi_";
break;
case LLCORE:
result << "Core_";
break;
default:
if (isHttpStatus())
{
result << "Http_";
error_value = mType;
}
else
{
result << "Unknown_";
}
break;
}
result << error_value;
return result.str();
}
// Pass true on statuses that might actually be cleared by a
// retry. Library failures, calling problems, etc. aren't
// going to be fixed by squirting bits all over the Net.
@ -206,6 +245,5 @@ bool HttpStatus::isRetryable() const
*this == inv_cont_range); // Short data read disagrees with content-range
}
} // end namespace LLCore

View File

@ -29,9 +29,9 @@
/// @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
/// This library implements a high-level, Indra-code-free (somewhat) 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.
@ -52,7 +52,7 @@
/// - "llcorehttp/httprequest.h"
/// - "llcorehttp/httpresponse.h"
///
/// The library is still under early development and particular users
/// The library is still under 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.
@ -66,6 +66,8 @@
/// . CRYPTO_set_id_callback(...)
/// - HttpRequest::createService() called to instantiate singletons
/// and support objects.
/// - HttpRequest::startThread() to kick off the worker thread and
/// begin servicing requests.
///
/// An HTTP consumer in an application, and an application may have many
/// consumers, does a few things:
@ -91,10 +93,12 @@
/// 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:
/// Code fragments.
///
/// Initialization. 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();
@ -103,7 +107,85 @@
///
/// the program is basically ready to issue requests.
///
/// HttpHandler. Having started life as a non-indra library,
/// this code broke away from the classic Responder model and
/// introduced a handler class to represent an interface for
/// request responses. This is a non-reference-counted entity
/// which can be used as a base class or a mixin. An instance
/// of a handler can be used for each request or can be shared
/// among any number of requests. Your choice but expect to
/// code something like the following:
///
/// class AppHandler : public LLCore::HttpHandler
/// {
/// public:
/// virtual void onCompleted(HttpHandle handle,
/// HttpResponse * response)
/// {
/// ...
/// }
/// ...
/// };
/// ...
/// handler = new handler(...);
///
///
/// Issuing requests. Using 'hr' above,
///
/// hr->requestGet(HttpRequest::DEFAULT_POLICY_ID,
/// 0, // Priority, not used yet
/// url,
/// NULL, // options
/// NULL, // additional headers
/// handler);
///
/// If that returns a value other than LLCORE_HTTP_HANDLE_INVALID,
/// the request was successfully issued and there will eventally
/// be a status delivered to the handler. If invalid is returnedd,
/// the actual status can be retrieved by calling hr->getStatus().
///
/// Completing requests and delivering notifications. Operations
/// are all performed by the worker thread and will be driven to
/// completion regardless of caller actions. Notification of
/// completion (success or failure) is done by calls to
/// HttpRequest::update() which will invoke handlers for completed
/// requests:
///
/// hr->update(0);
/// // Callbacks into handler->onCompleted()
///
///
/// Threads.
///
/// Threads are supported and used by this library. The various
/// classes, methods and members are documented with thread
/// constraints which programmers must follow and which are
/// defined as follows:
///
/// consumer Any thread that has instanced HttpRequest and is
/// issuing requests. A particular instance can only
/// be used by one consumer thread but a consumer may
/// have many instances available to it.
/// init Special consumer thread, usually the main thread,
/// involved in setting up the library at startup.
/// worker Thread used internally by the library to perform
/// HTTP operations. Consumers will not have to deal
/// with this thread directly but some APIs are reserved
/// to it.
/// any Consumer or worker thread.
///
/// For the most part, API users will not have to do much in the
/// way of ensuring thread safely. However, there is a tremendous
/// amount of sharing between threads of read-only data. So when
/// documentation declares that an option or header instance
/// becomes shared between consumer and worker, the consumer must
/// not modify the shared object.
///
/// Internally, there is almost no thread synchronization. During
/// normal operations (non-init, non-term), only the request queue
/// and the multiple reply queues are shared between threads and
/// only here are mutexes used.
///
#include "linden_common.h" // Modifies curl/curl.h interfaces
@ -164,7 +246,10 @@ enum HttpError
HE_OPT_NOT_DYNAMIC = 8,
// Invalid HTTP status code returned by server
HE_INVALID_HTTP_STATUS = 9
HE_INVALID_HTTP_STATUS = 9,
// Couldn't allocate resource, typically libcurl handle
HE_BAD_ALLOC = 10
}; // end enum HttpError
@ -239,9 +324,10 @@ struct HttpStatus
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;
static const type_enum_t EXT_CURL_EASY = 0; ///< mStatus is an error from a curl_easy_*() call
static const type_enum_t EXT_CURL_MULTI = 1; ///< mStatus is an error from a curl_multi_*() call
static const type_enum_t LLCORE = 2; ///< mStatus is an HE_* error code
///< 100-999 directly represent HTTP status codes
type_enum_t mType;
short mStatus;
@ -297,6 +383,14 @@ struct HttpStatus
/// LLCore itself).
std::string toString() const;
/// Convert status to a compact string representation
/// of the form: "<type>_<value>". The <type> will be
/// one of: Core, Http, Easy, Multi, Unknown. And
/// <value> will be an unsigned integer. More easily
/// interpreted than the hex representation, it's still
/// compact and easily searched.
std::string toTerseString() const;
/// Returns true if the status value represents an
/// HTTP response status (100 - 999).
bool isHttpStatus() const

View File

@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
* Copyright (C) 2012-2013, 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
@ -26,6 +26,8 @@
#include "httpheaders.h"
#include "llstring.h"
namespace LLCore
{
@ -40,5 +42,142 @@ HttpHeaders::~HttpHeaders()
{}
void
HttpHeaders::clear()
{
mHeaders.clear();
}
void HttpHeaders::append(const std::string & name, const std::string & value)
{
mHeaders.push_back(value_type(name, value));
}
void HttpHeaders::append(const char * name, const char * value)
{
mHeaders.push_back(value_type(name, value));
}
void HttpHeaders::appendNormal(const char * header, size_t size)
{
std::string name;
std::string value;
int col_pos(0);
for (; col_pos < size; ++col_pos)
{
if (':' == header[col_pos])
break;
}
if (col_pos < size)
{
// Looks like a header, split it and normalize.
// Name is everything before the colon, may be zero-length.
name.assign(header, col_pos);
// Value is everything after the colon, may also be zero-length.
const size_t val_len(size - col_pos - 1);
if (val_len)
{
value.assign(header + col_pos + 1, val_len);
}
// Clean the strings
LLStringUtil::toLower(name);
LLStringUtil::trim(name);
LLStringUtil::trimHead(value);
}
else
{
// Uncertain what this is, we'll pack it as
// a name without a value. Won't clean as we don't
// know what it is...
name.assign(header, size);
}
mHeaders.push_back(value_type(name, value));
}
// Find from end to simulate a tradition of using single-valued
// std::map for this in the past.
const std::string * HttpHeaders::find(const char * name) const
{
const_reverse_iterator iend(rend());
for (const_reverse_iterator iter(rbegin()); iend != iter; ++iter)
{
if ((*iter).first == name)
{
return &(*iter).second;
}
}
return NULL;
}
// Standard Iterators
HttpHeaders::iterator HttpHeaders::begin()
{
return mHeaders.begin();
}
HttpHeaders::const_iterator HttpHeaders::begin() const
{
return mHeaders.begin();
}
HttpHeaders::iterator HttpHeaders::end()
{
return mHeaders.end();
}
HttpHeaders::const_iterator HttpHeaders::end() const
{
return mHeaders.end();
}
// Standard Reverse Iterators
HttpHeaders::reverse_iterator HttpHeaders::rbegin()
{
return mHeaders.rbegin();
}
HttpHeaders::const_reverse_iterator HttpHeaders::rbegin() const
{
return mHeaders.rbegin();
}
HttpHeaders::reverse_iterator HttpHeaders::rend()
{
return mHeaders.rend();
}
HttpHeaders::const_reverse_iterator HttpHeaders::rend() const
{
return mHeaders.rend();
}
// Return the raw container to the caller.
//
// To be used FOR UNIT TESTS ONLY.
//
HttpHeaders::container_t & HttpHeaders::getContainerTESTONLY()
{
return mHeaders;
}
} // end namespace LLCore

View File

@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
* Copyright (C) 2012-2013, 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
@ -43,13 +43,26 @@ namespace LLCore
/// 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.
/// Class is mostly a thin wrapper around a vector of pairs
/// of strings. Methods provided are few and intended to
/// reflect actual use patterns. These include:
/// - Clearing the list
/// - Appending a name/value pair to the vector
/// - Processing a raw byte string into a normalized name/value
/// pair and appending the result.
/// - Simple case-sensitive find-last-by-name search
/// - Forward and reverse iterators over all pairs
///
/// Container is ordered and multi-valued. Headers are
/// written in the order in which they are appended and
/// are stored in the order in which they're received from
/// the wire. The same header may appear two or more times
/// in any container. Searches using the simple find()
/// interface will find only the last occurrence (somewhat
/// simulates the use of std::map). Fuller searches require
/// the use of an iterator. Headers received from the wire
/// are only returned from the last request when redirections
/// are involved.
///
/// Threading: Not intrinsically thread-safe. It *is* expected
/// that callers will build these objects and then share them
@ -63,6 +76,16 @@ namespace LLCore
class HttpHeaders : public LLCoreInt::RefCounted
{
public:
typedef std::pair<std::string, std::string> header_t;
typedef std::vector<header_t> container_t;
typedef container_t::iterator iterator;
typedef container_t::const_iterator const_iterator;
typedef container_t::reverse_iterator reverse_iterator;
typedef container_t::const_reverse_iterator const_reverse_iterator;
typedef container_t::value_type value_type;
typedef container_t::size_type size_type;
public:
/// @post In addition to the instance, caller has a refcount
/// to the instance. A call to @see release() will destroy
@ -76,7 +99,78 @@ protected:
void operator=(const HttpHeaders &); // Not defined
public:
typedef std::vector<std::string> container_t;
// Empty the list of headers.
void clear();
// Append a name/value pair supplied as either std::strings
// or NUL-terminated char * to the header list. No normalization
// is performed on the strings. No conformance test is
// performed (names may contain spaces, colons, etc.).
//
void append(const std::string & name, const std::string & value);
void append(const char * name, const char * value);
// Extract a name/value pair from a raw byte array using
// the first colon character as a separator. Input string
// does not need to be NUL-terminated. Resulting name/value
// pair is appended to the header list.
//
// Normalization is performed on the name/value pair as
// follows:
// - name is lower-cased according to mostly ASCII rules
// - name is left- and right-trimmed of spaces and tabs
// - value is left-trimmed of spaces and tabs
// - either or both of name and value may be zero-length
//
// By convention, headers read from the wire will be normalized
// in this fashion prior to delivery to any HttpHandler code.
// Headers to be written to the wire are left as appended to
// the list.
void appendNormal(const char * header, size_t size);
// Perform a simple, case-sensitive search of the header list
// returning a pointer to the value of the last matching header
// in the header list. If none is found, a NULL pointer is returned.
//
// Any pointer returned references objects in the container itself
// and will have the same lifetime as this class. If you want
// the value beyond the lifetime of this instance, make a copy.
//
// @arg name C-style string giving the name of a header
// to search. The comparison is case-sensitive
// though list entries may have been normalized
// to lower-case.
//
// @return NULL if the header wasn't found otherwise
// a pointer to a std::string in the container.
// Pointer is valid only for the lifetime of
// the container or until container is modifed.
//
const std::string * find(const char * name) const;
// Count of headers currently in the list.
size_type size() const
{
return mHeaders.size();
}
// Standard std::vector-based forward iterators.
iterator begin();
const_iterator begin() const;
iterator end();
const_iterator end() const;
// Standard std::vector-based reverse iterators.
reverse_iterator rbegin();
const_reverse_iterator rbegin() const;
reverse_iterator rend();
const_reverse_iterator rend() const;
public:
// For unit tests only - not a public API
container_t & getContainerTESTONLY();
protected:
container_t mHeaders;
}; // end class HttpHeaders

View File

@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
* Copyright (C) 2012-2013, 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
@ -38,7 +38,9 @@ HttpOptions::HttpOptions()
mWantHeaders(false),
mTracing(HTTP_TRACE_OFF),
mTimeout(HTTP_REQUEST_TIMEOUT_DEFAULT),
mRetries(HTTP_RETRY_COUNT_DEFAULT)
mTransferTimeout(HTTP_REQUEST_XFER_TIMEOUT_DEFAULT),
mRetries(HTTP_RETRY_COUNT_DEFAULT),
mUseRetryAfter(HTTP_USE_RETRY_AFTER_DEFAULT)
{}
@ -64,10 +66,21 @@ void HttpOptions::setTimeout(unsigned int timeout)
}
void HttpOptions::setTransferTimeout(unsigned int timeout)
{
mTransferTimeout = timeout;
}
void HttpOptions::setRetries(unsigned int retries)
{
mRetries = retries;
}
void HttpOptions::setUseRetryAfter(bool use_retry)
{
mUseRetryAfter = use_retry;
}
} // end namespace LLCore

View File

@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
* Copyright (C) 2012-2013, 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
@ -68,36 +68,55 @@ protected:
void operator=(const HttpOptions &); // Not defined
public:
// Default: false
void setWantHeaders(bool wanted);
bool getWantHeaders() const
{
return mWantHeaders;
}
// Default: 0
void setTrace(int long);
int getTrace() const
{
return mTracing;
}
// Default: 30
void setTimeout(unsigned int timeout);
unsigned int getTimeout() const
{
return mTimeout;
}
// Default: 0
void setTransferTimeout(unsigned int timeout);
unsigned int getTransferTimeout() const
{
return mTransferTimeout;
}
// Default: 8
void setRetries(unsigned int retries);
unsigned int getRetries() const
{
return mRetries;
}
// Default: true
void setUseRetryAfter(bool use_retry);
bool getUseRetryAfter() const
{
return mUseRetryAfter;
}
protected:
bool mWantHeaders;
int mTracing;
unsigned int mTimeout;
unsigned int mTransferTimeout;
unsigned int mRetries;
bool mUseRetryAfter;
}; // end class HttpOptions

View File

@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
* Copyright (C) 2012-2013, 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
@ -54,12 +54,8 @@ namespace LLCore
// ====================================
HttpRequest::policy_t HttpRequest::sNextPolicyID(1);
HttpRequest::HttpRequest()
: //HttpHandler(),
mReplyQueue(NULL),
: mReplyQueue(NULL),
mRequestQueue(NULL)
{
mRequestQueue = HttpRequestQueue::instanceOf();
@ -90,26 +86,6 @@ HttpRequest::~HttpRequest()
// ====================================
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())
@ -120,15 +96,81 @@ HttpRequest::policy_t HttpRequest::createPolicyClass()
}
HttpStatus HttpRequest::setPolicyClassOption(policy_t policy_id,
EClassPolicy opt,
long value)
HttpStatus HttpRequest::setStaticPolicyOption(EPolicyOption opt, policy_t pclass,
long value, long * ret_value)
{
if (HttpService::RUNNING == HttpService::instanceOf()->getState())
{
return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC);
}
return HttpService::instanceOf()->getClassOptions(policy_id).set(opt, value);
return HttpService::instanceOf()->setPolicyOption(opt, pclass, value, ret_value);
}
HttpStatus HttpRequest::setStaticPolicyOption(EPolicyOption opt, policy_t pclass,
const std::string & value, std::string * ret_value)
{
if (HttpService::RUNNING == HttpService::instanceOf()->getState())
{
return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC);
}
return HttpService::instanceOf()->setPolicyOption(opt, pclass, value, ret_value);
}
HttpHandle HttpRequest::setPolicyOption(EPolicyOption opt, policy_t pclass,
long value, HttpHandler * handler)
{
HttpStatus status;
HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID);
HttpOpSetGet * op = new HttpOpSetGet();
if (! (status = op->setupSet(opt, pclass, value)))
{
op->release();
mLastReqStatus = status;
return handle;
}
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;
}
HttpHandle HttpRequest::setPolicyOption(EPolicyOption opt, policy_t pclass,
const std::string & value, HttpHandler * handler)
{
HttpStatus status;
HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID);
HttpOpSetGet * op = new HttpOpSetGet();
if (! (status = op->setupSet(opt, pclass, value)))
{
op->release();
mLastReqStatus = status;
return handle;
}
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;
}
@ -474,31 +516,6 @@ HttpHandle HttpRequest::requestSpin(int mode)
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

View File

@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
* Copyright (C) 2012-2013, 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
@ -56,6 +56,9 @@ class BufferArray;
/// The class supports the current HTTP request operations:
///
/// - requestGetByteRange: GET with Range header for a single range of bytes
/// - requestGet:
/// - requestPost:
/// - requestPut:
///
/// Policy Classes
///
@ -100,9 +103,26 @@ public:
/// Represents a default, catch-all policy class that guarantees
/// eventual service for any HTTP request.
static const int DEFAULT_POLICY_ID = 0;
static const policy_t DEFAULT_POLICY_ID = 0;
static const policy_t INVALID_POLICY_ID = 0xFFFFFFFFU;
static const policy_t GLOBAL_POLICY_ID = 0xFFFFFFFEU;
enum EGlobalPolicy
/// Create a new policy class into which requests can be made.
///
/// All class creation must occur before threads are started and
/// transport begins. Policy classes are limited to a small value.
/// Currently that limit is the default class + 1.
///
/// @return If positive, the policy_id used to reference
/// the class in other methods. If 0, requests
/// for classes have exceeded internal limits
/// or caller has tried to create a class after
/// threads have been started. Caller must fallback
/// and recover.
///
static policy_t createPolicyClass();
enum EPolicyOption
{
/// Maximum number of connections the library will use to
/// perform operations. This is somewhat soft as the underlying
@ -113,24 +133,40 @@ public:
/// a somewhat soft value. There may be an additional five
/// connections per policy class depending upon runtime
/// behavior.
GP_CONNECTION_LIMIT,
///
/// Both global and per-class
PO_CONNECTION_LIMIT,
/// Limits the number of connections used for a single
/// literal address/port pair within the class.
///
/// Per-class only
PO_PER_HOST_CONNECTION_LIMIT,
/// String containing a system-appropriate directory name
/// where SSL certs are stored.
GP_CA_PATH,
///
/// Global only
PO_CA_PATH,
/// String giving a full path to a file containing SSL certs.
GP_CA_FILE,
///
/// Global only
PO_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,
///
/// Global only
PO_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,
// enabled, has priority over GP_HTTP_PROXY.
///
/// Global only
PO_LLPROXY,
/// Long value setting the logging trace level for the
/// library. Possible values are:
@ -143,50 +179,59 @@ public:
/// 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,
///
/// Global only
PO_TRACE,
/// Suitable requests are allowed to pipeline on their
/// connections when they ask for it.
CP_ENABLE_PIPELINING
///
/// Per-class only
PO_ENABLE_PIPELINING,
/// Controls whether client-side throttling should be
/// performed on this policy class. Positive values
/// enable throttling and specify the request rate
/// (requests per second) that should be targetted.
/// A value of zero, the default, specifies no throttling.
///
/// Per-class only
PO_THROTTLE_RATE,
PO_LAST // Always at end
};
/// Set a policy option for a global or class parameter at
/// startup time (prior to thread start).
///
/// @param opt Enum of option to be set.
/// @param pclass For class-based options, the policy class ID to
/// be changed. For globals, specify GLOBAL_POLICY_ID.
/// @param value Desired value of option.
/// @param ret_value Pointer to receive effective set value
/// if successful. May be NULL if effective
/// value not wanted.
/// @return Standard status code.
static HttpStatus setStaticPolicyOption(EPolicyOption opt, policy_t pclass,
long value, long * ret_value);
static HttpStatus setStaticPolicyOption(EPolicyOption opt, policy_t pclass,
const std::string & value, std::string * ret_value);
/// 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);
/// @param opt Enum of option to be set.
/// @param pclass For class-based options, the policy class ID to
/// be changed. Ignored for globals but recommend
/// using INVALID_POLICY_ID in this case.
/// @param value Desired value of option.
/// @return Handle of dynamic request. Use @see getStatus() if
/// the returned handle is invalid.
HttpHandle setPolicyOption(EPolicyOption opt, policy_t pclass, long value,
HttpHandler * handler);
HttpHandle setPolicyOption(EPolicyOption opt, policy_t pclass, const std::string & value,
HttpHandler * handler);
/// @}
@ -488,16 +533,6 @@ public:
/// @}
/// @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);
@ -519,7 +554,6 @@ private:
/// Must be established before any threading is allowed to
/// start.
///
static policy_t sNextPolicyID;
/// @}
// End Global State

View File

@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
* Copyright (C) 2012-2013, 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
@ -39,7 +39,9 @@ HttpResponse::HttpResponse()
mReplyLength(0U),
mReplyFullLength(0U),
mBufferArray(NULL),
mHeaders(NULL)
mHeaders(NULL),
mRetries(0U),
m503Retries(0U)
{}

View File

@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
* Copyright (C) 2012-2013, 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
@ -149,6 +149,25 @@ public:
mContentType = con_type;
}
/// Get and set retry attempt information on the request.
void getRetries(unsigned int * retries, unsigned int * retries_503) const
{
if (retries)
{
*retries = mRetries;
}
if (retries_503)
{
*retries_503 = m503Retries;
}
}
void setRetries(unsigned int retries, unsigned int retries_503)
{
mRetries = retries;
m503Retries = retries_503;
}
protected:
// Response data here
HttpStatus mStatus;
@ -158,6 +177,8 @@ protected:
BufferArray * mBufferArray;
HttpHeaders * mHeaders;
std::string mContentType;
unsigned int mRetries;
unsigned int m503Retries;
};

View File

@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
* Copyright (C) 2012-2013, 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
@ -36,7 +36,6 @@
using namespace LLCoreInt;
namespace tut
{
@ -63,7 +62,7 @@ void HttpHeadersTestObjectType::test<1>()
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());
ensure("Nothing in headers", 0 == headers->size());
// release the implicit reference, causing the object to be released
headers->release();
@ -85,14 +84,340 @@ void HttpHeadersTestObjectType::test<2>()
{
// Append a few strings
std::string str1("Pragma:");
headers->mHeaders.push_back(str1);
std::string str2("Accept: application/json");
headers->mHeaders.push_back(str2);
std::string str1n("Pragma");
std::string str1v("");
headers->append(str1n, str1v);
std::string str2n("Accept");
std::string str2v("application/json");
headers->append(str2n, str2v);
ensure("Headers retained", 2 == headers->mHeaders.size());
ensure("First is first", headers->mHeaders[0] == str1);
ensure("Second is second", headers->mHeaders[1] == str2);
ensure("Headers retained", 2 == headers->size());
HttpHeaders::container_t & c(headers->getContainerTESTONLY());
ensure("First name is first name", c[0].first == str1n);
ensure("First value is first value", c[0].second == str1v);
ensure("Second name is second name", c[1].first == str2n);
ensure("Second value is second value", c[1].second == str2v);
}
// 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<3>()
{
set_test_name("HttpHeaders basic find");
// 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 str1n("Uno");
std::string str1v("1");
headers->append(str1n, str1v);
std::string str2n("doS");
std::string str2v("2-2-2-2");
headers->append(str2n, str2v);
std::string str3n("TRES");
std::string str3v("trois gymnopedie");
headers->append(str3n, str3v);
ensure("Headers retained", 3 == headers->size());
const std::string * result(NULL);
// Find a header
result = headers->find("TRES");
ensure("Found the last item", result != NULL);
ensure("Last item is a nice", result != NULL && str3v == *result);
// appends above are raw and find is case sensitive
result = headers->find("TReS");
ensure("Last item not found due to case", result == NULL);
result = headers->find("TRE");
ensure("Last item not found due to prefixing (1)", result == NULL);
result = headers->find("TRESS");
ensure("Last item not found due to prefixing (2)", result == NULL);
}
// 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<4>()
{
set_test_name("HttpHeaders normalized header entry");
// record the total amount of dynamically allocated memory
mMemTotal = GetMemTotal();
// create a new ref counted object with an implicit reference
HttpHeaders * headers = new HttpHeaders();
{
static char line1[] = " AcCePT : image/yourfacehere";
static char line1v[] = "image/yourfacehere";
headers->appendNormal(line1, sizeof(line1) - 1);
ensure("First append worked in some fashion", 1 == headers->size());
const std::string * result(NULL);
// Find a header
result = headers->find("accept");
ensure("Found 'accept'", result != NULL);
ensure("accept value has face", result != NULL && *result == line1v);
// Left-clean on value
static char line2[] = " next : \t\tlinejunk \t";
headers->appendNormal(line2, sizeof(line2) - 1);
ensure("Second append worked", 2 == headers->size());
result = headers->find("next");
ensure("Found 'next'", result != NULL);
ensure("next value is left-clean", result != NULL &&
*result == "linejunk \t");
// First value unmolested
result = headers->find("accept");
ensure("Found 'accept' again", result != NULL);
ensure("accept value has face", result != NULL && *result == line1v);
// Colons in value are okay
static char line3[] = "FancY-PANTs::plop:-neuf-=vleem=";
static char line3v[] = ":plop:-neuf-=vleem=";
headers->appendNormal(line3, sizeof(line3) - 1);
ensure("Third append worked", 3 == headers->size());
result = headers->find("fancy-pants");
ensure("Found 'fancy-pants'", result != NULL);
ensure("fancy-pants value has colons", result != NULL && *result == line3v);
// Zero-length value
static char line4[] = "all-talk-no-walk:";
headers->appendNormal(line4, sizeof(line4) - 1);
ensure("Fourth append worked", 4 == headers->size());
result = headers->find("all-talk-no-walk");
ensure("Found 'all-talk'", result != NULL);
ensure("al-talk value is zero-length", result != NULL && result->size() == 0);
// Zero-length name
static char line5[] = ":all-talk-no-walk";
static char line5v[] = "all-talk-no-walk";
headers->appendNormal(line5, sizeof(line5) - 1);
ensure("Fifth append worked", 5 == headers->size());
result = headers->find("");
ensure("Found no-name", result != NULL);
ensure("no-name value is something", result != NULL && *result == line5v);
// Lone colon is still something
headers->clear();
static char line6[] = " :";
headers->appendNormal(line6, sizeof(line6) - 1);
ensure("Sixth append worked", 1 == headers->size());
result = headers->find("");
ensure("Found 2nd no-name", result != NULL);
ensure("2nd no-name value is nothing", result != NULL && result->size() == 0);
// Line without colons is taken as-is and unstripped in name
static char line7[] = " \toskdgioasdghaosdghoowg28342908tg8902hg0hwedfhqew890v7qh0wdebv78q0wdevbhq>?M>BNM<ZV>?NZ? \t";
headers->appendNormal(line7, sizeof(line7) - 1);
ensure("Seventh append worked", 2 == headers->size());
result = headers->find(line7);
ensure("Found whatsit line", result != NULL);
ensure("Whatsit line has no value", result != NULL && result->size() == 0);
// Normaling interface heeds the byte count, doesn't look for NUL-terminator
static char line8[] = "binary:ignorestuffontheendofthis";
headers->appendNormal(line8, 13);
ensure("Eighth append worked", 3 == headers->size());
result = headers->find("binary");
ensure("Found 'binary'", result != NULL);
ensure("binary value was limited to 'ignore'", result != NULL &&
*result == "ignore");
}
// release the implicit reference, causing the object to be released
headers->release();
// make sure we didn't leak any memory
ensure(mMemTotal == GetMemTotal());
}
// Verify forward iterator finds everything as expected
template <> template <>
void HttpHeadersTestObjectType::test<5>()
{
set_test_name("HttpHeaders iterator tests");
// record the total amount of dynamically allocated memory
mMemTotal = GetMemTotal();
// create a new ref counted object with an implicit reference
HttpHeaders * headers = new HttpHeaders();
HttpHeaders::iterator end(headers->end()), begin(headers->begin());
ensure("Empty container has equal begin/end const iterators", end == begin);
HttpHeaders::const_iterator cend(headers->end()), cbegin(headers->begin());
ensure("Empty container has equal rbegin/rend const iterators", cend == cbegin);
ensure("Empty container has equal begin/end iterators", headers->end() == headers->begin());
{
static char line1[] = " AcCePT : image/yourfacehere";
static char line1v[] = "image/yourfacehere";
headers->appendNormal(line1, sizeof(line1) - 1);
static char line2[] = " next : \t\tlinejunk \t";
static char line2v[] = "linejunk \t";
headers->appendNormal(line2, sizeof(line2) - 1);
static char line3[] = "FancY-PANTs::plop:-neuf-=vleem=";
static char line3v[] = ":plop:-neuf-=vleem=";
headers->appendNormal(line3, sizeof(line3) - 1);
static char line4[] = "all-talk-no-walk:";
static char line4v[] = "";
headers->appendNormal(line4, sizeof(line4) - 1);
static char line5[] = ":all-talk-no-walk";
static char line5v[] = "all-talk-no-walk";
headers->appendNormal(line5, sizeof(line5) - 1);
static char line6[] = " :";
static char line6v[] = "";
headers->appendNormal(line6, sizeof(line6) - 1);
ensure("All entries accounted for", 6 == headers->size());
static char * values[] = {
line1v,
line2v,
line3v,
line4v,
line5v,
line6v
};
int i(0);
HttpHeaders::const_iterator cend(headers->end());
for (HttpHeaders::const_iterator it(headers->begin());
cend != it;
++it, ++i)
{
std::ostringstream str;
str << "Const Iterator value # " << i << " was " << values[i];
ensure(str.str(), (*it).second == values[i]);
}
// Rewind, do non-consts
i = 0;
HttpHeaders::iterator end(headers->end());
for (HttpHeaders::iterator it(headers->begin());
end != it;
++it, ++i)
{
std::ostringstream str;
str << "Const Iterator value # " << i << " was " << values[i];
ensure(str.str(), (*it).second == values[i]);
}
}
// release the implicit reference, causing the object to be released
headers->release();
// make sure we didn't leak any memory
ensure(mMemTotal == GetMemTotal());
}
// Reverse iterators find everything as expected
template <> template <>
void HttpHeadersTestObjectType::test<6>()
{
set_test_name("HttpHeaders reverse iterator tests");
// record the total amount of dynamically allocated memory
mMemTotal = GetMemTotal();
// create a new ref counted object with an implicit reference
HttpHeaders * headers = new HttpHeaders();
HttpHeaders::reverse_iterator rend(headers->rend()), rbegin(headers->rbegin());
ensure("Empty container has equal rbegin/rend const iterators", rend == rbegin);
HttpHeaders::const_reverse_iterator crend(headers->rend()), crbegin(headers->rbegin());
ensure("Empty container has equal rbegin/rend const iterators", crend == crbegin);
{
static char line1[] = " AcCePT : image/yourfacehere";
static char line1v[] = "image/yourfacehere";
headers->appendNormal(line1, sizeof(line1) - 1);
static char line2[] = " next : \t\tlinejunk \t";
static char line2v[] = "linejunk \t";
headers->appendNormal(line2, sizeof(line2) - 1);
static char line3[] = "FancY-PANTs::plop:-neuf-=vleem=";
static char line3v[] = ":plop:-neuf-=vleem=";
headers->appendNormal(line3, sizeof(line3) - 1);
static char line4[] = "all-talk-no-walk:";
static char line4v[] = "";
headers->appendNormal(line4, sizeof(line4) - 1);
static char line5[] = ":all-talk-no-walk";
static char line5v[] = "all-talk-no-walk";
headers->appendNormal(line5, sizeof(line5) - 1);
static char line6[] = " :";
static char line6v[] = "";
headers->appendNormal(line6, sizeof(line6) - 1);
ensure("All entries accounted for", 6 == headers->size());
static char * values[] = {
line6v,
line5v,
line4v,
line3v,
line2v,
line1v
};
int i(0);
HttpHeaders::const_reverse_iterator cend(headers->rend());
for (HttpHeaders::const_reverse_iterator it(headers->rbegin());
cend != it;
++it, ++i)
{
std::ostringstream str;
str << "Const Iterator value # " << i << " was " << values[i];
ensure(str.str(), (*it).second == values[i]);
}
// Rewind, do non-consts
i = 0;
HttpHeaders::reverse_iterator end(headers->rend());
for (HttpHeaders::reverse_iterator it(headers->rbegin());
end != it;
++it, ++i)
{
std::ostringstream str;
str << "Iterator value # " << i << " was " << values[i];
ensure(str.str(), (*it).second == values[i]);
}
}
// release the implicit reference, causing the object to be released

View File

@ -69,6 +69,8 @@ void usleep(unsigned long usec);
namespace tut
{
typedef std::vector<std::pair<boost::regex, boost::regex> > regex_container_t;
struct HttpRequestTestData
{
// the test objects inherit from this so the member functions and variables
@ -118,11 +120,17 @@ public:
for (int i(0); i < mHeadersRequired.size(); ++i)
{
bool found = false;
for (HttpHeaders::container_t::const_iterator iter(header->mHeaders.begin());
header->mHeaders.end() != iter;
for (HttpHeaders::const_iterator iter(header->begin());
header->end() != iter;
++iter)
{
if (boost::regex_match(*iter, mHeadersRequired[i]))
// std::cerr << "Header: " << (*iter).first
// << ": " << (*iter).second << std::endl;
if (boost::regex_match((*iter).first,
mHeadersRequired[i].first) &&
boost::regex_match((*iter).second,
mHeadersRequired[i].second))
{
found = true;
break;
@ -138,11 +146,14 @@ public:
{
for (int i(0); i < mHeadersDisallowed.size(); ++i)
{
for (HttpHeaders::container_t::const_iterator iter(header->mHeaders.begin());
header->mHeaders.end() != iter;
for (HttpHeaders::const_iterator iter(header->begin());
header->end() != iter;
++iter)
{
if (boost::regex_match(*iter, mHeadersDisallowed[i]))
if (boost::regex_match((*iter).first,
mHeadersDisallowed[i].first) &&
boost::regex_match((*iter).second,
mHeadersDisallowed[i].second))
{
std::ostringstream str;
str << "Disallowed header # " << i << " not found in response";
@ -168,8 +179,8 @@ public:
std::string mName;
HttpHandle mExpectHandle;
std::string mCheckContentType;
std::vector<boost::regex> mHeadersRequired;
std::vector<boost::regex> mHeadersDisallowed;
regex_container_t mHeadersRequired;
regex_container_t mHeadersDisallowed;
};
typedef test_group<HttpRequestTestData> HttpRequestTestGroupType;
@ -1211,7 +1222,7 @@ void HttpRequestTestObjectType::test<12>()
HttpRequest::createService();
// Enable tracing
HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_TRACE, 2);
HttpRequest::setStaticPolicyOption(HttpRequest::PO_TRACE, HttpRequest::DEFAULT_POLICY_ID, 2, NULL);
// Start threading early so that thread memory is invariant
// over the test.
@ -1329,7 +1340,7 @@ void HttpRequestTestObjectType::test<13>()
HttpRequest::createService();
// Enable tracing
HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_TRACE, 2);
HttpRequest::setStaticPolicyOption(HttpRequest::PO_TRACE, HttpRequest::DEFAULT_POLICY_ID, 2, NULL);
// Start threading early so that thread memory is invariant
// over the test.
@ -1344,7 +1355,9 @@ void HttpRequestTestObjectType::test<13>()
// Issue a GET that succeeds
mStatus = HttpStatus(200);
handler.mHeadersRequired.push_back(boost::regex("\\W*X-LL-Special:.*", boost::regex::icase));
handler.mHeadersRequired.push_back(
regex_container_t::value_type(boost::regex("X-LL-Special", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID,
0U,
url_base,
@ -1711,18 +1724,54 @@ void HttpRequestTestObjectType::test<16>()
// Issue a GET that *can* connect
mStatus = HttpStatus(200);
handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase));
handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*\\*/\\*", boost::regex::icase));
handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough
handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*\\d+", boost::regex::icase));
handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-cache-control:.*", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-range:.*", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:.*", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-type:.*", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase));
handler.mHeadersRequired.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-connection", boost::regex::icase),
boost::regex("keep-alive", boost::regex::icase)));
handler.mHeadersRequired.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-accept", boost::regex::icase),
boost::regex("\\*/\\*", boost::regex::icase)));
handler.mHeadersRequired.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-accept-encoding", boost::regex::icase),
boost::regex("((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase))); // close enough
handler.mHeadersRequired.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-keep-alive", boost::regex::icase),
boost::regex("\\d+", boost::regex::icase)));
handler.mHeadersRequired.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-host", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-cache-control", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-pragma", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-range", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-transfer-encoding", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-referer", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-content-type", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-content-encoding", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
HttpHandle handle = req->requestGet(HttpRequest::DEFAULT_POLICY_ID,
0U,
url_base + "reflect/",
@ -1744,23 +1793,60 @@ void HttpRequestTestObjectType::test<16>()
// Do a texture-style fetch
headers = new HttpHeaders;
headers->mHeaders.push_back("Accept: image/x-j2c");
headers->append("Accept", "image/x-j2c");
mStatus = HttpStatus(200);
handler.mHeadersRequired.clear();
handler.mHeadersDisallowed.clear();
handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase));
handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*image/x-j2c", boost::regex::icase));
handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough
handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*\\d+", boost::regex::icase));
handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase));
handler.mHeadersRequired.push_back(boost::regex("\\W*X-Reflect-range:.*", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-cache-control:.*", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:.*", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-type:.*", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase));
handler.mHeadersRequired.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-connection", boost::regex::icase),
boost::regex("keep-alive", boost::regex::icase)));
handler.mHeadersRequired.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-accept", boost::regex::icase),
boost::regex("image/x-j2c", boost::regex::icase)));
handler.mHeadersRequired.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-accept-encoding", boost::regex::icase),
boost::regex("((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase))); // close enough
handler.mHeadersRequired.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-keep-alive", boost::regex::icase),
boost::regex("\\d+", boost::regex::icase)));
handler.mHeadersRequired.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-host", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
handler.mHeadersRequired.push_back(
regex_container_t::value_type(
boost::regex("\\W*X-Reflect-range", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-cache-control", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-pragma", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-transfer-encoding", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-referer", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-content-type", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-content-encoding", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID,
0U,
url_base + "reflect/",
@ -1901,20 +1987,63 @@ void HttpRequestTestObjectType::test<17>()
// Issue a default POST
mStatus = HttpStatus(200);
handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase));
handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*\\*/\\*", boost::regex::icase));
handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough
handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*\\d+", boost::regex::icase));
handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase));
handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-length:\\s*\\d+", boost::regex::icase));
handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-type:\\s*application/x-www-form-urlencoded", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-cache-control:.*", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-range:.*", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-expect:.*", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:\\s*.*chunked.*", boost::regex::icase));
handler.mHeadersRequired.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-connection", boost::regex::icase),
boost::regex("keep-alive", boost::regex::icase)));
handler.mHeadersRequired.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-accept", boost::regex::icase),
boost::regex("\\*/\\*", boost::regex::icase)));
handler.mHeadersRequired.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-accept-encoding", boost::regex::icase),
boost::regex("((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase))); // close enough
handler.mHeadersRequired.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-keep-alive", boost::regex::icase),
boost::regex("\\d+", boost::regex::icase)));
handler.mHeadersRequired.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-host", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
handler.mHeadersRequired.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-content-length", boost::regex::icase),
boost::regex("\\d+", boost::regex::icase)));
handler.mHeadersRequired.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-content-type", boost::regex::icase),
boost::regex("application/x-www-form-urlencoded", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-cache-control", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-pragma", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-range", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-referer", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-content-encoding", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-expect", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-transfer_encoding", boost::regex::icase),
boost::regex(".*chunked.*", boost::regex::icase)));
HttpHandle handle = req->requestPost(HttpRequest::DEFAULT_POLICY_ID,
0U,
url_base + "reflect/",
@ -2061,20 +2190,64 @@ void HttpRequestTestObjectType::test<18>()
// Issue a default PUT
mStatus = HttpStatus(200);
handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase));
handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*\\*/\\*", boost::regex::icase));
handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough
handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*\\d+", boost::regex::icase));
handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase));
handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-length:\\s*\\d+", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-cache-control:.*", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-range:.*", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-expect:.*", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:\\s*.*chunked.*", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-content-type:.*", boost::regex::icase));
handler.mHeadersRequired.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-connection", boost::regex::icase),
boost::regex("keep-alive", boost::regex::icase)));
handler.mHeadersRequired.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-accept", boost::regex::icase),
boost::regex("\\*/\\*", boost::regex::icase)));
handler.mHeadersRequired.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-accept-encoding", boost::regex::icase),
boost::regex("((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase))); // close enough
handler.mHeadersRequired.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-keep-alive", boost::regex::icase),
boost::regex("\\d+", boost::regex::icase)));
handler.mHeadersRequired.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-host", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
handler.mHeadersRequired.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-content-length", boost::regex::icase),
boost::regex("\\d+", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-cache-control", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-pragma", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-range", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-referer", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-content-encoding", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-expect", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-transfer-encoding", boost::regex::icase),
boost::regex(".*chunked.*", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-content-type", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
HttpHandle handle = req->requestPut(HttpRequest::DEFAULT_POLICY_ID,
0U,
url_base + "reflect/",
@ -2215,27 +2388,73 @@ void HttpRequestTestObjectType::test<19>()
// headers
headers = new HttpHeaders;
headers->mHeaders.push_back("Keep-Alive: 120");
headers->mHeaders.push_back("Accept-encoding: deflate");
headers->mHeaders.push_back("Accept: text/plain");
headers->append("Keep-Alive", "120");
headers->append("Accept-encoding", "deflate");
headers->append("Accept", "text/plain");
// Issue a GET with modified headers
mStatus = HttpStatus(200);
handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase));
handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*text/plain", boost::regex::icase));
handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*deflate", boost::regex::icase)); // close enough
handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*120", boost::regex::icase));
handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough
handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-keep-alive:\\s*300", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-accept:\\s*\\*/\\*", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-cache-control:.*", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-range:.*", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:.*", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-type:.*", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase));
handler.mHeadersRequired.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-connection", boost::regex::icase),
boost::regex("keep-alive", boost::regex::icase)));
handler.mHeadersRequired.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-accept", boost::regex::icase),
boost::regex("text/plain", boost::regex::icase)));
handler.mHeadersRequired.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-accept-encoding", boost::regex::icase),
boost::regex("deflate", boost::regex::icase))); // close enough
handler.mHeadersRequired.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-keep-alive", boost::regex::icase),
boost::regex("120", boost::regex::icase)));
handler.mHeadersRequired.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-host", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-accept-encoding", boost::regex::icase),
boost::regex("((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase))); // close enough
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-keep-alive", boost::regex::icase),
boost::regex("300", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-accept", boost::regex::icase),
boost::regex("\\*/\\*", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-cache-control", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-pragma", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-range", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-transfer-encoding", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-referer", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-content-type", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-content-encoding", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
HttpHandle handle = req->requestGet(HttpRequest::DEFAULT_POLICY_ID,
0U,
url_base + "reflect/",
@ -2368,10 +2587,10 @@ void HttpRequestTestObjectType::test<20>()
// headers
headers = new HttpHeaders();
headers->mHeaders.push_back("keep-Alive: 120");
headers->mHeaders.push_back("Accept: text/html");
headers->mHeaders.push_back("content-type: application/llsd+xml");
headers->mHeaders.push_back("cache-control: no-store");
headers->append("keep-Alive", "120");
headers->append("Accept", "text/html");
headers->append("content-type", "application/llsd+xml");
headers->append("cache-control", "no-store");
// And a buffer array
const char * msg("<xml><llsd><string>It was the best of times, it was the worst of times.</string></llsd></xml>");
@ -2380,23 +2599,76 @@ void HttpRequestTestObjectType::test<20>()
// Issue a default POST
mStatus = HttpStatus(200);
handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase));
handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*text/html", boost::regex::icase));
handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough
handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*120", boost::regex::icase));
handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase));
handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-length:\\s*\\d+", boost::regex::icase));
handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-type:\\s*application/llsd\\+xml", boost::regex::icase));
handler.mHeadersRequired.push_back(boost::regex("\\s*X-Reflect-cache-control:\\s*no-store", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-content-type:\\s*application/x-www-form-urlencoded", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-accept:\\s*\\*/\\*", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-keep-alive:\\s*300", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-range:.*", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-expect:.*", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:\\s*.*chunked.*", boost::regex::icase));
handler.mHeadersRequired.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-connection", boost::regex::icase),
boost::regex("keep-alive", boost::regex::icase)));
handler.mHeadersRequired.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-accept", boost::regex::icase),
boost::regex("text/html", boost::regex::icase)));
handler.mHeadersRequired.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-accept-encoding", boost::regex::icase),
boost::regex("((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase))); // close enough
handler.mHeadersRequired.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-keep-alive", boost::regex::icase),
boost::regex("120", boost::regex::icase)));
handler.mHeadersRequired.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-host", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
handler.mHeadersRequired.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-content-length", boost::regex::icase),
boost::regex("\\d+", boost::regex::icase)));
handler.mHeadersRequired.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-content-type", boost::regex::icase),
boost::regex("application/llsd\\+xml", boost::regex::icase)));
handler.mHeadersRequired.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-cache-control", boost::regex::icase),
boost::regex("no-store", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-content-type", boost::regex::icase),
boost::regex("application/x-www-form-urlencoded", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-accept", boost::regex::icase),
boost::regex("\\*/\\*", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-keep-alive", boost::regex::icase),
boost::regex("300", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-pragma", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-range", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-referer", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-content-encoding", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-expect", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-transfer-encoding", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
HttpHandle handle = req->requestPost(HttpRequest::DEFAULT_POLICY_ID,
0U,
url_base + "reflect/",
@ -2538,9 +2810,9 @@ void HttpRequestTestObjectType::test<21>()
// headers
headers = new HttpHeaders;
headers->mHeaders.push_back("content-type: text/plain");
headers->mHeaders.push_back("content-type: text/html");
headers->mHeaders.push_back("content-type: application/llsd+xml");
headers->append("content-type", "text/plain");
headers->append("content-type", "text/html");
headers->append("content-type", "application/llsd+xml");
// And a buffer array
const char * msg("<xml><llsd><string>It was the best of times, it was the worst of times.</string></llsd></xml>");
@ -2549,22 +2821,71 @@ void HttpRequestTestObjectType::test<21>()
// Issue a default PUT
mStatus = HttpStatus(200);
handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase));
handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*\\*/\\*", boost::regex::icase));
handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough
handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*\\d+", boost::regex::icase));
handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase));
handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-length:\\s*\\d+", boost::regex::icase));
handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-type:\\s*application/llsd\\+xml", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-cache-control:.*", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-range:.*", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-expect:.*", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:\\s*.*chunked.*", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-content-type:\\s*text/plain", boost::regex::icase));
handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-content-type:\\s*text/html", boost::regex::icase));
handler.mHeadersRequired.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-connection", boost::regex::icase),
boost::regex("keep-alive", boost::regex::icase)));
handler.mHeadersRequired.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-accept", boost::regex::icase),
boost::regex("\\*/\\*", boost::regex::icase)));
handler.mHeadersRequired.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-accept-encoding", boost::regex::icase),
boost::regex("((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase))); // close enough
handler.mHeadersRequired.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-keep-alive", boost::regex::icase),
boost::regex("\\d+", boost::regex::icase)));
handler.mHeadersRequired.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-host", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
handler.mHeadersRequired.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-content-length", boost::regex::icase),
boost::regex("\\d+", boost::regex::icase)));
handler.mHeadersRequired.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-content-type", boost::regex::icase),
boost::regex("application/llsd\\+xml", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-cache-control", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-pragma", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-range", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-referer", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-content-encoding", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-expect", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-transfer-encoding", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-content-type", boost::regex::icase),
boost::regex("text/plain", boost::regex::icase)));
handler.mHeadersDisallowed.push_back(
regex_container_t::value_type(
boost::regex("X-Reflect-content-type", boost::regex::icase),
boost::regex("text/html", boost::regex::icase)));
HttpHandle handle = req->requestPut(HttpRequest::DEFAULT_POLICY_ID,
0U,
url_base + "reflect/",
@ -2854,6 +3175,142 @@ void HttpRequestTestObjectType::test<22>()
}
}
template <> template <>
void HttpRequestTestObjectType::test<23>()
{
ScopedCurlInit ready;
set_test_name("HttpRequest GET 503s with 'Retry-After'");
// This tests mainly that the code doesn't fall over if
// various well- and mis-formed Retry-After headers are
// sent along with the response. Direct inspection of
// the parsing result isn't supported.
// Handler can be stack-allocated *if* there are no dangling
// references to it after completion of this method.
// Create before memory record as the string copy will bump numbers.
TestHandler2 handler(this, "handler");
std::string url_base(get_base_url() + "/503/"); // path to 503 generators
// record the total amount of dynamically allocated memory
mMemTotal = GetMemTotal();
mHandlerCalls = 0;
HttpRequest * req = NULL;
HttpOptions * opts = NULL;
try
{
// Get singletons created
HttpRequest::createService();
// Start threading early so that thread memory is invariant
// over the test.
HttpRequest::startThread();
// create a new ref counted object with an implicit reference
req = new HttpRequest();
ensure("Memory allocated on construction", mMemTotal < GetMemTotal());
opts = new HttpOptions();
opts->setRetries(1); // Retry once only
opts->setUseRetryAfter(true); // Try to parse the retry-after header
// Issue a GET that 503s with valid retry-after
mStatus = HttpStatus(503);
int url_limit(6);
for (int i(0); i < url_limit; ++i)
{
std::ostringstream url;
url << url_base << i << "/";
HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID,
0U,
url.str(),
0,
0,
opts,
NULL,
&handler);
std::ostringstream testtag;
testtag << "Valid handle returned for 503 request #" << i;
ensure(testtag.str(), handle != LLCORE_HTTP_HANDLE_INVALID);
}
// Run the notification pump.
int count(0);
int limit(LOOP_COUNT_LONG);
while (count++ < limit && mHandlerCalls < url_limit)
{
req->update(0);
usleep(LOOP_SLEEP_INTERVAL);
}
ensure("Request executed in reasonable time", count < limit);
ensure("One handler invocation for request", mHandlerCalls == url_limit);
// Okay, request a shutdown of the servicing thread
mStatus = HttpStatus();
mHandlerCalls = 0;
HttpHandle handle = req->requestStopThread(&handler);
ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID);
// Run the notification pump again
count = 0;
limit = LOOP_COUNT_LONG;
while (count++ < limit && mHandlerCalls < 1)
{
req->update(1000000);
usleep(LOOP_SLEEP_INTERVAL);
}
ensure("Second request executed in reasonable time", count < limit);
ensure("Second handler invocation", mHandlerCalls == 1);
// See that we actually shutdown the thread
count = 0;
limit = LOOP_COUNT_SHORT;
while (count++ < limit && ! HttpService::isStopped())
{
usleep(LOOP_SLEEP_INTERVAL);
}
ensure("Thread actually stopped running", HttpService::isStopped());
// release options
opts->release();
opts = NULL;
// release the request object
delete req;
req = NULL;
// Shut down service
HttpRequest::destroyService();
#if defined(WIN32)
// Can only do this memory test on Windows. On other platforms,
// the LL logging system holds on to memory and produces what looks
// like memory leaks...
// printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal());
ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal());
#endif
}
catch (...)
{
stop_thread(req);
if (opts)
{
opts->release();
opts = NULL;
}
delete req;
HttpRequest::destroyService();
throw;
}
}
} // end namespace tut
namespace

View File

@ -259,6 +259,65 @@ void HttpStatusTestObjectType::test<7>()
ensure(msg == "Unknown error");
}
template <> template <>
void HttpStatusTestObjectType::test<8>()
{
set_test_name("HttpStatus toHex() nominal function");
HttpStatus status(404);
std::string msg = status.toHex();
// std::cout << "Result: " << msg << std::endl;
ensure(msg == "01940001");
}
template <> template <>
void HttpStatusTestObjectType::test<9>()
{
set_test_name("HttpStatus toTerseString() nominal function");
HttpStatus status(404);
std::string msg = status.toTerseString();
// std::cout << "Result: " << msg << std::endl;
ensure("Normal HTTP 404", msg == "Http_404");
status = HttpStatus(200);
msg = status.toTerseString();
// std::cout << "Result: " << msg << std::endl;
ensure("Normal HTTP 200", msg == "Http_200");
status = HttpStatus(200, HE_REPLY_ERROR);
msg = status.toTerseString();
// std::cout << "Result: " << msg << std::endl;
ensure("Unsuccessful HTTP 200", msg == "Http_200"); // No distinction for error
status = HttpStatus(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT);
msg = status.toTerseString();
// std::cout << "Result: " << msg << std::endl;
ensure("Easy couldn't connect error", msg == "Easy_7");
status = HttpStatus(HttpStatus::EXT_CURL_MULTI, CURLM_OUT_OF_MEMORY);
msg = status.toTerseString();
// std::cout << "Result: " << msg << std::endl;
ensure("Multi out-of-memory error", msg == "Multi_3");
status = HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_SET);
msg = status.toTerseString();
// std::cout << "Result: " << msg << std::endl;
ensure("Core option not set error", msg == "Core_7");
status = HttpStatus(22000, 1);
msg = status.toTerseString();
// std::cout << "Result: " << msg << std::endl;
ensure("Undecodable error", msg == "Unknown_1");
status = HttpStatus(22000, -1);
msg = status.toTerseString();
// std::cout << "Result: " << msg << std::endl;
ensure("Undecodable error 65535", msg == "Unknown_65535");
}
} // end namespace tut
#endif // TEST_HTTP_STATUS_H

View File

@ -69,6 +69,15 @@ class TestHTTPRequestHandler(BaseHTTPRequestHandler):
"Content-Range: bytes 0-75/2983",
"Content-Length: 76"
-- '/bug2295/inv_cont_range/0/' Generates HE_INVALID_CONTENT_RANGE error in llcorehttp.
- '/503/' Generate 503 responses with various kinds
of 'retry-after' headers
-- '/503/0/' "Retry-After: 2"
-- '/503/1/' "Retry-After: Thu, 31 Dec 2043 23:59:59 GMT"
-- '/503/2/' "Retry-After: Fri, 31 Dec 1999 23:59:59 GMT"
-- '/503/3/' "Retry-After: "
-- '/503/4/' "Retry-After: (*#*(@*(@(")"
-- '/503/5/' "Retry-After: aklsjflajfaklsfaklfasfklasdfklasdgahsdhgasdiogaioshdgo"
-- '/503/6/' "Retry-After: 1 2 3 4 5 6 7 8 9 10"
Some combinations make no sense, there's no effort to protect
you from that.
@ -143,22 +152,40 @@ class TestHTTPRequestHandler(BaseHTTPRequestHandler):
if "/sleep/" in self.path:
time.sleep(30)
if "fail" in self.path:
status = data.get("status", 500)
# self.responses maps an int status to a (short, long) pair of
# strings. We want the longer string. That's why we pass a string
# pair to get(): the [1] will select the second string, whether it
# came from self.responses or from our default pair.
reason = data.get("reason",
self.responses.get(status,
("fail requested",
"Your request specified failure status %s "
"without providing a reason" % status))[1])
debug("fail requested: %s: %r", status, reason)
self.send_error(status, reason)
if "/503/" in self.path:
# Tests for various kinds of 'Retry-After' header parsing
body = None
if "/503/0/" in self.path:
self.send_response(503)
self.send_header("retry-after", "2")
elif "/503/1/" in self.path:
self.send_response(503)
self.send_header("retry-after", "Thu, 31 Dec 2043 23:59:59 GMT")
elif "/503/2/" in self.path:
self.send_response(503)
self.send_header("retry-after", "Fri, 31 Dec 1999 23:59:59 GMT")
elif "/503/3/" in self.path:
self.send_response(503)
self.send_header("retry-after", "")
elif "/503/4/" in self.path:
self.send_response(503)
self.send_header("retry-after", "(*#*(@*(@(")
elif "/503/5/" in self.path:
self.send_response(503)
self.send_header("retry-after", "aklsjflajfaklsfaklfasfklasdfklasdgahsdhgasdiogaioshdgo")
elif "/503/6/" in self.path:
self.send_response(503)
self.send_header("retry-after", "1 2 3 4 5 6 7 8 9 10")
else:
# Unknown request
self.send_response(400)
body = "Unknown /503/ path in server"
if "/reflect/" in self.path:
self.reflect_headers()
self.send_header("Content-type", "text/plain")
self.end_headers()
if body:
self.wfile.write(body)
elif "/bug2295/" in self.path:
# Test for https://jira.secondlife.com/browse/BUG-2295
#
@ -194,8 +221,7 @@ class TestHTTPRequestHandler(BaseHTTPRequestHandler):
self.end_headers()
if body:
self.wfile.write(body)
else:
# Normal response path
elif "fail" not in self.path:
data = data.copy() # we're going to modify
# Ensure there's a "reply" key in data, even if there wasn't before
data["reply"] = data.get("reply", llsd.LLSD("success"))
@ -210,6 +236,22 @@ class TestHTTPRequestHandler(BaseHTTPRequestHandler):
self.end_headers()
if withdata:
self.wfile.write(response)
else: # fail requested
status = data.get("status", 500)
# self.responses maps an int status to a (short, long) pair of
# strings. We want the longer string. That's why we pass a string
# pair to get(): the [1] will select the second string, whether it
# came from self.responses or from our default pair.
reason = data.get("reason",
self.responses.get(status,
("fail requested",
"Your request specified failure status %s "
"without providing a reason" % status))[1])
debug("fail requested: %s: %r", status, reason)
self.send_error(status, reason)
if "/reflect/" in self.path:
self.reflect_headers()
self.end_headers()
def reflect_headers(self):
for name in self.headers.keys():

View File

@ -6747,7 +6747,7 @@ BOOL LLVolumeFace::createSide(LLVolume* volume, BOOL partial_build)
return TRUE;
}
//adapted from Lengyel, Eric. “Computing Tangent Space Basis Vectors for an Arbitrary Mesh”. Terathon Software 3D Graphics Library, 2001. http://www.terathon.com/code/tangent.html
//adapted from Lengyel, Eric. "Computing Tangent Space Basis Vectors for an Arbitrary Mesh". Terathon Software 3D Graphics Library, 2001. http://www.terathon.com/code/tangent.html
void CalculateTangentArray(U32 vertexCount, const LLVector4a *vertex, const LLVector4a *normal,
const LLVector2 *texcoord, U32 triangleCount, const U16* index_array, LLVector4a *tangent)
{

View File

@ -6,7 +6,7 @@
*
* $LicenseInfo:firstyear=2006&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
* Copyright (C) 2010-2013, 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
@ -294,9 +294,12 @@ LLCurl::Easy* LLCurl::Easy::getEasy()
return NULL;
}
// set no DNS caching as default for all easy handles. This prevents them adopting a
// multi handles cache if they are added to one.
CURLcode result = curl_easy_setopt(easy->mCurlEasyHandle, CURLOPT_DNS_CACHE_TIMEOUT, 0);
// Enable a brief cache period for now. This was zero for the longest time
// which caused some routers grief and generated unneeded traffic. For the
// threaded resolver, we're using system resolution libraries and non-zero values
// are preferred. The c-ares resolver is another matter and it might not
// track server changes as well.
CURLcode result = curl_easy_setopt(easy->mCurlEasyHandle, CURLOPT_DNS_CACHE_TIMEOUT, 15);
check_curl_code(result);
result = curl_easy_setopt(easy->mCurlEasyHandle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
check_curl_code(result);

View File

@ -717,7 +717,14 @@ BOOL LLGLSLShader::mapUniforms(const vector<LLStaticHashedString> * uniforms)
BOOL LLGLSLShader::link(BOOL suppress_errors)
{
return LLShaderMgr::instance()->linkProgramObject(mProgramObject, suppress_errors);
BOOL success = LLShaderMgr::instance()->linkProgramObject(mProgramObject, suppress_errors);
if (!suppress_errors)
{
LLShaderMgr::instance()->dumpObjectLog(mProgramObject, !success, mName);
}
return success;
}
void LLGLSLShader::bind()

View File

@ -505,9 +505,25 @@ static std::string get_object_log(GLhandleARB ret)
return res;
}
void LLShaderMgr::dumpObjectLog(GLhandleARB ret, BOOL warns)
void LLShaderMgr::dumpObjectLog(GLhandleARB ret, BOOL warns, const std::string& filename)
{
std::string log = get_object_log(ret);
if (log.length() > 0 || warns)
{
if (!filename.empty())
{
if (warns)
{
LL_WARNS("ShaderLoading") << "From " << filename << ":" << LL_ENDL;
}
else
{
LL_INFOS("ShaderLoading") << "From " << filename << ":" << LL_ENDL;
}
}
}
if ( log.length() > 0 )
{
if (warns)
@ -558,7 +574,7 @@ GLhandleARB LLShaderMgr::loadShaderFile(const std::string& filename, S32 & shade
file = LLFile::fopen(fname.str(), "r"); /* Flawfinder: ignore */
if (file)
{
LL_INFOS("ShaderLoading") << "Loading file: shaders/class" << gpu_class << "/" << filename << " (Want class " << gpu_class << ")" << LL_ENDL;
LL_DEBUGS("ShaderLoading") << "Loading file: shaders/class" << gpu_class << "/" << filename << " (Want class " << gpu_class << ")" << LL_ENDL;
break; // done
}
}
@ -812,8 +828,8 @@ GLhandleARB LLShaderMgr::loadShaderFile(const std::string& filename, S32 & shade
if (error != GL_NO_ERROR || success == GL_FALSE)
{
//an error occured, print log
LL_WARNS("ShaderLoading") << "GLSL Compilation Error: (" << error << ") in " << filename << LL_ENDL;
dumpObjectLog(ret);
LL_WARNS("ShaderLoading") << "GLSL Compilation Error:" << LL_ENDL;
dumpObjectLog(ret, TRUE, filename);
#if LL_WINDOWS
std::stringstream ostr;
//dump shader source for debugging
@ -938,11 +954,6 @@ BOOL LLShaderMgr::linkProgramObject(GLhandleARB obj, BOOL suppress_errors)
suppress_errors = FALSE;
}
#endif
if (!suppress_errors)
{
dumpObjectLog(obj, !success);
}
return success;
}
@ -1146,6 +1157,7 @@ void LLShaderMgr::initAttribsAndUniforms()
mReservedUniforms.push_back("env_intensity");
mReservedUniforms.push_back("matrixPalette");
mReservedUniforms.push_back("translationPalette");
mReservedUniforms.push_back("screenTex");
mReservedUniforms.push_back("screenDepth");

View File

@ -176,6 +176,7 @@ public:
ENVIRONMENT_INTENSITY,
AVATAR_MATRIX,
AVATAR_TRANSLATION,
WATER_SCREENTEX,
WATER_SCREENDEPTH,
@ -224,7 +225,7 @@ DISPLAY_GAMMA,
virtual void initAttribsAndUniforms(void);
BOOL attachShaderFeatures(LLGLSLShader * shader);
void dumpObjectLog(GLhandleARB ret, BOOL warns = TRUE);
void dumpObjectLog(GLhandleARB ret, BOOL warns = TRUE, const std::string& filename = "");
BOOL linkProgramObject(GLhandleARB obj, BOOL suppress_errors = FALSE);
BOOL validateProgramObject(GLhandleARB obj);
GLhandleARB loadShaderFile(const std::string& filename, S32 & shader_level, GLenum type, boost::unordered_map<std::string, std::string>* defines = NULL, S32 texture_index_channels = -1);

View File

@ -641,7 +641,7 @@ void LLButton::draw()
bool use_glow_effect = FALSE;
LLColor4 highlighting_color = LLColor4::white;
LLColor4 glow_color;
LLColor4 glow_color = LLColor4::white;
LLRender::eBlendType glow_type = LLRender::BT_ADD_WITH_ALPHA;
LLUIImage* imagep = NULL;

View File

@ -50,8 +50,6 @@ const LLCommandId LLCommandId::null = LLCommandId("null command");
LLCommand::Params::Params()
: available_in_toybox("available_in_toybox", false)
, icon("icon")
, hover_icon_unselected("hover_icon_unselected")
, hover_icon_selected("hover_icon_selected")
, label_ref("label_ref")
, name("name")
, tooltip_ref("tooltip_ref")
@ -73,8 +71,6 @@ LLCommand::LLCommand(const LLCommand::Params& p)
: mIdentifier(p.name)
, mAvailableInToybox(p.available_in_toybox)
, mIcon(p.icon)
, mHoverIconUnselected(p.hover_icon_unselected)
, mHoverIconSelected(p.hover_icon_selected)
, mLabelRef(p.label_ref)
, mName(p.name)
, mTooltipRef(p.tooltip_ref)

View File

@ -96,9 +96,6 @@ public:
Mandatory<std::string> name;
Mandatory<std::string> tooltip_ref;
Optional<std::string> hover_icon_selected;
Optional<std::string> hover_icon_unselected;
Mandatory<std::string> execute_function;
Optional<LLSD> execute_parameters;
@ -127,8 +124,6 @@ public:
const std::string& labelRef() const { return mLabelRef; }
const std::string& name() const { return mName; }
const std::string& tooltipRef() const { return mTooltipRef; }
const std::string& hoverIconUnselected() const {return mHoverIconUnselected; }
const std::string& hoverIconSelected() const {return mHoverIconSelected; }
const std::string& executeFunctionName() const { return mExecuteFunction; }
const LLSD& executeParameters() const { return mExecuteParameters; }
@ -155,8 +150,6 @@ private:
std::string mLabelRef;
std::string mName;
std::string mTooltipRef;
std::string mHoverIconUnselected;
std::string mHoverIconSelected;
std::string mExecuteFunction;
LLSD mExecuteParameters;

View File

@ -193,15 +193,12 @@ LLTabContainer::TabParams::TabParams()
: tab_top_image_unselected("tab_top_image_unselected"),
tab_top_image_selected("tab_top_image_selected"),
tab_top_image_flash("tab_top_image_flash"),
tab_top_image_hovered("tab_top_image_hovered"),
tab_bottom_image_unselected("tab_bottom_image_unselected"),
tab_bottom_image_selected("tab_bottom_image_selected"),
tab_bottom_image_flash("tab_bottom_image_flash"),
tab_bottom_image_hovered("tab_bottom_image_hovered"),
tab_left_image_unselected("tab_left_image_unselected"),
tab_left_image_selected("tab_left_image_selected"),
tab_left_image_flash("tab_left_image_flash"),
tab_left_image_hovered("tab_left_image_hovered")
tab_left_image_flash("tab_left_image_flash")
{}
LLTabContainer::Params::Params()
@ -221,8 +218,7 @@ LLTabContainer::Params::Params()
open_tabs_on_drag_and_drop("open_tabs_on_drag_and_drop", false),
tab_icon_ctrl_pad("tab_icon_ctrl_pad", 0),
use_ellipses("use_ellipses"),
font_halign("halign"),
use_highlighting_on_hover("use_highlighting_on_hover",false)
font_halign("halign")
{}
LLTabContainer::LLTabContainer(const LLTabContainer::Params& p)
@ -258,8 +254,7 @@ LLTabContainer::LLTabContainer(const LLTabContainer::Params& p)
mCustomIconCtrlUsed(p.use_custom_icon_ctrl),
mOpenTabsOnDragAndDrop(p.open_tabs_on_drag_and_drop),
mTabIconCtrlPad(p.tab_icon_ctrl_pad),
mUseTabEllipses(p.use_ellipses),
mUseHighlightingOnHover(p.use_highlighting_on_hover)
mUseTabEllipses(p.use_ellipses)
{
static LLUICachedControl<S32> tabcntr_vert_tab_min_width ("UITabCntrVertTabMinWidth", 0);
@ -908,30 +903,18 @@ void LLTabContainer::update_images(LLTabTuple* tuple, TabParams params, LLTabCon
tuple->mButton->setImageUnselected(static_cast<LLUIImage*>(params.tab_top_image_unselected));
tuple->mButton->setImageSelected(static_cast<LLUIImage*>(params.tab_top_image_selected));
tuple->mButton->setImageFlash(static_cast<LLUIImage*>(params.tab_top_image_flash));
if(mUseHighlightingOnHover)
{
tuple->mButton->setImageHoverUnselected(static_cast<LLUIImage*>(params.tab_top_image_hovered));
}
}
else if (pos == LLTabContainer::BOTTOM)
{
tuple->mButton->setImageUnselected(static_cast<LLUIImage*>(params.tab_bottom_image_unselected));
tuple->mButton->setImageSelected(static_cast<LLUIImage*>(params.tab_bottom_image_selected));
tuple->mButton->setImageFlash(static_cast<LLUIImage*>(params.tab_bottom_image_flash));
if(mUseHighlightingOnHover)
{
tuple->mButton->setImageHoverUnselected(static_cast<LLUIImage*>(params.tab_bottom_image_hovered));
}
}
else if (pos == LLTabContainer::LEFT)
{
tuple->mButton->setImageUnselected(static_cast<LLUIImage*>(params.tab_left_image_unselected));
tuple->mButton->setImageSelected(static_cast<LLUIImage*>(params.tab_left_image_selected));
tuple->mButton->setImageFlash(static_cast<LLUIImage*>(params.tab_left_image_flash));
if(mUseHighlightingOnHover)
{
tuple->mButton->setImageHoverUnselected(static_cast<LLUIImage*>(params.tab_left_image_hovered));
}
}
}
}

View File

@ -62,15 +62,12 @@ public:
Optional<LLUIImage*> tab_top_image_unselected,
tab_top_image_selected,
tab_top_image_flash,
tab_top_image_hovered,
tab_bottom_image_unselected,
tab_bottom_image_selected,
tab_bottom_image_flash,
tab_bottom_image_hovered,
tab_left_image_unselected,
tab_left_image_selected,
tab_left_image_flash,
tab_left_image_hovered;
tab_left_image_flash;
TabParams();
};
@ -117,11 +114,6 @@ public:
*/
Optional<S32> tab_icon_ctrl_pad;
/**
* This variable is used to found out should we highlight tab button on hover
*/
Optional<bool> use_highlighting_on_hover;
Params();
};
@ -315,7 +307,6 @@ private:
bool mOpenTabsOnDragAndDrop;
S32 mTabIconCtrlPad;
bool mUseTabEllipses;
bool mUseHighlightingOnHover;
};
#endif // LL_TABCONTAINER_H

View File

@ -928,8 +928,6 @@ LLToolBarButton* LLToolBar::createButton(const LLCommandId& id)
button_p.label = LLTrans::getString(commandp->labelRef());
button_p.tool_tip = LLTrans::getString(commandp->tooltipRef());
button_p.image_overlay = LLUI::getUIImage(commandp->icon());
button_p.image_hover_unselected = LLUI::getUIImage(commandp->hoverIconUnselected());
button_p.image_hover_selected = LLUI::getUIImage(commandp->hoverIconSelected());
button_p.button_flash_enable = commandp->isFlashingAllowed();
button_p.overwriteFrom(mButtonParams[mButtonType]);
LLToolBarButton* button = LLUICtrlFactory::create<LLToolBarButton>(button_p);

View File

@ -376,13 +376,6 @@ attributedStringInfo getSegments(NSAttributedString *str)
[[self inputContext] handleEvent:theEvent];
}
if ([[theEvent charactersIgnoringModifiers] characterAtIndex:0] == NSCarriageReturnCharacter ||
[[theEvent charactersIgnoringModifiers] characterAtIndex:0] == NSEnterCharacter)
{
// callKeyDown won't return the value we expect for enter or return. Handle them as a separate case.
[[self inputContext] handleEvent:theEvent];
}
// OS X intentionally does not send us key-up information on cmd-key combinations.
// This behaviour is not a bug, and only applies to cmd-combinations (no others).
// Since SL assumes we receive those, we fake it here.

View File

@ -267,6 +267,7 @@ set(viewer_SOURCE_FILES
llfloaterregiondebugconsole.cpp
llfloaterregioninfo.cpp
llfloaterreporter.cpp
llfloaterregionrestarting.cpp
llfloaterscriptdebug.cpp
llfloaterscriptlimits.cpp
llfloatersearch.cpp
@ -855,6 +856,7 @@ set(viewer_HEADER_FILES
llfloaterregiondebugconsole.h
llfloaterregioninfo.h
llfloaterreporter.h
llfloaterregionrestarting.h
llfloaterscriptdebug.h
llfloaterscriptlimits.h
llfloatersearch.h

View File

@ -1 +1 @@
3.6.13
3.7.3

View File

@ -3,8 +3,6 @@
<command name="aboutland"
available_in_toybox="true"
icon="Command_AboutLand_Icon"
hover_icon_unselected="Command_Highlighting_Icon"
hover_icon_selected="Command_Highlighting_Selected_Icon"
label_ref="Command_AboutLand_Label"
tooltip_ref="Command_AboutLand_Tooltip"
execute_function="Floater.ToggleOrBringToFront"
@ -15,8 +13,6 @@
<command name="appearance"
available_in_toybox="true"
icon="Command_Appearance_Icon"
hover_icon_unselected="Command_Highlighting_Icon"
hover_icon_selected="Command_Highlighting_Selected_Icon"
label_ref="Command_Appearance_Label"
tooltip_ref="Command_Appearance_Tooltip"
execute_function="Floater.ToggleOrBringToFront"
@ -27,8 +23,6 @@
<command name="avatar"
available_in_toybox="true"
icon="Command_Avatar_Icon"
hover_icon_unselected="Command_Highlighting_Icon"
hover_icon_selected="Command_Highlighting_Selected_Icon"
label_ref="Command_Avatar_Label"
tooltip_ref="Command_Avatar_Tooltip"
execute_function="Floater.ToggleOrBringToFront"
@ -39,8 +33,6 @@
<command name="build"
available_in_toybox="true"
icon="Command_Build_Icon"
hover_icon_unselected="Command_Highlighting_Icon"
hover_icon_selected="Command_Highlighting_Selected_Icon"
label_ref="Command_Build_Label"
tooltip_ref="Command_Build_Tooltip"
execute_function="Build.Toggle"
@ -54,8 +46,6 @@
available_in_toybox="true"
is_flashing_allowed="true"
icon="Command_Chat_Icon"
hover_icon_unselected="Command_Highlighting_Icon"
hover_icon_selected="Command_Highlighting_Selected_Icon"
label_ref="Command_Chat_Label"
tooltip_ref="Command_Conversations_Tooltip"
execute_function="Floater.ToggleOrBringToFront"
@ -66,8 +56,6 @@
<command name="compass"
available_in_toybox="false"
icon="Command_Compass_Icon"
hover_icon_unselected="Command_Highlighting_Icon"
hover_icon_selected="Command_Highlighting_Selected_Icon"
label_ref="Command_Compass_Label"
tooltip_ref="Command_Compass_Tooltip"
execute_function="Floater.ToggleOrBringToFront"
@ -78,8 +66,6 @@
<command name="destinations"
available_in_toybox="true"
icon="Command_Destinations_Icon"
hover_icon_unselected="Command_Highlighting_Icon"
hover_icon_selected="Command_Highlighting_Selected_Icon"
label_ref="Command_Destinations_Label"
tooltip_ref="Command_Destinations_Tooltip"
execute_function="Floater.ToggleOrBringToFront"
@ -90,8 +76,6 @@
<command name="gestures"
available_in_toybox="true"
icon="Command_Gestures_Icon"
hover_icon_unselected="Command_Highlighting_Icon"
hover_icon_selected="Command_Highlighting_Selected_Icon"
label_ref="Command_Gestures_Label"
tooltip_ref="Command_Gestures_Tooltip"
execute_function="Floater.ToggleOrBringToFront"
@ -102,8 +86,6 @@
<command name="howto"
available_in_toybox="true"
icon="Command_HowTo_Icon"
hover_icon_unselected="Command_Highlighting_Icon"
hover_icon_selected="Command_Highlighting_Selected_Icon"
label_ref="Command_HowTo_Label"
tooltip_ref="Command_HowTo_Tooltip"
execute_function="Help.ToggleHowTo"
@ -112,8 +94,6 @@
<command name="inventory"
available_in_toybox="true"
icon="Command_Inventory_Icon"
hover_icon_unselected="Command_Highlighting_Icon"
hover_icon_selected="Command_Highlighting_Selected_Icon"
label_ref="Command_Inventory_Label"
tooltip_ref="Command_Inventory_Tooltip"
execute_function="Floater.ToggleOrBringToFront"
@ -124,8 +104,6 @@
<command name="map"
available_in_toybox="true"
icon="Command_Map_Icon"
hover_icon_unselected="Command_Highlighting_Icon"
hover_icon_selected="Command_Highlighting_Selected_Icon"
label_ref="Command_Map_Label"
tooltip_ref="Command_Map_Tooltip"
execute_function="Floater.ToggleOrBringToFront"
@ -136,8 +114,6 @@
<command name="marketplace"
available_in_toybox="false"
icon="Command_Marketplace_Icon"
hover_icon_unselected="Command_Highlighting_Icon"
hover_icon_selected="Command_Highlighting_Selected_Icon"
label_ref="Command_Marketplace_Label"
tooltip_ref="Command_Marketplace_Tooltip"
execute_function="Avatar.OpenMarketplace"
@ -145,8 +121,6 @@
<command name="minimap"
available_in_toybox="true"
icon="Command_MiniMap_Icon"
hover_icon_unselected="Command_Highlighting_Icon"
hover_icon_selected="Command_Highlighting_Selected_Icon"
label_ref="Command_MiniMap_Label"
tooltip_ref="Command_MiniMap_Tooltip"
execute_function="Floater.ToggleOrBringToFront"
@ -157,8 +131,6 @@
<command name="move"
available_in_toybox="true"
icon="Command_Move_Icon"
hover_icon_unselected="Command_Highlighting_Icon"
hover_icon_selected="Command_Highlighting_Selected_Icon"
label_ref="Command_Move_Label"
tooltip_ref="Command_Move_Tooltip"
execute_function="Floater.ToggleOrBringToFront"
@ -169,8 +141,6 @@
<command name="outbox"
available_in_toybox="false"
icon="Command_Outbox_Icon"
hover_icon_unselected="Command_Highlighting_Icon"
hover_icon_selected="Command_Highlighting_Selected_Icon"
label_ref="Command_Outbox_Label"
tooltip_ref="Command_Outbox_Tooltip"
execute_function="Floater.ToggleOrBringToFront"
@ -181,8 +151,6 @@
<command name="people"
available_in_toybox="true"
icon="Command_People_Icon"
hover_icon_unselected="Command_Highlighting_Icon"
hover_icon_selected="Command_Highlighting_Selected_Icon"
label_ref="Command_People_Label"
tooltip_ref="Command_People_Tooltip"
execute_function="Floater.ToggleOrBringToFront"
@ -193,8 +161,6 @@
<command name="picks"
available_in_toybox="true"
icon="Command_Picks_Icon"
hover_icon_unselected="Command_Highlighting_Icon"
hover_icon_selected="Command_Highlighting_Selected_Icon"
label_ref="Command_Picks_Label"
tooltip_ref="Command_Picks_Tooltip"
execute_function="Floater.ToggleOrBringToFront"
@ -205,8 +171,6 @@
<command name="places"
available_in_toybox="true"
icon="Command_Places_Icon"
hover_icon_unselected="Command_Highlighting_Icon"
hover_icon_selected="Command_Highlighting_Selected_Icon"
label_ref="Command_Places_Label"
tooltip_ref="Command_Places_Tooltip"
execute_function="Floater.ToggleOrBringToFront"
@ -217,8 +181,6 @@
<command name="preferences"
available_in_toybox="true"
icon="Command_Preferences_Icon"
hover_icon_unselected="Command_Highlighting_Icon"
hover_icon_selected="Command_Highlighting_Selected_Icon"
label_ref="Command_Preferences_Label"
tooltip_ref="Command_Preferences_Tooltip"
execute_function="Floater.ToggleOrBringToFront"
@ -229,8 +191,6 @@
<command name="profile"
available_in_toybox="true"
icon="Command_Profile_Icon"
hover_icon_unselected="Command_Highlighting_Icon"
hover_icon_selected="Command_Highlighting_Selected_Icon"
label_ref="Command_Profile_Label"
tooltip_ref="Command_Profile_Tooltip"
execute_function="Avatar.ToggleMyProfile"
@ -239,8 +199,6 @@
<command name="search"
available_in_toybox="true"
icon="Command_Search_Icon"
hover_icon_unselected="Command_Highlighting_Icon"
hover_icon_selected="Command_Highlighting_Selected_Icon"
label_ref="Command_Search_Label"
tooltip_ref="Command_Search_Tooltip"
execute_function="Floater.ToggleOrBringToFront"
@ -251,8 +209,6 @@
<command name="snapshot"
available_in_toybox="true"
icon="Command_Snapshot_Icon"
hover_icon_unselected="Command_Highlighting_Icon"
hover_icon_selected="Command_Highlighting_Selected_Icon"
label_ref="Command_Snapshot_Label"
tooltip_ref="Command_Snapshot_Tooltip"
execute_function="Floater.ToggleOrBringToFront"
@ -273,8 +229,6 @@
<command name="speak"
available_in_toybox="true"
icon="Command_Speak_Icon"
hover_icon_unselected="Command_Highlighting_Icon"
hover_icon_selected="Command_Highlighting_Selected_Icon"
label_ref="Command_Speak_Label"
tooltip_ref="Command_Speak_Tooltip"
execute_function="Agent.PressMicrophone"
@ -289,8 +243,6 @@
<command name="view"
available_in_toybox="true"
icon="Command_View_Icon"
hover_icon_unselected="Command_Highlighting_Icon"
hover_icon_selected="Command_Highlighting_Selected_Icon"
label_ref="Command_View_Label"
tooltip_ref="Command_View_Tooltip"
execute_function="Floater.ToggleOrBringToFront"

View File

@ -10079,17 +10079,49 @@
<key>Value</key>
<real>16</real>
</map>
<key>Mesh2MaxConcurrentRequests</key>
<map>
<key>Comment</key>
<string>Number of connections to use for loading meshes.</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>U32</string>
<key>Value</key>
<integer>8</integer>
</map>
<key>MeshMaxConcurrentRequests</key>
<map>
<key>Comment</key>
<string>Number of threads to use for loading meshes.</string>
<string>Number of connections to use for loading meshes (legacy system).</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>U32</string>
<key>Value</key>
<integer>32</integer>
</map>
<key>MeshUseHttpRetryAfter</key>
<map>
<key>Comment</key>
<string>If TRUE, use Retry-After response headers when rescheduling a mesh request that fails with an HTTP 503 status. Static.</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<boolean>1</boolean>
</map>
<key>MeshUseGetMesh1</key>
<map>
<key>Comment</key>
<string>If TRUE, use the legacy GetMesh capability for mesh download requests. Semi-dynamic (read at region crossings).</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<boolean>0</boolean>
</map>
<key>RunMultipleThreads</key>
<map>
@ -12643,6 +12675,17 @@
<key>Value</key>
<string>00000000-0000-0000-0000-000000000000</string>
</map>
<key>UISndRestart</key>
<map>
<key>Comment</key>
<string>Sound file for region restarting (uuid for sound asset)</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>String</string>
<key>Value</key>
<string>b92a0f64-7709-8811-40c5-16afd624a45f</string>
</map>
<key>UISndSnapshot</key>
<map>
<key>Comment</key>
@ -14882,17 +14925,6 @@
<key>Value</key>
<integer>0</integer>
</map>
<key>DisablePrecacheDelayAfterTeleporting</key>
<map>
<key>Comment</key>
<string>Disables the artificial delay in the viewer that precaches some incoming assets</string>
<key>Persist</key>
<integer>0</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>0</integer>
</map>
<key>FMODExProfilerEnable</key>
<map>
<key>Comment</key>

View File

@ -22,30 +22,46 @@
* $/LicenseInfo$
*/
ATTRIBUTE vec4 weight4;
uniform mat4 matrixPalette[32];
uniform mat3 matrixPalette[52];
uniform vec3 translationPalette[52];
mat4 getObjectSkinnedTransform()
{
int i;
int i;
vec4 w = fract(weight4);
vec4 index = floor(weight4);
index = min(index, vec4(31.0));
index = min(index, vec4(51.0));
index = max(index, vec4( 0.0));
float scale = 1.0/(w.x+w.y+w.z+w.w);
w *= scale;
mat4 mat = matrixPalette[int(index.x)]*w.x;
mat += matrixPalette[int(index.y)]*w.y;
mat += matrixPalette[int(index.z)]*w.z;
mat += matrixPalette[int(index.w)]*w.w;
int i1 = int(index.x);
int i2 = int(index.y);
int i3 = int(index.z);
int i4 = int(index.w);
return mat;
mat3 mat = matrixPalette[i1]*w.x;
mat += matrixPalette[i2]*w.y;
mat += matrixPalette[i3]*w.z;
mat += matrixPalette[i4]*w.w;
vec3 trans = translationPalette[i1]*w.x;
trans += translationPalette[i2]*w.y;
trans += translationPalette[i3]*w.z;
trans += translationPalette[i4]*w.w;
mat4 ret;
ret[0] = vec4(mat[0], 0);
ret[1] = vec4(mat[1], 0);
ret[2] = vec4(mat[2], 0);
ret[3] = vec4(trans, 1.0);
return ret;
}

View File

@ -534,6 +534,7 @@ void main()
#ifdef FOR_IMPOSTOR
vec4 color;
color.rgb = diff.rgb;
color.a = 1.0;
#ifdef USE_VERTEX_COLOR
float final_alpha = diff.a * vertex_color.a;

View File

@ -3825,7 +3825,11 @@
<volume_morph
name="BELLY"
scale="0.075 0.04 0.03"
pos="0.07 0 -0.07"/>
pos="0.07 0 -0.02"/>
<volume_morph
name="PELVIS"
scale="0.075 0.04 0.03"
pos="0.07 0 -0.02"/>
</param_morph>
</param>
@ -3844,7 +3848,16 @@
camera_elevation=".1"
camera_distance="1"
camera_angle="15">
<param_morph />
<param_morph>
<volume_morph
name="LEFT_PEC"
scale="0.0273 0.0273 0.0273"
pos="0.038 0.024 -0.016"/>
<volume_morph
name="RIGHT_PEC"
scale="0.0273 0.0273 0.0273"
pos="0.038 -0.024 -0.016"/>
</param_morph>
</param>
<param
@ -3861,7 +3874,16 @@
value_max="1"
camera_elevation="0"
camera_distance=".28">
<param_morph />
<param_morph>
<volume_morph
name="LEFT_PEC"
scale="-0.05 0.0 0.0"
pos="-0.01 -0.01 -0.02"/>
<volume_morph
name="RIGHT_PEC"
scale="-0.05 0.0 0.0"
pos="-0.01 -0.01 -0.02"/>
</param_morph>
</param>
<param
@ -3878,7 +3900,16 @@
value_max="1"
camera_elevation="0"
camera_distance=".28">
<param_morph />
<param_morph>
<volume_morph
name="LEFT_PEC"
scale="-0.051 0.0 0.0"
pos="-0.02 -0.01 -0.03"/>
<volume_morph
name="RIGHT_PEC"
scale="-0.051 0.0 0.0"
pos="-0.02 -0.01 -0.03"/>
</param_morph>
</param>
<param
@ -3943,6 +3974,10 @@
name="BELLY"
scale="0.0 -0.01 0.0"
pos="0.0 0.0 0"/>
<volume_morph
name="UPPER_BACK"
scale="-0.01 -0.01 0.0"
pos="0.0 0.0 0"/>
<volume_morph
name="CHEST"
scale="-0.01 -0.01 0.0"
@ -3993,6 +4028,10 @@
name="BELLY"
scale="-0.01 -0.01 0.0"
pos="0.01 0.0 0"/>
<volume_morph
name="UPPER_BACK"
scale="-0.01 -0.01 0.0"
pos="0.0 0.0 0"/>
<volume_morph
name="CHEST"
scale="-0.02 -0.02 0.0"
@ -4041,6 +4080,32 @@
name="CHEST"
scale="0.02 0.03 0.03"
pos="0 0 -0.03"/>
<volume_morph
name="PELVIS"
scale="0.02 0.03 0.03"
pos="0 0 -0.03"/>
<volume_morph
name="UPPER_BACK"
scale="0.01 0.03 0.0"
pos="-0.03 0 0"/>
<volume_morph
name="LOWER_BACK"
scale="0.04 0.06 0.0"
pos="-0.06 0 0"/>
<volume_morph
name="LEFT_HANDLE"
pos="0.0 0.08 0.0"/>
<volume_morph
name="RIGHT_HANDLE"
pos="0.0 -0.08 0.0"/>
<volume_morph
name="LEFT_PEC"
scale="0.0367 0.0367 0.016"
pos="0.00 -0.005 -0.013"/>
<volume_morph
name="RIGHT_PEC"
scale="0.0367 0.0367 0.016"
pos="0.00 0.005 -0.013"/>
<volume_morph
name="BELLY"
scale="0.09 0.08 0.07"
@ -4093,7 +4158,16 @@
value_max="2"
camera_elevation=".3"
camera_distance=".8">
<param_morph />
<param_morph>
<volume_morph
name="LEFT_PEC"
scale="0.0 0.0 0.0"
pos="0.004 0.0 -0.01"/>
<volume_morph
name="RIGHT_PEC"
scale="0.0 0.0 0.0"
pos="0.004 0.0 -0.01"/>
</param_morph>
</param>
<param
@ -4143,6 +4217,15 @@
<volume_morph
name="BELLY"
scale="0.0 0.02 0.0"/>
<volume_morph
name="LOWER_BACK"
scale="0.0 0.02 0.0"/>
<volume_morph
name="LEFT_HANDLE"
pos="0.0 0.025 0.0"/>
<volume_morph
name="RIGHT_HANDLE"
pos="0.0 -0.025 0.0"/>
</param_morph>
</param>
@ -4162,7 +4245,16 @@
value_max="1.3"
camera_elevation=".3"
camera_distance=".8">
<param_morph />
<param_morph>
<volume_morph
name="LEFT_PEC"
scale="0.0 0.0 0.0"
pos="0.0 -0.026 0.0"/>
<volume_morph
name="RIGHT_PEC"
scale="0.0 0.0 0.0"
pos="0.0 0.026 0.0"/>
</param_morph>
</param>
<param
@ -4177,11 +4269,20 @@
label_min="Big Pectorals"
label_max="Sunken Chest"
value_default="0"
value_min="-.5"
value_min="-1.0"
value_max="1.1"
camera_elevation=".3"
camera_distance="1.2">
<param_morph />
<param_morph>
<volume_morph
name="LEFT_PEC"
scale="0.0 0.0 0.0"
pos="-0.03 -0.024 -0.01"/>
<volume_morph
name="RIGHT_PEC"
scale="0.0 0.0 0.0"
pos="-0.03 0.024 -0.01"/>
</param_morph>
</param>
<!-- ############# #
@ -4205,6 +4306,14 @@
name="BELLY"
scale="0.03 0.03 0.0"
pos="-0.03 0 0.02"/>
<volume_morph
name="LEFT_PEC"
scale="0.0 0.0 0.0"
pos="0.008 -0.03 0.01"/>
<volume_morph
name="RIGHT_PEC"
scale="0.0 0.0 0.0"
pos="0.008 0.03 0.01"/>
<volume_morph
name="L_CLAVICLE"
scale="0.02 0.0 0.01"
@ -4376,7 +4485,16 @@
value_default="0"
value_min="-3"
value_max="3">
<param_morph />
<param_morph>
<volume_morph
name="LEFT_PEC"
scale="0.0 0.0 0.0"
pos="0.0 0.0 -0.01"/>
<volume_morph
name="RIGHT_PEC"
scale="0.0 0.0 0.0"
pos="0.0 0.0 -0.01"/>
</param_morph>
</param>
<param
@ -4389,7 +4507,16 @@
value_default="0"
value_min="-1.25"
value_max="1.25">
<param_morph />
<param_morph>
<volume_morph
name="LEFT_PEC"
scale="0.0 0.0 0.0"
pos="0.0 -0.026 0.0"/>
<volume_morph
name="RIGHT_PEC"
scale="0.0 0.0 0.0"
pos="0.0 0.026 -0.0"/>
</param_morph>
</param>
<param
@ -4402,7 +4529,12 @@
value_default="0"
value_min="-1"
value_max="1">
<param_morph />
<param_morph>
<volume_morph
name="BELLY"
scale="0.0 0.0 0.0"
pos="0.0 0.0 0.05"/>
</param_morph>
</param>
<param
@ -4415,7 +4547,16 @@
value_default="0"
value_min="-2"
value_max="2">
<param_morph />
<param_morph>
<volume_morph
name="LEFT_PEC"
scale="0.0 0.0 0.0"
pos="0.0 0.03 0.0"/>
<volume_morph
name="RIGHT_PEC"
scale="0.0 0.0 0.0"
pos="0.0 0.03 0.0"/>
</param_morph>
</param>
<!--
@ -4518,6 +4659,10 @@
name="PELVIS"
scale="-0.01 0.0 0.0"
pos="0.01 0 0.0"/>
<volume_morph
name="BUTT"
scale="0.0 0.0886 0.0"
pos="0.03 0 0.0"/>
</param_morph>
</param>
@ -4949,7 +5094,11 @@
value_default="0"
value_min="-1"
value_max="1">
<param_morph />
<param_morph>
<volume_morph
name="BUTT"
pos="0.0 0.0 0.05"/>
</param_morph>
</param>
<param
@ -4962,7 +5111,11 @@
value_default="0"
value_min="-1"
value_max="1">
<param_morph />
<param_morph>
<volume_morph
name="BUTT"
pos="0.0 0.05 0.0"/>
</param_morph>
</param>
<!--

View File

@ -1,11 +1,18 @@
<?xml version="1.0" encoding="US-ASCII" standalone="yes"?>
<linden_skeleton version="1.0" num_bones="46" num_collision_volumes="19">
<linden_skeleton version="1.0" num_bones="53" num_collision_volumes="26">
<bone name="mPelvis" pos="0.000 0.000 1.067" rot="0.000000 0.000000 0.000000" scale="1.000 1.000 1.000" pivot="0.000000 0.000000 1.067015">
<collision_volume name="PELVIS" pos = "-0.01 0 -0.02" rot="0.000000 8.00000 0.000000" scale="0.12 0.16 0.17"/>
<collision_volume name="BUTT" pos = "-0.06 0 -0.1" rot="0.000000 0.00000 0.000000" scale="0.1 0.1 0.1"/>
<bone name="mTorso" pos="0.000 0.000 0.084" rot="0.000000 0.000000 0.000000" scale="1.000 1.000 1.000" pivot="0.000000 0.000000 0.084073">
<collision_volume name="BELLY" pos = "0.028 0 0.04" rot="0.000000 8.00000 0.000000" scale="0.09 0.13 0.15"/>
<collision_volume name="LOWER_BACK" pos = "0.0 0.0 0.023" rot="0.000000 0.00000 0.000000" scale="0.09 0.13 0.15"/>
<collision_volume name="LEFT_HANDLE" pos = "0.0 0.10 0.058" rot="0.000000 0.00000 0.000000" scale="0.05 0.05 0.05"/>
<collision_volume name="RIGHT_HANDLE" pos = "0.0 -0.10 0.058" rot="0.000000 0.00000 0.000000" scale="0.05 0.05 0.05"/>
<bone name="mChest" pos="-0.015 0.000 0.205" rot="0.000000 0.000000 0.000000" scale="1.000 1.000 1.000" pivot="-0.015368 0.000000 0.204877">
<collision_volume name="CHEST" pos = "0.028 0 0.07" rot="0.000000 -10.00000 0.000000" scale="0.11 0.15 0.2"/>
<collision_volume name="UPPER_BACK" pos = "0.0 0.0 0.017" rot="0.000000 0.00000 0.000000" scale="0.09 0.13 0.15"/>
<collision_volume name="LEFT_PEC" pos = "0.119 0.082 0.042" rot="0.000000 4.29000 0.000000" scale="0.05 0.05 0.05"/>
<collision_volume name="RIGHT_PEC" pos = "0.119 -0.082 0.042" rot="0.000000 4.29000 0.000000" scale="0.05 0.05 0.05"/>
<bone name="mNeck" pos="-0.010 0.000 0.251" rot="0.000000 0.000000 0.000000" scale="1.000 1.000 1.000" pivot="-0.009507 0.000000 0.251108">
<collision_volume name="NECK" pos = "0.0 0 0.02" rot="0.000000 0.000000 0.000000" scale="0.05 0.06 0.08"/>
<bone name="mHead" pos="0.000 -0.000 0.076" rot="0.000000 0.000000 0.000000" scale="1.000 1.000 1.000" pivot="0.000000 -0.000000 0.075630">

View File

@ -301,6 +301,23 @@ Function CheckNetworkConnection
Return
FunctionEnd
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Function CheckOldExeName
; Viewer versions < 3.6.12 used the name 'SecondLife.exe'
; If that name is found in the install folder, delete it to invalidate any
; old shortcuts to it that may be in non-standard locations, so that the user
; does not end up running the old version (potentially getting caught in an
; infinite update loop). See MAINT-3575
; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Function CheckOldExeName
IfFileExists "$INSTDIR\SecondLife.exe" CHECKOLDEXE_FOUND CHECKOLDEXE_DONE
CHECKOLDEXE_FOUND:
Delete "$INSTDIR\SecondLife.exe"
CHECKOLDEXE_DONE:
FunctionEnd
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Function CheckWillUninstallV2
@ -673,6 +690,9 @@ Delete "$INSTDIR\*.glsl"
Delete "$INSTDIR\motions\*.lla"
Delete "$INSTDIR\trial\*.html"
Delete "$INSTDIR\newview.exe"
Delete "$INSTDIR\SecondLife.exe"
;; MAINT-3099 workaround - prevent these log files, if present, from causing a user alert
Delete "$INSTDIR\VivoxVoiceService-*.log"
;; Remove entire help directory
Delete "$INSTDIR\help\Advanced\*"
RMDir "$INSTDIR\help\Advanced"
@ -920,6 +940,7 @@ Call CheckIfAlreadyCurrent ; Make sure that we haven't already installed this v
Call CloseSecondLife ; Make sure we're not running
Call CheckNetworkConnection ; ping secondlife.com
Call CheckWillUninstallV2 ; See if a V2 install exists and will be removed.
Call CheckOldExeName ; Clean up a previous version of the exe
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
StrCmp $DO_UNINSTALL_V2 "" PRESERVE_DONE

View File

@ -259,11 +259,9 @@ bool handleSlowMotionAnimation(const LLSD& newvalue)
return true;
}
// static
void LLAgent::parcelChangedCallback()
void LLAgent::setCanEditParcel() // called via mParcelChangedSignal
{
bool can_edit = LLToolMgr::getInstance()->canEdit();
gAgent.mCanEditParcel = can_edit;
}
@ -425,6 +423,8 @@ LLAgent::LLAgent() :
mListener.reset(new LLAgentListener(*this));
addParcelChangedCallback(&setCanEditParcel);
mMoveTimer.stop();
}
@ -451,8 +451,6 @@ void LLAgent::init()
mLastKnownRequestMaturity = mLastKnownResponseMaturity;
mIsDoSendMaturityPreferenceToServer = true;
LLViewerParcelMgr::getInstance()->addAgentParcelChangedCallback(boost::bind(&LLAgent::parcelChangedCallback));
if (!mTeleportFinishedSlot.connected())
{
mTeleportFinishedSlot = LLViewerParcelMgr::getInstance()->setTeleportFinishedCallback(boost::bind(&LLAgent::handleTeleportFinished, this));
@ -835,22 +833,33 @@ void LLAgent::handleServerBakeRegionTransition(const LLUUID& region_id)
}
}
void LLAgent::changeParcels()
{
LL_DEBUGS("AgentLocation") << "Calling ParcelChanged callbacks" << LL_ENDL;
// Notify anything that wants to know about parcel changes
mParcelChangedSignal();
}
boost::signals2::connection LLAgent::addParcelChangedCallback(parcel_changed_callback_t cb)
{
return mParcelChangedSignal.connect(cb);
}
//-----------------------------------------------------------------------------
// setRegion()
//-----------------------------------------------------------------------------
void LLAgent::setRegion(LLViewerRegion *regionp)
{
bool teleport = true;
bool notifyRegionChange;
llassert(regionp);
if (mRegionp != regionp)
{
// std::string host_name;
// host_name = regionp->getHost().getHostName();
notifyRegionChange = true;
std::string ip = regionp->getHost().getString();
llinfos << "Moving agent into region: " << regionp->getName()
<< " located at " << ip << llendl;
LL_INFOS("AgentLocation") << "Moving agent into region: " << regionp->getName()
<< " located at " << ip << LL_ENDL;
if (mRegionp)
{
// We've changed regions, we're now going to change our agent coordinate frame.
@ -878,9 +887,6 @@ void LLAgent::setRegion(LLViewerRegion *regionp)
{
gSky.mVOGroundp->setRegion(regionp);
}
// Notify windlight managers
teleport = (gAgent.getTeleportState() != LLAgent::TELEPORT_NONE);
}
else
{
@ -902,8 +908,14 @@ void LLAgent::setRegion(LLViewerRegion *regionp)
// Pass new region along to metrics components that care about this level of detail.
LLAppViewer::metricsUpdateRegion(regionp->getHandle());
}
else
{
notifyRegionChange = false;
}
mRegionp = regionp;
// TODO - most of what follows probably should be moved into callbacks
// Pass the region host to LLUrlEntryParcel to resolve parcel name
// with a server request.
LLUrlEntryParcel::setRegionHost(getRegionHost());
@ -922,15 +934,6 @@ void LLAgent::setRegion(LLViewerRegion *regionp)
LLFloaterMove::sUpdateFlyingStatus();
if (teleport)
{
LLEnvManagerNew::instance().onTeleport();
}
else
{
LLEnvManagerNew::instance().onRegionCrossing();
}
// If the newly entered region is using server bakes, and our
// current appearance is non-baked, request appearance update from
// server.
@ -943,6 +946,12 @@ void LLAgent::setRegion(LLViewerRegion *regionp)
// Need to handle via callback after caps arrive.
mRegionp->setCapabilitiesReceivedCallback(boost::bind(&LLAgent::handleServerBakeRegionTransition,this,_1));
}
if (notifyRegionChange)
{
LL_DEBUGS("AgentLocation") << "Calling RegionChanged callbacks" << LL_ENDL;
mRegionChangedSignal();
}
}
@ -967,6 +976,16 @@ LLHost LLAgent::getRegionHost() const
}
}
boost::signals2::connection LLAgent::addRegionChangedCallback(const region_changed_signal_t::slot_type& cb)
{
return mRegionChangedSignal.connect(cb);
}
void LLAgent::removeRegionChangedCallback(boost::signals2::connection callback)
{
mRegionChangedSignal.disconnect(callback);
}
//-----------------------------------------------------------------------------
// inPrelude()
//-----------------------------------------------------------------------------

View File

@ -231,15 +231,54 @@ private:
LLVector3 mHomePosRegion;
//--------------------------------------------------------------------
// Region
// Parcel
//--------------------------------------------------------------------
public:
void changeParcels(); // called by LLViewerParcelMgr when we cross a parcel boundary
// Register a boost callback to be called when the agent changes parcels
typedef boost::function<void()> parcel_changed_callback_t;
boost::signals2::connection addParcelChangedCallback(parcel_changed_callback_t);
private:
typedef boost::signals2::signal<void()> parcel_changed_signal_t;
parcel_changed_signal_t mParcelChangedSignal;
//--------------------------------------------------------------------
// Region
//--------------------------------------------------------------------
public:
void setRegion(LLViewerRegion *regionp);
LLViewerRegion *getRegion() const;
LLHost getRegionHost() const;
BOOL inPrelude();
private:
/**
* Register a boost callback to be called when the agent changes regions
* Note that if you need to access a capability for the region, you may need to wait
* for the capabilities to be received, since in some cases your region changed
* callback will be called before the capabilities have been received. Your callback
* may need to look something like:
*
* LLViewerRegion* region = gAgent.getRegion();
* if (region->capabilitiesReceived())
* {
* useCapability(region);
* }
* else // Need to handle via callback after caps arrive.
* {
* region->setCapabilitiesReceivedCallback(boost::bind(&useCapability,region,_1));
* // you may or may not want to remove that callback
* }
*/
typedef boost::signals2::signal<void()> region_changed_signal_t;
boost::signals2::connection addRegionChangedCallback(const region_changed_signal_t::slot_type& cb);
void removeRegionChangedCallback(boost::signals2::connection callback);
private:
LLViewerRegion *mRegionp;
region_changed_signal_t mRegionChangedSignal;
//--------------------------------------------------------------------
// History
@ -640,9 +679,10 @@ private:
public:
bool canEditParcel() const { return mCanEditParcel; }
private:
static void setCanEditParcel();
bool mCanEditParcel;
static void parcelChangedCallback();
/********************************************************************************
** **

View File

@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
* Copyright (C) 2012-2013, 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
@ -28,18 +28,81 @@
#include "llappcorehttp.h"
#include "llappviewer.h"
#include "llviewercontrol.h"
// Here is where we begin to get our connection usage under control.
// This establishes llcorehttp policy classes that, among other
// things, limit the maximum number of connections to outside
// services. Each of the entries below maps to a policy class and
// has a limit, sometimes configurable, of how many connections can
// be open at a time.
const F64 LLAppCoreHttp::MAX_THREAD_WAIT_TIME(10.0);
static const struct
{
LLAppCoreHttp::EAppPolicy mPolicy;
U32 mDefault;
U32 mMin;
U32 mMax;
U32 mRate;
std::string mKey;
const char * mUsage;
} init_data[] = // Default and dynamic values for classes
{
{
LLAppCoreHttp::AP_DEFAULT, 8, 8, 8, 0,
"",
"other"
},
{
LLAppCoreHttp::AP_TEXTURE, 8, 1, 12, 0,
"TextureFetchConcurrency",
"texture fetch"
},
{
LLAppCoreHttp::AP_MESH1, 32, 1, 128, 100,
"MeshMaxConcurrentRequests",
"mesh fetch"
},
{
LLAppCoreHttp::AP_MESH2, 8, 1, 32, 100,
"Mesh2MaxConcurrentRequests",
"mesh2 fetch"
},
{
LLAppCoreHttp::AP_LARGE_MESH, 2, 1, 8, 0,
"",
"large mesh fetch"
},
{
LLAppCoreHttp::AP_UPLOADS, 2, 1, 8, 0,
"",
"asset upload"
},
{
LLAppCoreHttp::AP_LONG_POLL, 32, 32, 32, 0,
"",
"long poll"
}
};
static void setting_changed();
LLAppCoreHttp::LLAppCoreHttp()
: mRequest(NULL),
mStopHandle(LLCORE_HTTP_HANDLE_INVALID),
mStopRequested(0.0),
mStopped(false),
mPolicyDefault(-1)
{}
mStopped(false)
{
for (int i(0); i < LL_ARRAY_SIZE(mPolicies); ++i)
{
mPolicies[i] = LLCore::HttpRequest::DEFAULT_POLICY_ID;
mSettings[i] = 0U;
}
}
LLAppCoreHttp::~LLAppCoreHttp()
@ -54,30 +117,28 @@ void LLAppCoreHttp::init()
LLCore::HttpStatus status = LLCore::HttpRequest::createService();
if (! status)
{
LL_ERRS("Init") << "Failed to initialize HTTP services. Reason: "
<< status.toString()
LL_ERRS("Init") << "Failed to initialize HTTP services. Reason: " << status.toString()
<< LL_ENDL;
}
// Point to our certs or SSH/https: will fail on connect
status = LLCore::HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_CA_FILE,
gDirUtilp->getCAFile());
status = LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_CA_FILE,
LLCore::HttpRequest::GLOBAL_POLICY_ID,
gDirUtilp->getCAFile(), NULL);
if (! status)
{
LL_ERRS("Init") << "Failed to set CA File for HTTP services. Reason: "
<< status.toString()
LL_ERRS("Init") << "Failed to set CA File for HTTP services. Reason: " << status.toString()
<< LL_ENDL;
}
// Establish HTTP Proxy. "LLProxy" is a special string which directs
// the code to use LLProxy::applyProxySettings() to establish any
// HTTP or SOCKS proxy for http operations.
status = LLCore::HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_LLPROXY, 1);
// Establish HTTP Proxy, if desired.
status = LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_LLPROXY,
LLCore::HttpRequest::GLOBAL_POLICY_ID,
1, NULL);
if (! status)
{
LL_ERRS("Init") << "Failed to set HTTP proxy for HTTP services. Reason: "
<< status.toString()
<< LL_ENDL;
LL_WARNS("Init") << "Failed to set HTTP proxy for HTTP services. Reason: " << status.toString()
<< LL_ENDL;
}
// Tracing levels for library & libcurl (note that 2 & 3 are beyond spammy):
@ -90,47 +151,74 @@ void LLAppCoreHttp::init()
{
long trace_level(0L);
trace_level = long(gSavedSettings.getU32(http_trace));
status = LLCore::HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_TRACE, trace_level);
status = LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_TRACE,
LLCore::HttpRequest::GLOBAL_POLICY_ID,
trace_level, NULL);
}
// Setup default policy and constrain if directed to
mPolicyDefault = LLCore::HttpRequest::DEFAULT_POLICY_ID;
static const std::string texture_concur("TextureFetchConcurrency");
if (gSavedSettings.controlExists(texture_concur))
{
U32 concur(llmin(gSavedSettings.getU32(texture_concur), U32(12)));
mPolicies[AP_DEFAULT] = LLCore::HttpRequest::DEFAULT_POLICY_ID;
if (concur > 0)
// Setup additional policies based on table and some special rules
for (int i(0); i < LL_ARRAY_SIZE(init_data); ++i)
{
const EAppPolicy policy(init_data[i].mPolicy);
if (AP_DEFAULT == policy)
{
LLCore::HttpStatus status;
status = LLCore::HttpRequest::setPolicyClassOption(mPolicyDefault,
LLCore::HttpRequest::CP_CONNECTION_LIMIT,
concur);
if (! status)
{
LL_WARNS("Init") << "Unable to set texture fetch concurrency. Reason: "
<< status.toString()
<< LL_ENDL;
}
else
{
LL_INFOS("Init") << "Application settings overriding default texture fetch concurrency. New value: "
<< concur
<< LL_ENDL;
}
// Pre-created
continue;
}
mPolicies[policy] = LLCore::HttpRequest::createPolicyClass();
if (! mPolicies[policy])
{
// Use default policy (but don't accidentally modify default)
LL_WARNS("Init") << "Failed to create HTTP policy class for " << init_data[i].mUsage
<< ". Using default policy."
<< LL_ENDL;
mPolicies[policy] = mPolicies[AP_DEFAULT];
continue;
}
}
// Need a request object to handle dynamic options before setting them
mRequest = new LLCore::HttpRequest;
// Apply initial settings
refreshSettings(true);
// Kick the thread
status = LLCore::HttpRequest::startThread();
if (! status)
{
LL_ERRS("Init") << "Failed to start HTTP servicing thread. Reason: "
<< status.toString()
LL_ERRS("Init") << "Failed to start HTTP servicing thread. Reason: " << status.toString()
<< LL_ENDL;
}
mRequest = new LLCore::HttpRequest;
// Register signals for settings and state changes
for (int i(0); i < LL_ARRAY_SIZE(init_data); ++i)
{
if (! init_data[i].mKey.empty() && gSavedSettings.controlExists(init_data[i].mKey))
{
LLPointer<LLControlVariable> cntrl_ptr = gSavedSettings.getControl(init_data[i].mKey);
if (cntrl_ptr.isNull())
{
LL_WARNS("Init") << "Unable to set signal on global setting '" << init_data[i].mKey
<< "'" << LL_ENDL;
}
else
{
mSettingsSignal[i] = cntrl_ptr->getCommitSignal()->connect(boost::bind(&setting_changed));
}
}
}
}
void setting_changed()
{
LLAppViewer::instance()->getAppCoreHttp().refreshSettings(false);
}
@ -173,6 +261,11 @@ void LLAppCoreHttp::cleanup()
}
}
for (int i(0); i < LL_ARRAY_SIZE(init_data); ++i)
{
mSettingsSignal[i].disconnect();
}
delete mRequest;
mRequest = NULL;
@ -185,6 +278,78 @@ void LLAppCoreHttp::cleanup()
}
}
void LLAppCoreHttp::refreshSettings(bool initial)
{
LLCore::HttpStatus status;
for (int i(0); i < LL_ARRAY_SIZE(init_data); ++i)
{
const EAppPolicy policy(init_data[i].mPolicy);
// Set any desired throttle
if (initial && init_data[i].mRate)
{
// Init-time only, can use the static setters here
status = LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_THROTTLE_RATE,
mPolicies[policy],
init_data[i].mRate,
NULL);
if (! status)
{
LL_WARNS("Init") << "Unable to set " << init_data[i].mUsage
<< " throttle rate. Reason: " << status.toString()
<< LL_ENDL;
}
}
// Get target connection concurrency value
U32 setting(init_data[i].mDefault);
if (! init_data[i].mKey.empty() && gSavedSettings.controlExists(init_data[i].mKey))
{
U32 new_setting(gSavedSettings.getU32(init_data[i].mKey));
if (new_setting)
{
// Treat zero settings as an ask for default
setting = llclamp(new_setting, init_data[i].mMin, init_data[i].mMax);
}
}
if (! initial && setting == mSettings[policy])
{
// Unchanged, try next setting
continue;
}
// Set it and report
// *TODO: These are intended to be per-host limits when we can
// support that in llcorehttp/libcurl.
LLCore::HttpHandle handle;
handle = mRequest->setPolicyOption(LLCore::HttpRequest::PO_CONNECTION_LIMIT,
mPolicies[policy],
setting, NULL);
if (LLCORE_HTTP_HANDLE_INVALID == handle)
{
status = mRequest->getStatus();
LL_WARNS("Init") << "Unable to set " << init_data[i].mUsage
<< " concurrency. Reason: " << status.toString()
<< LL_ENDL;
}
else
{
LL_DEBUGS("Init") << "Changed " << init_data[i].mUsage
<< " concurrency. New value: " << setting
<< LL_ENDL;
mSettings[policy] = setting;
if (initial && setting != init_data[i].mDefault)
{
LL_INFOS("Init") << "Application settings overriding default " << init_data[i].mUsage
<< " concurrency. New value: " << setting
<< LL_ENDL;
}
}
}
}
void LLAppCoreHttp::onCompleted(LLCore::HttpHandle, LLCore::HttpResponse *)
{

View File

@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
* Copyright (C) 2012-2013, 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
@ -40,6 +40,117 @@
// as a singleton and static construction is fine.
class LLAppCoreHttp : public LLCore::HttpHandler
{
public:
typedef LLCore::HttpRequest::policy_t policy_t;
enum EAppPolicy
{
/// Catchall policy class. Not used yet
/// but will have a generous concurrency
/// limit. Deep queueing possible by having
/// a chatty HTTP user.
///
/// Destination: anywhere
/// Protocol: http: or https:
/// Transfer size: KB-MB
/// Long poll: no
/// Concurrency: high
/// Request rate: unknown
/// Pipelined: no
AP_DEFAULT,
/// Texture fetching policy class. Used to
/// download textures via capability or SSA
/// baking service. Deep queueing of requests.
/// Do not share.
///
/// Destination: simhost:12046 & bake-texture:80
/// Protocol: http:
/// Transfer size: KB-MB
/// Long poll: no
/// Concurrency: high
/// Request rate: high
/// Pipelined: soon
AP_TEXTURE,
/// Legacy mesh fetching policy class. Used to
/// download textures via 'GetMesh' capability.
/// To be deprecated. Do not share.
///
/// Destination: simhost:12046
/// Protocol: http:
/// Transfer size: KB-MB
/// Long poll: no
/// Concurrency: dangerously high
/// Request rate: high
/// Pipelined: no
AP_MESH1,
/// New mesh fetching policy class. Used to
/// download textures via 'GetMesh2' capability.
/// Used when fetch request (typically one LOD)
/// is 'small', currently defined as 2MB.
/// Very deeply queued. Do not share.
///
/// Destination: simhost:12046
/// Protocol: http:
/// Transfer size: KB-MB
/// Long poll: no
/// Concurrency: high
/// Request rate: high
/// Pipelined: soon
AP_MESH2,
/// Large mesh fetching policy class. Used to
/// download textures via 'GetMesh' or 'GetMesh2'
/// capability. Used when fetch request
/// is not small to avoid head-of-line problem
/// when large requests block a sequence of small,
/// fast requests. Can be shared with similar
/// traffic that can wait for longish stalls
/// (default timeout 600S).
///
/// Destination: simhost:12046
/// Protocol: http:
/// Transfer size: MB
/// Long poll: no
/// Concurrency: low
/// Request rate: low
/// Pipelined: soon
AP_LARGE_MESH,
/// Asset upload policy class. Used to store
/// assets (mesh only at the moment) via
/// changeable URL. Responses may take some
/// time (default timeout 240S).
///
/// Destination: simhost:12043
/// Protocol: https:
/// Transfer size: KB-MB
/// Long poll: no
/// Concurrency: low
/// Request rate: low
/// Pipelined: no
AP_UPLOADS,
/// Long-poll-type HTTP requests. Not
/// bound by a connection limit. Requests
/// will typically hang around for a long
/// time (~30S). Only shareable with other
/// long-poll requests.
///
/// Destination: simhost:12043
/// Protocol: https:
/// Transfer size: KB
/// Long poll: yes
/// Concurrency: unlimited but low in practice
/// Request rate: low
/// Pipelined: no
AP_LONG_POLL,
AP_COUNT // Must be last
};
public:
LLAppCoreHttp();
~LLAppCoreHttp();
@ -65,21 +176,27 @@ public:
// Notification when the stop request is complete.
virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response);
// Retrieve the policy class for default operations.
int getPolicyDefault() const
// Retrieve a policy class identifier for desired
// application function.
policy_t getPolicy(EAppPolicy policy) const
{
return mPolicyDefault;
return mPolicies[policy];
}
// Apply initial or new settings from the environment.
void refreshSettings(bool initial);
private:
static const F64 MAX_THREAD_WAIT_TIME;
private:
LLCore::HttpRequest * mRequest;
LLCore::HttpRequest * mRequest; // Request queue to issue shutdowns
LLCore::HttpHandle mStopHandle;
F64 mStopRequested;
bool mStopped;
int mPolicyDefault;
policy_t mPolicies[AP_COUNT]; // Policy class id for each connection set
U32 mSettings[AP_COUNT];
boost::signals2::connection mSettingsSignal[AP_COUNT]; // Signals to global settings that affect us
};

View File

@ -55,6 +55,7 @@ static U32 sDataMask = LLDrawPoolAvatar::VERTEX_DATA_MASK;
static U32 sBufferUsage = GL_STREAM_DRAW_ARB;
static U32 sShaderLevel = 0;
#define JOINT_COUNT 52
LLGLSLShader* LLDrawPoolAvatar::sVertexProgram = NULL;
BOOL LLDrawPoolAvatar::sSkipOpaque = FALSE;
@ -1582,10 +1583,11 @@ void LLDrawPoolAvatar::updateRiggedFaceVertexBuffer(LLVOAvatar* avatar, LLFace*
LLVector4a* norm = has_normal ? (LLVector4a*) normal.get() : NULL;
//build matrix palette
LLMatrix4a mp[64];
LLMatrix4a mp[JOINT_COUNT];
LLMatrix4* mat = (LLMatrix4*) mp;
for (U32 j = 0; j < skin->mJointNames.size(); ++j)
U32 count = llmin((U32) skin->mJointNames.size(), (U32) JOINT_COUNT);
for (U32 j = 0; j < count; ++j)
{
LLJoint* joint = avatar->getJoint(skin->mJointNames[j]);
if (joint)
@ -1642,6 +1644,7 @@ void LLDrawPoolAvatar::updateRiggedFaceVertexBuffer(LLVOAvatar* avatar, LLFace*
LLVector4a& n = vol_face.mNormals[j];
bind_shape_matrix.rotate(n, t);
final_mat.rotate(t, dst);
dst.normalize3fast();
norm[j] = dst;
}
}
@ -1708,9 +1711,9 @@ void LLDrawPoolAvatar::renderRigged(LLVOAvatar* avatar, U32 type, bool glow)
{
if (sShaderLevel > 0)
{ //upload matrix palette to shader
LLMatrix4 mat[32];
LLMatrix4 mat[JOINT_COUNT];
U32 count = llmin((U32) skin->mJointNames.size(), (U32) 32);
U32 count = llmin((U32) skin->mJointNames.size(), (U32) JOINT_COUNT);
for (U32 i = 0; i < count; ++i)
{
@ -1724,10 +1727,42 @@ void LLDrawPoolAvatar::renderRigged(LLVOAvatar* avatar, U32 type, bool glow)
stop_glerror();
LLDrawPoolAvatar::sVertexProgram->uniformMatrix4fv(LLViewerShaderMgr::AVATAR_MATRIX,
F32 mp[JOINT_COUNT*9];
F32 transp[JOINT_COUNT*3];
for (U32 i = 0; i < count; ++i)
{
F32* m = (F32*) mat[i].mMatrix;
U32 idx = i*9;
mp[idx+0] = m[0];
mp[idx+1] = m[1];
mp[idx+2] = m[2];
mp[idx+3] = m[4];
mp[idx+4] = m[5];
mp[idx+5] = m[6];
mp[idx+6] = m[8];
mp[idx+7] = m[9];
mp[idx+8] = m[10];
idx = i*3;
transp[idx+0] = m[12];
transp[idx+1] = m[13];
transp[idx+2] = m[14];
}
LLDrawPoolAvatar::sVertexProgram->uniformMatrix3fv(LLViewerShaderMgr::AVATAR_MATRIX,
count,
FALSE,
(GLfloat*) mat[0].mMatrix);
(GLfloat*) mp);
LLDrawPoolAvatar::sVertexProgram->uniform3fv(LLShaderMgr::AVATAR_TRANSLATION, count, transp);
stop_glerror();
}

View File

@ -92,9 +92,11 @@ void LLEnvPrefs::setUseDayCycle(const std::string& name)
}
//=============================================================================
LLEnvManagerNew::LLEnvManagerNew()
LLEnvManagerNew::LLEnvManagerNew():
mInterpNextChangeMessage(true),
mCurRegionUUID(LLUUID::null),
mLastReceivedID(LLUUID::null)
{
mInterpNextChangeMessage = true;
// Set default environment settings.
mUserPrefs.mUseRegionSettings = true;
@ -102,6 +104,9 @@ LLEnvManagerNew::LLEnvManagerNew()
mUserPrefs.mWaterPresetName = "Default";
mUserPrefs.mSkyPresetName = "Default";
mUserPrefs.mDayCycleName = "Default";
LL_DEBUGS("Windlight")<<LL_ENDL;
gAgent.addRegionChangedCallback(boost::bind(&LLEnvManagerNew::onRegionChange, this));
}
bool LLEnvManagerNew::getUseRegionSettings() const
@ -300,6 +305,11 @@ void LLEnvManagerNew::loadUserPrefs()
mUserPrefs.mUseRegionSettings = gSavedSettings.getBOOL("UseEnvironmentFromRegion");
mUserPrefs.mUseDayCycle = gSavedSettings.getBOOL("UseDayCycle");
if (mUserPrefs.mUseRegionSettings)
{
requestRegionSettings();
}
}
void LLEnvManagerNew::saveUserPrefs()
@ -398,6 +408,7 @@ void LLEnvManagerNew::dumpPresets()
void LLEnvManagerNew::requestRegionSettings()
{
LL_DEBUGS("Windlight") << LL_ENDL;
LLEnvironmentRequest::initiate();
}
@ -422,11 +433,6 @@ boost::signals2::connection LLEnvManagerNew::setRegionSettingsChangeCallback(con
return mRegionSettingsChangeSignal.connect(cb);
}
boost::signals2::connection LLEnvManagerNew::setRegionChangeCallback(const region_change_signal_t::slot_type& cb)
{
return mRegionChangeSignal.connect(cb);
}
boost::signals2::connection LLEnvManagerNew::setRegionSettingsAppliedCallback(const region_settings_applied_signal_t::slot_type& cb)
{
return mRegionSettingsAppliedSignal.connect(cb);
@ -457,25 +463,13 @@ const std::string LLEnvManagerNew::getScopeString(LLEnvKey::EScope scope)
}
}
void LLEnvManagerNew::onRegionCrossing()
{
LL_DEBUGS("Windlight") << "Crossed region" << LL_ENDL;
onRegionChange(true);
}
void LLEnvManagerNew::onTeleport()
{
LL_DEBUGS("Windlight") << "Teleported" << LL_ENDL;
onRegionChange(false);
}
void LLEnvManagerNew::onRegionSettingsResponse(const LLSD& content)
{
// If the message was valid, grab the UUID from it and save it for next outbound update message.
mLastReceivedID = content[0]["messageID"].asUUID();
// Refresh cached region settings.
LL_DEBUGS("Windlight") << "Caching region environment settings: " << content << LL_ENDL;
LL_DEBUGS("Windlight") << "Received region environment settings: " << content << LL_ENDL;
F32 sun_hour = 0; // *TODO
LLEnvironmentSettings new_settings(content[1], content[2], content[3], sun_hour);
mCachedRegionPrefs = new_settings;
@ -594,6 +588,7 @@ void LLEnvManagerNew::updateWaterFromPrefs(bool interpolate)
void LLEnvManagerNew::updateManagersFromPrefs(bool interpolate)
{
LL_DEBUGS("Windlight")<<LL_ENDL;
// Apply water settings.
updateWaterFromPrefs(interpolate);
@ -651,28 +646,35 @@ bool LLEnvManagerNew::useDefaultWater()
}
void LLEnvManagerNew::onRegionChange(bool interpolate)
void LLEnvManagerNew::onRegionChange()
{
// Avoid duplicating region setting requests
// by checking whether the region is actually changing.
LLViewerRegion* regionp = gAgent.getRegion();
LLUUID region_uuid = regionp ? regionp->getRegionID() : LLUUID::null;
if (region_uuid == mCurRegionUUID)
if (region_uuid != mCurRegionUUID)
{
return;
// Clear locally modified region settings.
mNewRegionPrefs.clear();
// *TODO: clear environment settings of the previous region?
// Request environment settings of the new region.
mCurRegionUUID = region_uuid;
// for region crossings, interpolate the change; for teleports, don't
mInterpNextChangeMessage = (gAgent.getTeleportState() == LLAgent::TELEPORT_NONE);
LL_DEBUGS("Windlight") << (mInterpNextChangeMessage ? "Crossed" : "Teleported")
<< " to new region: " << region_uuid
<< LL_ENDL;
requestRegionSettings();
}
else
{
LL_DEBUGS("Windlight") << "disregarding region change; interp: "
<< (mInterpNextChangeMessage ? "true" : "false")
<< " regionp: " << regionp
<< " old: " << mCurRegionUUID
<< " new: " << region_uuid
<< LL_ENDL;
}
// Clear locally modified region settings.
mNewRegionPrefs.clear();
// *TODO: clear environment settings of the previous region?
// Request environment settings of the new region.
LL_DEBUGS("Windlight") << "New viewer region: " << region_uuid << LL_ENDL;
mCurRegionUUID = region_uuid;
mInterpNextChangeMessage = interpolate;
requestRegionSettings();
// Let interested parties know agent region has been changed.
mRegionChangeSignal();
}

View File

@ -166,7 +166,6 @@ class LLEnvManagerNew : public LLSingleton<LLEnvManagerNew>
public:
typedef boost::signals2::signal<void()> prefs_change_signal_t;
typedef boost::signals2::signal<void()> region_settings_change_signal_t;
typedef boost::signals2::signal<void()> region_change_signal_t;
typedef boost::signals2::signal<void(bool)> region_settings_applied_signal_t;
LLEnvManagerNew();
@ -222,15 +221,12 @@ public:
bool sendRegionSettings(const LLEnvironmentSettings& new_settings);
boost::signals2::connection setPreferencesChangeCallback(const prefs_change_signal_t::slot_type& cb);
boost::signals2::connection setRegionSettingsChangeCallback(const region_settings_change_signal_t::slot_type& cb);
boost::signals2::connection setRegionChangeCallback(const region_change_signal_t::slot_type& cb);
boost::signals2::connection setRegionSettingsAppliedCallback(const region_settings_applied_signal_t::slot_type& cb);
static bool canEditRegionSettings(); /// @return true if we have access to editing region environment
static const std::string getScopeString(LLEnvKey::EScope scope);
// Public callbacks.
void onRegionCrossing();
void onTeleport();
void onRegionSettingsResponse(const LLSD& content);
void onRegionSettingsApplyResponse(bool ok);
@ -251,7 +247,7 @@ private:
bool useDefaultSky();
bool useDefaultWater();
void onRegionChange(bool interpolate);
void onRegionChange();
/// Emitted when user environment preferences change.
prefs_change_signal_t mUsePrefsChangeSignal;
@ -259,9 +255,6 @@ private:
/// Emitted when region environment settings update comes.
region_settings_change_signal_t mRegionSettingsChangeSignal;
/// Emitted when agent region changes. Move to LLAgent?
region_change_signal_t mRegionChangeSignal;
/// Emitted when agent region changes. Move to LLAgent?
region_settings_applied_signal_t mRegionSettingsAppliedSignal;

View File

@ -145,7 +145,7 @@ void LLFloaterEditDayCycle::initCallbacks(void)
// Connect to env manager events.
LLEnvManagerNew& env_mgr = LLEnvManagerNew::instance();
env_mgr.setRegionSettingsChangeCallback(boost::bind(&LLFloaterEditDayCycle::onRegionSettingsChange, this));
env_mgr.setRegionChangeCallback(boost::bind(&LLFloaterEditDayCycle::onRegionChange, this));
gAgent.addRegionChangedCallback(boost::bind(&LLFloaterEditDayCycle::onRegionChange, this));
env_mgr.setRegionSettingsAppliedCallback(boost::bind(&LLFloaterEditDayCycle::onRegionSettingsApplied, this, _1));
// Connect to day cycle manager events.

View File

@ -61,6 +61,9 @@
#include "llnotificationmanager.h"
#include "llautoreplace.h"
const F32 ME_TYPING_TIMEOUT = 4.0f;
const F32 OTHER_TYPING_TIMEOUT = 9.0f;
floater_showed_signal_t LLFloaterIMSession::sIMFloaterShowedSignal;
LLFloaterIMSession::LLFloaterIMSession(const LLUUID& session_id)
@ -75,7 +78,10 @@ LLFloaterIMSession::LLFloaterIMSession(const LLUUID& session_id)
mTypingTimer(),
mTypingTimeoutTimer(),
mPositioned(false),
mSessionInitialized(false)
mSessionInitialized(false),
mMeTypingTimer(),
mOtherTypingTimer(),
mImInfo()
{
mIsNearbyChat = false;
@ -96,13 +102,31 @@ LLFloaterIMSession::LLFloaterIMSession(const LLUUID& session_id)
void LLFloaterIMSession::refresh()
{
if (mMeTyping)
{
{
// Send an additional Start Typing packet every ME_TYPING_TIMEOUT seconds
if (mMeTypingTimer.getElapsedTimeF32() > ME_TYPING_TIMEOUT && false == mShouldSendTypingState)
{
LL_DEBUGS("TypingMsgs") << "Send additional Start Typing packet" << LL_ENDL;
LLIMModel::instance().sendTypingState(mSessionID, mOtherParticipantUUID, TRUE);
mMeTypingTimer.reset();
}
// Time out if user hasn't typed for a while.
if (mTypingTimeoutTimer.getElapsedTimeF32() > LLAgent::TYPING_TIMEOUT_SECS)
{
setTyping(false);
setTyping(false);
LL_DEBUGS("TypingMsgs") << "Send stop typing due to timeout" << LL_ENDL;
}
}
// Clear <name is typing> message if no data received for OTHER_TYPING_TIMEOUT seconds
if (mOtherTyping && mOtherTypingTimer.getElapsedTimeF32() > OTHER_TYPING_TIMEOUT)
{
LL_DEBUGS("TypingMsgs") << "Received: is typing cleared due to timeout" << LL_ENDL;
removeTypingIndicator(mImInfo);
mOtherTyping = false;
}
}
// virtual
@ -953,13 +977,21 @@ void LLFloaterIMSession::setTyping(bool typing)
// much network traffic. Only send in person-to-person IMs.
if ( mShouldSendTypingState && mDialog == IM_NOTHING_SPECIAL )
{
// Still typing, send 'start typing' notification or
// send 'stop typing' notification immediately
if (!mMeTyping || mTypingTimer.getElapsedTimeF32() > 1.f)
if ( mMeTyping )
{
LLIMModel::instance().sendTypingState(mSessionID,
mOtherParticipantUUID, mMeTyping);
mShouldSendTypingState = false;
if ( mTypingTimer.getElapsedTimeF32() > 1.f )
{
// Still typing, send 'start typing' notification
LLIMModel::instance().sendTypingState(mSessionID, mOtherParticipantUUID, TRUE);
mShouldSendTypingState = false;
mMeTypingTimer.reset();
}
}
else
{
// Send 'stop typing' notification immediately
LLIMModel::instance().sendTypingState(mSessionID, mOtherParticipantUUID, FALSE);
mShouldSendTypingState = false;
}
}
@ -975,10 +1007,12 @@ void LLFloaterIMSession::setTyping(bool typing)
void LLFloaterIMSession::processIMTyping(const LLIMInfo* im_info, BOOL typing)
{
LL_DEBUGS("TypingMsgs") << "typing=" << typing << LL_ENDL;
if ( typing )
{
// other user started typing
addTypingIndicator(im_info);
mOtherTypingTimer.reset();
}
else
{
@ -1202,10 +1236,40 @@ BOOL LLFloaterIMSession::inviteToSession(const uuid_vec_t& ids)
void LLFloaterIMSession::addTypingIndicator(const LLIMInfo* im_info)
{
/* Operation of "<name> is typing" state machine:
Not Typing state:
User types in P2P IM chat ... Send Start Typing, save Started time,
start Idle Timer (N seconds) go to Typing state
Typing State:
User enters a non-return character: if Now - Started > ME_TYPING_TIMEOUT, send
Start Typing, restart Idle Timer
User enters a return character: stop Idle Timer, send IM and Stop
Typing, go to Not Typing state
Idle Timer expires: send Stop Typing, go to Not Typing state
The recipient has a complementary state machine in which a Start Typing
that is not followed by either an IM or another Start Typing within OTHER_TYPING_TIMEOUT
seconds switches the sender out of typing state.
This has the nice quality of being self-healing for lost start/stop
messages while adding messages only for the (relatively rare) case of a
user who types a very long message (one that takes more than ME_TYPING_TIMEOUT seconds
to type).
Note: OTHER_TYPING_TIMEOUT must be > ME_TYPING_TIMEOUT for proper operation of the state machine
*/
// We may have lost a "stop-typing" packet, don't add it twice
if (im_info && !mOtherTyping)
{
mOtherTyping = true;
mOtherTypingTimer.reset();
// Save im_info so that removeTypingIndicator can be properly called because a timeout has occurred
mImInfo = im_info;
// Update speaker
LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID);

View File

@ -187,6 +187,8 @@ private:
LLFrameTimer mTypingTimer;
LLFrameTimer mTypingTimeoutTimer;
bool mSessionNameUpdatedForTyping;
LLFrameTimer mMeTypingTimer;
LLFrameTimer mOtherTypingTimer;
bool mSessionInitialized;
LLSD mQueuedMsgsForInit;
@ -196,6 +198,8 @@ private:
// connection to voice channel state change signal
boost::signals2::connection mVoiceChannelStateChangeConnection;
const LLIMInfo* mImInfo;
};
#endif // LL_FLOATERIMSESSION_H

View File

@ -1791,10 +1791,15 @@ void LLPanelLandObjects::onCommitClean(LLUICtrl *caller, void* user_data)
LLParcel* parcel = lop->mParcel->getParcel();
if (parcel)
{
lop->mOtherTime = atoi(lop->mCleanOtherObjectsTime->getText().c_str());
S32 return_time = atoi(lop->mCleanOtherObjectsTime->getText().c_str());
// Only send return time if it has changed
if (return_time != lop->mOtherTime)
{
lop->mOtherTime = return_time;
parcel->setCleanOtherTime(lop->mOtherTime);
send_other_clean_time_message(parcel->getLocalID(), lop->mOtherTime);
parcel->setCleanOtherTime(lop->mOtherTime);
send_other_clean_time_message(parcel->getLocalID(), lop->mOtherTime);
}
}
}

View File

@ -535,9 +535,16 @@ BOOL LLFloaterModelPreview::postBuild()
mUploadBtn = getChild<LLButton>("ok_btn");
mCalculateBtn = getChild<LLButton>("calculate_btn");
mCalculateBtn->setClickedCallback(boost::bind(&LLFloaterModelPreview::onClickCalculateBtn, this));
if (LLConvexDecomposition::getInstance() != NULL)
{
mCalculateBtn->setClickedCallback(boost::bind(&LLFloaterModelPreview::onClickCalculateBtn, this));
toggleCalculateButton(true);
toggleCalculateButton(true);
}
else
{
mCalculateBtn->setEnabled(false);
}
return TRUE;
}

View File

@ -34,11 +34,11 @@
#include <boost/signals2.hpp>
#include "llagent.h"
#include "llbutton.h"
#include "llcheckboxctrl.h"
#include "llcombobox.h"
#include "llcontrol.h"
#include "llenvmanager.h"
#include "llfloaterpathfindingcharacters.h"
#include "llfloaterpathfindinglinksets.h"
#include "llfloaterreg.h"
@ -224,7 +224,7 @@ void LLFloaterPathfindingConsole::onOpen(const LLSD& pKey)
if (!mRegionBoundarySlot.connected())
{
mRegionBoundarySlot = LLEnvManagerNew::instance().setRegionChangeCallback(boost::bind(&LLFloaterPathfindingConsole::onRegionBoundaryCross, this));
mRegionBoundarySlot = gAgent.addRegionChangedCallback(boost::bind(&LLFloaterPathfindingConsole::onRegionBoundaryCross, this));
}
if (!mTeleportFailedSlot.connected())

View File

@ -41,7 +41,6 @@
#include "llavatarnamecache.h"
#include "llbutton.h"
#include "llcheckboxctrl.h"
#include "llenvmanager.h"
#include "llfloater.h"
#include "llfontgl.h"
#include "llnotifications.h"
@ -85,7 +84,7 @@ void LLFloaterPathfindingObjects::onOpen(const LLSD &pKey)
if (!mRegionBoundaryCrossingSlot.connected())
{
mRegionBoundaryCrossingSlot = LLEnvManagerNew::getInstance()->setRegionChangeCallback(boost::bind(&LLFloaterPathfindingObjects::onRegionBoundaryCrossed, this));
mRegionBoundaryCrossingSlot = gAgent.addRegionChangedCallback(boost::bind(&LLFloaterPathfindingObjects::onRegionBoundaryCrossed, this));
}
if (!mGodLevelChangeSlot.connected())

View File

@ -91,6 +91,7 @@
#include "lltrans.h"
#include "llagentui.h"
#include "llmeshrepository.h"
#include "llfloaterregionrestarting.h"
const S32 TERRAIN_TEXTURE_COUNT = 4;
const S32 CORNER_COUNT = 4;
@ -219,7 +220,7 @@ BOOL LLFloaterRegionInfo::postBuild()
&processEstateOwnerRequest);
// Request region info when agent region changes.
LLEnvManagerNew::instance().setRegionChangeCallback(boost::bind(&LLFloaterRegionInfo::requestRegionInfo, this));
gAgent.addRegionChangedCallback(boost::bind(&LLFloaterRegionInfo::requestRegionInfo, this));
return TRUE;
}

View File

@ -0,0 +1,176 @@
/**
* @file llfloaterregionrestarting.cpp
* @brief Shows countdown timer during region restart
*
* $LicenseInfo:firstyear=2006&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "llviewerprecompiledheaders.h"
#include "llfloaterregionrestarting.h"
#include "llfloaterreg.h"
#include "lluictrl.h"
#include "llagent.h"
#include "llagentcamera.h"
#include "llviewerwindow.h"
static S32 sSeconds;
static U32 sShakeState;
LLFloaterRegionRestarting::LLFloaterRegionRestarting(const LLSD& key) :
LLFloater(key),
LLEventTimer(1)
{
mName = (std::string)key["NAME"];
sSeconds = (LLSD::Integer)key["SECONDS"];
}
LLFloaterRegionRestarting::~LLFloaterRegionRestarting()
{
mRegionChangedConnection.disconnect();
}
BOOL LLFloaterRegionRestarting::postBuild()
{
mRegionChangedConnection = gAgent.addRegionChangedCallback(boost::bind(&LLFloaterRegionRestarting::regionChange, this));
LLStringUtil::format_map_t args;
std::string text;
args["[NAME]"] = mName;
text = getString("RegionName", args);
LLTextBox* textbox = getChild<LLTextBox>("region_name");
textbox->setValue(text);
sShakeState = SHAKE_START;
refresh();
return TRUE;
}
void LLFloaterRegionRestarting::regionChange()
{
close();
}
BOOL LLFloaterRegionRestarting::tick()
{
refresh();
return FALSE;
}
void LLFloaterRegionRestarting::refresh()
{
LLStringUtil::format_map_t args;
std::string text;
args["[SECONDS]"] = llformat("%d", sSeconds);
getChild<LLTextBox>("restart_seconds")->setValue(getString("RestartSeconds", args));
sSeconds = sSeconds - 1;
if(sSeconds < 0.0)
{
sSeconds = 0;
}
}
void LLFloaterRegionRestarting::draw()
{
LLFloater::draw();
const F32 SHAKE_INTERVAL = 0.025;
const F32 SHAKE_TOTAL_DURATION = 1.8; // the length of the default alert tone for this
const F32 SHAKE_INITIAL_MAGNITUDE = 1.5;
const F32 SHAKE_HORIZONTAL_BIAS = 0.25;
F32 time_shaking;
if(SHAKE_START == sShakeState)
{
mShakeTimer.setTimerExpirySec(SHAKE_INTERVAL);
sShakeState = SHAKE_LEFT;
mShakeIterations = 0;
mShakeMagnitude = SHAKE_INITIAL_MAGNITUDE;
}
if(SHAKE_DONE != sShakeState && mShakeTimer.hasExpired())
{
gAgentCamera.unlockView();
switch(sShakeState)
{
case SHAKE_LEFT:
gAgentCamera.setPanLeftKey(mShakeMagnitude * SHAKE_HORIZONTAL_BIAS);
sShakeState = SHAKE_UP;
break;
case SHAKE_UP:
gAgentCamera.setPanUpKey(mShakeMagnitude);
sShakeState = SHAKE_RIGHT;
break;
case SHAKE_RIGHT:
gAgentCamera.setPanRightKey(mShakeMagnitude * SHAKE_HORIZONTAL_BIAS);
sShakeState = SHAKE_DOWN;
break;
case SHAKE_DOWN:
gAgentCamera.setPanDownKey(mShakeMagnitude);
mShakeIterations++;
time_shaking = SHAKE_INTERVAL * (mShakeIterations * 4 /* left, up, right, down */);
if(SHAKE_TOTAL_DURATION <= time_shaking)
{
sShakeState = SHAKE_DONE;
mShakeMagnitude = 0.0;
}
else
{
sShakeState = SHAKE_LEFT;
F32 percent_done_shaking = (SHAKE_TOTAL_DURATION - time_shaking) / SHAKE_TOTAL_DURATION;
mShakeMagnitude = SHAKE_INITIAL_MAGNITUDE * (percent_done_shaking * percent_done_shaking); // exponential decay
}
break;
default:
break;
}
mShakeTimer.setTimerExpirySec(SHAKE_INTERVAL);
}
}
void LLFloaterRegionRestarting::close()
{
LLFloaterRegionRestarting* floaterp = LLFloaterReg::findTypedInstance<LLFloaterRegionRestarting>("region_restarting");
if (floaterp)
{
floaterp->closeFloater();
}
}
void LLFloaterRegionRestarting::updateTime(S32 time)
{
sSeconds = time;
sShakeState = SHAKE_START;
}

View File

@ -0,0 +1,69 @@
/**
* @file llfloaterregionrestarting.h
* @brief Shows countdown timer during region restart
*
* $LicenseInfo:firstyear=2006&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LL_LLFLOATERREGIONRESTARTING_H
#define LL_LLFLOATERREGIONRESTARTING_H
#include "llfloater.h"
#include "lltextbox.h"
#include "lleventtimer.h"
class LLFloaterRegionRestarting : public LLFloater, public LLEventTimer
{
friend class LLFloaterReg;
public:
static void close();
static void updateTime(S32 time);
private:
LLFloaterRegionRestarting(const LLSD& key);
virtual ~LLFloaterRegionRestarting();
virtual BOOL postBuild();
virtual BOOL tick();
virtual void refresh();
virtual void draw();
virtual void regionChange();
std::string mName;
U32 mShakeIterations;
F32 mShakeMagnitude;
LLTimer mShakeTimer;
boost::signals2::connection mRegionChangedConnection;
enum
{
SHAKE_START,
SHAKE_LEFT,
SHAKE_UP,
SHAKE_RIGHT,
SHAKE_DOWN,
SHAKE_DONE
};
};
#endif // LL_LLFLOATERREGIONRESTARTING_H

View File

@ -372,7 +372,10 @@ void LLFloaterWebContent::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent
}
else if(event == MEDIA_EVENT_GEOMETRY_CHANGE)
{
geometryChanged(self->getGeometryX(), self->getGeometryY(), self->getGeometryWidth(), self->getGeometryHeight());
if (mCurrentURL.find("facebook.com/dialog/oauth") == std::string::npos) // HACK to fix ACME-1317 - Cho
{
geometryChanged(self->getGeometryX(), self->getGeometryY(), self->getGeometryWidth(), self->getGeometryHeight());
}
}
else if(event == MEDIA_EVENT_STATUS_TEXT_CHANGED )
{

View File

@ -627,8 +627,8 @@ void LLFloaterWorldMap::trackLocation(const LLVector3d& pos_global)
if (!sim_info)
{
// We haven't found a region for that point yet, leave the tracking to the world map
LLWorldMap::getInstance()->setTracking(pos_global);
LLTracker::stopTracking(NULL);
LLWorldMap::getInstance()->setTracking(pos_global);
S32 world_x = S32(pos_global.mdV[0] / 256);
S32 world_y = S32(pos_global.mdV[1] / 256);
LLWorldMapMessage::getInstance()->sendMapBlockRequest(world_x, world_y, world_x, world_y, true);
@ -643,9 +643,9 @@ void LLFloaterWorldMap::trackLocation(const LLVector3d& pos_global)
{
// Down region. Show the blue circle of death!
// i.e. let the world map that this and tell it it's invalid
LLTracker::stopTracking(NULL);
LLWorldMap::getInstance()->setTracking(pos_global);
LLWorldMap::getInstance()->setTrackingInvalid();
LLTracker::stopTracking(NULL);
setDefaultBtn("");
// clicked on a down region - turn off coord display
@ -665,8 +665,8 @@ void LLFloaterWorldMap::trackLocation(const LLVector3d& pos_global)
std::string tooltip("");
mTrackedStatus = LLTracker::TRACKING_LOCATION;
LLTracker::trackLocation(pos_global, full_name, tooltip);
LLWorldMap::getInstance()->cancelTracking(); // The floater is taking over the tracking
LLTracker::trackLocation(pos_global, full_name, tooltip);
LLVector3d coord_pos = LLTracker::getTrackedPositionGlobal();
updateTeleportCoordsDisplay( coord_pos );

View File

@ -74,6 +74,7 @@
#include "llvoavatarself.h"
#include "llwearablelist.h"
#include "lllandmarkactions.h"
#include "llpanellandmarks.h"
void copy_slurl_to_clipboard_callback_inv(const std::string& slurl);
@ -1449,6 +1450,38 @@ void LLItemBridge::performAction(LLInventoryModel* model, std::string action)
}
}
}
else if ("show_on_map" == action)
{
doActionOnCurSelectedLandmark(boost::bind(&LLItemBridge::doShowOnMap, this, _1));
}
}
void LLItemBridge::doActionOnCurSelectedLandmark(LLLandmarkList::loaded_callback_t cb)
{
LLViewerInventoryItem* cur_item = getItem();
if(cur_item && cur_item->getInventoryType() == LLInventoryType::IT_LANDMARK)
{
LLLandmark* landmark = LLLandmarkActions::getLandmark(cur_item->getUUID(), cb);
if (landmark)
{
cb(landmark);
}
}
}
void LLItemBridge::doShowOnMap(LLLandmark* landmark)
{
LLVector3d landmark_global_pos;
// landmark has already been tested for NULL by calling routine
if (landmark->getGlobalPos(landmark_global_pos))
{
LLFloaterWorldMap* worldmap_instance = LLFloaterWorldMap::getInstance();
if (!landmark_global_pos.isExactlyZero() && worldmap_instance)
{
worldmap_instance->trackLocation(landmark_global_pos);
LLFloaterReg::showInstance("world_map", "center");
}
}
}
void copy_slurl_to_clipboard_callback_inv(const std::string& slurl)
@ -4580,6 +4613,7 @@ void LLLandmarkBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
items.push_back(std::string("Landmark Separator"));
items.push_back(std::string("url_copy"));
items.push_back(std::string("About Landmark"));
items.push_back(std::string("show_on_map"));
}
// Disable "About Landmark" menu item for

View File

@ -36,6 +36,7 @@
#include "llviewercontrol.h"
#include "llviewerwearable.h"
#include "lltooldraganddrop.h"
#include "lllandmarklist.h"
class LLInventoryFilter;
class LLInventoryPanel;
@ -239,7 +240,10 @@ protected:
BOOL confirmRemoveItem(const LLSD& notification, const LLSD& response);
virtual BOOL isItemPermissive() const;
virtual void buildDisplayName() const;
void doActionOnCurSelectedLandmark(LLLandmarkList::loaded_callback_t cb);
private:
void doShowOnMap(LLLandmark* landmark);
};
class LLFolderBridge : public LLInvFVBridge

View File

@ -407,14 +407,14 @@ LLLocationInputCtrl::LLLocationInputCtrl(const LLLocationInputCtrl::Params& p)
// - Make the "Add landmark" button updated when either current parcel gets changed
// or a landmark gets created or removed from the inventory.
// - Update the location string on parcel change.
mParcelMgrConnection = LLViewerParcelMgr::getInstance()->addAgentParcelChangedCallback(
mParcelMgrConnection = gAgent.addParcelChangedCallback(
boost::bind(&LLLocationInputCtrl::onAgentParcelChange, this));
// LLLocationHistory instance is being created before the location input control, so we have to update initial state of button manually.
mButton->setEnabled(LLLocationHistory::instance().getItemCount() > 0);
mLocationHistoryConnection = LLLocationHistory::getInstance()->setChangedCallback(
boost::bind(&LLLocationInputCtrl::onLocationHistoryChanged, this,_1));
mRegionCrossingSlot = LLEnvManagerNew::getInstance()->setRegionChangeCallback(boost::bind(&LLLocationInputCtrl::onRegionBoundaryCrossed, this));
mRegionCrossingSlot = gAgent.addRegionChangedCallback(boost::bind(&LLLocationInputCtrl::onRegionBoundaryCrossed, this));
createNavMeshStatusListenerForCurrentRegion();
mRemoveLandmarkObserver = new LLRemoveLandmarkObserver(this);

View File

@ -79,7 +79,7 @@ void LLMenuOptionPathfindingRebakeNavmesh::initialize()
if ( !mRegionCrossingSlot.connected() )
{
mRegionCrossingSlot = LLEnvManagerNew::getInstance()->setRegionChangeCallback(boost::bind(&LLMenuOptionPathfindingRebakeNavmesh::handleRegionBoundaryCrossed, this));
mRegionCrossingSlot = gAgent.addRegionChangedCallback(boost::bind(&LLMenuOptionPathfindingRebakeNavmesh::handleRegionBoundaryCrossed, this));
}
if (!mAgentStateSlot.connected())

File diff suppressed because it is too large Load Diff

View File

@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
* Copyright (C) 2010-2013, 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
@ -32,6 +32,12 @@
#include "lluuid.h"
#include "llviewertexture.h"
#include "llvolume.h"
#include "lldeadmantimer.h"
#include "httpcommon.h"
#include "httprequest.h"
#include "httpoptions.h"
#include "httpheaders.h"
#include "httphandler.h"
#define LLCONVEXDECOMPINTER_STATIC 1
@ -39,8 +45,6 @@
#include "lluploadfloaterobservers.h"
class LLVOVolume;
class LLMeshResponder;
class LLCurlRequest;
class LLMutex;
class LLCondition;
class LLVFS;
@ -215,17 +219,17 @@ class LLMeshRepoThread : public LLThread
{
public:
static S32 sActiveHeaderRequests;
static S32 sActiveLODRequests;
volatile static S32 sActiveHeaderRequests;
volatile static S32 sActiveLODRequests;
static U32 sMaxConcurrentRequests;
static S32 sRequestLowWater;
static S32 sRequestHighWater;
static S32 sRequestWaterLevel; // Stats-use only, may read outside of thread
LLCurlRequest* mCurlRequest;
LLMutex* mMutex;
LLMutex* mHeaderMutex;
LLCondition* mSignal;
bool mWaiting;
//map of known mesh headers
typedef std::map<LLUUID, LLSD> mesh_header_map;
mesh_header_map mMeshHeader;
@ -287,8 +291,8 @@ public:
//set of requested skin info
std::set<LLUUID> mSkinRequests;
//queue of completed skin info requests
std::queue<LLMeshSkinInfo> mSkinInfoQ;
// list of completed skin info requests
std::list<LLMeshSkinInfo> mSkinInfoQ;
//set of requested decompositions
std::set<LLUUID> mDecompositionRequests;
@ -296,8 +300,8 @@ public:
//set of requested physics shapes
std::set<LLUUID> mPhysicsShapeRequests;
//queue of completed Decomposition info requests
std::queue<LLModel::Decomposition*> mDecompositionQ;
// list of completed Decomposition info requests
std::list<LLModel::Decomposition*> mDecompositionQ;
//queue of requested headers
std::queue<HeaderRequest> mHeaderReqQ;
@ -315,7 +319,23 @@ public:
typedef std::map<LLVolumeParams, std::vector<S32> > pending_lod_map;
pending_lod_map mPendingLOD;
static std::string constructUrl(LLUUID mesh_id);
// llcorehttp library interface objects.
LLCore::HttpStatus mHttpStatus;
LLCore::HttpRequest * mHttpRequest;
LLCore::HttpOptions * mHttpOptions;
LLCore::HttpOptions * mHttpLargeOptions;
LLCore::HttpHeaders * mHttpHeaders;
LLCore::HttpRequest::policy_t mHttpPolicyClass;
LLCore::HttpRequest::policy_t mHttpLegacyPolicyClass;
LLCore::HttpRequest::policy_t mHttpLargePolicyClass;
LLCore::HttpRequest::priority_t mHttpPriority;
typedef std::set<LLCore::HttpHandler *> http_request_set;
http_request_set mHttpRequestSet; // Outstanding HTTP requests
std::string mGetMeshCapability;
std::string mGetMesh2Capability;
int mGetMeshVersion;
LLMeshRepoThread();
~LLMeshRepoThread();
@ -325,8 +345,8 @@ public:
void lockAndLoadMeshLOD(const LLVolumeParams& mesh_params, S32 lod);
void loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod);
bool fetchMeshHeader(const LLVolumeParams& mesh_params, U32& count);
bool fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, U32& count);
bool fetchMeshHeader(const LLVolumeParams& mesh_params);
bool fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod);
bool headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size);
bool lodReceived(const LLVolumeParams& mesh_params, S32 lod, U8* data, S32 data_size);
bool skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 data_size);
@ -358,9 +378,37 @@ public:
static void incActiveHeaderRequests();
static void decActiveHeaderRequests();
// Set the caps strings and preferred version for constructing
// mesh fetch URLs.
//
// Mutex: must be holding mMutex when called
void setGetMeshCaps(const std::string & get_mesh1,
const std::string & get_mesh2,
int pref_version);
// Mutex: acquires mMutex
void constructUrl(LLUUID mesh_id, std::string * url, int * version);
private:
// Issue a GET request to a URL with 'Range' header using
// the correct policy class and other attributes. If an invalid
// handle is returned, the request failed and caller must retry
// or dispose of handler.
//
// Threads: Repo thread only
LLCore::HttpHandle getByteRange(const std::string & url, int cap_version,
size_t offset, size_t len,
LLCore::HttpHandler * handler);
};
class LLMeshUploadThread : public LLThread
// Class whose instances represent a single upload-type request for
// meshes: one fee query or one actual upload attempt. Yes, it creates
// a unique thread for that single request. As it is 1:1, it can also
// trivially serve as the HttpHandler object for request completion
// notifications.
class LLMeshUploadThread : public LLThread, public LLCore::HttpHandler
{
private:
S32 mMeshUploadTimeOut ; //maximum time in seconds to execute an uploading request.
@ -381,44 +429,41 @@ public:
};
LLPointer<DecompRequest> mFinalDecomp;
bool mPhysicsComplete;
volatile bool mPhysicsComplete;
typedef std::map<LLPointer<LLModel>, std::vector<LLVector3> > hull_map;
hull_map mHullMap;
hull_map mHullMap;
typedef std::vector<LLModelInstance> instance_list;
instance_list mInstanceList;
instance_list mInstanceList;
typedef std::map<LLPointer<LLModel>, instance_list> instance_map;
instance_map mInstance;
instance_map mInstance;
LLMutex* mMutex;
LLCurlRequest* mCurlRequest;
LLMutex* mMutex;
S32 mPendingUploads;
LLVector3 mOrigin;
bool mFinished;
bool mUploadTextures;
bool mUploadSkin;
bool mUploadJoints;
BOOL mDiscarded ;
volatile bool mDiscarded;
LLHost mHost;
std::string mWholeModelFeeCapability;
std::string mWholeModelUploadURL;
LLMeshUploadThread(instance_list& data, LLVector3& scale, bool upload_textures,
bool upload_skin, bool upload_joints, std::string upload_url, bool do_upload = true,
LLHandle<LLWholeModelFeeObserver> fee_observer= (LLHandle<LLWholeModelFeeObserver>()), LLHandle<LLWholeModelUploadObserver> upload_observer = (LLHandle<LLWholeModelUploadObserver>()));
bool upload_skin, bool upload_joints, const std::string & upload_url, bool do_upload = true,
LLHandle<LLWholeModelFeeObserver> fee_observer = (LLHandle<LLWholeModelFeeObserver>()),
LLHandle<LLWholeModelUploadObserver> upload_observer = (LLHandle<LLWholeModelUploadObserver>()));
~LLMeshUploadThread();
void startRequest() { ++mPendingUploads; }
void stopRequest() { --mPendingUploads; }
bool finished() { return mFinished; }
bool finished() const { return mFinished; }
virtual void run();
void preStart();
void discard() ;
BOOL isDiscarded();
bool isDiscarded() const;
void generateHulls();
@ -435,11 +480,23 @@ public:
void setFeeObserverHandle(LLHandle<LLWholeModelFeeObserver> observer_handle) { mFeeObserverHandle = observer_handle; }
void setUploadObserverHandle(LLHandle<LLWholeModelUploadObserver> observer_handle) { mUploadObserverHandle = observer_handle; }
// Inherited from LLCore::HttpHandler
virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response);
private:
LLHandle<LLWholeModelFeeObserver> mFeeObserverHandle;
LLHandle<LLWholeModelUploadObserver> mUploadObserverHandle;
bool mDoUpload; // if FALSE only model data will be requested, otherwise the model will be uploaded
LLSD mModelData;
// llcorehttp library interface objects.
LLCore::HttpStatus mHttpStatus;
LLCore::HttpRequest * mHttpRequest;
LLCore::HttpOptions * mHttpOptions;
LLCore::HttpHeaders * mHttpHeaders;
LLCore::HttpRequest::policy_t mHttpPolicyClass;
LLCore::HttpRequest::priority_t mHttpPriority;
};
class LLMeshRepository
@ -448,21 +505,28 @@ public:
//metrics
static U32 sBytesReceived;
static U32 sHTTPRequestCount;
static U32 sHTTPRetryCount;
static U32 sMeshRequestCount; // Total request count, http or cached, all component types
static U32 sHTTPRequestCount; // Http GETs issued (not large)
static U32 sHTTPLargeRequestCount; // Http GETs issued for large requests
static U32 sHTTPRetryCount; // Total request retries whether successful or failed
static U32 sHTTPErrorCount; // Requests ending in error
static U32 sLODPending;
static U32 sLODProcessing;
static U32 sCacheBytesRead;
static U32 sCacheBytesWritten;
static U32 sPeakKbps;
static U32 sCacheReads;
static U32 sCacheWrites;
static U32 sMaxLockHoldoffs; // Maximum sequential locking failures
static LLDeadmanTimer sQuiescentTimer; // Time-to-complete-mesh-downloads after significant events
static F32 getStreamingCost(LLSD& header, F32 radius, S32* bytes = NULL, S32* visible_bytes = NULL, S32 detail = -1, F32 *unscaled_value = NULL);
LLMeshRepository();
void init();
void shutdown();
S32 update() ;
S32 update();
//mesh management functions
S32 loadMesh(LLVOVolume* volume, const LLVolumeParams& mesh_params, S32 detail = 0, S32 last_lod = -1);
@ -495,6 +559,12 @@ public:
S32 getMeshSize(const LLUUID& mesh_id, S32 lod);
// Quiescent timer management, main thread only.
static void metricsStart();
static void metricsStop();
static void metricsProgress(unsigned int count);
static void metricsUpdate();
typedef std::map<LLVolumeParams, std::set<LLUUID> > mesh_load_map;
mesh_load_map mLoadingMeshes[4];
@ -556,8 +626,7 @@ public:
void uploadError(LLSD& args);
void updateInventory(inventory_data data);
std::string mGetMeshCapability;
int mGetMeshVersion; // Shadows value in LLMeshRepoThread
};
extern LLMeshRepository gMeshRepo;

View File

@ -140,7 +140,7 @@ BOOL LLFloaterMove::postBuild()
initMovementMode();
LLViewerParcelMgr::getInstance()->addAgentParcelChangedCallback(LLFloaterMove::sUpdateFlyingStatus);
gAgent.addParcelChangedCallback(LLFloaterMove::sUpdateFlyingStatus);
return TRUE;
}

View File

@ -251,7 +251,7 @@ LLPanelPlaces::LLPanelPlaces()
gInventory.addObserver(mInventoryObserver);
mAgentParcelChangedConnection = LLViewerParcelMgr::getInstance()->addAgentParcelChangedCallback(
mAgentParcelChangedConnection = gAgent.addParcelChangedCallback(
boost::bind(&LLPanelPlaces::updateVerbs, this));
//buildFromFile( "panel_places.xml"); // Called from LLRegisterPanelClass::defaultPanelClassBuilder()

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