Merge HTTP

Wed, 12 Feb 2014 18:58:40 -0800

author
Tank_Master
date
Wed, 12 Feb 2014 18:58:40 -0800
changeset 40758
2a17f37517e8
parent 40757
a20f94d25c9e
parent 40746
f7a37d5509f9
child 40759
d461d91f64e4

Merge HTTP

.hgtags file | annotate | diff | revisions
autobuild.xml file | annotate | diff | revisions
indra/llcommon/CMakeLists.txt file | annotate | diff | revisions
indra/llcommon/llthread.cpp file | annotate | diff | revisions
indra/llcommon/llthread.h file | annotate | diff | revisions
indra/llcorehttp/_httpoprequest.cpp file | annotate | diff | revisions
indra/llmessage/llcurl.cpp file | annotate | diff | revisions
indra/newview/VIEWER_VERSION.txt file | annotate | diff | revisions
indra/newview/app_settings/settings.xml file | annotate | diff | revisions
indra/newview/llmeshrepository.cpp file | annotate | diff | revisions
indra/newview/llmeshrepository.h file | annotate | diff | revisions
indra/newview/lltexturefetch.cpp file | annotate | diff | revisions
indra/newview/lltexturefetch.h file | annotate | diff | revisions
indra/newview/lltextureview.cpp file | annotate | diff | revisions
indra/newview/llviewerregion.cpp file | annotate | diff | revisions
     1.1 --- a/.hgtags	Wed Feb 12 17:18:03 2014 -0800
     1.2 +++ b/.hgtags	Wed Feb 12 18:58:40 2014 -0800
     1.3 @@ -509,3 +509,4 @@
     1.4  2feb70a4cfde43f2898d95ff8fcae3e67805c7c2 3.6.11-release
     1.5  88bbfd7a6971033f3aa103f3a3500ceb4c73521b 3.6.12-release
     1.6  0d9b9e50f1a8880e05f15688a9ec7d09e0e81013 3.6.13-release
     1.7 +5d746de933a98ca17887cde2fece80e9c7ab0b98 3.7.0-release
     2.1 --- a/autobuild.xml	Wed Feb 12 17:18:03 2014 -0800
     2.2 +++ b/autobuild.xml	Wed Feb 12 18:58:40 2014 -0800
     2.3 @@ -338,9 +338,9 @@
     2.4              <key>archive</key>
     2.5              <map>
     2.6                <key>hash</key>
     2.7 -              <string>2d9377951d99a1aa4735cea8d4b5aa71</string>
     2.8 +              <string>58b7bf45383c1b1bc24afb303b1519c8</string>
     2.9                <key>url</key>
    2.10 -              <string>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/curl-7.21.1-linux-20110316.tar.bz2</string>
    2.11 +              <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>
    2.12              </map>
    2.13              <key>name</key>
    2.14              <string>linux</string>
     3.1 --- a/indra/edit-me-to-trigger-new-build.txt	Wed Feb 12 17:18:03 2014 -0800
     3.2 +++ b/indra/edit-me-to-trigger-new-build.txt	Wed Feb 12 18:58:40 2014 -0800
     3.3 @@ -4,3 +4,4 @@
     3.4  
     3.5  
     3.6  
     3.7 +
     4.1 --- a/indra/llcommon/CMakeLists.txt	Wed Feb 12 17:18:03 2014 -0800
     4.2 +++ b/indra/llcommon/CMakeLists.txt	Wed Feb 12 18:58:40 2014 -0800
     4.3 @@ -43,6 +43,7 @@
     4.4      llcriticaldamp.cpp
     4.5      llcursortypes.cpp
     4.6      lldate.cpp
     4.7 +    lldeadmantimer.cpp
     4.8      lldependencies.cpp
     4.9      lldictionary.cpp
    4.10      llerror.cpp
    4.11 @@ -79,6 +80,7 @@
    4.12      llptrto.cpp 
    4.13      llprocess.cpp
    4.14      llprocessor.cpp
    4.15 +    llprocinfo.cpp
    4.16      llqueuedthread.cpp
    4.17      llrand.cpp
    4.18      llrefcount.cpp
    4.19 @@ -146,6 +148,7 @@
    4.20      lldarray.h
    4.21      lldarrayptr.h
    4.22      lldate.h
    4.23 +    lldeadmantimer.h
    4.24      lldefs.h
    4.25      lldependencies.h
    4.26      lldeleteutils.h
    4.27 @@ -207,6 +210,7 @@
    4.28      llpriqueuemap.h
    4.29      llprocess.h
    4.30      llprocessor.h
    4.31 +    llprocinfo.h
    4.32      llptrskiplist.h
    4.33      llptrskipmap.h
    4.34      llptrto.h
    4.35 @@ -361,12 +365,14 @@
    4.36    LL_ADD_INTEGRATION_TEST(bitpack "" "${test_libs}")
    4.37    LL_ADD_INTEGRATION_TEST(llbase64 "" "${test_libs}")
    4.38    LL_ADD_INTEGRATION_TEST(lldate "" "${test_libs}")
    4.39 +  LL_ADD_INTEGRATION_TEST(lldeadmantimer "" "${test_libs}")
    4.40    LL_ADD_INTEGRATION_TEST(lldependencies "" "${test_libs}")
    4.41    LL_ADD_INTEGRATION_TEST(llerror "" "${test_libs}")
    4.42    LL_ADD_INTEGRATION_TEST(llframetimer "" "${test_libs}")
    4.43    LL_ADD_INTEGRATION_TEST(llinstancetracker "" "${test_libs}")
    4.44    LL_ADD_INTEGRATION_TEST(lllazy "" "${test_libs}")
    4.45    LL_ADD_INTEGRATION_TEST(llprocessor "" "${test_libs}")
    4.46 +  LL_ADD_INTEGRATION_TEST(llprocinfo "" "${test_libs}")
    4.47    LL_ADD_INTEGRATION_TEST(llrand "" "${test_libs}")
    4.48    LL_ADD_INTEGRATION_TEST(llsdserialize "" "${test_libs}")
    4.49    LL_ADD_INTEGRATION_TEST(llsingleton "" "${test_libs}")                          
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/indra/llcommon/lldeadmantimer.cpp	Wed Feb 12 18:58:40 2014 -0800
     5.3 @@ -0,0 +1,188 @@
     5.4 +/** 
     5.5 +* @file lldeadmantimer.cpp
     5.6 +* @brief Simple deadman-switch timer.
     5.7 +* @author monty@lindenlab.com
     5.8 +*
     5.9 +* $LicenseInfo:firstyear=2013&license=viewerlgpl$
    5.10 +* Second Life Viewer Source Code
    5.11 +* Copyright (C) 2013, Linden Research, Inc.
    5.12 +*
    5.13 +* This library is free software; you can redistribute it and/or
    5.14 +* modify it under the terms of the GNU Lesser General Public
    5.15 +* License as published by the Free Software Foundation;
    5.16 +* version 2.1 of the License only.
    5.17 +*
    5.18 +* This library is distributed in the hope that it will be useful,
    5.19 +* but WITHOUT ANY WARRANTY; without even the implied warranty of
    5.20 +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    5.21 +* Lesser General Public License for more details.
    5.22 +*
    5.23 +* You should have received a copy of the GNU Lesser General Public
    5.24 +* License along with this library; if not, write to the Free Software
    5.25 +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
    5.26 +*
    5.27 +* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
    5.28 +* $/LicenseInfo$
    5.29 +*/
    5.30 +
    5.31 +
    5.32 +#include "lldeadmantimer.h"
    5.33 +
    5.34 +
    5.35 +// *TODO:  Currently, this uses lltimer functions for its time
    5.36 +// aspects and this leaks into the apis in the U64s/F64s.  Would
    5.37 +// like to perhaps switch this over to TSC register-based timers
    5.38 +// sometime and drop the overhead some more.
    5.39 +
    5.40 +
    5.41 +//  Flag states and their meaning:
    5.42 +//  mActive  mDone   Meaning
    5.43 +//   false   false   Nothing running, no result available
    5.44 +//    true   false   Timer running, no result available
    5.45 +//   false    true   Timer finished, result can be read once
    5.46 +//    true    true   Not allowed
    5.47 +//
    5.48 +LLDeadmanTimer::LLDeadmanTimer(F64 horizon, bool inc_cpu)
    5.49 +	: mHorizon(time_type(llmax(horizon, F64(0.0)) * gClockFrequency)),
    5.50 +	  mActive(false),			// If true, a timer is running.
    5.51 +	  mDone(false),				// If true, timer has completed and can be read (once)
    5.52 +	  mStarted(U64L(0)),
    5.53 +	  mExpires(U64L(0)),
    5.54 +	  mStopped(U64L(0)),
    5.55 +	  mCount(U64L(0)),
    5.56 +	  mIncCPU(inc_cpu),
    5.57 +	  mUStartCPU(LLProcInfo::time_type(U64L(0))),
    5.58 +	  mUEndCPU(LLProcInfo::time_type(U64L(0))),
    5.59 +	  mSStartCPU(LLProcInfo::time_type(U64L(0))),
    5.60 +	  mSEndCPU(LLProcInfo::time_type(U64L(0)))
    5.61 +{}
    5.62 +
    5.63 +
    5.64 +// static
    5.65 +LLDeadmanTimer::time_type LLDeadmanTimer::getNow()
    5.66 +{
    5.67 +	return LLTimer::getCurrentClockCount();
    5.68 +}
    5.69 +
    5.70 +
    5.71 +void LLDeadmanTimer::start(time_type now)
    5.72 +{
    5.73 +	// *TODO:  If active, let's complete an existing timer and save
    5.74 +	// the result to the side.  I think this will be useful later.
    5.75 +	// For now, wipe out anything in progress, start fresh.
    5.76 +	
    5.77 +	if (! now)
    5.78 +	{
    5.79 +		now = LLTimer::getCurrentClockCount();
    5.80 +	}
    5.81 +	mActive = true;
    5.82 +	mDone = false;
    5.83 +	mStarted = now;
    5.84 +	mExpires = now + mHorizon;
    5.85 +	mStopped = now;
    5.86 +	mCount = U64L(0);
    5.87 +	if (mIncCPU)
    5.88 +	{
    5.89 +		LLProcInfo::getCPUUsage(mUStartCPU, mSStartCPU);
    5.90 +	}
    5.91 +}
    5.92 +
    5.93 +
    5.94 +void LLDeadmanTimer::stop(time_type now)
    5.95 +{
    5.96 +	if (! mActive)
    5.97 +	{
    5.98 +		return;
    5.99 +	}
   5.100 +
   5.101 +	if (! now)
   5.102 +	{
   5.103 +		now = getNow();
   5.104 +	}
   5.105 +	mStopped = now;
   5.106 +	mActive = false;
   5.107 +	mDone = true;
   5.108 +	if (mIncCPU)
   5.109 +	{
   5.110 +		LLProcInfo::getCPUUsage(mUEndCPU, mSEndCPU);
   5.111 +	}
   5.112 +}
   5.113 +
   5.114 +
   5.115 +bool LLDeadmanTimer::isExpired(time_type now, F64 & started, F64 & stopped, U64 & count,
   5.116 +							   U64 & user_cpu, U64 & sys_cpu)
   5.117 +{
   5.118 +	const bool status(isExpired(now, started, stopped, count));
   5.119 +	if (status)
   5.120 +	{
   5.121 +		user_cpu = U64(mUEndCPU - mUStartCPU);
   5.122 +		sys_cpu = U64(mSEndCPU - mSStartCPU);
   5.123 +	}
   5.124 +	return status;
   5.125 +}
   5.126 +
   5.127 +		
   5.128 +bool LLDeadmanTimer::isExpired(time_type now, F64 & started, F64 & stopped, U64 & count)
   5.129 +{
   5.130 +	if (mActive && ! mDone)
   5.131 +	{
   5.132 +		if (! now)
   5.133 +		{
   5.134 +			now = getNow();
   5.135 +		}
   5.136 +
   5.137 +		if (now >= mExpires)
   5.138 +		{
   5.139 +			// mStopped from ringBell() is the value we want
   5.140 +			mActive = false;
   5.141 +			mDone = true;
   5.142 +		}
   5.143 +	}
   5.144 +
   5.145 +	if (! mDone)
   5.146 +	{
   5.147 +		return false;
   5.148 +	}
   5.149 +	
   5.150 +	started = mStarted * gClockFrequencyInv;
   5.151 +	stopped = mStopped * gClockFrequencyInv;
   5.152 +	count = mCount;
   5.153 +	mDone = false;
   5.154 +
   5.155 +	return true;
   5.156 +}
   5.157 +
   5.158 +	
   5.159 +void LLDeadmanTimer::ringBell(time_type now, unsigned int count)
   5.160 +{
   5.161 +	if (! mActive)
   5.162 +	{
   5.163 +		return;
   5.164 +	}
   5.165 +	
   5.166 +	if (! now)
   5.167 +	{
   5.168 +		now = getNow();
   5.169 +	}
   5.170 +
   5.171 +	if (now >= mExpires)
   5.172 +	{
   5.173 +		// Timer has expired, this event will be dropped
   5.174 +		mActive = false;
   5.175 +		mDone = true;
   5.176 +	}
   5.177 +	else
   5.178 +	{
   5.179 +		// Timer renewed, keep going
   5.180 +		mStopped = now;
   5.181 +		mExpires = now + mHorizon;
   5.182 +		mCount += count;
   5.183 +		if (mIncCPU)
   5.184 +		{
   5.185 +			LLProcInfo::getCPUUsage(mUEndCPU, mSEndCPU);
   5.186 +		}
   5.187 +	}
   5.188 +	
   5.189 +	return;
   5.190 +}
   5.191 +
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/indra/llcommon/lldeadmantimer.h	Wed Feb 12 18:58:40 2014 -0800
     6.3 @@ -0,0 +1,214 @@
     6.4 +/** 
     6.5 +* @file   lldeadmantimer.h
     6.6 +* @brief  Interface to a simple event timer with a deadman's switch
     6.7 +* @author monty@lindenlab.com
     6.8 +*
     6.9 +* $LicenseInfo:firstyear=2013&license=viewerlgpl$
    6.10 +* Second Life Viewer Source Code
    6.11 +* Copyright (C) 2013, Linden Research, Inc.
    6.12 +*
    6.13 +* This library is free software; you can redistribute it and/or
    6.14 +* modify it under the terms of the GNU Lesser General Public
    6.15 +* License as published by the Free Software Foundation;
    6.16 +* version 2.1 of the License only.
    6.17 +*
    6.18 +* This library is distributed in the hope that it will be useful,
    6.19 +* but WITHOUT ANY WARRANTY; without even the implied warranty of
    6.20 +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    6.21 +* Lesser General Public License for more details.
    6.22 +*
    6.23 +* You should have received a copy of the GNU Lesser General Public
    6.24 +* License along with this library; if not, write to the Free Software
    6.25 +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
    6.26 +*
    6.27 +* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
    6.28 +* $/LicenseInfo$
    6.29 +*/
    6.30 +
    6.31 +#ifndef	LL_DEADMANTIMER_H
    6.32 +#define	LL_DEADMANTIMER_H
    6.33 +
    6.34 +
    6.35 +#include "linden_common.h"
    6.36 +
    6.37 +#include "lltimer.h"
    6.38 +#include "llprocinfo.h"
    6.39 +
    6.40 +
    6.41 +/// @file lldeadmantimer.h
    6.42 +///
    6.43 +/// There are interesting user-experienced events in the viewer that
    6.44 +/// would seem to have well-defined start and stop points but which
    6.45 +/// actually lack such milestones in the code.  Such events (like
    6.46 +/// time to load meshes after logging in, initial inventory load,
    6.47 +/// display name fetch) can be defined somewhat after-the-fact by
    6.48 +/// noticing when we no longer perform operations towards their
    6.49 +/// completion.  This class is intended to help in such applications.
    6.50 +///
    6.51 +/// What it implements is a deadman's switch (also known as a
    6.52 +/// keepalive switch and a doorbell switch).  The basic operation is
    6.53 +/// as follows:
    6.54 +///
    6.55 +/// * LLDeadmanTimer is instantiated with a horizon value in seconds,
    6.56 +///   one for each event of interest.
    6.57 +/// * When an event starts, @see start() is invoked to begin a
    6.58 +///   timing operation.
    6.59 +/// * As operations are performed in service of the event (issuing
    6.60 +///   HTTP requests, receiving responses), @see ringBell() is invoked
    6.61 +///   to inform the timer that the operation is still active.
    6.62 +/// * If the operation is canceled or otherwise terminated, @see
    6.63 +///   stop() can be called to end the timing operation.
    6.64 +/// * Concurrent with the ringBell() calls, the program makes
    6.65 +///   periodic (shorter than the horizon but not too short) calls
    6.66 +///   to @see isExpired() to see if the event has expired due to
    6.67 +///   either a stop() call or lack of activity (defined as a ringBell()
    6.68 +///   call in the previous 'horizon' seconds).  If it has expired,
    6.69 +///   the caller also receives start, stop and count values for the
    6.70 +///   event which the application can then report in whatever manner
    6.71 +///   it sees fit.
    6.72 +/// * The timer becomes passive after an isExpired() call that returns
    6.73 +///   true.  It can then be restarted with a new start() call.
    6.74 +///
    6.75 +/// Threading:  Instances are not thread-safe.  They also use
    6.76 +/// timing code from lltimer.h which is also unsafe.
    6.77 +///
    6.78 +/// Allocation:  Not refcounted, may be stack or heap allocated.
    6.79 +///
    6.80 +
    6.81 +class LL_COMMON_API LLDeadmanTimer
    6.82 +{
    6.83 +public:
    6.84 +	/// Public types
    6.85 +
    6.86 +	/// Low-level time type chosen for compatibility with
    6.87 +	/// LLTimer::getCurrentClockCount() which is the basis
    6.88 +	/// of time operations in this class.  This is likely
    6.89 +	/// to change in a future version in a move to TSC-based
    6.90 +	/// timing.
    6.91 +	typedef U64 time_type;
    6.92 +	
    6.93 +public:
    6.94 +	/// Construct and initialize an LLDeadmanTimer
    6.95 +	///
    6.96 +	/// @param horizon	Time, in seconds, after the last @see ringBell()
    6.97 +	///                 call at which point the timer will consider itself
    6.98 +	///					expired.
    6.99 +	///
   6.100 +	/// @param inc_cpu	If true, gather system and user cpu stats while
   6.101 +	///					running the timer.  This does require more syscalls
   6.102 +	///					during updates.  If false, cpu usage data isn't
   6.103 +	///					collected and will be zero if queried.
   6.104 +	LLDeadmanTimer(F64 horizon, bool inc_cpu);
   6.105 +
   6.106 +	~LLDeadmanTimer() 
   6.107 +		{}
   6.108 +	
   6.109 +private:
   6.110 +	LLDeadmanTimer(const LLDeadmanTimer &);				// Not defined
   6.111 +	void operator=(const LLDeadmanTimer &);				// Not defined
   6.112 +
   6.113 +public:
   6.114 +	/// Get the current time.  Zero-basis for this time
   6.115 +	/// representation is not defined and is different on
   6.116 +	/// different platforms.  Do not attempt to compute
   6.117 +	/// negative times relative to the first value returned,
   6.118 +	/// there may not be enough 'front porch' on the range
   6.119 +	/// to prevent wraparound.
   6.120 +	///
   6.121 +	/// Note:  Implementation is expected to change in a
   6.122 +	/// future release as well.
   6.123 +	///
   6.124 +	static time_type getNow();
   6.125 +
   6.126 +	/// Begin timing.  If the timer is already active, it is reset
   6.127 +	///	and timing begins now.
   6.128 +	///
   6.129 +	/// @param now		Current time as returned by @see
   6.130 +	///					LLTimer::getCurrentClockCount().  If zero,
   6.131 +	///					method will lookup current time.
   6.132 +	///
   6.133 +	void start(time_type now);
   6.134 +
   6.135 +	/// End timing.  Actively declare the end of the event independent
   6.136 +	/// of the deadman's switch operation.  @see isExpired() will return
   6.137 +	/// true and appropriate values will be returned.
   6.138 +	///
   6.139 +	/// @param now		Current time as returned by @see
   6.140 +	///					LLTimer::getCurrentClockCount().  If zero,
   6.141 +	///					method will lookup current time.
   6.142 +	///
   6.143 +	void stop(time_type now);
   6.144 +
   6.145 +	/// Declare that something interesting happened.  This has two
   6.146 +	/// effects on an unexpired-timer.  1)  The expiration time
   6.147 +	/// is extended for 'horizon' seconds after the 'now' value.
   6.148 +	/// 2)  An internal counter associated with the event is incremented
   6.149 +	/// by the @ref count parameter.  This count is returned via the
   6.150 +	/// @see isExpired() method.
   6.151 +	///
   6.152 +	/// @param now		Current time as returned by @see
   6.153 +	///					LLTimer::getCurrentClockCount().  If zero,
   6.154 +	///					method will lookup current time.
   6.155 +	///
   6.156 +	/// @param count	Count of events to be associated with
   6.157 +	///					this bell ringing.
   6.158 +	///
   6.159 +	void ringBell(time_type now, unsigned int count);
   6.160 +	
   6.161 +	/// Checks the status of the timer.  If the timer has expired,
   6.162 +	/// also returns various timer-related stats.  Unlike ringBell(),
   6.163 +	/// does not extend the horizon, it only checks for expiration.
   6.164 +	///
   6.165 +	/// @param now		Current time as returned by @see
   6.166 +	///					LLTimer::getCurrentClockCount().  If zero,
   6.167 +	///					method will lookup current time.
   6.168 +	///
   6.169 +	/// @param started	If expired, the starting time of the event is
   6.170 +	///					returned to the caller via this reference.
   6.171 +	///
   6.172 +	/// @param stopped	If expired, the ending time of the event is
   6.173 +	///					returned to the caller via this reference.
   6.174 +	///					Ending time will be that provided in the
   6.175 +	///					stop() method or the last ringBell() call
   6.176 +	///					leading to expiration, whichever (stop() call
   6.177 +	///					or notice of expiration) happened first.
   6.178 +	///
   6.179 +	/// @param count	If expired, the number of ringBell() calls
   6.180 +	///					made prior to expiration.
   6.181 +	///
   6.182 +	/// @param user_cpu	Amount of CPU spent in user mode by the process
   6.183 +	///					during the event.  Value in microseconds and will
   6.184 +	///					read zero if not enabled by the constructor.
   6.185 +	///
   6.186 +	/// @param sys_cpu	Amount of CPU spent in system mode by the process.
   6.187 +	///
   6.188 +	/// @return			true if the timer has expired, false otherwise.
   6.189 +	///					If true, it also returns the started,
   6.190 +	///					stopped and count values otherwise these are
   6.191 +	///					left unchanged.
   6.192 +	///
   6.193 +	bool isExpired(time_type now, F64 & started, F64 & stopped, U64 & count,
   6.194 +				   U64 & user_cpu, U64 & sys_cpu);
   6.195 +
   6.196 +	/// Identical to the six-arugment form except it does without the
   6.197 +	/// CPU time return if the caller isn't interested in it.
   6.198 +	bool isExpired(time_type now, F64 & started, F64 & stopped, U64 & count);
   6.199 +
   6.200 +protected:
   6.201 +	time_type					mHorizon;
   6.202 +	bool						mActive;
   6.203 +	bool						mDone;
   6.204 +	time_type					mStarted;
   6.205 +	time_type					mExpires;
   6.206 +	time_type					mStopped;
   6.207 +	time_type					mCount;
   6.208 +
   6.209 +	const bool					mIncCPU;		// Include CPU metrics in timer
   6.210 +	LLProcInfo::time_type		mUStartCPU;
   6.211 +	LLProcInfo::time_type		mUEndCPU;
   6.212 +	LLProcInfo::time_type		mSStartCPU;
   6.213 +	LLProcInfo::time_type		mSEndCPU;
   6.214 +};
   6.215 +	
   6.216 +
   6.217 +#endif	// LL_DEADMANTIMER_H
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/indra/llcommon/llprocinfo.cpp	Wed Feb 12 18:58:40 2014 -0800
     7.3 @@ -0,0 +1,94 @@
     7.4 +/** 
     7.5 +* @file llprocinfo.cpp
     7.6 +* @brief Process, cpu and resource usage information APIs.
     7.7 +* @author monty@lindenlab.com
     7.8 +*
     7.9 +* $LicenseInfo:firstyear=2013&license=viewerlgpl$
    7.10 +* Second Life Viewer Source Code
    7.11 +* Copyright (C) 2013, Linden Research, Inc.
    7.12 +*
    7.13 +* This library is free software; you can redistribute it and/or
    7.14 +* modify it under the terms of the GNU Lesser General Public
    7.15 +* License as published by the Free Software Foundation;
    7.16 +* version 2.1 of the License only.
    7.17 +*
    7.18 +* This library is distributed in the hope that it will be useful,
    7.19 +* but WITHOUT ANY WARRANTY; without even the implied warranty of
    7.20 +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    7.21 +* Lesser General Public License for more details.
    7.22 +*
    7.23 +* You should have received a copy of the GNU Lesser General Public
    7.24 +* License along with this library; if not, write to the Free Software
    7.25 +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
    7.26 +*
    7.27 +* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
    7.28 +* $/LicenseInfo$
    7.29 +*/
    7.30 +
    7.31 +
    7.32 +#include "llprocinfo.h"
    7.33 +
    7.34 +#if LL_WINDOWS
    7.35 +
    7.36 +#define	PSAPI_VERSION	1
    7.37 +#include "windows.h"
    7.38 +#include "psapi.h"
    7.39 +
    7.40 +#elif LL_DARWIN
    7.41 +
    7.42 +#include <sys/resource.h>
    7.43 +#include <mach/mach.h>
    7.44 +	
    7.45 +#else
    7.46 +
    7.47 +#include <sys/time.h>
    7.48 +#include <sys/resource.h>
    7.49 +
    7.50 +#endif // LL_WINDOWS/LL_DARWIN
    7.51 +
    7.52 +
    7.53 +// static
    7.54 +void LLProcInfo::getCPUUsage(time_type & user_time, time_type & system_time)
    7.55 +{
    7.56 +#if LL_WINDOWS
    7.57 +
    7.58 +	HANDLE self(GetCurrentProcess());			// Does not have to be closed
    7.59 +	FILETIME ft_dummy, ft_system, ft_user;
    7.60 +
    7.61 +	GetProcessTimes(self, &ft_dummy, &ft_dummy, &ft_system, &ft_user);
    7.62 +	ULARGE_INTEGER uli;
    7.63 +	uli.u.LowPart = ft_system.dwLowDateTime;
    7.64 +	uli.u.HighPart = ft_system.dwHighDateTime;
    7.65 +	system_time = uli.QuadPart / U64L(10);		// Convert to uS
    7.66 +	uli.u.LowPart = ft_user.dwLowDateTime;
    7.67 +	uli.u.HighPart = ft_user.dwHighDateTime;
    7.68 +	user_time = uli.QuadPart / U64L(10);
    7.69 +	
    7.70 +#elif LL_DARWIN
    7.71 +
    7.72 +	struct rusage usage;
    7.73 +
    7.74 +	if (getrusage(RUSAGE_SELF, &usage))
    7.75 +	{
    7.76 +		user_time = system_time = time_type(0U);
    7.77 +		return;
    7.78 +	}
    7.79 +	user_time = U64(usage.ru_utime.tv_sec) * U64L(1000000) + usage.ru_utime.tv_usec;
    7.80 +	system_time = U64(usage.ru_stime.tv_sec) * U64L(1000000) + usage.ru_stime.tv_usec;
    7.81 +
    7.82 +#else // Linux
    7.83 +
    7.84 +	struct rusage usage;
    7.85 +
    7.86 +	if (getrusage(RUSAGE_SELF, &usage))
    7.87 +	{
    7.88 +		user_time = system_time = time_type(0U);
    7.89 +		return;
    7.90 +	}
    7.91 +	user_time = U64(usage.ru_utime.tv_sec) * U64L(1000000) + usage.ru_utime.tv_usec;
    7.92 +	system_time = U64(usage.ru_stime.tv_sec) * U64L(1000000) + usage.ru_stime.tv_usec;
    7.93 +	
    7.94 +#endif // LL_WINDOWS/LL_DARWIN/Linux
    7.95 +}
    7.96 +
    7.97 +
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/indra/llcommon/llprocinfo.h	Wed Feb 12 18:58:40 2014 -0800
     8.3 @@ -0,0 +1,68 @@
     8.4 +/** 
     8.5 +* @file   llprocinfo.h
     8.6 +* @brief  Interface to process/cpu/resource information services.
     8.7 +* @author monty@lindenlab.com
     8.8 +*
     8.9 +* $LicenseInfo:firstyear=2013&license=viewerlgpl$
    8.10 +* Second Life Viewer Source Code
    8.11 +* Copyright (C) 2013, Linden Research, Inc.
    8.12 +*
    8.13 +* This library is free software; you can redistribute it and/or
    8.14 +* modify it under the terms of the GNU Lesser General Public
    8.15 +* License as published by the Free Software Foundation;
    8.16 +* version 2.1 of the License only.
    8.17 +*
    8.18 +* This library is distributed in the hope that it will be useful,
    8.19 +* but WITHOUT ANY WARRANTY; without even the implied warranty of
    8.20 +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    8.21 +* Lesser General Public License for more details.
    8.22 +*
    8.23 +* You should have received a copy of the GNU Lesser General Public
    8.24 +* License along with this library; if not, write to the Free Software
    8.25 +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
    8.26 +*
    8.27 +* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
    8.28 +* $/LicenseInfo$
    8.29 +*/
    8.30 +
    8.31 +#ifndef	LL_PROCINFO_H
    8.32 +#define	LL_PROCINFO_H
    8.33 +
    8.34 +
    8.35 +#include "linden_common.h"
    8.36 +
    8.37 +/// @file llprocinfo.h
    8.38 +///
    8.39 +/// Right now, this is really a namespace disguised as a class.
    8.40 +/// It wraps some types and functions to return information about
    8.41 +/// process resource consumption in a non-OS-specific manner.
    8.42 +///
    8.43 +/// Threading:  No instances so that's thread-safe.  Implementations
    8.44 +/// of static functions should be thread-safe, they mostly involve
    8.45 +/// direct syscall invocations.
    8.46 +///
    8.47 +/// Allocation:  Not instantiatable.
    8.48 +
    8.49 +class LL_COMMON_API LLProcInfo
    8.50 +{
    8.51 +public:
    8.52 +	/// Public types
    8.53 +
    8.54 +	typedef U64 time_type;								/// Relative microseconds
    8.55 +	
    8.56 +private:
    8.57 +	LLProcInfo();										// Not defined
    8.58 +	~LLProcInfo();										// Not defined
    8.59 +	LLProcInfo(const LLProcInfo &);						// Not defined
    8.60 +	void operator=(const LLProcInfo &);					// Not defined
    8.61 +
    8.62 +public:
    8.63 +	/// Get accumulated system and user CPU time in
    8.64 +	/// microseconds.  Syscalls involved in every invocation.
    8.65 +	///
    8.66 +	/// Threading:  expected to be safe.
    8.67 +	static void getCPUUsage(time_type & user_time, time_type & system_time);
    8.68 +};
    8.69 +	
    8.70 +
    8.71 +#endif	// LL_PROCINFO_H
     9.1 --- a/indra/llcommon/llthread.cpp	Wed Feb 12 17:18:03 2014 -0800
     9.2 +++ b/indra/llcommon/llthread.cpp	Wed Feb 12 18:58:40 2014 -0800
     9.3 @@ -3,7 +3,7 @@
     9.4   *
     9.5   * $LicenseInfo:firstyear=2004&license=viewerlgpl$
     9.6   * Second Life Viewer Source Code
     9.7 - * Copyright (C) 2010, Linden Research, Inc.
     9.8 + * Copyright (C) 2010-2013, Linden Research, Inc.
     9.9   * 
    9.10   * This library is free software; you can redistribute it and/or
    9.11   * modify it under the terms of the GNU Lesser General Public
    9.12 @@ -385,6 +385,36 @@
    9.13  #endif
    9.14  }
    9.15  
    9.16 +bool LLMutex::trylock()
    9.17 +{
    9.18 +	if(isSelfLocked())
    9.19 +	{ //redundant lock
    9.20 +		mCount++;
    9.21 +		return true;
    9.22 +	}
    9.23 +	
    9.24 +	apr_status_t status(apr_thread_mutex_trylock(mAPRMutexp));
    9.25 +	if (APR_STATUS_IS_EBUSY(status))
    9.26 +	{
    9.27 +		return false;
    9.28 +	}
    9.29 +	
    9.30 +#if MUTEX_DEBUG
    9.31 +	// Have to have the lock before we can access the debug info
    9.32 +	U32 id = LLThread::currentID();
    9.33 +	if (mIsLocked[id] != FALSE)
    9.34 +		llerrs << "Already locked in Thread: " << id << llendl;
    9.35 +	mIsLocked[id] = TRUE;
    9.36 +#endif
    9.37 +
    9.38 +#if LL_DARWIN
    9.39 +	mLockingThread = LLThread::currentID();
    9.40 +#else
    9.41 +	mLockingThread = sThreadID;
    9.42 +#endif
    9.43 +	return true;
    9.44 +}
    9.45 +
    9.46  void LLMutex::unlock()
    9.47  {
    9.48  	if (mCount > 0)
    10.1 --- a/indra/llcommon/llthread.h	Wed Feb 12 17:18:03 2014 -0800
    10.2 +++ b/indra/llcommon/llthread.h	Wed Feb 12 18:58:40 2014 -0800
    10.3 @@ -4,7 +4,7 @@
    10.4   *
    10.5   * $LicenseInfo:firstyear=2004&license=viewerlgpl$
    10.6   * Second Life Viewer Source Code
    10.7 - * Copyright (C) 2010, Linden Research, Inc.
    10.8 + * Copyright (C) 2010-2013, Linden Research, Inc.
    10.9   * 
   10.10   * This library is free software; you can redistribute it and/or
   10.11   * modify it under the terms of the GNU Lesser General Public
   10.12 @@ -156,7 +156,8 @@
   10.13  	virtual ~LLMutex();
   10.14  	
   10.15  	void lock();		// blocks
   10.16 -	void unlock();
   10.17 +	bool trylock();		// non-blocking, returns true if lock held.
   10.18 +	void unlock();		// undefined behavior when called on mutex not being held
   10.19  	bool isLocked(); 	// non-blocking, but does do a lock/unlock so not free
   10.20  	bool isSelfLocked(); //return true if locked in a same thread
   10.21  	U32 lockingThread() const; //get ID of locking thread
   10.22 @@ -174,6 +175,8 @@
   10.23  #endif
   10.24  };
   10.25  
   10.26 +//============================================================================
   10.27 +
   10.28  // Actually a condition/mutex pair (since each condition needs to be associated with a mutex).
   10.29  class LL_COMMON_API LLCondition : public LLMutex
   10.30  {
   10.31 @@ -189,6 +192,8 @@
   10.32  	apr_thread_cond_t *mAPRCondp;
   10.33  };
   10.34  
   10.35 +//============================================================================
   10.36 +
   10.37  class LLMutexLock
   10.38  {
   10.39  public:
   10.40 @@ -210,6 +215,43 @@
   10.41  
   10.42  //============================================================================
   10.43  
   10.44 +// Scoped locking class similar in function to LLMutexLock but uses
   10.45 +// the trylock() method to conditionally acquire lock without
   10.46 +// blocking.  Caller resolves the resulting condition by calling
   10.47 +// the isLocked() method and either punts or continues as indicated.
   10.48 +//
   10.49 +// Mostly of interest to callers needing to avoid stalls who can
   10.50 +// guarantee another attempt at a later time.
   10.51 +
   10.52 +class LLMutexTrylock
   10.53 +{
   10.54 +public:
   10.55 +	LLMutexTrylock(LLMutex* mutex)
   10.56 +		: mMutex(mutex),
   10.57 +		  mLocked(false)
   10.58 +	{
   10.59 +		if (mMutex)
   10.60 +			mLocked = mMutex->trylock();
   10.61 +	}
   10.62 +
   10.63 +	~LLMutexTrylock()
   10.64 +	{
   10.65 +		if (mMutex && mLocked)
   10.66 +			mMutex->unlock();
   10.67 +	}
   10.68 +
   10.69 +	bool isLocked() const
   10.70 +	{
   10.71 +		return mLocked;
   10.72 +	}
   10.73 +	
   10.74 +private:
   10.75 +	LLMutex*	mMutex;
   10.76 +	bool		mLocked;
   10.77 +};
   10.78 +
   10.79 +//============================================================================
   10.80 +
   10.81  void LLThread::lockData()
   10.82  {
   10.83  	mDataLock->lock();
    11.1 --- a/indra/llcommon/lltimer.h	Wed Feb 12 17:18:03 2014 -0800
    11.2 +++ b/indra/llcommon/lltimer.h	Wed Feb 12 18:58:40 2014 -0800
    11.3 @@ -146,6 +146,13 @@
    11.4  	}
    11.5  }
    11.6  
    11.7 +// These are really statics but they've been global for awhile
    11.8 +// and they're material to other timing classes.  If you are
    11.9 +// not implementing a timer class, do not use these directly.
   11.10 +extern LL_COMMON_API F64 gClockFrequency;
   11.11 +extern LL_COMMON_API F64 gClockFrequencyInv;
   11.12 +extern LL_COMMON_API F64 gClocksToMicroseconds;
   11.13 +
   11.14  // Correction factor used by time_corrected() above.
   11.15  extern LL_COMMON_API S32 gUTCOffset;
   11.16  
    12.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    12.2 +++ b/indra/llcommon/tests/lldeadmantimer_test.cpp	Wed Feb 12 18:58:40 2014 -0800
    12.3 @@ -0,0 +1,628 @@
    12.4 +/** 
    12.5 + * @file lldeadmantimer_test.cpp
    12.6 + * @brief Tests for the LLDeadmanTimer class.
    12.7 + *
    12.8 + * $LicenseInfo:firstyear=2013&license=viewerlgpl$
    12.9 + * Second Life Viewer Source Code
   12.10 + * Copyright (C) 2013, Linden Research, Inc.
   12.11 + * 
   12.12 + * This library is free software; you can redistribute it and/or
   12.13 + * modify it under the terms of the GNU Lesser General Public
   12.14 + * License as published by the Free Software Foundation;
   12.15 + * version 2.1 of the License only.
   12.16 + * 
   12.17 + * This library is distributed in the hope that it will be useful,
   12.18 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   12.19 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   12.20 + * Lesser General Public License for more details.
   12.21 + * 
   12.22 + * You should have received a copy of the GNU Lesser General Public
   12.23 + * License along with this library; if not, write to the Free Software
   12.24 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
   12.25 + * 
   12.26 + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
   12.27 + * $/LicenseInfo$
   12.28 + */
   12.29 +
   12.30 +#include "linden_common.h"
   12.31 +
   12.32 +#include "../lldeadmantimer.h"
   12.33 +#include "../llsd.h"
   12.34 +#include "../lltimer.h"
   12.35 +
   12.36 +#include "../test/lltut.h"
   12.37 +
   12.38 +// Convert between floating point time deltas and U64 time deltas.
   12.39 +// Reflects an implementation detail inside lldeadmantimer.cpp
   12.40 +
   12.41 +static LLDeadmanTimer::time_type float_time_to_u64(F64 delta)
   12.42 +{
   12.43 +	return LLDeadmanTimer::time_type(delta * gClockFrequency);
   12.44 +}
   12.45 +
   12.46 +static F64 u64_time_to_float(LLDeadmanTimer::time_type delta)
   12.47 +{
   12.48 +	return delta * gClockFrequencyInv;
   12.49 +}
   12.50 +
   12.51 +
   12.52 +namespace tut
   12.53 +{
   12.54 +
   12.55 +struct deadmantimer_test
   12.56 +{
   12.57 +	deadmantimer_test()
   12.58 +		{
   12.59 +			// LLTimer internals updating
   12.60 +			update_clock_frequencies();
   12.61 +		}
   12.62 +};
   12.63 +
   12.64 +typedef test_group<deadmantimer_test> deadmantimer_group_t;
   12.65 +typedef deadmantimer_group_t::object deadmantimer_object_t;
   12.66 +tut::deadmantimer_group_t deadmantimer_instance("LLDeadmanTimer");
   12.67 +
   12.68 +// Basic construction test and isExpired() call
   12.69 +template<> template<>
   12.70 +void deadmantimer_object_t::test<1>()
   12.71 +{
   12.72 +	{
   12.73 +		// Without cpu metrics
   12.74 +		F64 started(42.0), stopped(97.0);
   12.75 +		U64 count(U64L(8));
   12.76 +		LLDeadmanTimer timer(10.0, false);
   12.77 +
   12.78 +		ensure_equals("WOCM isExpired() returns false after ctor()", timer.isExpired(0, started, stopped, count), false);
   12.79 +		ensure_approximately_equals("WOCM t1 - isExpired() does not modify started", started, F64(42.0), 2);
   12.80 +		ensure_approximately_equals("WOCM t1 - isExpired() does not modify stopped", stopped, F64(97.0), 2);
   12.81 +		ensure_equals("WOCM t1 - isExpired() does not modify count", count, U64L(8));
   12.82 +	}
   12.83 +
   12.84 +	{
   12.85 +		// With cpu metrics
   12.86 +		F64 started(42.0), stopped(97.0);
   12.87 +		U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
   12.88 +		LLDeadmanTimer timer(10.0, true);
   12.89 +
   12.90 +		ensure_equals("WCM isExpired() returns false after ctor()", timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu), false);
   12.91 +		ensure_approximately_equals("WCM t1 - isExpired() does not modify started", started, F64(42.0), 2);
   12.92 +		ensure_approximately_equals("WCM t1 - isExpired() does not modify stopped", stopped, F64(97.0), 2);
   12.93 +		ensure_equals("WCM t1 - isExpired() does not modify count", count, U64L(8));
   12.94 +		ensure_equals("WCM t1 - isExpired() does not modify user_cpu", user_cpu, U64L(29000));
   12.95 +		ensure_equals("WCM t1 - isExpired() does not modify sys_cpu", sys_cpu, U64L(57000));
   12.96 +	}
   12.97 +}
   12.98 +
   12.99 +
  12.100 +// Construct with zero horizon - not useful generally but will be useful in testing
  12.101 +template<> template<>
  12.102 +void deadmantimer_object_t::test<2>()
  12.103 +{
  12.104 +	{
  12.105 +		// Without cpu metrics
  12.106 +		F64 started(42.0), stopped(97.0);
  12.107 +		U64 count(U64L(8));
  12.108 +		LLDeadmanTimer timer(0.0, false);			// Zero is pre-expired
  12.109 +		
  12.110 +		ensure_equals("WOCM isExpired() still returns false with 0.0 time ctor()",
  12.111 +					  timer.isExpired(0, started, stopped, count), false);
  12.112 +	}
  12.113 +
  12.114 +	{
  12.115 +		// With cpu metrics
  12.116 +		F64 started(42.0), stopped(97.0);
  12.117 +		U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
  12.118 +		LLDeadmanTimer timer(0.0, true);			// Zero is pre-expired
  12.119 +		
  12.120 +		ensure_equals("WCM isExpired() still returns false with 0.0 time ctor()",
  12.121 +					  timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu), false);
  12.122 +	}
  12.123 +}
  12.124 +
  12.125 +
  12.126 +// "pre-expired" timer - starting a timer with a 0.0 horizon will result in
  12.127 +// expiration on first test.
  12.128 +template<> template<>
  12.129 +void deadmantimer_object_t::test<3>()
  12.130 +{
  12.131 +	{
  12.132 +		// Without cpu metrics
  12.133 +		F64 started(42.0), stopped(97.0);
  12.134 +		U64 count(U64L(8));
  12.135 +		LLDeadmanTimer timer(0.0, false);
  12.136 +
  12.137 +		timer.start(0);
  12.138 +		ensure_equals("WOCM isExpired() returns true with 0.0 horizon time",
  12.139 +					  timer.isExpired(0, started, stopped, count), true);
  12.140 + 		ensure_approximately_equals("WOCM expired timer with no bell ringing has stopped == started", started, stopped, 8);
  12.141 +	}
  12.142 +	{
  12.143 +		// With cpu metrics
  12.144 +		F64 started(42.0), stopped(97.0);
  12.145 +		U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
  12.146 +		LLDeadmanTimer timer(0.0, true);
  12.147 +
  12.148 +		timer.start(0);
  12.149 +		ensure_equals("WCM isExpired() returns true with 0.0 horizon time",
  12.150 +					  timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu), true);
  12.151 +		ensure_approximately_equals("WCM expired timer with no bell ringing has stopped == started", started, stopped, 8);
  12.152 +	}
  12.153 +}
  12.154 +
  12.155 +
  12.156 +// "pre-expired" timer - bell rings are ignored as we're already expired.
  12.157 +template<> template<>
  12.158 +void deadmantimer_object_t::test<4>()
  12.159 +{
  12.160 +	{
  12.161 +		// Without cpu metrics
  12.162 +		F64 started(42.0), stopped(97.0);
  12.163 +		U64 count(U64L(8));
  12.164 +		LLDeadmanTimer timer(0.0, false);
  12.165 +	
  12.166 +		timer.start(0);
  12.167 +		timer.ringBell(LLDeadmanTimer::getNow() + float_time_to_u64(1000.0), 1);
  12.168 +		ensure_equals("WOCM isExpired() returns true with 0.0 horizon time after bell ring",
  12.169 +					  timer.isExpired(0, started, stopped, count), true);
  12.170 +		ensure_approximately_equals("WOCM ringBell has no impact on expired timer leaving stopped == started", started, stopped, 8);
  12.171 +	}
  12.172 +	{
  12.173 +		// With cpu metrics
  12.174 +		F64 started(42.0), stopped(97.0);
  12.175 +		U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
  12.176 +		LLDeadmanTimer timer(0.0, true);
  12.177 +	
  12.178 +		timer.start(0);
  12.179 +		timer.ringBell(LLDeadmanTimer::getNow() + float_time_to_u64(1000.0), 1);
  12.180 +		ensure_equals("WCM isExpired() returns true with 0.0 horizon time after bell ring",
  12.181 +					  timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu), true);
  12.182 +		ensure_approximately_equals("WCM ringBell has no impact on expired timer leaving stopped == started", started, stopped, 8);
  12.183 +	}
  12.184 +}
  12.185 +
  12.186 +
  12.187 +// start(0) test - unexpired timer reports unexpired
  12.188 +template<> template<>
  12.189 +void deadmantimer_object_t::test<5>()
  12.190 +{
  12.191 +	{
  12.192 +		// Without cpu metrics
  12.193 +		F64 started(42.0), stopped(97.0);
  12.194 +		U64 count(U64L(8));
  12.195 +		LLDeadmanTimer timer(10.0, false);
  12.196 +	
  12.197 +		timer.start(0);
  12.198 +		ensure_equals("WOCM isExpired() returns false after starting with 10.0 horizon time",
  12.199 +					  timer.isExpired(0, started, stopped, count), false);
  12.200 +		ensure_approximately_equals("WOCM t5 - isExpired() does not modify started", started, F64(42.0), 2);
  12.201 +		ensure_approximately_equals("WOCM t5 - isExpired() does not modify stopped", stopped, F64(97.0), 2);
  12.202 +		ensure_equals("WOCM t5 - isExpired() does not modify count", count, U64L(8));
  12.203 +	}
  12.204 +	{
  12.205 +		// With cpu metrics
  12.206 +		F64 started(42.0), stopped(97.0);
  12.207 +		U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
  12.208 +		LLDeadmanTimer timer(10.0, true);
  12.209 +	
  12.210 +		timer.start(0);
  12.211 +		ensure_equals("WCM isExpired() returns false after starting with 10.0 horizon time",
  12.212 +					  timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu), false);
  12.213 +		ensure_approximately_equals("WCM t5 - isExpired() does not modify started", started, F64(42.0), 2);
  12.214 +		ensure_approximately_equals("WCM t5 - isExpired() does not modify stopped", stopped, F64(97.0), 2);
  12.215 +		ensure_equals("WCM t5 - isExpired() does not modify count", count, U64L(8));
  12.216 +		ensure_equals("WCM t5 - isExpired() does not modify user_cpu", user_cpu, U64L(29000));
  12.217 +		ensure_equals("WCM t5 - isExpired() does not modify sys_cpu", sys_cpu, U64L(57000));
  12.218 +	}
  12.219 +}
  12.220 +
  12.221 +
  12.222 +// start() test - start in the past but not beyond 1 horizon
  12.223 +template<> template<>
  12.224 +void deadmantimer_object_t::test<6>()
  12.225 +{
  12.226 +	{
  12.227 +		// Without cpu metrics
  12.228 +		F64 started(42.0), stopped(97.0);
  12.229 +		U64 count(U64L(8));
  12.230 +		LLDeadmanTimer timer(10.0, false);
  12.231 +
  12.232 +		// Would like to do subtraction on current time but can't because
  12.233 +		// the implementation on Windows is zero-based.  We wrap around
  12.234 +		// the backside resulting in a large U64 number.
  12.235 +	
  12.236 +		LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow());
  12.237 +		LLDeadmanTimer::time_type now(the_past + float_time_to_u64(5.0));
  12.238 +		timer.start(the_past);
  12.239 +		ensure_equals("WOCM t6 - isExpired() returns false with 10.0 horizon time starting 5.0 in past",
  12.240 +					  timer.isExpired(now, started, stopped, count), false);
  12.241 +		ensure_approximately_equals("WOCM t6 - isExpired() does not modify started", started, F64(42.0), 2);
  12.242 +		ensure_approximately_equals("WOCM t6 - isExpired() does not modify stopped", stopped, F64(97.0), 2);
  12.243 +		ensure_equals("WOCM t6 - isExpired() does not modify count", count, U64L(8));
  12.244 +	}
  12.245 +	{
  12.246 +		// With cpu metrics
  12.247 +		F64 started(42.0), stopped(97.0);
  12.248 +		U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
  12.249 +		LLDeadmanTimer timer(10.0, true);
  12.250 +
  12.251 +		// Would like to do subtraction on current time but can't because
  12.252 +		// the implementation on Windows is zero-based.  We wrap around
  12.253 +		// the backside resulting in a large U64 number.
  12.254 +	
  12.255 +		LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow());
  12.256 +		LLDeadmanTimer::time_type now(the_past + float_time_to_u64(5.0));
  12.257 +		timer.start(the_past);
  12.258 +		ensure_equals("WCM t6 - isExpired() returns false with 10.0 horizon time starting 5.0 in past",
  12.259 +					  timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false);
  12.260 +		ensure_approximately_equals("WCM t6 - isExpired() does not modify started", started, F64(42.0), 2);
  12.261 +		ensure_approximately_equals("WCM t6 - isExpired() does not modify stopped", stopped, F64(97.0), 2);
  12.262 +		ensure_equals("t6 - isExpired() does not modify count", count, U64L(8));
  12.263 +		ensure_equals("WCM t6 - isExpired() does not modify user_cpu", user_cpu, U64L(29000));
  12.264 +		ensure_equals("WCM t6 - isExpired() does not modify sys_cpu", sys_cpu, U64L(57000));
  12.265 +	}
  12.266 +}
  12.267 +
  12.268 +
  12.269 +// start() test - start in the past but well beyond 1 horizon
  12.270 +template<> template<>
  12.271 +void deadmantimer_object_t::test<7>()
  12.272 +{
  12.273 +	{
  12.274 +		// Without cpu metrics
  12.275 +		F64 started(42.0), stopped(97.0);
  12.276 +		U64 count(U64L(8));
  12.277 +		LLDeadmanTimer timer(10.0, false);
  12.278 +		
  12.279 +		// Would like to do subtraction on current time but can't because
  12.280 +		// the implementation on Windows is zero-based.  We wrap around
  12.281 +		// the backside resulting in a large U64 number.
  12.282 +	
  12.283 +		LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow());
  12.284 +		LLDeadmanTimer::time_type now(the_past + float_time_to_u64(20.0));
  12.285 +		timer.start(the_past);
  12.286 +		ensure_equals("WOCM t7 - isExpired() returns true with 10.0 horizon time starting 20.0 in past",
  12.287 +					  timer.isExpired(now,started, stopped, count), true);
  12.288 +		ensure_approximately_equals("WOCM t7 - starting before horizon still gives equal started / stopped", started, stopped, 8);
  12.289 +	}
  12.290 +	{
  12.291 +		// With cpu metrics
  12.292 +		F64 started(42.0), stopped(97.0);
  12.293 +		U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
  12.294 +		LLDeadmanTimer timer(10.0, true);
  12.295 +		
  12.296 +		// Would like to do subtraction on current time but can't because
  12.297 +		// the implementation on Windows is zero-based.  We wrap around
  12.298 +		// the backside resulting in a large U64 number.
  12.299 +	
  12.300 +		LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow());
  12.301 +		LLDeadmanTimer::time_type now(the_past + float_time_to_u64(20.0));
  12.302 +		timer.start(the_past);
  12.303 +		ensure_equals("WCM t7 - isExpired() returns true with 10.0 horizon time starting 20.0 in past",
  12.304 +					  timer.isExpired(now,started, stopped, count, user_cpu, sys_cpu), true);
  12.305 +		ensure_approximately_equals("WOCM t7 - starting before horizon still gives equal started / stopped", started, stopped, 8);
  12.306 +	}
  12.307 +}
  12.308 +
  12.309 +
  12.310 +// isExpired() test - results are read-once.  Probes after first true are false.
  12.311 +template<> template<>
  12.312 +void deadmantimer_object_t::test<8>()
  12.313 +{
  12.314 +	{
  12.315 +		// Without cpu metrics
  12.316 +		F64 started(42.0), stopped(97.0);
  12.317 +		U64 count(U64L(8));
  12.318 +		LLDeadmanTimer timer(10.0, false);
  12.319 +
  12.320 +		// Would like to do subtraction on current time but can't because
  12.321 +		// the implementation on Windows is zero-based.  We wrap around
  12.322 +		// the backside resulting in a large U64 number.
  12.323 +	
  12.324 +		LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow());
  12.325 +		LLDeadmanTimer::time_type now(the_past + float_time_to_u64(20.0));
  12.326 +		timer.start(the_past);
  12.327 +		ensure_equals("WOCM t8 - isExpired() returns true with 10.0 horizon time starting 20.0 in past",
  12.328 +					  timer.isExpired(now, started, stopped, count), true);
  12.329 +		
  12.330 +		started = 42.0;
  12.331 +		stopped = 97.0;
  12.332 +		count = U64L(8);
  12.333 +		ensure_equals("WOCM t8 - second isExpired() returns false after true",
  12.334 +					  timer.isExpired(now, started, stopped, count), false);
  12.335 +		ensure_approximately_equals("WOCM t8 - 2nd isExpired() does not modify started", started, F64(42.0), 2);
  12.336 +		ensure_approximately_equals("WOCM t8 - 2nd isExpired() does not modify stopped", stopped, F64(97.0), 2);
  12.337 +		ensure_equals("WOCM t8 - 2nd isExpired() does not modify count", count, U64L(8));
  12.338 +	}
  12.339 +	{
  12.340 +		// With cpu metrics
  12.341 +		F64 started(42.0), stopped(97.0);
  12.342 +		U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
  12.343 +		LLDeadmanTimer timer(10.0, true);
  12.344 +
  12.345 +		// Would like to do subtraction on current time but can't because
  12.346 +		// the implementation on Windows is zero-based.  We wrap around
  12.347 +		// the backside resulting in a large U64 number.
  12.348 +	
  12.349 +		LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow());
  12.350 +		LLDeadmanTimer::time_type now(the_past + float_time_to_u64(20.0));
  12.351 +		timer.start(the_past);
  12.352 +		ensure_equals("WCM t8 - isExpired() returns true with 10.0 horizon time starting 20.0 in past",
  12.353 +					  timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), true);
  12.354 +		
  12.355 +		started = 42.0;
  12.356 +		stopped = 97.0;
  12.357 +		count = U64L(8);
  12.358 +		user_cpu = 29000;
  12.359 +		sys_cpu = 57000;
  12.360 +		ensure_equals("WCM t8 - second isExpired() returns false after true",
  12.361 +					  timer.isExpired(now, started, stopped, count), false);
  12.362 +		ensure_approximately_equals("WCM t8 - 2nd isExpired() does not modify started", started, F64(42.0), 2);
  12.363 +		ensure_approximately_equals("WCM t8 - 2nd isExpired() does not modify stopped", stopped, F64(97.0), 2);
  12.364 +		ensure_equals("WCM t8 - 2nd isExpired() does not modify count", count, U64L(8));
  12.365 +		ensure_equals("WCM t8 - 2nd isExpired() does not modify user_cpu", user_cpu, U64L(29000));
  12.366 +		ensure_equals("WCM t8 - 2nd isExpired() does not modify sys_cpu", sys_cpu, U64L(57000));
  12.367 +	}
  12.368 +}
  12.369 +
  12.370 +
  12.371 +// ringBell() test - see that we can keep a timer from expiring
  12.372 +template<> template<>
  12.373 +void deadmantimer_object_t::test<9>()
  12.374 +{
  12.375 +	{
  12.376 +		// Without cpu metrics
  12.377 +		F64 started(42.0), stopped(97.0);
  12.378 +		U64 count(U64L(8));
  12.379 +		LLDeadmanTimer timer(5.0, false);
  12.380 +
  12.381 +		LLDeadmanTimer::time_type now(LLDeadmanTimer::getNow());
  12.382 +		F64 real_start(u64_time_to_float(now));
  12.383 +		timer.start(0);
  12.384 +
  12.385 +		now += float_time_to_u64(1.0);
  12.386 +		timer.ringBell(now, 1);
  12.387 +		now += float_time_to_u64(1.0);
  12.388 +		timer.ringBell(now, 1);
  12.389 +		now += float_time_to_u64(1.0);
  12.390 +		timer.ringBell(now, 1);
  12.391 +		now += float_time_to_u64(1.0);
  12.392 +		timer.ringBell(now, 1);
  12.393 +		now += float_time_to_u64(1.0);
  12.394 +		timer.ringBell(now, 1);
  12.395 +		now += float_time_to_u64(1.0);
  12.396 +		timer.ringBell(now, 1);
  12.397 +		now += float_time_to_u64(1.0);
  12.398 +		timer.ringBell(now, 1);
  12.399 +		now += float_time_to_u64(1.0);
  12.400 +		timer.ringBell(now, 1);
  12.401 +		now += float_time_to_u64(1.0);
  12.402 +		timer.ringBell(now, 1);
  12.403 +		now += float_time_to_u64(1.0);
  12.404 +		timer.ringBell(now, 1);
  12.405 +		ensure_equals("WOCM t9 - 5.0 horizon timer has not timed out after 10 1-second bell rings",
  12.406 +					  timer.isExpired(now, started, stopped, count), false);
  12.407 +		F64 last_good_ring(u64_time_to_float(now));
  12.408 +
  12.409 +		// Jump forward and expire
  12.410 +		now += float_time_to_u64(10.0);
  12.411 +		ensure_equals("WOCM t9 - 5.0 horizon timer expires on 10-second jump",
  12.412 +					  timer.isExpired(now, started, stopped, count), true);
  12.413 +		ensure_approximately_equals("WOCM t9 - started matches start() time", started, real_start, 4);
  12.414 +		ensure_approximately_equals("WOCM t9 - stopped matches last ringBell() time", stopped, last_good_ring, 4);
  12.415 +		ensure_equals("WOCM t9 - 10 good ringBell()s", count, U64L(10));
  12.416 +		ensure_equals("WOCM t9 - single read only", timer.isExpired(now, started, stopped, count), false);
  12.417 +	}
  12.418 +	{
  12.419 +		// With cpu metrics
  12.420 +		F64 started(42.0), stopped(97.0);
  12.421 +		U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
  12.422 +		LLDeadmanTimer timer(5.0, true);
  12.423 +
  12.424 +		LLDeadmanTimer::time_type now(LLDeadmanTimer::getNow());
  12.425 +		F64 real_start(u64_time_to_float(now));
  12.426 +		timer.start(0);
  12.427 +
  12.428 +		now += float_time_to_u64(1.0);
  12.429 +		timer.ringBell(now, 1);
  12.430 +		now += float_time_to_u64(1.0);
  12.431 +		timer.ringBell(now, 1);
  12.432 +		now += float_time_to_u64(1.0);
  12.433 +		timer.ringBell(now, 1);
  12.434 +		now += float_time_to_u64(1.0);
  12.435 +		timer.ringBell(now, 1);
  12.436 +		now += float_time_to_u64(1.0);
  12.437 +		timer.ringBell(now, 1);
  12.438 +		now += float_time_to_u64(1.0);
  12.439 +		timer.ringBell(now, 1);
  12.440 +		now += float_time_to_u64(1.0);
  12.441 +		timer.ringBell(now, 1);
  12.442 +		now += float_time_to_u64(1.0);
  12.443 +		timer.ringBell(now, 1);
  12.444 +		now += float_time_to_u64(1.0);
  12.445 +		timer.ringBell(now, 1);
  12.446 +		now += float_time_to_u64(1.0);
  12.447 +		timer.ringBell(now, 1);
  12.448 +		ensure_equals("WCM t9 - 5.0 horizon timer has not timed out after 10 1-second bell rings",
  12.449 +					  timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false);
  12.450 +		F64 last_good_ring(u64_time_to_float(now));
  12.451 +
  12.452 +		// Jump forward and expire
  12.453 +		now += float_time_to_u64(10.0);
  12.454 +		ensure_equals("WCM t9 - 5.0 horizon timer expires on 10-second jump",
  12.455 +					  timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), true);
  12.456 +		ensure_approximately_equals("WCM t9 - started matches start() time", started, real_start, 4);
  12.457 +		ensure_approximately_equals("WCM t9 - stopped matches last ringBell() time", stopped, last_good_ring, 4);
  12.458 +		ensure_equals("WCM t9 - 10 good ringBell()s", count, U64L(10));
  12.459 +		ensure_equals("WCM t9 - single read only", timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false);
  12.460 +	}
  12.461 +}
  12.462 +
  12.463 +
  12.464 +// restart after expiration test - verify that restarts behave well
  12.465 +template<> template<>
  12.466 +void deadmantimer_object_t::test<10>()
  12.467 +{
  12.468 +	{
  12.469 +		// Without cpu metrics
  12.470 +		F64 started(42.0), stopped(97.0);
  12.471 +		U64 count(U64L(8));
  12.472 +		LLDeadmanTimer timer(5.0, false);
  12.473 +
  12.474 +		LLDeadmanTimer::time_type now(LLDeadmanTimer::getNow());
  12.475 +		F64 real_start(u64_time_to_float(now));
  12.476 +		timer.start(0);
  12.477 +
  12.478 +		now += float_time_to_u64(1.0);
  12.479 +		timer.ringBell(now, 1);
  12.480 +		now += float_time_to_u64(1.0);
  12.481 +		timer.ringBell(now, 1);
  12.482 +		now += float_time_to_u64(1.0);
  12.483 +		timer.ringBell(now, 1);
  12.484 +		now += float_time_to_u64(1.0);
  12.485 +		timer.ringBell(now, 1);
  12.486 +		now += float_time_to_u64(1.0);
  12.487 +		timer.ringBell(now, 1);
  12.488 +		now += float_time_to_u64(1.0);
  12.489 +		timer.ringBell(now, 1);
  12.490 +		now += float_time_to_u64(1.0);
  12.491 +		timer.ringBell(now, 1);
  12.492 +		now += float_time_to_u64(1.0);
  12.493 +		timer.ringBell(now, 1);
  12.494 +		now += float_time_to_u64(1.0);
  12.495 +		timer.ringBell(now, 1);
  12.496 +		now += float_time_to_u64(1.0);
  12.497 +		timer.ringBell(now, 1);
  12.498 +		ensure_equals("WOCM t10 - 5.0 horizon timer has not timed out after 10 1-second bell rings",
  12.499 +					  timer.isExpired(now, started, stopped, count), false);
  12.500 +		F64 last_good_ring(u64_time_to_float(now));
  12.501 +
  12.502 +		// Jump forward and expire
  12.503 +		now += float_time_to_u64(10.0);
  12.504 +		ensure_equals("WOCM t10 - 5.0 horizon timer expires on 10-second jump",
  12.505 +					  timer.isExpired(now, started, stopped, count), true);
  12.506 +		ensure_approximately_equals("WOCM t10 - started matches start() time", started, real_start, 4);
  12.507 +		ensure_approximately_equals("WOCM t10 - stopped matches last ringBell() time", stopped, last_good_ring, 4);
  12.508 +		ensure_equals("WOCM t10 - 10 good ringBell()s", count, U64L(10));
  12.509 +		ensure_equals("WOCM t10 - single read only", timer.isExpired(now, started, stopped, count), false);
  12.510 +		
  12.511 +		// Jump forward and restart
  12.512 +		now += float_time_to_u64(1.0);
  12.513 +		real_start = u64_time_to_float(now);
  12.514 +		timer.start(now);
  12.515 +		
  12.516 +		// Run a modified bell ring sequence
  12.517 +		now += float_time_to_u64(1.0);
  12.518 +		timer.ringBell(now, 1);
  12.519 +		now += float_time_to_u64(1.0);
  12.520 +		timer.ringBell(now, 1);
  12.521 +		now += float_time_to_u64(1.0);
  12.522 +		timer.ringBell(now, 1);
  12.523 +		now += float_time_to_u64(1.0);
  12.524 +		timer.ringBell(now, 1);
  12.525 +		now += float_time_to_u64(1.0);
  12.526 +		timer.ringBell(now, 1);
  12.527 +		now += float_time_to_u64(1.0);
  12.528 +		timer.ringBell(now, 1);
  12.529 +		now += float_time_to_u64(1.0);
  12.530 +		timer.ringBell(now, 1);
  12.531 +		now += float_time_to_u64(1.0);
  12.532 +		timer.ringBell(now, 1);
  12.533 +		ensure_equals("WOCM t10 - 5.0 horizon timer has not timed out after 8 1-second bell rings",
  12.534 +					  timer.isExpired(now, started, stopped, count), false);
  12.535 +		last_good_ring = u64_time_to_float(now);
  12.536 +
  12.537 +		// Jump forward and expire
  12.538 +		now += float_time_to_u64(10.0);
  12.539 +		ensure_equals("WOCM t10 - 5.0 horizon timer expires on 8-second jump",
  12.540 +					  timer.isExpired(now, started, stopped, count), true);
  12.541 +		ensure_approximately_equals("WOCM t10 - 2nd started matches start() time", started, real_start, 4);
  12.542 +		ensure_approximately_equals("WOCM t10 - 2nd stopped matches last ringBell() time", stopped, last_good_ring, 4);
  12.543 +		ensure_equals("WOCM t10 - 8 good ringBell()s", count, U64L(8));
  12.544 +		ensure_equals("WOCM t10 - single read only - 2nd start",
  12.545 +					  timer.isExpired(now, started, stopped, count), false);
  12.546 +	}
  12.547 +	{
  12.548 +		// With cpu metrics
  12.549 +		F64 started(42.0), stopped(97.0);
  12.550 +		U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
  12.551 +
  12.552 +		LLDeadmanTimer timer(5.0, true);
  12.553 +
  12.554 +		LLDeadmanTimer::time_type now(LLDeadmanTimer::getNow());
  12.555 +		F64 real_start(u64_time_to_float(now));
  12.556 +		timer.start(0);
  12.557 +
  12.558 +		now += float_time_to_u64(1.0);
  12.559 +		timer.ringBell(now, 1);
  12.560 +		now += float_time_to_u64(1.0);
  12.561 +		timer.ringBell(now, 1);
  12.562 +		now += float_time_to_u64(1.0);
  12.563 +		timer.ringBell(now, 1);
  12.564 +		now += float_time_to_u64(1.0);
  12.565 +		timer.ringBell(now, 1);
  12.566 +		now += float_time_to_u64(1.0);
  12.567 +		timer.ringBell(now, 1);
  12.568 +		now += float_time_to_u64(1.0);
  12.569 +		timer.ringBell(now, 1);
  12.570 +		now += float_time_to_u64(1.0);
  12.571 +		timer.ringBell(now, 1);
  12.572 +		now += float_time_to_u64(1.0);
  12.573 +		timer.ringBell(now, 1);
  12.574 +		now += float_time_to_u64(1.0);
  12.575 +		timer.ringBell(now, 1);
  12.576 +		now += float_time_to_u64(1.0);
  12.577 +		timer.ringBell(now, 1);
  12.578 +		ensure_equals("WCM t10 - 5.0 horizon timer has not timed out after 10 1-second bell rings",
  12.579 +					  timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false);
  12.580 +		F64 last_good_ring(u64_time_to_float(now));
  12.581 +
  12.582 +		// Jump forward and expire
  12.583 +		now += float_time_to_u64(10.0);
  12.584 +		ensure_equals("WCM t10 - 5.0 horizon timer expires on 10-second jump",
  12.585 +					  timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), true);
  12.586 +		ensure_approximately_equals("WCM t10 - started matches start() time", started, real_start, 4);
  12.587 +		ensure_approximately_equals("WCM t10 - stopped matches last ringBell() time", stopped, last_good_ring, 4);
  12.588 +		ensure_equals("WCM t10 - 10 good ringBell()s", count, U64L(10));
  12.589 +		ensure_equals("WCM t10 - single read only", timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false);
  12.590 +		
  12.591 +		// Jump forward and restart
  12.592 +		now += float_time_to_u64(1.0);
  12.593 +		real_start = u64_time_to_float(now);
  12.594 +		timer.start(now);
  12.595 +		
  12.596 +		// Run a modified bell ring sequence
  12.597 +		now += float_time_to_u64(1.0);
  12.598 +		timer.ringBell(now, 1);
  12.599 +		now += float_time_to_u64(1.0);
  12.600 +		timer.ringBell(now, 1);
  12.601 +		now += float_time_to_u64(1.0);
  12.602 +		timer.ringBell(now, 1);
  12.603 +		now += float_time_to_u64(1.0);
  12.604 +		timer.ringBell(now, 1);
  12.605 +		now += float_time_to_u64(1.0);
  12.606 +		timer.ringBell(now, 1);
  12.607 +		now += float_time_to_u64(1.0);
  12.608 +		timer.ringBell(now, 1);
  12.609 +		now += float_time_to_u64(1.0);
  12.610 +		timer.ringBell(now, 1);
  12.611 +		now += float_time_to_u64(1.0);
  12.612 +		timer.ringBell(now, 1);
  12.613 +		ensure_equals("WCM t10 - 5.0 horizon timer has not timed out after 8 1-second bell rings",
  12.614 +					  timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false);
  12.615 +		last_good_ring = u64_time_to_float(now);
  12.616 +
  12.617 +		// Jump forward and expire
  12.618 +		now += float_time_to_u64(10.0);
  12.619 +		ensure_equals("WCM t10 - 5.0 horizon timer expires on 8-second jump",
  12.620 +					  timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), true);
  12.621 +		ensure_approximately_equals("WCM t10 - 2nd started matches start() time", started, real_start, 4);
  12.622 +		ensure_approximately_equals("WCM t10 - 2nd stopped matches last ringBell() time", stopped, last_good_ring, 4);
  12.623 +		ensure_equals("WCM t10 - 8 good ringBell()s", count, U64L(8));
  12.624 +		ensure_equals("WCM t10 - single read only - 2nd start",
  12.625 +					  timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false);
  12.626 +	}
  12.627 +}
  12.628 +
  12.629 +
  12.630 +
  12.631 +} // end namespace tut
    13.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    13.2 +++ b/indra/llcommon/tests/llprocinfo_test.cpp	Wed Feb 12 18:58:40 2014 -0800
    13.3 @@ -0,0 +1,91 @@
    13.4 +/** 
    13.5 + * @file llprocinfo_test.cpp
    13.6 + * @brief Tests for the LLProcInfo class.
    13.7 + *
    13.8 + * $LicenseInfo:firstyear=2013&license=viewerlgpl$
    13.9 + * Second Life Viewer Source Code
   13.10 + * Copyright (C) 2013, Linden Research, Inc.
   13.11 + * 
   13.12 + * This library is free software; you can redistribute it and/or
   13.13 + * modify it under the terms of the GNU Lesser General Public
   13.14 + * License as published by the Free Software Foundation;
   13.15 + * version 2.1 of the License only.
   13.16 + * 
   13.17 + * This library is distributed in the hope that it will be useful,
   13.18 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   13.19 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   13.20 + * Lesser General Public License for more details.
   13.21 + * 
   13.22 + * You should have received a copy of the GNU Lesser General Public
   13.23 + * License along with this library; if not, write to the Free Software
   13.24 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
   13.25 + * 
   13.26 + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
   13.27 + * $/LicenseInfo$
   13.28 + */
   13.29 +
   13.30 +#include "linden_common.h"
   13.31 +
   13.32 +#include "../llprocinfo.h"
   13.33 +
   13.34 +#include "../test/lltut.h"
   13.35 +#include "../lltimer.h"
   13.36 +
   13.37 +
   13.38 +static const LLProcInfo::time_type bad_user(289375U), bad_system(275U);
   13.39 +
   13.40 +
   13.41 +namespace tut
   13.42 +{
   13.43 +
   13.44 +struct procinfo_test
   13.45 +{
   13.46 +	procinfo_test()
   13.47 +		{
   13.48 +		}
   13.49 +};
   13.50 +
   13.51 +typedef test_group<procinfo_test> procinfo_group_t;
   13.52 +typedef procinfo_group_t::object procinfo_object_t;
   13.53 +tut::procinfo_group_t procinfo_instance("LLProcInfo");
   13.54 +
   13.55 +
   13.56 +// Basic invocation works
   13.57 +template<> template<>
   13.58 +void procinfo_object_t::test<1>()
   13.59 +{
   13.60 +	LLProcInfo::time_type user(bad_user), system(bad_system);
   13.61 +
   13.62 +	set_test_name("getCPUUsage() basic function");
   13.63 +
   13.64 +	LLProcInfo::getCPUUsage(user, system);
   13.65 +	
   13.66 +	ensure_not_equals("getCPUUsage() writes to its user argument", user, bad_user);
   13.67 +	ensure_not_equals("getCPUUsage() writes to its system argument", system, bad_system);
   13.68 +}
   13.69 +
   13.70 +
   13.71 +// Time increases
   13.72 +template<> template<>
   13.73 +void procinfo_object_t::test<2>()
   13.74 +{
   13.75 +	LLProcInfo::time_type user(bad_user), system(bad_system);
   13.76 +	LLProcInfo::time_type user2(bad_user), system2(bad_system);
   13.77 +
   13.78 +	set_test_name("getCPUUsage() increases over time");
   13.79 +
   13.80 +	LLProcInfo::getCPUUsage(user, system);
   13.81 +	
   13.82 +	for (int i(0); i < 100000; ++i)
   13.83 +	{
   13.84 +		ms_sleep(0);
   13.85 +	}
   13.86 +	
   13.87 +	LLProcInfo::getCPUUsage(user2, system2);
   13.88 +
   13.89 +	ensure_equals("getCPUUsage() user value doesn't decrease over time", user2 >= user, true);
   13.90 +	ensure_equals("getCPUUsage() system value doesn't decrease over time", system2 >= system, true);
   13.91 +}
   13.92 +
   13.93 +
   13.94 +} // end namespace tut
    14.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    14.2 +++ b/indra/llcorehttp/README.Linden	Wed Feb 12 18:58:40 2014 -0800
    14.3 @@ -0,0 +1,671 @@
    14.4 +
    14.5 +
    14.6 +
    14.7 +1.  HTTP Fetching in 15 Minutes
    14.8 +
    14.9 +    Let's start with a trivial working example.  You'll need a throwaway
   14.10 +    build of the viewer.  And we'll use indra/newview/llappviewer.cpp as
   14.11 +    the host module for these hacks.
   14.12 +
   14.13 +    First, add some headers:
   14.14 +
   14.15 +
   14.16 +        #include "httpcommon.h"
   14.17 +        #include "httprequest.h"
   14.18 +        #include "httphandler.h"
   14.19 +
   14.20 +
   14.21 +    You'll need to derive a class from HttpHandler (not HttpHandle).
   14.22 +    This is used to deliver notifications of HTTP completion to your
   14.23 +    code.  Place it near the top, before LLDeferredTaskList, say:
   14.24 +
   14.25 +
   14.26 +        class MyHandler : public LLCore::HttpHandler
   14.27 +        {
   14.28 +        public:
   14.29 +            MyHandler()
   14.30 +            : LLCore::HttpHandler()
   14.31 +            {}
   14.32 +
   14.33 +            virtual void onCompleted(LLCore::HttpHandle /* handle */,
   14.34 +                                     LLCore::HttpResponse * /* response */)
   14.35 +            {
   14.36 +                LL_INFOS("Hack") << "It is happening again." << LL_ENDL;
   14.37 +
   14.38 +                delete this;    // Last statement
   14.39 +            }
   14.40 +        };
   14.41 +
   14.42 +
   14.43 +    Add some statics up there as well:
   14.44 +
   14.45 +
   14.46 +        // Our request object.  Allocate during initialiation.
   14.47 +        static LLCore::HttpRequest * my_request(NULL);
   14.48 +
   14.49 +        // The policy class for HTTP traffic.
   14.50 +        // Use HttpRequest::DEFAULT_POLICY_ID, but DO NOT SHIP WITH THIS VALUE!!
   14.51 +        static LLCore::HttpRequest::policy_t my_policy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
   14.52 +
   14.53 +        // Priority for HTTP requests.  Use 0U.
   14.54 +        static LLCore::HttpRequest::priority_t my_priority(0U);
   14.55 +
   14.56 +
   14.57 +    In LLAppViewer::init() after mAppCoreHttp.init(), create a request object:
   14.58 +
   14.59 +
   14.60 +        my_request = new LLCore::HttpRequest();
   14.61 +
   14.62 +
   14.63 +    In LLAppViewer::mainLoop(), just before entering the while loop,
   14.64 +    we'll kick off one HTTP request:
   14.65 +
   14.66 +
   14.67 +        // Construct a handler object (we'll use the heap this time):
   14.68 +        MyHandler * my_handler = new MyHandler;
   14.69 +
   14.70 +        // Issue a GET request to 'http://www.example.com/' kicking off
   14.71 +        // all the I/O, retry logic, etc.
   14.72 +        LLCore::HttpHandle handle;
   14.73 +        handle = my_request->requestGet(my_policy,
   14.74 +                                        my_priority,
   14.75 +                                        "http://www.example.com/",
   14.76 +                                        NULL,
   14.77 +                                        NULL,
   14.78 +                                        my_handler);
   14.79 +        if (LLCORE_HTTP_HANDLE_INVALID == handle)
   14.80 +        {
   14.81 +            LL_WARNS("Hack") << "Failed to launch HTTP request.  Try again."
   14.82 +                             << LL_ENDL;
   14.83 +        }
   14.84 +
   14.85 +
   14.86 +    Finally, arrange to periodically call update() on the request object
   14.87 +    to find out when the request completes.  This will be done by
   14.88 +    calling the onCompleted() method with status information and
   14.89 +    response data from the HTTP operation.  Add this to the
   14.90 +    LLAppViewer::idle() method after the ping:
   14.91 +
   14.92 +
   14.93 +        my_request->update(0);
   14.94 +
   14.95 +
   14.96 +    That's it.  Build it, run it and watch the log file.  You should get
   14.97 +    the "It is happening again." message indicating that the HTTP
   14.98 +    operation completed in some manner.
   14.99 +
  14.100 +
  14.101 +2.  What Does All That Mean
  14.102 +
  14.103 +    MyHandler/HttpHandler.  This class replaces the Responder-style in
  14.104 +    legacy code.  One method is currently defined.  It is used for all
  14.105 +    request completions, successful or failed:
  14.106 +
  14.107 +
  14.108 +        void onCompleted(LLCore::HttpHandle /* handle */,
  14.109 +                         LLCore::HttpResponse * /* response */);
  14.110 +
  14.111 +
  14.112 +    The onCompleted() method is invoked as a callback during calls to
  14.113 +    HttpRequest::update().  All I/O is completed asynchronously in
  14.114 +    another thread.  But notifications are polled by calling update()
  14.115 +    and invoking a handler for completed requests.
  14.116 +
  14.117 +    In this example, the invocation also deletes the handler (which is
  14.118 +    never referenced by the llcorehttp code again).  But other
  14.119 +    allocation models are possible including handlers shared by many
  14.120 +    requests, stack-based handlers and handlers mixed in with other,
  14.121 +    unrelated classes.
  14.122 +
  14.123 +    LLCore::HttpRequest().  Instances of this class are used to request
  14.124 +    all major functions of the library.  Initialization, starting
  14.125 +    requests, delivering final notification of completion and various
  14.126 +    utility operations are all done via instances.  There is one very
  14.127 +    important rule for instances:
  14.128 +
  14.129 +        Request objects may NOT be shared between threads.
  14.130 +
  14.131 +    my_priority.  The APIs support the idea of priority ordering of
  14.132 +    requests but it hasn't been implemented and the hope is that this
  14.133 +    will become useless and removed from the interface.  Use 0U except
  14.134 +    as noted.
  14.135 +
  14.136 +    my_policy.  This is an important one.  This library attempts to
  14.137 +    manage TCP connection usage more rigorously than in the past.  This
  14.138 +    is done by issuing requests to a queue that has various settable
  14.139 +    properties.  These establish connection usage for the queue as well
  14.140 +    as how queues compete with one another.  (This is patterned after
  14.141 +    class-based queueing used in various networking stacks.)  Several
  14.142 +    classes are pre-defined.  Deciding when to use an existing class and
  14.143 +    when to create a new one will determine what kind of experience
  14.144 +    users have.  We'll pick up this question in detail below.
  14.145 +
  14.146 +    requestGet().  Issues an ordinary HTTP GET request to a given URL
  14.147 +    and associating the request with a policy class, a priority and an
  14.148 +    response handler.  Two additional arguments, not used here, allow
  14.149 +    for additional headers on the request and for per-request options.
  14.150 +    If successful, the call returns a handle whose value is other than
  14.151 +    LLCORE_HTTP_HANDLE_INVALID.  The HTTP operation is then performed
  14.152 +    asynchronously by another thread without any additional work by the
  14.153 +    caller.  If the handle returned is invalid, you can get the status
  14.154 +    code by calling my_request->getStatus().
  14.155 +
  14.156 +    update().  To get notification that the request has completed, a
  14.157 +    call to update() will invoke onCompleted() methods.
  14.158 +
  14.159 +
  14.160 +3.  Refinements, Necessary and Otherwise
  14.161 +
  14.162 +    MyHandler::onCompleted().  You'll want to do something useful with
  14.163 +    your response.  Distinguish errors from successes and getting the
  14.164 +    response body back in some form.
  14.165 +
  14.166 +    Add a new header:
  14.167 +
  14.168 +
  14.169 +        #include "bufferarray.h"
  14.170 +
  14.171 +
  14.172 +    Replace the existing MyHandler::onCompleted() definition with:
  14.173 +
  14.174 +
  14.175 +        virtual void onCompleted(LLCore::HttpHandle /* handle */,
  14.176 +                                 LLCore::HttpResponse * response)
  14.177 +        {
  14.178 +            LLCore::HttpStatus status = response->getStatus();
  14.179 +            if (status)
  14.180 +            {
  14.181 +                // Successful request.  Try to fetch the data
  14.182 +                LLCore::BufferArray * data = response->getBody();
  14.183 +
  14.184 +                if (data && data->size())
  14.185 +                {
  14.186 +                    // There's some data.  A BufferArray is a linked list
  14.187 +                    // of buckets.  We'll create a linear buffer and copy
  14.188 +                    // the data into it.
  14.189 +                    size_t data_len = data->size();
  14.190 +                    char * data_blob = new char [data_len + 1];
  14.191 +                    data->read(0, data_blob, data_len);
  14.192 +                    data_blob[data_len] = '\0';
  14.193 +
  14.194 +                    // Process the data now in NUL-terminated string.
  14.195 +                    // Needs more scrubbing but this will do.
  14.196 +                    LL_INFOS("Hack") << "Received:  " << data_blob << LL_ENDL;
  14.197 +
  14.198 +                    // Free the temporary data
  14.199 +                    delete [] data_blob;
  14.200 +                }
  14.201 +            }
  14.202 +            else
  14.203 +            {
  14.204 +                // Something went wrong.  Translate the status to
  14.205 +                // a meaningful message.
  14.206 +                LL_WARNS("Hack") << "HTTP GET failed.  Status:  "
  14.207 +                                 << status.toTerseString()
  14.208 +                                 << ", Reason:  " << status.toString()
  14.209 +                                 << LL_ENDL;
  14.210 +            }           
  14.211 +
  14.212 +            delete this;    // Last statement
  14.213 +        }
  14.214 +
  14.215 +
  14.216 +    HttpHeaders.  The header file "httprequest.h" documents the expected
  14.217 +    important headers that will go out with the request.  You can add to
  14.218 +    these by including an HttpHeaders object with the requestGet() call.
  14.219 +    These are typically setup once as part of init rather than
  14.220 +    dynamically created.
  14.221 +
  14.222 +    Add another header:
  14.223 +
  14.224 +
  14.225 +        #include "httpheaders.h"
  14.226 +
  14.227 +
  14.228 +    In LLAppViewer::mainLoop(), add this alongside the allocation of
  14.229 +    my_handler:
  14.230 +
  14.231 +
  14.232 +        // Additional headers for all requests
  14.233 +        LLCore::HttpHeaders * my_headers = new LLCore::HttpHeaders();
  14.234 +        my_headers->append("Accept", "text/html, application/llsd+xml");
  14.235 +
  14.236 +
  14.237 +    HttpOptions.  Options are similar and include a mix of value types.
  14.238 +    One interesting per-request option is the trace setting.  This
  14.239 +    enables various debug-type messages in the log file that show the
  14.240 +    progress of the request through the library.  It takes values from
  14.241 +    zero to three with higher values giving more verbose logging.  We'll
  14.242 +    use '2' and this will also give us a chance to verify that
  14.243 +    HttpHeaders works as expected.
  14.244 +
  14.245 +    Same as above, a new header:
  14.246 +
  14.247 +
  14.248 +        #include "httpoptions.h"
  14.249 +
  14.250 +
  14.251 +    And in LLAppView::mainLoop():
  14.252 +
  14.253 +
  14.254 +        // Special options for requests
  14.255 +        LLCore::HttpOptions * my_options = new LLCore::HttpOptions();
  14.256 +        my_options->setTrace(2);
  14.257 +
  14.258 +
  14.259 +    Now let's put that all together into a more complete requesting
  14.260 +    sequence.  Replace the existing invocation of requestGet() with this
  14.261 +    slightly more elaborate block:
  14.262 +
  14.263 +
  14.264 +        LLCore::HttpHandle handle;
  14.265 +        handle = my_request->requestGet(my_policy,
  14.266 +                                        my_priority,
  14.267 +                                        "http://www.example.com/",
  14.268 +                                        my_options,
  14.269 +                                        my_headers,
  14.270 +                                        my_handler);
  14.271 +        if (LLCORE_HTTP_HANDLE_INVALID == handle)
  14.272 +        {
  14.273 +             LLCore::HttpStatus status = my_request->getStatus();
  14.274 +
  14.275 +             LL_WARNS("Hack") << "Failed to request HTTP GET.  Status:  "
  14.276 +                              << status.toTerseString()
  14.277 +                              << ", Reason:  " << status.toString()
  14.278 +                              << LL_ENDL;
  14.279 +
  14.280 +             delete my_handler;    // No longer needed.
  14.281 +             my_handler = NULL;
  14.282 +        }
  14.283 +
  14.284 +
  14.285 +    Build, run and examine the log file.  You'll get some new data with
  14.286 +    this run.  First, you should get the www.example.com home page
  14.287 +    content:
  14.288 +
  14.289 +
  14.290 +----------------------------------------------------------------------------
  14.291 +2013-09-17T20:26:51Z INFO: MyHandler::onCompleted: Received:  <!doctype html>
  14.292 +<html>
  14.293 +<head>
  14.294 +    <title>Example Domain</title>
  14.295 +
  14.296 +    <meta charset="utf-8" />
  14.297 +    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
  14.298 +    <meta name="viewport" content="width=device-width, initial-scale=1" />
  14.299 +    <style type="text/css">
  14.300 +    body {
  14.301 +        background-color: #f0f0f2;
  14.302 +        margin: 0;
  14.303 +        padding: 0;
  14.304 +        font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
  14.305 +        
  14.306 +    }
  14.307 +    div {
  14.308 +        width: 600px;
  14.309 +        margin: 5em auto;
  14.310 +        padding: 50px;
  14.311 +        background-color: #fff;
  14.312 +        border-radius: 1em;
  14.313 +    }
  14.314 +    a:link, a:visited {
  14.315 +        color: #38488f;
  14.316 +        text-decoration: none;
  14.317 +    }
  14.318 +    @media (max-width: 700px) {
  14.319 +        body {
  14.320 +            background-color: #fff;
  14.321 +        }
  14.322 +        div {
  14.323 +            width: auto;
  14.324 +            margin: 0 auto;
  14.325 +            border-radius: 0;
  14.326 +            padding: 1em;
  14.327 +        }
  14.328 +    }
  14.329 +    </style>    
  14.330 +</head>
  14.331 +
  14.332 +<body>
  14.333 +<div>
  14.334 +    <h1>Example Domain</h1>
  14.335 +    <p>This domain is established to be used for illustrative examples in documents. You may use this
  14.336 +    domain in examples without prior coordination or asking for permission.</p>
  14.337 +    <p><a href="http://www.iana.org/domains/example">More information...</a></p>
  14.338 +</div>
  14.339 +</body>
  14.340 +</html>
  14.341 +----------------------------------------------------------------------------
  14.342 +
  14.343 +
  14.344 +    You'll also get a detailed trace of the HTTP operation itself.  Note
  14.345 +    the HEADEROUT line which shows the additional header added to the
  14.346 +    request.
  14.347 +
  14.348 +
  14.349 +----------------------------------------------------------------------------
  14.350 +HttpService::processRequestQueue: TRACE, FromRequestQueue, Handle:  086D3148
  14.351 +HttpLibcurl::addOp: TRACE, ToActiveQueue, Handle:  086D3148, Actives:  0, Readies:  0
  14.352 +HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle:  086D3148, Type:  TEXT, Data:  About to connect() to www.example.com port 80 (#0) 
  14.353 +HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle:  086D3148, Type:  TEXT, Data:    Trying 93.184.216.119... 
  14.354 +HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle:  086D3148, Type:  TEXT, Data:  Connected to www.example.com (93.184.216.119) port 80 (#0) 
  14.355 +HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle:  086D3148, Type:  TEXT, Data:  Connected to www.example.com (93.184.216.119) port 80 (#0) 
  14.356 +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
  14.357 +HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle:  086D3148, Type:  HEADERIN, Data:  HTTP/1.1 200 OK  
  14.358 +HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle:  086D3148, Type:  HEADERIN, Data:  Accept-Ranges: bytes  
  14.359 +HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle:  086D3148, Type:  HEADERIN, Data:  Cache-Control: max-age=604800  
  14.360 +HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle:  086D3148, Type:  HEADERIN, Data:  Content-Type: text/html  
  14.361 +HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle:  086D3148, Type:  HEADERIN, Data:  Date: Tue, 17 Sep 2013 20:26:56 GMT  
  14.362 +HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle:  086D3148, Type:  HEADERIN, Data:  Etag: "3012602696"  
  14.363 +HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle:  086D3148, Type:  HEADERIN, Data:  Expires: Tue, 24 Sep 2013 20:26:56 GMT  
  14.364 +HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle:  086D3148, Type:  HEADERIN, Data:  Last-Modified: Fri, 09 Aug 2013 23:54:35 GMT  
  14.365 +HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle:  086D3148, Type:  HEADERIN, Data:  Server: ECS (ewr/1590)  
  14.366 +HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle:  086D3148, Type:  HEADERIN, Data:  X-Cache: HIT  
  14.367 +HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle:  086D3148, Type:  HEADERIN, Data:  x-ec-custom-error: 1  
  14.368 +HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle:  086D3148, Type:  HEADERIN, Data:  Content-Length: 1270  
  14.369 +HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle:  086D3148, Type:  HEADERIN, Data:    
  14.370 +HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle:  086D3148, Type:  DATAIN, Data:  256 Bytes
  14.371 +HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle:  086D3148, Type:  TEXT, Data:  Connection #0 to host www.example.com left intact 
  14.372 +HttpLibcurl::completeRequest: TRACE, RequestComplete, Handle:  086D3148, Status:  Http_200
  14.373 +HttpOperation::addAsReply: TRACE, ToReplyQueue, Handle:  086D3148
  14.374 +----------------------------------------------------------------------------
  14.375 +
  14.376 +
  14.377 +4.  What Does All That Mean, Part 2
  14.378 +
  14.379 +    HttpStatus.  The HttpStatus object encodes errors from libcurl, the
  14.380 +    library itself and HTTP status values.  It does this to avoid
  14.381 +    collapsing all non-HTTP error into a single '499' HTTP status and to
  14.382 +    make errors distinct.
  14.383 +
  14.384 +    To aid programming, the usual bool conversions are available so that
  14.385 +    you can write 'if (status)' and the expected thing will happen
  14.386 +    whether it's an HTTP, libcurl or library error.  There's also
  14.387 +    provision to override the treatment of HTTP errors (making 404 a
  14.388 +    success, say).
  14.389 +
  14.390 +    Share data, don't copy it.  The library was started with the goal of
  14.391 +    avoiding data copies as much as possible.  Instead, read-only data
  14.392 +    sharing across threads with atomic reference counts is used for a
  14.393 +    number of data types.  These currently are:
  14.394 +
  14.395 +        * BufferArray.  Linked list of data blocks/HTTP bodies.
  14.396 +        * HttpHeaders.  Shared headers for both requests and responses.
  14.397 +        * HttpOptions.  Request-only data modifying HTTP behavior.
  14.398 +        * HttpResponse.  HTTP response description given to onCompleted.
  14.399 +
  14.400 +    Using objects of these types requires a few rules:
  14.401 +
  14.402 +        * Constructor always gives a reference to caller.
  14.403 +        * References are dropped with release() not delete.
  14.404 +        * Additional references may be taken out with addRef().
  14.405 +        * Unless otherwise stated, once an object is shared with another
  14.406 +          thread it should be treated as read-only.  There's no
  14.407 +          synchronization on the objects themselves.
  14.408 +
  14.409 +    HttpResponse.  You'll encounter this mainly in onCompleted() methods.
  14.410 +    Commonly-used interfaces on this object:
  14.411 +
  14.412 +        * getStatus() to return the final status of the request.
  14.413 +        * getBody() to retrieve the response body which may be NULL or
  14.414 +          zero-length.
  14.415 +        * getContentType() to return the value of the 'Content-Type'
  14.416 +          header or an empty string if none was sent.
  14.417 +
  14.418 +    This is a reference-counted object so you can call addRef() on it
  14.419 +    and hold onto the response for an arbitrary time.  But you'll
  14.420 +    usually just call a few methods and return from onCompleted() whose
  14.421 +    caller will release the object.
  14.422 +
  14.423 +    BufferArray.  The core data representation for request and response
  14.424 +    bodies.  In HTTP responses, it's fetched with the getBody() method
  14.425 +    and may be NULL or non-NULL with zero length.  All successful data
  14.426 +    handling should check both conditions before attempting to fetch
  14.427 +    data from the object.  Data access model uses simple read/write
  14.428 +    semantics:
  14.429 +
  14.430 +        * append()
  14.431 +        * size()
  14.432 +        * read()
  14.433 +        * write()
  14.434 +
  14.435 +    (There is a more sophisticated stream adapter that extends these
  14.436 +    methods and will be covered below.)  So, one way to retrieve data
  14.437 +    from a request is as follows:
  14.438 +
  14.439 +
  14.440 +        LLCore::BufferArray * data = response->getBody();
  14.441 +        if (data && data->size())
  14.442 +        {
  14.443 +            size_t data_len = data->size();
  14.444 +            char * data_blob = new char [data_len + 1];
  14.445 +            data->read(0, data_blob, data_len);
  14.446 +
  14.447 +
  14.448 +    HttpOptions and HttpResponse.  Really just simple containers of POD
  14.449 +    and std::string pairs.  But reference counted and the rule about not
  14.450 +    modifying after sharing must be followed.  You'll have the urge to
  14.451 +    change options dynamically at some point.  And you'll try to do that
  14.452 +    by just writing new values to the shared object.  And in tests
  14.453 +    everything will appear to work.  Then you ship and people in the
  14.454 +    real world start hitting read/write races in strings and then crash.
  14.455 +    Don't be lazy.
  14.456 +
  14.457 +    HttpHandle.  Uniquely identifies a request and can be used to
  14.458 +    identify it in an onCompleted() method or cancel it if it's still
  14.459 +    queued.  But as soon as a request's onCompleted() invocation
  14.460 +    returns, the handle becomes invalid and may be reused immediately
  14.461 +    for new requests.  Don't hold on to handles after notification.
  14.462 +
  14.463 +
  14.464 +5.  And Still More Refinements
  14.465 +
  14.466 +    (Note: The following refinements are just code fragments.  They
  14.467 +    don't directly fit into the working example above.  But they
  14.468 +    demonstrate several idioms you'll want to copy.)
  14.469 +
  14.470 +    LLSD, std::streambuf, std::iostream.  The read(), write() and
  14.471 +    append() methods may be adequate for your purposes.  But we use a
  14.472 +    lot of LLSD.  Its interfaces aren't particularly compatible with
  14.473 +    BufferArray.  And so two adapters are available to give
  14.474 +    stream-like behaviors:  BufferArrayStreamBuf and BufferArrayStream,
  14.475 +    which implement the std::streambuf and std::iostream interfaces,
  14.476 +    respectively.
  14.477 +
  14.478 +    A std::streambuf interface isn't something you'll want to use
  14.479 +    directly.  Instead, you'll use the much friendlier std::iostream
  14.480 +    interface found in BufferArrayStream.  This adapter gives you all
  14.481 +    the '>>' and '<<' operators you'll want as well as working
  14.482 +    directly with the LLSD conversion operators.
  14.483 +
  14.484 +    Some new headers:
  14.485 +
  14.486 +
  14.487 +        #include "bufferstream.h"
  14.488 +        #include "llsdserialize.h"
  14.489 +
  14.490 +
  14.491 +    And an updated fragment based on onCompleted() above:
  14.492 +
  14.493 +
  14.494 +                // Successful request.  Try to fetch the data
  14.495 +                LLCore::BufferArray * data = response->getBody();
  14.496 +                LLSD resp_llsd;
  14.497 +
  14.498 +                if (data && data->size())
  14.499 +                {
  14.500 +                    // There's some data and we expect this to be
  14.501 +                    // LLSD.  Checking of content type and validation
  14.502 +                    // during parsing would be admirable additions.
  14.503 +                    // But we'll forgo that now.
  14.504 +                    LLCore::BufferArrayStream data_stream(data);
  14.505 +                    LLSDSerialize::fromXML(resp_llsd, data_stream);
  14.506 +                }
  14.507 +                LL_INFOS("Hack") << "LLSD Received:  " << resp_llsd << LL_ENDL;
  14.508 +            }
  14.509 +            else
  14.510 +            {
  14.511 +
  14.512 +
  14.513 +    Converting an LLSD object into an XML stream stored in a
  14.514 +    BufferArray is just the reverse of the above:
  14.515 +
  14.516 +
  14.517 +        BufferArray * data = new BufferArray();
  14.518 +        LLCore::BufferArrayStream data_stream(data);
  14.519 +
  14.520 +        LLSD src_llsd;
  14.521 +        src_llsd["foo"] = "bar";
  14.522 +
  14.523 +        LLSDSerialize::toXML(src_llsd, data_stream);
  14.524 +
  14.525 +        // 'data' now contains an XML payload and can be sent
  14.526 +        // to a web service using the requestPut() or requestPost()
  14.527 +        //  methods.
  14.528 +        ...  requestPost(...);
  14.529 +
  14.530 +        // And don't forget to release the BufferArray.
  14.531 +        data->release();
  14.532 +        data = NULL;
  14.533 +
  14.534 +
  14.535 +    LLSD will often go hand-in-hand with BufferArray and data
  14.536 +    transport.  But you can also do all the streaming I/O you'd expect
  14.537 +    of a std::iostream object:
  14.538 +
  14.539 +
  14.540 +        BufferArray * data = new BufferArray();
  14.541 +        LLCore::BufferArrayStream data_stream(data);
  14.542 +
  14.543 +        data_stream << "Hello, World!" << 29.4 << '\n';
  14.544 +        std::string str;
  14.545 +        data_stream >> str;
  14.546 +        std::cout << str << std::endl;
  14.547 +
  14.548 +        data->release();
  14.549 +        // Actual delete will occur when 'data_stream'
  14.550 +        // falls out of scope and is destructed.
  14.551 +
  14.552 +
  14.553 +    Scoping objects and cleaning up.  The examples haven't bothered
  14.554 +    with cleanup of objects that are no longer needed.  Instead, most
  14.555 +    objects have been allocated as if they were global and eternal.
  14.556 +    You'll put the objects in more appropriate feature objects and
  14.557 +    clean them up as a group.  Here's a checklist for actions you may
  14.558 +    need to take on cleanup:
  14.559 +
  14.560 +    * Call delete on:
  14.561 +      o HttpHandlers created on the heap
  14.562 +      o HttpRequest objects
  14.563 +    * Call release() on:
  14.564 +      o BufferArray objects
  14.565 +      o HttpHeaders objects
  14.566 +      o HttpOptions objects
  14.567 +      o HttpResponse objects
  14.568 +
  14.569 +    On program exit, as threads wind down, the library continues to
  14.570 +    operate safely.  Threads don't interact via the library and even
  14.571 +    dangling references to HttpHandler objects are safe.  If you don't
  14.572 +    call HttpRequest::update(), handler references are never
  14.573 +    dereferenced.
  14.574 +
  14.575 +    You can take a more thorough approach to wind-down.  Keep a list
  14.576 +    of HttpHandles (not HttpHandlers) of outstanding requests.  For
  14.577 +    each of these, call HttpRequest::requestCancel() to cancel the
  14.578 +    operation.  (Don't add the cancel requests' handled to the list.)
  14.579 +    This will cancel the outstanding requests that haven't completed.
  14.580 +    Canceled or completed, all requests will queue notifications.  You
  14.581 +    can now cycle calling update() discarding responses.  Continue
  14.582 +    until all requests notify or a few seconds have passed.
  14.583 +
  14.584 +    Global startup and shutdown is handled in the viewer.  But you can
  14.585 +    learn about it in the code or in the documentation in the headers.
  14.586 +
  14.587 +
  14.588 +6.  Choosing a Policy Class
  14.589 +
  14.590 +    Now it's time to get rid of the default policy class.  Take a look
  14.591 +    at the policy class definitions in newview/llappcorehttp.h.
  14.592 +    Ideally, you'll find one that's compatible with what you're doing.
  14.593 +    Some of the compatibility guidelines are:
  14.594 +
  14.595 +    * Destination: Pair of host and port.  Mixing requests with
  14.596 +      different destinations may cause more connection setup and tear
  14.597 +      down.
  14.598 +
  14.599 +    * Method: http or https.  Usually moot given destination.  But
  14.600 +      mixing these may also cause connection churn.
  14.601 +
  14.602 +    * Transfer size: If you're moving 100MB at a time and you make your
  14.603 +      requests to the same policy class as a lot of small, fast event
  14.604 +      information that fast traffic is going to get stuck behind you
  14.605 +      and someone's experience is going to be miserable.
  14.606 +
  14.607 +    * Long poll requests: These are long-lived, must- do operations.
  14.608 +      They have a special home called AP_LONG_POLL.
  14.609 +
  14.610 +    * Concurrency: High concurrency (5 or more) and large transfer
  14.611 +      sizes are incompatible.  Another head-of-the-line problem.  High
  14.612 +      concurrency is tolerated when it's desired to get maximal
  14.613 +      throughput.  Mesh and texture downloads, for example.
  14.614 +
  14.615 +    * Pipelined: If your requests are not idempotent, stay away from
  14.616 +      anything marked 'soon' or 'yes'.  Hidden retries may be a
  14.617 +      problem for you.  For now, would also recommend keeping PUT and
  14.618 +      POST requests out of classes that may be pipelined.  Support for
  14.619 +      that is still a bit new.
  14.620 +
  14.621 +    If you haven't found a compatible match, you can either create a
  14.622 +    new class (llappcorehttp.*) or just use AP_DEFAULT, the catchall
  14.623 +    class when all else fails.  Inventory query operations might be a
  14.624 +    candidate for a new class that supported pipelining on https:.
  14.625 +    Same with display name lookups and other bursty-at-login
  14.626 +    operations.  For other things, AP_DEFAULT will do what it can and
  14.627 +    will, in some way or another, tolerate any usage.  Whether the
  14.628 +    users' experiences are good are for you to determine.
  14.629 +
  14.630 +    
  14.631 +7.  FAQ
  14.632 +
  14.633 +    Q1.  What do these policy classes achieve?
  14.634 +
  14.635 +    A1.  Previously, HTTP-using code in the viewer was written as if
  14.636 +    it were some isolated, local operation that didn't have to
  14.637 +    consider resources, contention or impact on services and the
  14.638 +    larger environment.  The result was an application with on the
  14.639 +    order of 100 HTTP launch points in its codebase that could create
  14.640 +    dozens or even 100's of TCP connections zeroing in on grid
  14.641 +    services and disrupting networking equipment, web services and
  14.642 +    innocent users.  The use of policy classes (modeled on
  14.643 +    http://en.wikipedia.org/wiki/Class-based_queueing) is a means to
  14.644 +    restrict connection concurrency, good and necessary in itself.  In
  14.645 +    turn, that reduces demands on an expensive resource (connection
  14.646 +    setup and concurrency) which relieves strain on network points.
  14.647 +    That enables connection keepalive and opportunites for true
  14.648 +    improvements in throughput and user experience.
  14.649 +
  14.650 +    Another aspect of the classes is that they give some control over
  14.651 +    how competing demands for the network will be apportioned.  If
  14.652 +    mesh fetches, texture fetches and inventory queries are all being
  14.653 +    made at once, the relative weights of their classes' concurrency
  14.654 +    limits established that apportioning.  We now have an opportunity
  14.655 +    to balance the entire viewer system.
  14.656 +
  14.657 +    Q2.  How's that data sharing with refcounts working for you?
  14.658 +
  14.659 +    A2.  Meh.  It does reduce memory churn and the frequency at which
  14.660 +    free blocks must be moved between threads.  But it's also a design
  14.661 +    for static configuration and dynamic reconfiguration (not
  14.662 +    requiring a restart) is favored.  Creating new options for every
  14.663 +    request isn't too bad, it a sequence of "new, fill, request,
  14.664 +    release" for each requested operation.  That in contrast to doing
  14.665 +    the "new, fill, release" at startup.  The bad comes in getting at
  14.666 +    the source data.  One rule in this work was "no new thread
  14.667 +    problems."  And one source for those is pulling setting values out
  14.668 +    of gSettings in threads.  None of that is thread safe though we
  14.669 +    tend to get away with it.
  14.670 +
  14.671 +    Q3.  What needs to be done?
  14.672 +
  14.673 +    A3.  There's a To-Do list in _httpinternal.h.  It has both large
  14.674 +    and small projects here if someone would like to try changes.
    15.1 --- a/indra/llcorehttp/_httpinternal.h	Wed Feb 12 17:18:03 2014 -0800
    15.2 +++ b/indra/llcorehttp/_httpinternal.h	Wed Feb 12 18:58:40 2014 -0800
    15.3 @@ -4,7 +4,7 @@
    15.4   *
    15.5   * $LicenseInfo:firstyear=2012&license=viewerlgpl$
    15.6   * Second Life Viewer Source Code
    15.7 - * Copyright (C) 2012, Linden Research, Inc.
    15.8 + * Copyright (C) 2012-2013, Linden Research, Inc.
    15.9   *
   15.10   * This library is free software; you can redistribute it and/or
   15.11   * modify it under the terms of the GNU Lesser General Public
   15.12 @@ -36,7 +36,8 @@
   15.13  // General library to-do list
   15.14  //
   15.15  // - Implement policy classes.  Structure is mostly there just didn't
   15.16 -//   need it for the first consumer.
   15.17 +//   need it for the first consumer.  [Classes are there.  More
   15.18 +//   advanced features, like borrowing, aren't there yet.]
   15.19  // - Consider Removing 'priority' from the request interface.  Its use
   15.20  //   in an always active class can lead to starvation of low-priority
   15.21  //   requests.  Requires coodination of priority values across all
   15.22 @@ -46,6 +47,7 @@
   15.23  //   may not really need it.
   15.24  // - Set/get for global policy and policy classes is clumsy.  Rework
   15.25  //   it heading in a direction that allows for more dynamic behavior.
   15.26 +//   [Mostly fixed]
   15.27  // - Move HttpOpRequest::prepareRequest() to HttpLibcurl for the
   15.28  //   pedantic.
   15.29  // - Update downloader and other long-duration services are going to
   15.30 @@ -64,6 +66,12 @@
   15.31  //   This won't help in the face of the router problems we've looked
   15.32  //   at, however.  Detect starvation due to UDP activity and provide
   15.33  //   feedback to it.
   15.34 +// - Change the transfer timeout scheme.  We're less interested in
   15.35 +//   absolute time, in most cases, than in continuous progress.
   15.36 +// - Many of the policy class settings are currently applied to the
   15.37 +//   entire class.  Some, like connection limits, would be better
   15.38 +//   applied to each destination target making multiple targets
   15.39 +//   independent.
   15.40  //
   15.41  // Integration to-do list
   15.42  // - LLTextureFetch still needs a major refactor.  The use of
   15.43 @@ -73,7 +81,6 @@
   15.44  //   the main source file.
   15.45  // - Expand areas of usage eventually leading to the removal of LLCurl.
   15.46  //   Rough order of expansion:
   15.47 -//   .  Mesh fetch
   15.48  //   .  Avatar names
   15.49  //   .  Group membership lists
   15.50  //   .  Caps access in general
   15.51 @@ -97,8 +104,8 @@
   15.52  {
   15.53  
   15.54  // Maxium number of policy classes that can be defined.
   15.55 -// *TODO:  Currently limited to the default class, extend.
   15.56 -const int HTTP_POLICY_CLASS_LIMIT = 1;
   15.57 +// *TODO:  Currently limited to the default class + 1, extend.
   15.58 +const int HTTP_POLICY_CLASS_LIMIT = 8;
   15.59  
   15.60  // Debug/informational tracing.  Used both
   15.61  // as a global option and in per-request traces.
   15.62 @@ -129,6 +136,7 @@
   15.63  // Retries and time-on-queue are not included and aren't
   15.64  // accounted for.
   15.65  const long HTTP_REQUEST_TIMEOUT_DEFAULT = 30L;
   15.66 +const long HTTP_REQUEST_XFER_TIMEOUT_DEFAULT = 0L;
   15.67  const long HTTP_REQUEST_TIMEOUT_MIN = 0L;
   15.68  const long HTTP_REQUEST_TIMEOUT_MAX = 3600L;
   15.69  
   15.70 @@ -137,6 +145,11 @@
   15.71  const int HTTP_CONNECTION_LIMIT_MIN = 1;
   15.72  const int HTTP_CONNECTION_LIMIT_MAX = 256;
   15.73  
   15.74 +// Miscellaneous defaults
   15.75 +const long HTTP_PIPELINING_DEFAULT = 0L;
   15.76 +const bool HTTP_USE_RETRY_AFTER_DEFAULT = true;
   15.77 +const long HTTP_THROTTLE_RATE_DEFAULT = 0L;
   15.78 +
   15.79  // Tuning parameters
   15.80  
   15.81  // Time worker thread sleeps after a pass through the
    16.1 --- a/indra/llcorehttp/_httplibcurl.cpp	Wed Feb 12 17:18:03 2014 -0800
    16.2 +++ b/indra/llcorehttp/_httplibcurl.cpp	Wed Feb 12 18:58:40 2014 -0800
    16.3 @@ -4,7 +4,7 @@
    16.4   *
    16.5   * $LicenseInfo:firstyear=2012&license=viewerlgpl$
    16.6   * Second Life Viewer Source Code
    16.7 - * Copyright (C) 2012, Linden Research, Inc.
    16.8 + * Copyright (C) 2012-2013, Linden Research, Inc.
    16.9   *
   16.10   * This library is free software; you can redistribute it and/or
   16.11   * modify it under the terms of the GNU Lesser General Public
   16.12 @@ -41,7 +41,8 @@
   16.13  HttpLibcurl::HttpLibcurl(HttpService * service)
   16.14  	: mService(service),
   16.15  	  mPolicyCount(0),
   16.16 -	  mMultiHandles(NULL)
   16.17 +	  mMultiHandles(NULL),
   16.18 +	  mActiveHandles(NULL)
   16.19  {}
   16.20  
   16.21  
   16.22 @@ -77,6 +78,9 @@
   16.23  
   16.24  		delete [] mMultiHandles;
   16.25  		mMultiHandles = NULL;
   16.26 +
   16.27 +		delete [] mActiveHandles;
   16.28 +		mActiveHandles = NULL;
   16.29  	}
   16.30  
   16.31  	mPolicyCount = 0;
   16.32 @@ -90,9 +94,12 @@
   16.33  	
   16.34  	mPolicyCount = policy_count;
   16.35  	mMultiHandles = new CURLM * [mPolicyCount];
   16.36 +	mActiveHandles = new int [mPolicyCount];
   16.37 +	
   16.38  	for (int policy_class(0); policy_class < mPolicyCount; ++policy_class)
   16.39  	{
   16.40  		mMultiHandles[policy_class] = curl_multi_init();
   16.41 +		mActiveHandles[policy_class] = 0;
   16.42  	}
   16.43  }
   16.44  
   16.45 @@ -110,8 +117,10 @@
   16.46  	// Give libcurl some cycles to do I/O & callbacks
   16.47  	for (int policy_class(0); policy_class < mPolicyCount; ++policy_class)
   16.48  	{
   16.49 -		if (! mMultiHandles[policy_class])
   16.50 +		if (! mActiveHandles[policy_class] || ! mMultiHandles[policy_class])
   16.51 +		{
   16.52  			continue;
   16.53 +		}
   16.54  		
   16.55  		int running(0);
   16.56  		CURLMcode status(CURLM_CALL_MULTI_PERFORM);
   16.57 @@ -132,12 +141,10 @@
   16.58  				CURL * handle(msg->easy_handle);
   16.59  				CURLcode result(msg->data.result);
   16.60  
   16.61 -				if (completeRequest(mMultiHandles[policy_class], handle, result))
   16.62 -				{
   16.63 -					// Request is still active, don't get too sleepy
   16.64 -					ret = HttpService::NORMAL;
   16.65 -				}
   16.66 -				handle = NULL;			// No longer valid on return
   16.67 +				completeRequest(mMultiHandles[policy_class], handle, result);
   16.68 +				handle = NULL;					// No longer valid on return
   16.69 +				ret = HttpService::NORMAL;		// If anything completes, we may have a free slot.
   16.70 +												// Turning around quickly reduces connection gap by 7-10mS.
   16.71  			}
   16.72  			else if (CURLMSG_NONE == msg->msg)
   16.73  			{
   16.74 @@ -193,6 +200,7 @@
   16.75  	
   16.76  	// On success, make operation active
   16.77  	mActiveOps.insert(op);
   16.78 +	++mActiveHandles[op->mReqPolicy];
   16.79  }
   16.80  
   16.81  
   16.82 @@ -214,6 +222,7 @@
   16.83  
   16.84  	// Drop references
   16.85  	mActiveOps.erase(it);
   16.86 +	--mActiveHandles[op->mReqPolicy];
   16.87  	op->release();
   16.88  
   16.89  	return true;
   16.90 @@ -240,7 +249,7 @@
   16.91  	{
   16.92  		LL_INFOS("CoreHttp") << "TRACE, RequestCanceled, Handle:  "
   16.93  							 << static_cast<HttpHandle>(op)
   16.94 -							 << ", Status:  " << op->mStatus.toHex()
   16.95 +							 << ", Status:  " << op->mStatus.toTerseString()
   16.96  							 << LL_ENDL;
   16.97  	}
   16.98  
   16.99 @@ -275,6 +284,7 @@
  16.100  
  16.101  	// Deactivate request
  16.102  	mActiveOps.erase(it);
  16.103 +	--mActiveHandles[op->mReqPolicy];
  16.104  	op->mCurlActive = false;
  16.105  
  16.106  	// Set final status of request if it hasn't failed by other mechanisms yet
  16.107 @@ -316,7 +326,7 @@
  16.108  	{
  16.109  		LL_INFOS("CoreHttp") << "TRACE, RequestComplete, Handle:  "
  16.110  							 << static_cast<HttpHandle>(op)
  16.111 -							 << ", Status:  " << op->mStatus.toHex()
  16.112 +							 << ", Status:  " << op->mStatus.toTerseString()
  16.113  							 << LL_ENDL;
  16.114  	}
  16.115  
  16.116 @@ -336,19 +346,9 @@
  16.117  
  16.118  int HttpLibcurl::getActiveCountInClass(int policy_class) const
  16.119  {
  16.120 -	int count(0);
  16.121 -	
  16.122 -	for (active_set_t::const_iterator iter(mActiveOps.begin());
  16.123 -		 mActiveOps.end() != iter;
  16.124 -		 ++iter)
  16.125 -	{
  16.126 -		if ((*iter)->mReqPolicy == policy_class)
  16.127 -		{
  16.128 -			++count;
  16.129 -		}
  16.130 -	}
  16.131 -	
  16.132 -	return count;
  16.133 +	llassert_always(policy_class < mPolicyCount);
  16.134 +
  16.135 +	return mActiveHandles ? mActiveHandles[policy_class] : 0;
  16.136  }
  16.137  
  16.138  
  16.139 @@ -359,12 +359,17 @@
  16.140  
  16.141  struct curl_slist * append_headers_to_slist(const HttpHeaders * headers, struct curl_slist * slist)
  16.142  {
  16.143 -	for (HttpHeaders::container_t::const_iterator it(headers->mHeaders.begin());
  16.144 -
  16.145 -		headers->mHeaders.end() != it;
  16.146 -		 ++it)
  16.147 +	const HttpHeaders::const_iterator end(headers->end());
  16.148 +	for (HttpHeaders::const_iterator it(headers->begin()); end != it; ++it)
  16.149  	{
  16.150 -		slist = curl_slist_append(slist, (*it).c_str());
  16.151 +		static const char sep[] = ": ";
  16.152 +		std::string header;
  16.153 +		header.reserve((*it).first.size() + (*it).second.size() + sizeof(sep));
  16.154 +		header.append((*it).first);
  16.155 +		header.append(sep);
  16.156 +		header.append((*it).second);
  16.157 +		
  16.158 +		slist = curl_slist_append(slist, header.c_str());
  16.159  	}
  16.160  	return slist;
  16.161  }
    17.1 --- a/indra/llcorehttp/_httplibcurl.h	Wed Feb 12 17:18:03 2014 -0800
    17.2 +++ b/indra/llcorehttp/_httplibcurl.h	Wed Feb 12 18:58:40 2014 -0800
    17.3 @@ -4,7 +4,7 @@
    17.4   *
    17.5   * $LicenseInfo:firstyear=2012&license=viewerlgpl$
    17.6   * Second Life Viewer Source Code
    17.7 - * Copyright (C) 2012, Linden Research, Inc.
    17.8 + * Copyright (C) 2012-2013, Linden Research, Inc.
    17.9   *
   17.10   * This library is free software; you can redistribute it and/or
   17.11   * modify it under the terms of the GNU Lesser General Public
   17.12 @@ -71,16 +71,22 @@
   17.13  	///
   17.14  	/// @return			Indication of how long this method is
   17.15  	///					willing to wait for next service call.
   17.16 +	///
   17.17 +	/// Threading:  called by worker thread.
   17.18  	HttpService::ELoopSpeed processTransport();
   17.19  
   17.20  	/// Add request to the active list.  Caller is expected to have
   17.21  	/// provided us with a reference count on the op to hold the
   17.22  	/// request.  (No additional references will be added.)
   17.23 +	///
   17.24 +	/// Threading:  called by worker thread.
   17.25  	void addOp(HttpOpRequest * op);
   17.26  
   17.27  	/// One-time call to set the number of policy classes to be
   17.28  	/// serviced and to create the resources for each.  Value
   17.29  	/// must agree with HttpPolicy::setPolicies() call.
   17.30 +	///
   17.31 +	/// Threading:  called by init thread.
   17.32  	void start(int policy_count);
   17.33  
   17.34  	/// Synchronously stop libcurl operations.  All active requests
   17.35 @@ -91,9 +97,13 @@
   17.36  	/// respective reply queues.
   17.37  	///
   17.38  	/// Can be restarted with a start() call.
   17.39 +	///
   17.40 +	/// Threading:  called by worker thread.
   17.41  	void shutdown();
   17.42  
   17.43  	/// Return global and per-class counts of active requests.
   17.44 +	///
   17.45 +	/// Threading:  called by worker thread.
   17.46  	int getActiveCount() const;
   17.47  	int getActiveCountInClass(int policy_class) const;
   17.48  
   17.49 @@ -103,6 +113,7 @@
   17.50  	///
   17.51  	/// @return			True if handle was found and operation canceled.
   17.52  	///
   17.53 +	/// Threading:  called by worker thread.
   17.54  	bool cancel(HttpHandle handle);
   17.55  
   17.56  protected:
   17.57 @@ -121,7 +132,8 @@
   17.58  	HttpService *		mService;				// Simple reference, not owner
   17.59  	active_set_t		mActiveOps;
   17.60  	int					mPolicyCount;
   17.61 -	CURLM **			mMultiHandles;
   17.62 +	CURLM **			mMultiHandles;			// One handle per policy class
   17.63 +	int *				mActiveHandles;			// Active count per policy class
   17.64  }; // end class HttpLibcurl
   17.65  
   17.66  }  // end namespace LLCore
    18.1 --- a/indra/llcorehttp/_httpoperation.cpp	Wed Feb 12 17:18:03 2014 -0800
    18.2 +++ b/indra/llcorehttp/_httpoperation.cpp	Wed Feb 12 18:58:40 2014 -0800
    18.3 @@ -4,7 +4,7 @@
    18.4   *
    18.5   * $LicenseInfo:firstyear=2012&license=viewerlgpl$
    18.6   * Second Life Viewer Source Code
    18.7 - * Copyright (C) 2012, Linden Research, Inc.
    18.8 + * Copyright (C) 2012-2013, Linden Research, Inc.
    18.9   *
   18.10   * This library is free software; you can redistribute it and/or
   18.11   * modify it under the terms of the GNU Lesser General Public
   18.12 @@ -53,7 +53,7 @@
   18.13  	  mUserHandler(NULL),
   18.14  	  mReqPolicy(HttpRequest::DEFAULT_POLICY_ID),
   18.15  	  mReqPriority(0U),
   18.16 -	  mTracing(0)
   18.17 +	  mTracing(HTTP_TRACE_OFF)
   18.18  {
   18.19  	mMetricCreated = totalTime();
   18.20  }
   18.21 @@ -94,7 +94,7 @@
   18.22  	// Default implementation should never be called.  This
   18.23  	// indicates an operation making a transition that isn't
   18.24  	// defined.
   18.25 -	LL_ERRS("HttpCore") << "Default stageFromRequest method may not be called."
   18.26 +	LL_ERRS("CoreHttp") << "Default stageFromRequest method may not be called."
   18.27  						<< LL_ENDL;
   18.28  }
   18.29  
   18.30 @@ -104,7 +104,7 @@
   18.31  	// Default implementation should never be called.  This
   18.32  	// indicates an operation making a transition that isn't
   18.33  	// defined.
   18.34 -	LL_ERRS("HttpCore") << "Default stageFromReady method may not be called."
   18.35 +	LL_ERRS("CoreHttp") << "Default stageFromReady method may not be called."
   18.36  						<< LL_ENDL;
   18.37  }
   18.38  
   18.39 @@ -114,7 +114,7 @@
   18.40  	// Default implementation should never be called.  This
   18.41  	// indicates an operation making a transition that isn't
   18.42  	// defined.
   18.43 -	LL_ERRS("HttpCore") << "Default stageFromActive method may not be called."
   18.44 +	LL_ERRS("CoreHttp") << "Default stageFromActive method may not be called."
   18.45  						<< LL_ENDL;
   18.46  }
   18.47  
    19.1 --- a/indra/llcorehttp/_httpoperation.h	Wed Feb 12 17:18:03 2014 -0800
    19.2 +++ b/indra/llcorehttp/_httpoperation.h	Wed Feb 12 18:58:40 2014 -0800
    19.3 @@ -4,7 +4,7 @@
    19.4   *
    19.5   * $LicenseInfo:firstyear=2012&license=viewerlgpl$
    19.6   * Second Life Viewer Source Code
    19.7 - * Copyright (C) 2012, Linden Research, Inc.
    19.8 + * Copyright (C) 2012-2013, Linden Research, Inc.
    19.9   *
   19.10   * This library is free software; you can redistribute it and/or
   19.11   * modify it under the terms of the GNU Lesser General Public
   19.12 @@ -72,7 +72,7 @@
   19.13  class HttpOperation : public LLCoreInt::RefCounted
   19.14  {
   19.15  public:
   19.16 -	/// Threading:  called by a consumer/application thread.
   19.17 +	/// Threading:  called by consumer thread.
   19.18  	HttpOperation();
   19.19  
   19.20  protected:
   19.21 @@ -108,7 +108,7 @@
   19.22  	///							by the worker thread.  This is passible data
   19.23  	///							until notification is performed.
   19.24  	///
   19.25 -	/// Threading:  called by application thread.
   19.26 +	/// Threading:  called by consumer thread.
   19.27  	///
   19.28  	void setReplyPath(HttpReplyQueue * reply_queue,
   19.29  					  HttpHandler * handler);
   19.30 @@ -141,7 +141,7 @@
   19.31  	/// call to HttpRequest::update().  This method does the necessary
   19.32  	/// dispatching.
   19.33  	///
   19.34 -	/// Threading:  called by application thread.
   19.35 +	/// Threading:  called by consumer thread.
   19.36  	///
   19.37  	virtual void visitNotifier(HttpRequest *);
   19.38  
    20.1 --- a/indra/llcorehttp/_httpoprequest.cpp	Wed Feb 12 17:18:03 2014 -0800
    20.2 +++ b/indra/llcorehttp/_httpoprequest.cpp	Wed Feb 12 18:58:40 2014 -0800
    20.3 @@ -64,6 +64,15 @@
    20.4  							   unsigned int * last,
    20.5  							   unsigned int * length);
    20.6  
    20.7 +// Similar for Retry-After headers.  Only parses the delta form
    20.8 +// of the header, HTTP time formats aren't interesting for client
    20.9 +// purposes.
   20.10 +//
   20.11 +// @return		0 if successfully parsed and seconds time delta
   20.12 +//				returned in time argument.
   20.13 +//
   20.14 +int parse_retry_after_header(char * buffer, int * time);
   20.15 +
   20.16  
   20.17  // Take data from libcurl's CURLOPT_DEBUGFUNCTION callback and
   20.18  // escape and format it for a tracing line in logging.  Absolutely
   20.19 @@ -74,14 +83,16 @@
   20.20  							   std::string & safe_line);
   20.21  
   20.22  
   20.23 -// OS-neutral string comparisons of various types
   20.24 -int os_strncasecmp(const char *s1, const char *s2, size_t n);
   20.25 -int os_strcasecmp(const char *s1, const char *s2);
   20.26 -char * os_strtok_r(char *str, const char *delim, char **saveptr);
   20.27 +// OS-neutral string comparisons of various types.
   20.28 +int os_strcasecmp(const char * s1, const char * s2);
   20.29 +char * os_strtok_r(char * str, const char * delim, char ** saveptr);
   20.30 +char * os_strtrim(char * str);
   20.31 +char * os_strltrim(char * str);
   20.32 +void os_strlower(char * str);
   20.33  
   20.34 -
   20.35 -static const char * const hdr_whitespace(" \t");
   20.36 -static const char * const hdr_separator(": \t");
   20.37 +// Error testing and reporting for libcurl status codes
   20.38 +void check_curl_easy_code(CURLcode code);
   20.39 +void check_curl_easy_code(CURLcode code, int curl_setopt_option);
   20.40  
   20.41  } // end anonymous namespace
   20.42  
   20.43 @@ -104,12 +115,15 @@
   20.44  	  mCurlService(NULL),
   20.45  	  mCurlHeaders(NULL),
   20.46  	  mCurlBodyPos(0),
   20.47 +	  mCurlTemp(NULL),
   20.48 +	  mCurlTempLen(0),
   20.49  	  mReplyBody(NULL),
   20.50  	  mReplyOffset(0),
   20.51  	  mReplyLength(0),
   20.52  	  mReplyFullLength(0),
   20.53  	  mReplyHeaders(NULL),
   20.54  	  mPolicyRetries(0),
   20.55 +	  mPolicy503Retries(0),
   20.56  	  mPolicyRetryAt(HttpTime(0)),
   20.57  	  mPolicyRetryLimit(HTTP_RETRY_COUNT_DEFAULT)
   20.58  {
   20.59 @@ -153,6 +167,10 @@
   20.60  		mCurlHeaders = NULL;
   20.61  	}
   20.62  
   20.63 +	delete [] mCurlTemp;
   20.64 +	mCurlTemp = NULL;
   20.65 +	mCurlTempLen = 0;
   20.66 +	
   20.67  	if (mReplyBody)
   20.68  	{
   20.69  		mReplyBody->release();
   20.70 @@ -208,6 +226,11 @@
   20.71  		mCurlHeaders = NULL;
   20.72  	}
   20.73  
   20.74 +	// Also not needed on the other side
   20.75 +	delete [] mCurlTemp;
   20.76 +	mCurlTemp = NULL;
   20.77 +	mCurlTempLen = 0;
   20.78 +	
   20.79  	addAsReply();
   20.80  }
   20.81  
   20.82 @@ -226,6 +249,7 @@
   20.83  			response->setRange(mReplyOffset, mReplyLength, mReplyFullLength);
   20.84  		}
   20.85  		response->setContentType(mReplyConType);
   20.86 +		response->setRetries(mPolicyRetries, mPolicy503Retries);
   20.87  		
   20.88  		mUserHandler->onCompleted(static_cast<HttpHandle>(this), response);
   20.89  
   20.90 @@ -335,6 +359,10 @@
   20.91  		{
   20.92  			mProcFlags |= PF_SAVE_HEADERS;
   20.93  		}
   20.94 +		if (options->getUseRetryAfter())
   20.95 +		{
   20.96 +			mProcFlags |= PF_USE_RETRY_AFTER;
   20.97 +		}
   20.98  		mPolicyRetryLimit = options->getRetries();
   20.99  		mPolicyRetryLimit = llclamp(mPolicyRetryLimit, HTTP_RETRY_COUNT_MIN, HTTP_RETRY_COUNT_MAX);
  20.100  		mTracing = (std::max)(mTracing, llclamp(options->getTrace(), HTTP_TRACE_MIN, HTTP_TRACE_MAX));
  20.101 @@ -350,6 +378,8 @@
  20.102  //
  20.103  HttpStatus HttpOpRequest::prepareRequest(HttpService * service)
  20.104  {
  20.105 +	CURLcode code;
  20.106 +	
  20.107  	// Scrub transport and result data for retried op case
  20.108  	mCurlActive = false;
  20.109  	mCurlHandle = NULL;
  20.110 @@ -379,64 +409,108 @@
  20.111  	// *FIXME:  better error handling later
  20.112  	HttpStatus status;
  20.113  
  20.114 -	// Get policy options
  20.115 +	// Get global policy options
  20.116  	HttpPolicyGlobal & policy(service->getPolicy().getGlobalOptions());
  20.117  	
  20.118  	mCurlHandle = LLCurl::createStandardCurlHandle();
  20.119 +	if (! mCurlHandle)
  20.120 +	{
  20.121 +		// We're in trouble.  We'll continue but it won't go well.
  20.122 +		LL_WARNS("CoreHttp") << "Failed to allocate libcurl easy handle.  Continuing."
  20.123 +							 << LL_ENDL;
  20.124 +		return HttpStatus(HttpStatus::LLCORE, HE_BAD_ALLOC);
  20.125 +	}
  20.126 +	code = curl_easy_setopt(mCurlHandle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
  20.127 +	check_curl_easy_code(code, CURLOPT_IPRESOLVE);
  20.128 +	code = curl_easy_setopt(mCurlHandle, CURLOPT_NOSIGNAL, 1);
  20.129 +	check_curl_easy_code(code, CURLOPT_NOSIGNAL);
  20.130 +	code = curl_easy_setopt(mCurlHandle, CURLOPT_NOPROGRESS, 1);
  20.131 +	check_curl_easy_code(code, CURLOPT_NOPROGRESS);
  20.132 +	code = curl_easy_setopt(mCurlHandle, CURLOPT_URL, mReqURL.c_str());
  20.133 +	check_curl_easy_code(code, CURLOPT_URL);
  20.134 +	code = curl_easy_setopt(mCurlHandle, CURLOPT_PRIVATE, this);
  20.135 +	check_curl_easy_code(code, CURLOPT_PRIVATE);
  20.136 +	code = curl_easy_setopt(mCurlHandle, CURLOPT_ENCODING, "");
  20.137 +	check_curl_easy_code(code, CURLOPT_ENCODING);
  20.138  
  20.139 -	curl_easy_setopt(mCurlHandle, CURLOPT_WRITEFUNCTION, writeCallback);
  20.140 -	curl_easy_setopt(mCurlHandle, CURLOPT_READFUNCTION,  readCallback);	
  20.141 -	curl_easy_setopt(mCurlHandle, CURLOPT_READDATA, this);
  20.142 -	curl_easy_setopt(mCurlHandle, CURLOPT_WRITEDATA, this);
  20.143 -	curl_easy_setopt(mCurlHandle, CURLOPT_URL, mReqURL.c_str());
  20.144 -	curl_easy_setopt(mCurlHandle, CURLOPT_PRIVATE, this);
  20.145 -	curl_easy_setopt(mCurlHandle, CURLOPT_MAXREDIRS, HTTP_REDIRECTS_DEFAULT);	
  20.146 +	// The Linksys WRT54G V5 router has an issue with frequent
  20.147 +	// DNS lookups from LAN machines.  If they happen too often,
  20.148 +	// like for every HTTP request, the router gets annoyed after
  20.149 +	// about 700 or so requests and starts issuing TCP RSTs to
  20.150 +	// new connections.  Reuse the DNS lookups for even a few
  20.151 +	// seconds and no RSTs.
  20.152 +	code = curl_easy_setopt(mCurlHandle, CURLOPT_DNS_CACHE_TIMEOUT, 15);
  20.153 +	check_curl_easy_code(code, CURLOPT_DNS_CACHE_TIMEOUT);
  20.154 +	code = curl_easy_setopt(mCurlHandle, CURLOPT_AUTOREFERER, 1);
  20.155 +	check_curl_easy_code(code, CURLOPT_AUTOREFERER);
  20.156 +	code = curl_easy_setopt(mCurlHandle, CURLOPT_FOLLOWLOCATION, 1);
  20.157 +	check_curl_easy_code(code, CURLOPT_FOLLOWLOCATION);
  20.158 +	code = curl_easy_setopt(mCurlHandle, CURLOPT_MAXREDIRS, HTTP_REDIRECTS_DEFAULT);
  20.159 +	check_curl_easy_code(code, CURLOPT_MAXREDIRS);
  20.160 +	code = curl_easy_setopt(mCurlHandle, CURLOPT_WRITEFUNCTION, writeCallback);
  20.161 +	check_curl_easy_code(code, CURLOPT_WRITEFUNCTION);
  20.162 +	code = curl_easy_setopt(mCurlHandle, CURLOPT_WRITEDATA, this);
  20.163 +	check_curl_easy_code(code, CURLOPT_WRITEDATA);
  20.164 +	code = curl_easy_setopt(mCurlHandle, CURLOPT_READFUNCTION, readCallback);
  20.165 +	check_curl_easy_code(code, CURLOPT_READFUNCTION);
  20.166 +	code = curl_easy_setopt(mCurlHandle, CURLOPT_READDATA, this);
  20.167 +	check_curl_easy_code(code, CURLOPT_READDATA);
  20.168 +	code = curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYPEER, 1);
  20.169 +	check_curl_easy_code(code, CURLOPT_SSL_VERIFYPEER);
  20.170 +	code = curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYHOST, 0);
  20.171 +	check_curl_easy_code(code, CURLOPT_SSL_VERIFYHOST);
  20.172  
  20.173 -	const std::string * opt_value(NULL);
  20.174 -	long opt_long(0L);
  20.175 -	policy.get(HttpRequest::GP_LLPROXY, &opt_long);
  20.176 -	if (opt_long)
  20.177 +	if (policy.mUseLLProxy)
  20.178  	{
  20.179  		// Use the viewer-based thread-safe API which has a
  20.180  		// fast/safe check for proxy enable.  Would like to
  20.181  		// encapsulate this someway...
  20.182  		LLProxy::getInstance()->applyProxySettings(mCurlHandle);
  20.183  	}
  20.184 -	else if (policy.get(HttpRequest::GP_HTTP_PROXY, &opt_value))
  20.185 +	else if (policy.mHttpProxy.size())
  20.186  	{
  20.187  		// *TODO:  This is fine for now but get fuller socks5/
  20.188  		// authentication thing going later....
  20.189 -		curl_easy_setopt(mCurlHandle, CURLOPT_PROXY, opt_value->c_str());
  20.190 -		curl_easy_setopt(mCurlHandle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
  20.191 +		code = curl_easy_setopt(mCurlHandle, CURLOPT_PROXY, policy.mHttpProxy.c_str());
  20.192 +		check_curl_easy_code(code, CURLOPT_PROXY);
  20.193 +		code = curl_easy_setopt(mCurlHandle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
  20.194 +		check_curl_easy_code(code, CURLOPT_PROXYTYPE);
  20.195  	}
  20.196 -	if (policy.get(HttpRequest::GP_CA_PATH, &opt_value))
  20.197 +	if (policy.mCAPath.size())
  20.198  	{
  20.199 -		curl_easy_setopt(mCurlHandle, CURLOPT_CAPATH, opt_value->c_str());
  20.200 +		code = curl_easy_setopt(mCurlHandle, CURLOPT_CAPATH, policy.mCAPath.c_str());
  20.201 +		check_curl_easy_code(code, CURLOPT_CAPATH);
  20.202  	}
  20.203 -	if (policy.get(HttpRequest::GP_CA_FILE, &opt_value))
  20.204 +	if (policy.mCAFile.size())
  20.205  	{
  20.206 -		curl_easy_setopt(mCurlHandle, CURLOPT_CAINFO, opt_value->c_str());
  20.207 +		code = curl_easy_setopt(mCurlHandle, CURLOPT_CAINFO, policy.mCAFile.c_str());
  20.208 +		check_curl_easy_code(code, CURLOPT_CAINFO);
  20.209  	}
  20.210  	
  20.211  	switch (mReqMethod)
  20.212  	{
  20.213  	case HOR_GET:
  20.214 -		curl_easy_setopt(mCurlHandle, CURLOPT_HTTPGET, 1);
  20.215 +		code = curl_easy_setopt(mCurlHandle, CURLOPT_HTTPGET, 1);
  20.216 +		check_curl_easy_code(code, CURLOPT_HTTPGET);
  20.217  		mCurlHeaders = curl_slist_append(mCurlHeaders, "Connection: keep-alive");
  20.218  		mCurlHeaders = curl_slist_append(mCurlHeaders, "Keep-alive: 300");
  20.219  		break;
  20.220  		
  20.221  	case HOR_POST:
  20.222  		{
  20.223 -			curl_easy_setopt(mCurlHandle, CURLOPT_POST, 1);
  20.224 -			curl_easy_setopt(mCurlHandle, CURLOPT_ENCODING, "");
  20.225 +			code = curl_easy_setopt(mCurlHandle, CURLOPT_POST, 1);
  20.226 +			check_curl_easy_code(code, CURLOPT_POST);
  20.227 +			code = curl_easy_setopt(mCurlHandle, CURLOPT_ENCODING, "");
  20.228 +			check_curl_easy_code(code, CURLOPT_ENCODING);
  20.229  			long data_size(0);
  20.230  			if (mReqBody)
  20.231  			{
  20.232  				data_size = mReqBody->size();
  20.233  			}
  20.234 -			curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDS, static_cast<void *>(NULL));
  20.235 -			curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDSIZE, data_size);
  20.236 +			code = curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDS, static_cast<void *>(NULL));
  20.237 +			check_curl_easy_code(code, CURLOPT_POSTFIELDS);
  20.238 +			code = curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDSIZE, data_size);
  20.239 +			check_curl_easy_code(code, CURLOPT_POSTFIELDSIZE);
  20.240  			mCurlHeaders = curl_slist_append(mCurlHeaders, "Expect:");
  20.241  			mCurlHeaders = curl_slist_append(mCurlHeaders, "Connection: keep-alive");
  20.242  			mCurlHeaders = curl_slist_append(mCurlHeaders, "Keep-alive: 300");
  20.243 @@ -445,14 +519,17 @@
  20.244  		
  20.245  	case HOR_PUT:
  20.246  		{
  20.247 -			curl_easy_setopt(mCurlHandle, CURLOPT_UPLOAD, 1);
  20.248 +			code = curl_easy_setopt(mCurlHandle, CURLOPT_UPLOAD, 1);
  20.249 +			check_curl_easy_code(code, CURLOPT_UPLOAD);
  20.250  			long data_size(0);
  20.251  			if (mReqBody)
  20.252  			{
  20.253  				data_size = mReqBody->size();
  20.254  			}
  20.255 -			curl_easy_setopt(mCurlHandle, CURLOPT_INFILESIZE, data_size);
  20.256 -			curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDS, (void *) NULL);
  20.257 +			code = curl_easy_setopt(mCurlHandle, CURLOPT_INFILESIZE, data_size);
  20.258 +			check_curl_easy_code(code, CURLOPT_INFILESIZE);
  20.259 +			code = curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDS, (void *) NULL);
  20.260 +			check_curl_easy_code(code, CURLOPT_POSTFIELDS);
  20.261  			mCurlHeaders = curl_slist_append(mCurlHeaders, "Expect:");
  20.262  			mCurlHeaders = curl_slist_append(mCurlHeaders, "Connection: keep-alive");
  20.263  			mCurlHeaders = curl_slist_append(mCurlHeaders, "Keep-alive: 300");
  20.264 @@ -469,9 +546,12 @@
  20.265  	// Tracing
  20.266  	if (mTracing >= HTTP_TRACE_CURL_HEADERS)
  20.267  	{
  20.268 -		curl_easy_setopt(mCurlHandle, CURLOPT_VERBOSE, 1);
  20.269 -		curl_easy_setopt(mCurlHandle, CURLOPT_DEBUGDATA, this);
  20.270 -		curl_easy_setopt(mCurlHandle, CURLOPT_DEBUGFUNCTION, debugCallback);
  20.271 +		code = curl_easy_setopt(mCurlHandle, CURLOPT_VERBOSE, 1);
  20.272 +		check_curl_easy_code(code, CURLOPT_VERBOSE);
  20.273 +		code = curl_easy_setopt(mCurlHandle, CURLOPT_DEBUGDATA, this);
  20.274 +		check_curl_easy_code(code, CURLOPT_DEBUGDATA);
  20.275 +		code = curl_easy_setopt(mCurlHandle, CURLOPT_DEBUGFUNCTION, debugCallback);
  20.276 +		check_curl_easy_code(code, CURLOPT_DEBUGFUNCTION);
  20.277  	}
  20.278  	
  20.279  	// There's a CURLOPT for this now...
  20.280 @@ -499,13 +579,22 @@
  20.281  
  20.282  	// Request options
  20.283  	long timeout(HTTP_REQUEST_TIMEOUT_DEFAULT);
  20.284 +	long xfer_timeout(HTTP_REQUEST_XFER_TIMEOUT_DEFAULT);
  20.285  	if (mReqOptions)
  20.286 -	{
  20.287 + 	{
  20.288  		timeout = mReqOptions->getTimeout();
  20.289  		timeout = llclamp(timeout, HTTP_REQUEST_TIMEOUT_MIN, HTTP_REQUEST_TIMEOUT_MAX);
  20.290 +		xfer_timeout = mReqOptions->getTransferTimeout();
  20.291 +		xfer_timeout = llclamp(xfer_timeout, HTTP_REQUEST_TIMEOUT_MIN, HTTP_REQUEST_TIMEOUT_MAX);
  20.292  	}
  20.293 -	curl_easy_setopt(mCurlHandle, CURLOPT_TIMEOUT, timeout);
  20.294 -	curl_easy_setopt(mCurlHandle, CURLOPT_CONNECTTIMEOUT, timeout);
  20.295 +	if (xfer_timeout == 0L)
  20.296 +	{
  20.297 +		xfer_timeout = timeout;
  20.298 +	}
  20.299 +	code = curl_easy_setopt(mCurlHandle, CURLOPT_TIMEOUT, xfer_timeout);
  20.300 +	check_curl_easy_code(code, CURLOPT_TIMEOUT);
  20.301 +	code = curl_easy_setopt(mCurlHandle, CURLOPT_CONNECTTIMEOUT, timeout);
  20.302 +	check_curl_easy_code(code, CURLOPT_CONNECTTIMEOUT);
  20.303  
  20.304  	// Request headers
  20.305  	if (mReqHeaders)
  20.306 @@ -513,12 +602,15 @@
  20.307  		// Caller's headers last to override
  20.308  		mCurlHeaders = append_headers_to_slist(mReqHeaders, mCurlHeaders);
  20.309  	}
  20.310 -	curl_easy_setopt(mCurlHandle, CURLOPT_HTTPHEADER, mCurlHeaders);
  20.311 +	code = curl_easy_setopt(mCurlHandle, CURLOPT_HTTPHEADER, mCurlHeaders);
  20.312 +	check_curl_easy_code(code, CURLOPT_HTTPHEADER);
  20.313  
  20.314 -	if (mProcFlags & (PF_SCAN_RANGE_HEADER | PF_SAVE_HEADERS))
  20.315 +	if (mProcFlags & (PF_SCAN_RANGE_HEADER | PF_SAVE_HEADERS | PF_USE_RETRY_AFTER))
  20.316  	{
  20.317 -		curl_easy_setopt(mCurlHandle, CURLOPT_HEADERFUNCTION, headerCallback);
  20.318 -		curl_easy_setopt(mCurlHandle, CURLOPT_HEADERDATA, this);
  20.319 +		code = curl_easy_setopt(mCurlHandle, CURLOPT_HEADERFUNCTION, headerCallback);
  20.320 +		check_curl_easy_code(code, CURLOPT_HEADERFUNCTION);
  20.321 +		code = curl_easy_setopt(mCurlHandle, CURLOPT_HEADERDATA, this);
  20.322 +		check_curl_easy_code(code, CURLOPT_HEADERDATA);
  20.323  	}
  20.324  	
  20.325  	if (status)
  20.326 @@ -559,7 +651,7 @@
  20.327  		{
  20.328  			// Warn but continue if the read position moves beyond end-of-body
  20.329  			// for some reason.
  20.330 -			LL_WARNS("HttpCore") << "Request body position beyond body size.  Truncating request body."
  20.331 +			LL_WARNS("CoreHttp") << "Request body position beyond body size.  Truncating request body."
  20.332  								 << LL_ENDL;
  20.333  		}
  20.334  		return 0;
  20.335 @@ -576,15 +668,15 @@
  20.336  {
  20.337  	static const char status_line[] = "HTTP/";
  20.338  	static const size_t status_line_len = sizeof(status_line) - 1;
  20.339 -
  20.340 -	static const char con_ran_line[] = "content-range:";
  20.341 -	static const size_t con_ran_line_len = sizeof(con_ran_line) - 1;
  20.342 -
  20.343 +	static const char con_ran_line[] = "content-range";
  20.344 +	static const char con_retry_line[] = "retry-after";
  20.345 +	
  20.346  	HttpOpRequest * op(static_cast<HttpOpRequest *>(userdata));
  20.347  
  20.348  	const size_t hdr_size(size * nmemb);
  20.349  	const char * hdr_data(static_cast<const char *>(data));		// Not null terminated
  20.350 -
  20.351 +	bool is_header(true);
  20.352 +	
  20.353  	if (hdr_size >= status_line_len && ! strncmp(status_line, hdr_data, status_line_len))
  20.354  	{
  20.355  		// One of possibly several status lines.  Reset what we know and start over
  20.356 @@ -592,11 +684,13 @@
  20.357  		op->mReplyOffset = 0;
  20.358  		op->mReplyLength = 0;
  20.359  		op->mReplyFullLength = 0;
  20.360 +		op->mReplyRetryAfter = 0;
  20.361  		op->mStatus = HttpStatus();
  20.362  		if (op->mReplyHeaders)
  20.363  		{
  20.364 -			op->mReplyHeaders->mHeaders.clear();
  20.365 +			op->mReplyHeaders->clear();
  20.366  		}
  20.367 +		is_header = false;
  20.368  	}
  20.369  
  20.370  	// Nothing in here wants a final CR/LF combination.  Remove
  20.371 @@ -609,52 +703,109 @@
  20.372  			--wanted_hdr_size;
  20.373  		}
  20.374  	}
  20.375 +
  20.376 +	// Copy and normalize header fragments for the following
  20.377 +	// stages.  Would like to modify the data in-place but that
  20.378 +	// may not be allowed and we need one byte extra for NUL.
  20.379 +	// At the end of this we will have:
  20.380 +	//
  20.381 +	// If ':' present in header:
  20.382 +	//   1.  name points to text to left of colon which
  20.383 +	//       will be ascii lower-cased and left and right
  20.384 +	//       trimmed of whitespace.
  20.385 +	//   2.  value points to text to right of colon which
  20.386 +	//       will be left trimmed of whitespace.
  20.387 +	// Otherwise:
  20.388 +	//   1.  name points to header which will be left
  20.389 +	//       trimmed of whitespace.
  20.390 +	//   2.  value is NULL
  20.391 +	// Any non-NULL pointer may point to a zero-length string.
  20.392 +	//
  20.393 +	if (wanted_hdr_size >= op->mCurlTempLen)
  20.394 +	{
  20.395 +		delete [] op->mCurlTemp;
  20.396 +		op->mCurlTempLen = 2 * wanted_hdr_size + 1;
  20.397 +		op->mCurlTemp = new char [op->mCurlTempLen];
  20.398 +	}
  20.399 +	memcpy(op->mCurlTemp, hdr_data, wanted_hdr_size);
  20.400 +	op->mCurlTemp[wanted_hdr_size] = '\0';
  20.401 +	char * name(op->mCurlTemp);
  20.402 +	char * value(strchr(name, ':'));
  20.403 +	if (value)
  20.404 +	{
  20.405 +		*value++ = '\0';
  20.406 +		os_strlower(name);
  20.407 +		name = os_strtrim(name);
  20.408 +		value = os_strltrim(value);
  20.409 +	}
  20.410 +	else
  20.411 +	{
  20.412 +		// Doesn't look well-formed, do minimal normalization on it
  20.413 +		name = os_strltrim(name);
  20.414 +	}
  20.415 +
  20.416 +	// Normalized, now reject headers with empty names.
  20.417 +	if (! *name)
  20.418 +	{
  20.419 +		// No use continuing
  20.420 +		return hdr_size;
  20.421 +	}
  20.422  	
  20.423  	// Save header if caller wants them in the response
  20.424 -	if (op->mProcFlags & PF_SAVE_HEADERS)
  20.425 +	if (is_header && op->mProcFlags & PF_SAVE_HEADERS)
  20.426  	{
  20.427  		// Save headers in response
  20.428  		if (! op->mReplyHeaders)
  20.429  		{
  20.430  			op->mReplyHeaders = new HttpHeaders;
  20.431  		}
  20.432 -		op->mReplyHeaders->mHeaders.push_back(std::string(hdr_data, wanted_hdr_size));
  20.433 +		op->mReplyHeaders->append(name, value ? value : "");
  20.434  	}
  20.435  
  20.436 +	// From this point, header-specific processors are free to
  20.437 +	// modify the header value.
  20.438 +	
  20.439  	// Detect and parse 'Content-Range' headers
  20.440 -	if (op->mProcFlags & PF_SCAN_RANGE_HEADER)
  20.441 +	if (is_header
  20.442 +		&& op->mProcFlags & PF_SCAN_RANGE_HEADER
  20.443 +		&& value && *value
  20.444 +		&& ! strcmp(name, con_ran_line))
  20.445  	{
  20.446 -		char hdr_buffer[128];			// Enough for a reasonable header
  20.447 -		size_t frag_size((std::min)(wanted_hdr_size, sizeof(hdr_buffer) - 1));
  20.448 -		
  20.449 -		memcpy(hdr_buffer, hdr_data, frag_size);
  20.450 -		hdr_buffer[frag_size] = '\0';
  20.451 -		if (frag_size > con_ran_line_len &&
  20.452 -			! os_strncasecmp(hdr_buffer, con_ran_line, con_ran_line_len))
  20.453 +		unsigned int first(0), last(0), length(0);
  20.454 +		int status;
  20.455 +
  20.456 +		if (! (status = parse_content_range_header(value, &first, &last, &length)))
  20.457  		{
  20.458 -			unsigned int first(0), last(0), length(0);
  20.459 -			int status;
  20.460 +			// Success, record the fragment position
  20.461 +			op->mReplyOffset = first;
  20.462 +			op->mReplyLength = last - first + 1;
  20.463 +			op->mReplyFullLength = length;
  20.464 +		}
  20.465 +		else if (-1 == status)
  20.466 +		{
  20.467 +			// Response is badly formed and shouldn't be accepted
  20.468 +			op->mStatus = HttpStatus(HttpStatus::LLCORE, HE_INV_CONTENT_RANGE_HDR);
  20.469 +		}
  20.470 +		else
  20.471 +		{
  20.472 +			// Ignore the unparsable.
  20.473 +			LL_INFOS_ONCE("CoreHttp") << "Problem parsing odd Content-Range header:  '"
  20.474 +									  << std::string(hdr_data, wanted_hdr_size)
  20.475 +									  << "'.  Ignoring."
  20.476 +									  << LL_ENDL;
  20.477 +		}
  20.478 +	}
  20.479  
  20.480 -			if (! (status = parse_content_range_header(hdr_buffer, &first, &last, &length)))
  20.481 -			{
  20.482 -				// Success, record the fragment position
  20.483 -				op->mReplyOffset = first;
  20.484 -				op->mReplyLength = last - first + 1;
  20.485 -				op->mReplyFullLength = length;
  20.486 -			}
  20.487 -			else if (-1 == status)
  20.488 -			{
  20.489 -				// Response is badly formed and shouldn't be accepted
  20.490 -				op->mStatus = HttpStatus(HttpStatus::LLCORE, HE_INV_CONTENT_RANGE_HDR);
  20.491 -			}
  20.492 -			else
  20.493 -			{
  20.494 -				// Ignore the unparsable.
  20.495 -				LL_INFOS_ONCE("CoreHttp") << "Problem parsing odd Content-Range header:  '"
  20.496 -										  << std::string(hdr_data, frag_size)
  20.497 -										  << "'.  Ignoring."
  20.498 -										  << LL_ENDL;
  20.499 -			}
  20.500 +	// Detect and parse 'Retry-After' headers
  20.501 +	if (is_header
  20.502 +		&& op->mProcFlags & PF_USE_RETRY_AFTER
  20.503 +		&& value && *value
  20.504 +		&& ! strcmp(name, con_retry_line))
  20.505 +	{
  20.506 +		int time(0);
  20.507 +		if (! parse_retry_after_header(value, &time))
  20.508 +		{
  20.509 +			op->mReplyRetryAfter = time;
  20.510  		}
  20.511  	}
  20.512  
  20.513 @@ -769,14 +920,16 @@
  20.514  							   unsigned int * last,
  20.515  							   unsigned int * length)
  20.516  {
  20.517 +	static const char * const hdr_whitespace(" \t");
  20.518 +
  20.519  	char * tok_state(NULL), * tok(NULL);
  20.520  	bool match(true);
  20.521  			
  20.522 -	if (! os_strtok_r(buffer, hdr_separator, &tok_state))
  20.523 +	if (! (tok = os_strtok_r(buffer, hdr_whitespace, &tok_state)))
  20.524  		match = false;
  20.525 -	if (match && (tok = os_strtok_r(NULL, hdr_whitespace, &tok_state)))
  20.526 -		match = 0 == os_strcasecmp("bytes", tok);
  20.527 -	if (match && ! (tok = os_strtok_r(NULL, " \t", &tok_state)))
  20.528 +	else
  20.529 +		match = (0 == os_strcasecmp("bytes", tok));
  20.530 +	if (match && ! (tok = os_strtok_r(NULL, hdr_whitespace, &tok_state)))
  20.531  		match = false;
  20.532  	if (match)
  20.533  	{
  20.534 @@ -815,6 +968,25 @@
  20.535  }
  20.536  
  20.537  
  20.538 +int parse_retry_after_header(char * buffer, int * time)
  20.539 +{
  20.540 +	char * endptr(buffer);
  20.541 +	long lcl_time(strtol(buffer, &endptr, 10));
  20.542 +	if (*endptr == '\0' && endptr != buffer && lcl_time > 0)
  20.543 +	{
  20.544 +		*time = lcl_time;
  20.545 +		return 0;
  20.546 +	}
  20.547 +
  20.548 +	// Could attempt to parse HTTP time here but we're not really
  20.549 +	// interested in it.  Scheduling based on wallclock time on
  20.550 +	// user hardware will lead to tears.
  20.551 +	
  20.552 +	// Header is there but badly/unexpectedly formed, try to ignore it.
  20.553 +	return 1;
  20.554 +}
  20.555 +
  20.556 +
  20.557  void escape_libcurl_debug_data(char * buffer, size_t len, bool scrub, std::string & safe_line)
  20.558  {
  20.559  	std::string out;
  20.560 @@ -851,15 +1023,6 @@
  20.561  }
  20.562  
  20.563  
  20.564 -int os_strncasecmp(const char *s1, const char *s2, size_t n)
  20.565 -{
  20.566 -#if LL_WINDOWS
  20.567 -	return _strnicmp(s1, s2, n);
  20.568 -#else
  20.569 -	return strncasecmp(s1, s2, n);
  20.570 -#endif	// LL_WINDOWS
  20.571 -}
  20.572 -
  20.573  
  20.574  int os_strcasecmp(const char *s1, const char *s2)
  20.575  {
  20.576 @@ -881,6 +1044,73 @@
  20.577  }
  20.578  
  20.579  
  20.580 +void os_strlower(char * str)
  20.581 +{
  20.582 +	for (char c(0); (c = *str); ++str)
  20.583 +	{
  20.584 +		*str = tolower(c);
  20.585 +	}
  20.586 +}
  20.587 +
  20.588 +
  20.589 +char * os_strtrim(char * lstr)
  20.590 +{
  20.591 +	while (' ' == *lstr || '\t' == *lstr)
  20.592 +	{
  20.593 +		++lstr;
  20.594 +	}
  20.595 +	if (*lstr)
  20.596 +	{
  20.597 +		char * rstr(lstr + strlen(lstr));
  20.598 +		while (lstr < rstr && *--rstr)
  20.599 +		{
  20.600 +			if (' ' == *rstr || '\t' == *rstr)
  20.601 +			{
  20.602 +				*rstr = '\0';
  20.603 +			}
  20.604 +		}
  20.605 +		llassert(lstr <= rstr);
  20.606 +	}
  20.607 +	return lstr;
  20.608 +}
  20.609 +
  20.610 +
  20.611 +char * os_strltrim(char * lstr)
  20.612 +{
  20.613 +	while (' ' == *lstr || '\t' == *lstr)
  20.614 +	{
  20.615 +		++lstr;
  20.616 +	}
  20.617 +	return lstr;
  20.618 +}
  20.619 +
  20.620 +
  20.621 +void check_curl_easy_code(CURLcode code, int curl_setopt_option)
  20.622 +{
  20.623 +	if (CURLE_OK != code)
  20.624 +	{
  20.625 +		// Comment from old llcurl code which may no longer apply:
  20.626 +		//
  20.627 +		// linux appears to throw a curl error once per session for a bad initialization
  20.628 +		// at a pretty random time (when enabling cookies).
  20.629 +		LL_WARNS("CoreHttp") << "libcurl error detected:  " << curl_easy_strerror(code)
  20.630 +							 << ", curl_easy_setopt option:  " << curl_setopt_option
  20.631 +							 << LL_ENDL;
  20.632 +	}
  20.633 +}
  20.634 +
  20.635 +
  20.636 +void check_curl_easy_code(CURLcode code)
  20.637 +{
  20.638 +	if (CURLE_OK != code)
  20.639 +	{
  20.640 +		// Comment from old llcurl code which may no longer apply:
  20.641 +		//
  20.642 +		// linux appears to throw a curl error once per session for a bad initialization
  20.643 +		// at a pretty random time (when enabling cookies).
  20.644 +		LL_WARNS("CoreHttp") << "libcurl error detected:  " << curl_easy_strerror(code)
  20.645 +							 << LL_ENDL;
  20.646 +	}
  20.647 +}
  20.648 +
  20.649  }  // end anonymous namespace
  20.650 -
  20.651 -		
    21.1 --- a/indra/llcorehttp/_httpoprequest.h	Wed Feb 12 17:18:03 2014 -0800
    21.2 +++ b/indra/llcorehttp/_httpoprequest.h	Wed Feb 12 18:58:40 2014 -0800
    21.3 @@ -4,7 +4,7 @@
    21.4   *
    21.5   * $LicenseInfo:firstyear=2012&license=viewerlgpl$
    21.6   * Second Life Viewer Source Code
    21.7 - * Copyright (C) 2012, Linden Research, Inc.
    21.8 + * Copyright (C) 2012-2013, Linden Research, Inc.
    21.9   *
   21.10   * This library is free software; you can redistribute it and/or
   21.11   * modify it under the terms of the GNU Lesser General Public
   21.12 @@ -157,6 +157,7 @@
   21.13  	unsigned int		mProcFlags;
   21.14  	static const unsigned int	PF_SCAN_RANGE_HEADER = 0x00000001U;
   21.15  	static const unsigned int	PF_SAVE_HEADERS = 0x00000002U;
   21.16 +	static const unsigned int	PF_USE_RETRY_AFTER = 0x00000004U;
   21.17  
   21.18  public:
   21.19  	// Request data
   21.20 @@ -174,6 +175,8 @@
   21.21  	HttpService *		mCurlService;
   21.22  	curl_slist *		mCurlHeaders;
   21.23  	size_t				mCurlBodyPos;
   21.24 +	char *				mCurlTemp;				// Scratch buffer for header processing
   21.25 +	size_t				mCurlTempLen;
   21.26  	
   21.27  	// Result data
   21.28  	HttpStatus			mStatus;
   21.29 @@ -183,9 +186,11 @@
   21.30  	size_t				mReplyFullLength;
   21.31  	HttpHeaders *		mReplyHeaders;
   21.32  	std::string			mReplyConType;
   21.33 +	int					mReplyRetryAfter;
   21.34  
   21.35  	// Policy data
   21.36  	int					mPolicyRetries;
   21.37 +	int					mPolicy503Retries;
   21.38  	HttpTime			mPolicyRetryAt;
   21.39  	int					mPolicyRetryLimit;
   21.40  };  // end class HttpOpRequest
    22.1 --- a/indra/llcorehttp/_httpopsetget.cpp	Wed Feb 12 17:18:03 2014 -0800
    22.2 +++ b/indra/llcorehttp/_httpopsetget.cpp	Wed Feb 12 18:58:40 2014 -0800
    22.3 @@ -4,7 +4,7 @@
    22.4   *
    22.5   * $LicenseInfo:firstyear=2012&license=viewerlgpl$
    22.6   * Second Life Viewer Source Code
    22.7 - * Copyright (C) 2012, Linden Research, Inc.
    22.8 + * Copyright (C) 2012-2013, Linden Research, Inc.
    22.9   *
   22.10   * This library is free software; you can redistribute it and/or
   22.11   * modify it under the terms of the GNU Lesser General Public
   22.12 @@ -27,6 +27,7 @@
   22.13  #include "_httpopsetget.h"
   22.14  
   22.15  #include "httpcommon.h"
   22.16 +#include "httprequest.h"
   22.17  
   22.18  #include "_httpservice.h"
   22.19  #include "_httppolicy.h"
   22.20 @@ -43,10 +44,11 @@
   22.21  
   22.22  HttpOpSetGet::HttpOpSetGet()
   22.23  	: HttpOperation(),
   22.24 -	  mIsGlobal(false),
   22.25 -	  mDoSet(false),
   22.26 -	  mSetting(-1),				// Nothing requested
   22.27 -	  mLongValue(0L)
   22.28 +	  mReqOption(HttpRequest::PO_CONNECTION_LIMIT),
   22.29 +	  mReqClass(HttpRequest::INVALID_POLICY_ID),
   22.30 +	  mReqDoSet(false),
   22.31 +	  mReqLongValue(0L),
   22.32 +	  mReplyLongValue(0L)
   22.33  {}
   22.34  
   22.35  
   22.36 @@ -54,37 +56,84 @@
   22.37  {}
   22.38  
   22.39  
   22.40 -void HttpOpSetGet::setupGet(HttpRequest::EGlobalPolicy setting)
   22.41 +HttpStatus HttpOpSetGet::setupGet(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass)
   22.42  {
   22.43 -	mIsGlobal = true;
   22.44 -	mSetting = setting;
   22.45 +	HttpStatus status;
   22.46 +	
   22.47 +	mReqOption = opt;
   22.48 +	mReqClass = pclass;
   22.49 +	return status;
   22.50  }
   22.51  
   22.52  
   22.53 -void HttpOpSetGet::setupSet(HttpRequest::EGlobalPolicy setting, const std::string & value)
   22.54 +HttpStatus HttpOpSetGet::setupSet(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass, long value)
   22.55  {
   22.56 -	mIsGlobal = true;
   22.57 -	mDoSet = true;
   22.58 -	mSetting = setting;
   22.59 -	mStrValue = value;
   22.60 +	HttpStatus status;
   22.61 +
   22.62 +	if (! HttpService::sOptionDesc[opt].mIsLong)
   22.63 +	{
   22.64 +		return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
   22.65 +	}
   22.66 +	if (! HttpService::sOptionDesc[opt].mIsDynamic)
   22.67 +	{
   22.68 +		return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC);
   22.69 +	}
   22.70 +	
   22.71 +	mReqOption = opt;
   22.72 +	mReqClass = pclass;
   22.73 +	mReqDoSet = true;
   22.74 +	mReqLongValue = value;
   22.75 +	
   22.76 +	return status;
   22.77 +}
   22.78 +
   22.79 +
   22.80 +HttpStatus HttpOpSetGet::setupSet(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass, const std::string & value)
   22.81 +{
   22.82 +	HttpStatus status;
   22.83 +
   22.84 +	if (HttpService::sOptionDesc[opt].mIsLong)
   22.85 +	{
   22.86 +		return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
   22.87 +	}
   22.88 +	if (! HttpService::sOptionDesc[opt].mIsDynamic)
   22.89 +	{
   22.90 +		return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC);
   22.91 +	}
   22.92 +
   22.93 +	mReqOption = opt;
   22.94 +	mReqClass = pclass;
   22.95 +	mReqDoSet = true;
   22.96 +	mReqStrValue = value;
   22.97 +	
   22.98 +	return status;
   22.99  }
  22.100  
  22.101  
  22.102  void HttpOpSetGet::stageFromRequest(HttpService * service)
  22.103  {
  22.104 -	HttpPolicyGlobal & pol_opt(service->getPolicy().getGlobalOptions());
  22.105 -	HttpRequest::EGlobalPolicy setting(static_cast<HttpRequest::EGlobalPolicy>(mSetting));
  22.106 -	
  22.107 -	if (mDoSet)
  22.108 +	if (mReqDoSet)
  22.109  	{
  22.110 -		mStatus = pol_opt.set(setting, mStrValue);
  22.111 +		if (HttpService::sOptionDesc[mReqOption].mIsLong)
  22.112 +		{
  22.113 +			mStatus = service->setPolicyOption(mReqOption, mReqClass,
  22.114 +											   mReqLongValue, &mReplyLongValue);
  22.115 +		}
  22.116 +		else
  22.117 +		{
  22.118 +			mStatus = service->setPolicyOption(mReqOption, mReqClass,
  22.119 +											   mReqStrValue, &mReplyStrValue);
  22.120 +		}
  22.121  	}
  22.122 -	if (mStatus)
  22.123 +	else
  22.124  	{
  22.125 -		const std::string * value(NULL);
  22.126 -		if ((mStatus = pol_opt.get(setting, &value)))
  22.127 +		if (HttpService::sOptionDesc[mReqOption].mIsLong)
  22.128  		{
  22.129 -			mStrValue = *value;
  22.130 +			mStatus = service->getPolicyOption(mReqOption, mReqClass, &mReplyLongValue);
  22.131 +		}
  22.132 +		else
  22.133 +		{
  22.134 +			mStatus = service->getPolicyOption(mReqOption, mReqClass, &mReplyStrValue);
  22.135  		}
  22.136  	}
  22.137  	
    23.1 --- a/indra/llcorehttp/_httpopsetget.h	Wed Feb 12 17:18:03 2014 -0800
    23.2 +++ b/indra/llcorehttp/_httpopsetget.h	Wed Feb 12 18:58:40 2014 -0800
    23.3 @@ -4,7 +4,7 @@
    23.4   *
    23.5   * $LicenseInfo:firstyear=2012&license=viewerlgpl$
    23.6   * Second Life Viewer Source Code
    23.7 - * Copyright (C) 2012, Linden Research, Inc.
    23.8 + * Copyright (C) 2012-2013, Linden Research, Inc.
    23.9   *
   23.10   * This library is free software; you can redistribute it and/or
   23.11   * modify it under the terms of the GNU Lesser General Public
   23.12 @@ -46,7 +46,10 @@
   23.13  /// configuration settings.
   23.14  ///
   23.15  /// *NOTE:  Expect this to change.  Don't really like it yet.
   23.16 -
   23.17 +///
   23.18 +/// *TODO:  Can't return values to caller yet.  Need to do
   23.19 +/// something better with HttpResponse and visitNotifier().
   23.20 +///
   23.21  class HttpOpSetGet : public HttpOperation
   23.22  {
   23.23  public:
   23.24 @@ -61,19 +64,23 @@
   23.25  
   23.26  public:
   23.27  	/// Threading:  called by application thread
   23.28 -	void setupGet(HttpRequest::EGlobalPolicy setting);
   23.29 -	void setupSet(HttpRequest::EGlobalPolicy setting, const std::string & value);
   23.30 +	HttpStatus setupGet(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass);
   23.31 +	HttpStatus setupSet(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass, long value);
   23.32 +	HttpStatus setupSet(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass, const std::string & value);
   23.33  
   23.34  	virtual void stageFromRequest(HttpService *);
   23.35  
   23.36  public:
   23.37  	// Request data
   23.38 -	bool				mIsGlobal;
   23.39 -	bool				mDoSet;
   23.40 -	int					mSetting;
   23.41 -	long				mLongValue;
   23.42 -	std::string			mStrValue;
   23.43 +	HttpRequest::EPolicyOption	mReqOption;
   23.44 +	HttpRequest::policy_t		mReqClass;
   23.45 +	bool						mReqDoSet;
   23.46 +	long						mReqLongValue;
   23.47 +	std::string					mReqStrValue;
   23.48  
   23.49 +	// Reply Data
   23.50 +	long						mReplyLongValue;
   23.51 +	std::string					mReplyStrValue;
   23.52  };  // end class HttpOpSetGet
   23.53  
   23.54  
    24.1 --- a/indra/llcorehttp/_httppolicy.cpp	Wed Feb 12 17:18:03 2014 -0800
    24.2 +++ b/indra/llcorehttp/_httppolicy.cpp	Wed Feb 12 18:58:40 2014 -0800
    24.3 @@ -41,57 +41,70 @@
    24.4  
    24.5  
    24.6  // Per-policy-class data for a running system.
    24.7 -// Collection of queues, parameters, history, metrics, etc.
    24.8 +// Collection of queues, options and other data
    24.9  // for a single policy class.
   24.10  //
   24.11  // Threading:  accessed only by worker thread
   24.12 -struct HttpPolicy::State
   24.13 +struct HttpPolicy::ClassState
   24.14  {
   24.15  public:
   24.16 -	State()
   24.17 -		: mConnMax(HTTP_CONNECTION_LIMIT_DEFAULT),
   24.18 -		  mConnAt(HTTP_CONNECTION_LIMIT_DEFAULT),
   24.19 -		  mConnMin(1),
   24.20 -		  mNextSample(0),
   24.21 -		  mErrorCount(0),
   24.22 -		  mErrorFactor(0)
   24.23 +	ClassState()
   24.24 +		: mThrottleEnd(0),
   24.25 +		  mThrottleLeft(0L),
   24.26 +		  mRequestCount(0L)
   24.27  		{}
   24.28  	
   24.29  	HttpReadyQueue		mReadyQueue;
   24.30  	HttpRetryQueue		mRetryQueue;
   24.31  
   24.32  	HttpPolicyClass		mOptions;
   24.33 -
   24.34 -	long				mConnMax;
   24.35 -	long				mConnAt;
   24.36 -	long				mConnMin;
   24.37 -
   24.38 -	HttpTime			mNextSample;
   24.39 -	unsigned long		mErrorCount;
   24.40 -	unsigned long		mErrorFactor;
   24.41 +	HttpTime			mThrottleEnd;
   24.42 +	long				mThrottleLeft;
   24.43 +	long				mRequestCount;
   24.44  };
   24.45  
   24.46  
   24.47  HttpPolicy::HttpPolicy(HttpService * service)
   24.48 -	: mActiveClasses(0),
   24.49 -	  mState(NULL),
   24.50 -	  mService(service)
   24.51 -{}
   24.52 +	: mService(service)
   24.53 +{
   24.54 +	// Create default class
   24.55 +	mClasses.push_back(new ClassState());
   24.56 +}
   24.57  
   24.58  
   24.59  HttpPolicy::~HttpPolicy()
   24.60  {
   24.61  	shutdown();
   24.62 +
   24.63 +	for (class_list_t::iterator it(mClasses.begin()); it != mClasses.end(); ++it)
   24.64 +	{
   24.65 +		delete (*it);
   24.66 +	}
   24.67 +	mClasses.clear();
   24.68  	
   24.69  	mService = NULL;
   24.70  }
   24.71  
   24.72  
   24.73 +HttpRequest::policy_t HttpPolicy::createPolicyClass()
   24.74 +{
   24.75 +	const HttpRequest::policy_t policy_class(mClasses.size());
   24.76 +	if (policy_class >= HTTP_POLICY_CLASS_LIMIT)
   24.77 +	{
   24.78 +		return HttpRequest::INVALID_POLICY_ID;
   24.79 +	}
   24.80 +	mClasses.push_back(new ClassState());
   24.81 +	return policy_class;
   24.82 +}
   24.83 +
   24.84 +
   24.85  void HttpPolicy::shutdown()
   24.86  {
   24.87 -	for (int policy_class(0); policy_class < mActiveClasses; ++policy_class)
   24.88 +	for (int policy_class(0); policy_class < mClasses.size(); ++policy_class)
   24.89  	{
   24.90 -		HttpRetryQueue & retryq(mState[policy_class].mRetryQueue);
   24.91 +		ClassState & state(*mClasses[policy_class]);
   24.92 +		
   24.93 +		HttpRetryQueue & retryq(state.mRetryQueue);
   24.94  		while (! retryq.empty())
   24.95  		{
   24.96  			HttpOpRequest * op(retryq.top());
   24.97 @@ -101,7 +114,7 @@
   24.98  			op->release();
   24.99  		}
  24.100  
  24.101 -		HttpReadyQueue & readyq(mState[policy_class].mReadyQueue);
  24.102 +		HttpReadyQueue & readyq(state.mReadyQueue);
  24.103  		while (! readyq.empty())
  24.104  		{
  24.105  			HttpOpRequest * op(readyq.top());
  24.106 @@ -111,28 +124,11 @@
  24.107  			op->release();
  24.108  		}
  24.109  	}
  24.110 -	delete [] mState;
  24.111 -	mState = NULL;
  24.112 -	mActiveClasses = 0;
  24.113  }
  24.114  
  24.115  
  24.116 -void HttpPolicy::start(const HttpPolicyGlobal & global,
  24.117 -					   const std::vector<HttpPolicyClass> & classes)
  24.118 -{
  24.119 -	llassert_always(! mState);
  24.120 -
  24.121 -	mGlobalOptions = global;
  24.122 -	mActiveClasses = classes.size();
  24.123 -	mState = new State [mActiveClasses];
  24.124 -	for (int i(0); i < mActiveClasses; ++i)
  24.125 -	{
  24.126 -		mState[i].mOptions = classes[i];
  24.127 -		mState[i].mConnMax = classes[i].mConnectionLimit;
  24.128 -		mState[i].mConnAt = mState[i].mConnMax;
  24.129 -		mState[i].mConnMin = 2;
  24.130 -	}
  24.131 -}
  24.132 +void HttpPolicy::start()
  24.133 +{}
  24.134  
  24.135  
  24.136  void HttpPolicy::addOp(HttpOpRequest * op)
  24.137 @@ -140,7 +136,8 @@
  24.138  	const int policy_class(op->mReqPolicy);
  24.139  	
  24.140  	op->mPolicyRetries = 0;
  24.141 -	mState[policy_class].mReadyQueue.push(op);
  24.142 +	op->mPolicy503Retries = 0;
  24.143 +	mClasses[policy_class]->mReadyQueue.push(op);
  24.144  }
  24.145  
  24.146  
  24.147 @@ -155,25 +152,39 @@
  24.148  			5000000				// ... to every 5.0 S.
  24.149  		};
  24.150  	static const int delta_max(int(LL_ARRAY_SIZE(retry_deltas)) - 1);
  24.151 -	
  24.152 +	static const HttpStatus error_503(503);
  24.153 +
  24.154  	const HttpTime now(totalTime());
  24.155  	const int policy_class(op->mReqPolicy);
  24.156 -	
  24.157 -	const HttpTime delta(retry_deltas[llclamp(op->mPolicyRetries, 0, delta_max)]);
  24.158 +	HttpTime delta(retry_deltas[llclamp(op->mPolicyRetries, 0, delta_max)]);
  24.159 +	bool external_delta(false);
  24.160 +
  24.161 +	if (op->mReplyRetryAfter > 0 && op->mReplyRetryAfter < 30)
  24.162 +	{
  24.163 +		delta = op->mReplyRetryAfter * U64L(1000000);
  24.164 +		external_delta = true;
  24.165 +	}
  24.166  	op->mPolicyRetryAt = now + delta;
  24.167  	++op->mPolicyRetries;
  24.168 -	LL_WARNS("CoreHttp") << "HTTP request " << static_cast<HttpHandle>(op)
  24.169 -						 << " retry " << op->mPolicyRetries
  24.170 -						 << " scheduled for +" << (delta / HttpTime(1000))
  24.171 -						 << " mS.  Status:  " << op->mStatus.toHex()
  24.172 -						 << LL_ENDL;
  24.173 -	if (op->mTracing > 0)
  24.174 +	if (error_503 == op->mStatus)
  24.175 +	{
  24.176 +		++op->mPolicy503Retries;
  24.177 +	}
  24.178 +	LL_DEBUGS("CoreHttp") << "HTTP request " << static_cast<HttpHandle>(op)
  24.179 +						  << " retry " << op->mPolicyRetries
  24.180 +						  << " scheduled in " << (delta / HttpTime(1000))
  24.181 +						  << " mS (" << (external_delta ? "external" : "internal")
  24.182 +						  << ").  Status:  " << op->mStatus.toTerseString()
  24.183 +						  << LL_ENDL;
  24.184 +	if (op->mTracing > HTTP_TRACE_OFF)
  24.185  	{
  24.186  		LL_INFOS("CoreHttp") << "TRACE, ToRetryQueue, Handle:  "
  24.187  							 << static_cast<HttpHandle>(op)
  24.188 +							 << ", Delta:  " << (delta / HttpTime(1000))
  24.189 +							 << ", Retries:  " << op->mPolicyRetries
  24.190  							 << LL_ENDL;
  24.191  	}
  24.192 -	mState[policy_class].mRetryQueue.push(op);
  24.193 +	mClasses[policy_class]->mRetryQueue.push(op);
  24.194  }
  24.195  
  24.196  
  24.197 @@ -188,21 +199,43 @@
  24.198  // the worker thread may sleep hard otherwise will ask for
  24.199  // normal polling frequency.
  24.200  //
  24.201 +// Implements a client-side request rate throttle as well.
  24.202 +// This is intended to mimic and predict throttling behavior
  24.203 +// of grid services but that is difficult to do with different
  24.204 +// time bases.  This also represents a rigid coupling between
  24.205 +// viewer and server that makes it hard to change parameters
  24.206 +// and I hope we can make this go away with pipelining.
  24.207 +//
  24.208  HttpService::ELoopSpeed HttpPolicy::processReadyQueue()
  24.209  {
  24.210  	const HttpTime now(totalTime());
  24.211  	HttpService::ELoopSpeed result(HttpService::REQUEST_SLEEP);
  24.212  	HttpLibcurl & transport(mService->getTransport());
  24.213  	
  24.214 -	for (int policy_class(0); policy_class < mActiveClasses; ++policy_class)
  24.215 +	for (int policy_class(0); policy_class < mClasses.size(); ++policy_class)
  24.216  	{
  24.217 -		State & state(mState[policy_class]);
  24.218 -		int active(transport.getActiveCountInClass(policy_class));
  24.219 -		int needed(state.mConnAt - active);		// Expect negatives here
  24.220 -
  24.221 +		ClassState & state(*mClasses[policy_class]);
  24.222  		HttpRetryQueue & retryq(state.mRetryQueue);
  24.223  		HttpReadyQueue & readyq(state.mReadyQueue);
  24.224 +
  24.225 +		if (retryq.empty() && readyq.empty())
  24.226 +		{
  24.227 +			continue;
  24.228 +		}
  24.229  		
  24.230 +		const bool throttle_enabled(state.mOptions.mThrottleRate > 0L);
  24.231 +		const bool throttle_current(throttle_enabled && now < state.mThrottleEnd);
  24.232 +
  24.233 +		if (throttle_current && state.mThrottleLeft <= 0)
  24.234 +		{
  24.235 +			// Throttled condition, don't serve this class but don't sleep hard.
  24.236 +			result = HttpService::NORMAL;
  24.237 +			continue;
  24.238 +		}
  24.239 +
  24.240 +		int active(transport.getActiveCountInClass(policy_class));
  24.241 +		int needed(state.mOptions.mConnectionLimit - active);		// Expect negatives here
  24.242 +
  24.243  		if (needed > 0)
  24.244  		{
  24.245  			// First see if we have any retries...
  24.246 @@ -216,10 +249,27 @@
  24.247  				
  24.248  				op->stageFromReady(mService);
  24.249  				op->release();
  24.250 -					
  24.251 +
  24.252 +				++state.mRequestCount;
  24.253  				--needed;
  24.254 +				if (throttle_enabled)
  24.255 +				{
  24.256 +					if (now >= state.mThrottleEnd)
  24.257 +					{
  24.258 +						// Throttle expired, move to next window
  24.259 +						LL_DEBUGS("CoreHttp") << "Throttle expired with " << state.mThrottleLeft
  24.260 +											  << " requests to go and " << state.mRequestCount
  24.261 +											  << " requests issued." << LL_ENDL;
  24.262 +						state.mThrottleLeft = state.mOptions.mThrottleRate;
  24.263 +						state.mThrottleEnd = now + HttpTime(1000000);
  24.264 +					}
  24.265 +					if (--state.mThrottleLeft <= 0)
  24.266 +					{
  24.267 +						goto throttle_on;
  24.268 +					}
  24.269 +				}
  24.270  			}
  24.271 -		
  24.272 +			
  24.273  			// Now go on to the new requests...
  24.274  			while (needed > 0 && ! readyq.empty())
  24.275  			{
  24.276 @@ -229,10 +279,29 @@
  24.277  				op->stageFromReady(mService);
  24.278  				op->release();
  24.279  					
  24.280 +				++state.mRequestCount;
  24.281  				--needed;
  24.282 +				if (throttle_enabled)
  24.283 +				{
  24.284 +					if (now >= state.mThrottleEnd)
  24.285 +					{
  24.286 +						// Throttle expired, move to next window
  24.287 +						LL_DEBUGS("CoreHttp") << "Throttle expired with " << state.mThrottleLeft
  24.288 +											  << " requests to go and " << state.mRequestCount
  24.289 +											  << " requests issued." << LL_ENDL;
  24.290 +						state.mThrottleLeft = state.mOptions.mThrottleRate;
  24.291 +						state.mThrottleEnd = now + HttpTime(1000000);
  24.292 +					}
  24.293 +					if (--state.mThrottleLeft <= 0)
  24.294 +					{
  24.295 +						goto throttle_on;
  24.296 +					}
  24.297 +				}
  24.298  			}
  24.299  		}
  24.300 -				
  24.301 +
  24.302 +	throttle_on:
  24.303 +		
  24.304  		if (! readyq.empty() || ! retryq.empty())
  24.305  		{
  24.306  			// If anything is ready, continue looping...
  24.307 @@ -246,9 +315,9 @@
  24.308  
  24.309  bool HttpPolicy::changePriority(HttpHandle handle, HttpRequest::priority_t priority)
  24.310  {
  24.311 -	for (int policy_class(0); policy_class < mActiveClasses; ++policy_class)
  24.312 +	for (int policy_class(0); policy_class < mClasses.size(); ++policy_class)
  24.313  	{
  24.314 -		State & state(mState[policy_class]);
  24.315 +		ClassState & state(*mClasses[policy_class]);
  24.316  		// We don't scan retry queue because a priority change there
  24.317  		// is meaningless.  The request will be issued based on retry
  24.318  		// intervals not priority value, which is now moot.
  24.319 @@ -276,9 +345,9 @@
  24.320  
  24.321  bool HttpPolicy::cancel(HttpHandle handle)
  24.322  {
  24.323 -	for (int policy_class(0); policy_class < mActiveClasses; ++policy_class)
  24.324 +	for (int policy_class(0); policy_class < mClasses.size(); ++policy_class)
  24.325  	{
  24.326 -		State & state(mState[policy_class]);
  24.327 +		ClassState & state(*mClasses[policy_class]);
  24.328  
  24.329  		// Scan retry queue
  24.330  		HttpRetryQueue::container_type & c1(state.mRetryQueue.get_container());
  24.331 @@ -337,14 +406,14 @@
  24.332  		LL_WARNS("CoreHttp") << "HTTP request " << static_cast<HttpHandle>(op)
  24.333  							 << " failed after " << op->mPolicyRetries
  24.334  							 << " retries.  Reason:  " << op->mStatus.toString()
  24.335 -							 << " (" << op->mStatus.toHex() << ")"
  24.336 +							 << " (" << op->mStatus.toTerseString() << ")"
  24.337  							 << LL_ENDL;
  24.338  	}
  24.339  	else if (op->mPolicyRetries)
  24.340  	{
  24.341 -		LL_WARNS("CoreHttp") << "HTTP request " << static_cast<HttpHandle>(op)
  24.342 -							 << " succeeded on retry " << op->mPolicyRetries << "."
  24.343 -							 << LL_ENDL;
  24.344 +		LL_DEBUGS("CoreHttp") << "HTTP request " << static_cast<HttpHandle>(op)
  24.345 +							  << " succeeded on retry " << op->mPolicyRetries << "."
  24.346 +							  << LL_ENDL;
  24.347  	}
  24.348  
  24.349  	op->stageFromActive(mService);
  24.350 @@ -352,13 +421,21 @@
  24.351  	return false;						// not active
  24.352  }
  24.353  
  24.354 +	
  24.355 +HttpPolicyClass & HttpPolicy::getClassOptions(HttpRequest::policy_t pclass)
  24.356 +{
  24.357 +	llassert_always(pclass >= 0 && pclass < mClasses.size());
  24.358 +	
  24.359 +	return mClasses[pclass]->mOptions;
  24.360 +}
  24.361 +
  24.362  
  24.363  int HttpPolicy::getReadyCount(HttpRequest::policy_t policy_class) const
  24.364  {
  24.365 -	if (policy_class < mActiveClasses)
  24.366 +	if (policy_class < mClasses.size())
  24.367  	{
  24.368 -		return (mState[policy_class].mReadyQueue.size()
  24.369 -				+ mState[policy_class].mRetryQueue.size());
  24.370 +		return (mClasses[policy_class]->mReadyQueue.size()
  24.371 +				+ mClasses[policy_class]->mRetryQueue.size());
  24.372  	}
  24.373  	return 0;
  24.374  }
    25.1 --- a/indra/llcorehttp/_httppolicy.h	Wed Feb 12 17:18:03 2014 -0800
    25.2 +++ b/indra/llcorehttp/_httppolicy.h	Wed Feb 12 18:58:40 2014 -0800
    25.3 @@ -4,7 +4,7 @@
    25.4   *
    25.5   * $LicenseInfo:firstyear=2012&license=viewerlgpl$
    25.6   * Second Life Viewer Source Code
    25.7 - * Copyright (C) 2012, Linden Research, Inc.
    25.8 + * Copyright (C) 2012-2013, Linden Research, Inc.
    25.9   *
   25.10   * This library is free software; you can redistribute it and/or
   25.11   * modify it under the terms of the GNU Lesser General Public
   25.12 @@ -60,6 +60,9 @@
   25.13  	void operator=(const HttpPolicy &);			// Not defined
   25.14  
   25.15  public:
   25.16 +	/// Threading:  called by init thread.
   25.17 +	HttpRequest::policy_t createPolicyClass();
   25.18 +	
   25.19  	/// Cancel all ready and retry requests sending them to
   25.20  	/// their notification queues.  Release state resources
   25.21  	/// making further request handling impossible.
   25.22 @@ -71,9 +74,8 @@
   25.23  	/// requests.  One-time call invoked before starting
   25.24  	/// the worker thread.
   25.25  	///
   25.26 -	/// Threading:  called by application thread
   25.27 -	void start(const HttpPolicyGlobal & global,
   25.28 -			   const std::vector<HttpPolicyClass> & classes);
   25.29 +	/// Threading:  called by init thread
   25.30 +	void start();
   25.31  
   25.32  	/// Give the policy layer some cycles to scan the ready
   25.33  	/// queue promoting higher-priority requests to active
   25.34 @@ -93,7 +95,7 @@
   25.35  	/// and should not be modified by anyone until retrieved
   25.36  	/// from queue.
   25.37  	///
   25.38 -	/// Threading:  called by any thread
   25.39 +	/// Threading:  called by worker thread
   25.40  	void addOp(HttpOpRequest *);
   25.41  
   25.42  	/// Similar to addOp, used when a caller wants to retry a
   25.43 @@ -130,30 +132,39 @@
   25.44  	/// Threading:  called by worker thread
   25.45  	bool stageAfterCompletion(HttpOpRequest * op);
   25.46  	
   25.47 -	// Get pointer to global policy options.  Caller is expected
   25.48 -	// to do context checks like no setting once running.
   25.49 +	/// Get a reference to global policy options.  Caller is expected
   25.50 +	/// to do context checks like no setting once running.  These
   25.51 +	/// are done, for example, in @see HttpService interfaces.
   25.52  	///
   25.53  	/// Threading:  called by any thread *but* the object may
   25.54  	/// only be modified by the worker thread once running.
   25.55 -	///
   25.56  	HttpPolicyGlobal & getGlobalOptions()
   25.57  		{
   25.58  			return mGlobalOptions;
   25.59  		}
   25.60  
   25.61 +	/// Get a reference to class policy options.  Caller is expected
   25.62 +	/// to do context checks like no setting once running.  These
   25.63 +	/// are done, for example, in @see HttpService interfaces.
   25.64 +	///
   25.65 +	/// Threading:  called by any thread *but* the object may
   25.66 +	/// only be modified by the worker thread once running and
   25.67 +	/// read accesses by other threads are exposed to races at
   25.68 +	/// that point.
   25.69 +	HttpPolicyClass & getClassOptions(HttpRequest::policy_t pclass);
   25.70 +	
   25.71  	/// Get ready counts for a particular policy class
   25.72  	///
   25.73  	/// Threading:  called by worker thread
   25.74  	int getReadyCount(HttpRequest::policy_t policy_class) const;
   25.75  	
   25.76  protected:
   25.77 -	struct State;
   25.78 -
   25.79 -	int									mActiveClasses;
   25.80 -	State *								mState;
   25.81 +	struct ClassState;
   25.82 +	typedef std::vector<ClassState *>	class_list_t;
   25.83 +	
   25.84 +	HttpPolicyGlobal					mGlobalOptions;
   25.85 +	class_list_t						mClasses;
   25.86  	HttpService *						mService;				// Naked pointer, not refcounted, not owner
   25.87 -	HttpPolicyGlobal					mGlobalOptions;
   25.88 -	
   25.89  };  // end class HttpPolicy
   25.90  
   25.91  }  // end namespace LLCore
    26.1 --- a/indra/llcorehttp/_httppolicyclass.cpp	Wed Feb 12 17:18:03 2014 -0800
    26.2 +++ b/indra/llcorehttp/_httppolicyclass.cpp	Wed Feb 12 18:58:40 2014 -0800
    26.3 @@ -4,7 +4,7 @@
    26.4   *
    26.5   * $LicenseInfo:firstyear=2012&license=viewerlgpl$
    26.6   * Second Life Viewer Source Code
    26.7 - * Copyright (C) 2012, Linden Research, Inc.
    26.8 + * Copyright (C) 2012-2013, Linden Research, Inc.
    26.9   *
   26.10   * This library is free software; you can redistribute it and/or
   26.11   * modify it under the terms of the GNU Lesser General Public
   26.12 @@ -34,10 +34,10 @@
   26.13  
   26.14  
   26.15  HttpPolicyClass::HttpPolicyClass()
   26.16 -	: mSetMask(0UL),
   26.17 -	  mConnectionLimit(HTTP_CONNECTION_LIMIT_DEFAULT),
   26.18 +	: mConnectionLimit(HTTP_CONNECTION_LIMIT_DEFAULT),
   26.19  	  mPerHostConnectionLimit(HTTP_CONNECTION_LIMIT_DEFAULT),
   26.20 -	  mPipelining(0)
   26.21 +	  mPipelining(HTTP_PIPELINING_DEFAULT),
   26.22 +	  mThrottleRate(HTTP_THROTTLE_RATE_DEFAULT)
   26.23  {}
   26.24  
   26.25  
   26.26 @@ -49,75 +49,75 @@
   26.27  {
   26.28  	if (this != &other)
   26.29  	{
   26.30 -		mSetMask = other.mSetMask;
   26.31  		mConnectionLimit = other.mConnectionLimit;
   26.32  		mPerHostConnectionLimit = other.mPerHostConnectionLimit;
   26.33  		mPipelining = other.mPipelining;
   26.34 +		mThrottleRate = other.mThrottleRate;
   26.35  	}
   26.36  	return *this;
   26.37  }
   26.38  
   26.39  
   26.40  HttpPolicyClass::HttpPolicyClass(const HttpPolicyClass & other)
   26.41 -	: mSetMask(other.mSetMask),
   26.42 -	  mConnectionLimit(other.mConnectionLimit),
   26.43 +	: mConnectionLimit(other.mConnectionLimit),
   26.44  	  mPerHostConnectionLimit(other.mPerHostConnectionLimit),
   26.45 -	  mPipelining(other.mPipelining)
   26.46 +	  mPipelining(other.mPipelining),
   26.47 +	  mThrottleRate(other.mThrottleRate)
   26.48  {}
   26.49  
   26.50  
   26.51 -HttpStatus HttpPolicyClass::set(HttpRequest::EClassPolicy opt, long value)
   26.52 +HttpStatus HttpPolicyClass::set(HttpRequest::EPolicyOption opt, long value)
   26.53  {
   26.54  	switch (opt)
   26.55  	{
   26.56 -	case HttpRequest::CP_CONNECTION_LIMIT:
   26.57 +	case HttpRequest::PO_CONNECTION_LIMIT:
   26.58  		mConnectionLimit = llclamp(value, long(HTTP_CONNECTION_LIMIT_MIN), long(HTTP_CONNECTION_LIMIT_MAX));
   26.59  		break;
   26.60  
   26.61 -	case HttpRequest::CP_PER_HOST_CONNECTION_LIMIT:
   26.62 +	case HttpRequest::PO_PER_HOST_CONNECTION_LIMIT:
   26.63  		mPerHostConnectionLimit = llclamp(value, long(HTTP_CONNECTION_LIMIT_MIN), mConnectionLimit);
   26.64  		break;
   26.65  
   26.66 -	case HttpRequest::CP_ENABLE_PIPELINING:
   26.67 +	case HttpRequest::PO_ENABLE_PIPELINING:
   26.68  		mPipelining = llclamp(value, 0L, 1L);
   26.69  		break;
   26.70  
   26.71 +	case HttpRequest::PO_THROTTLE_RATE:
   26.72 +		mThrottleRate = llclamp(value, 0L, 1000000L);
   26.73 +		break;
   26.74 +
   26.75  	default:
   26.76  		return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
   26.77  	}
   26.78  
   26.79 -	mSetMask |= 1UL << int(opt);
   26.80  	return HttpStatus();
   26.81  }
   26.82  
   26.83  
   26.84 -HttpStatus HttpPolicyClass::get(HttpRequest::EClassPolicy opt, long * value)
   26.85 +HttpStatus HttpPolicyClass::get(HttpRequest::EPolicyOption opt, long * value) const
   26.86  {
   26.87 -	static const HttpStatus not_set(HttpStatus::LLCORE, HE_OPT_NOT_SET);
   26.88 -	long * src(NULL);
   26.89 -	
   26.90  	switch (opt)
   26.91  	{
   26.92 -	case HttpRequest::CP_CONNECTION_LIMIT:
   26.93 -		src = &mConnectionLimit;
   26.94 +	case HttpRequest::PO_CONNECTION_LIMIT:
   26.95 +		*value = mConnectionLimit;
   26.96  		break;
   26.97  
   26.98 -	case HttpRequest::CP_PER_HOST_CONNECTION_LIMIT:
   26.99 -		src = &mPerHostConnectionLimit;
  26.100 +	case HttpRequest::PO_PER_HOST_CONNECTION_LIMIT:
  26.101 +		*value = mPerHostConnectionLimit;
  26.102  		break;
  26.103  
  26.104 -	case HttpRequest::CP_ENABLE_PIPELINING:
  26.105 -		src = &mPipelining;
  26.106 +	case HttpRequest::PO_ENABLE_PIPELINING:
  26.107 +		*value = mPipelining;
  26.108 +		break;
  26.109 +
  26.110 +	case HttpRequest::PO_THROTTLE_RATE:
  26.111 +		*value = mThrottleRate;
  26.112  		break;
  26.113  
  26.114  	default:
  26.115  		return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
  26.116  	}
  26.117  
  26.118 -	if (! (mSetMask & (1UL << int(opt))))
  26.119 -		return not_set;
  26.120 -
  26.121 -	*value = *src;
  26.122  	return HttpStatus();
  26.123  }
  26.124  
    27.1 --- a/indra/llcorehttp/_httppolicyclass.h	Wed Feb 12 17:18:03 2014 -0800
    27.2 +++ b/indra/llcorehttp/_httppolicyclass.h	Wed Feb 12 18:58:40 2014 -0800
    27.3 @@ -4,7 +4,7 @@
    27.4   *
    27.5   * $LicenseInfo:firstyear=2012&license=viewerlgpl$
    27.6   * Second Life Viewer Source Code
    27.7 - * Copyright (C) 2012, Linden Research, Inc.
    27.8 + * Copyright (C) 2012-2013, Linden Research, Inc.
    27.9   *
   27.10   * This library is free software; you can redistribute it and/or
   27.11   * modify it under the terms of the GNU Lesser General Public
   27.12 @@ -34,6 +34,18 @@
   27.13  namespace LLCore
   27.14  {
   27.15  
   27.16 +/// Options struct for per-class policy options.
   27.17 +///
   27.18 +/// Combines both raw blob data access with semantics-enforcing
   27.19 +/// set/get interfaces.  For internal operations by the worker
   27.20 +/// thread, just grab the setting directly from instance and test/use
   27.21 +/// as needed.  When attached to external APIs (the public API
   27.22 +/// options interfaces) the set/get methods are available to
   27.23 +/// enforce correct ranges, data types, contexts, etc. and suitable
   27.24 +/// status values are returned.
   27.25 +///
   27.26 +/// Threading:  Single-threaded.  In practice, init thread before
   27.27 +/// worker starts, worker thread after.
   27.28  class HttpPolicyClass
   27.29  {
   27.30  public:
   27.31 @@ -44,14 +56,14 @@
   27.32  	HttpPolicyClass(const HttpPolicyClass &);			// Not defined
   27.33  
   27.34  public:
   27.35 -	HttpStatus set(HttpRequest::EClassPolicy opt, long value);
   27.36 -	HttpStatus get(HttpRequest::EClassPolicy opt, long * value);
   27.37 +	HttpStatus set(HttpRequest::EPolicyOption opt, long value);
   27.38 +	HttpStatus get(HttpRequest::EPolicyOption opt, long * value) const;
   27.39  	
   27.40  public:
   27.41 -	unsigned long				mSetMask;
   27.42  	long						mConnectionLimit;
   27.43  	long						mPerHostConnectionLimit;
   27.44  	long						mPipelining;
   27.45 +	long						mThrottleRate;
   27.46  };  // end class HttpPolicyClass
   27.47  
   27.48  }  // end namespace LLCore
    28.1 --- a/indra/llcorehttp/_httppolicyglobal.cpp	Wed Feb 12 17:18:03 2014 -0800
    28.2 +++ b/indra/llcorehttp/_httppolicyglobal.cpp	Wed Feb 12 18:58:40 2014 -0800
    28.3 @@ -4,7 +4,7 @@
    28.4   *
    28.5   * $LicenseInfo:firstyear=2012&license=viewerlgpl$
    28.6   * Second Life Viewer Source Code
    28.7 - * Copyright (C) 2012, Linden Research, Inc.
    28.8 + * Copyright (C) 2012-2013, Linden Research, Inc.
    28.9   *
   28.10   * This library is free software; you can redistribute it and/or
   28.11   * modify it under the terms of the GNU Lesser General Public
   28.12 @@ -34,8 +34,7 @@
   28.13  
   28.14  
   28.15  HttpPolicyGlobal::HttpPolicyGlobal()
   28.16 -	: mSetMask(0UL),
   28.17 -	  mConnectionLimit(HTTP_CONNECTION_LIMIT_DEFAULT),
   28.18 +	: mConnectionLimit(HTTP_CONNECTION_LIMIT_DEFAULT),
   28.19  	  mTrace(HTTP_TRACE_OFF),
   28.20  	  mUseLLProxy(0)
   28.21  {}
   28.22 @@ -49,7 +48,6 @@
   28.23  {
   28.24  	if (this != &other)
   28.25  	{
   28.26 -		mSetMask = other.mSetMask;
   28.27  		mConnectionLimit = other.mConnectionLimit;
   28.28  		mCAPath = other.mCAPath;
   28.29  		mCAFile = other.mCAFile;
   28.30 @@ -61,19 +59,19 @@
   28.31  }
   28.32  
   28.33  
   28.34 -HttpStatus HttpPolicyGlobal::set(HttpRequest::EGlobalPolicy opt, long value)
   28.35 +HttpStatus HttpPolicyGlobal::set(HttpRequest::EPolicyOption opt, long value)
   28.36  {
   28.37  	switch (opt)
   28.38  	{
   28.39 -	case HttpRequest::GP_CONNECTION_LIMIT:
   28.40 +	case HttpRequest::PO_CONNECTION_LIMIT:
   28.41  		mConnectionLimit = llclamp(value, long(HTTP_CONNECTION_LIMIT_MIN), long(HTTP_CONNECTION_LIMIT_MAX));
   28.42  		break;
   28.43  
   28.44 -	case HttpRequest::GP_TRACE:
   28.45 +	case HttpRequest::PO_TRACE:
   28.46  		mTrace = llclamp(value, long(HTTP_TRACE_MIN), long(HTTP_TRACE_MAX));
   28.47  		break;
   28.48  
   28.49 -	case HttpRequest::GP_LLPROXY:
   28.50 +	case HttpRequest::PO_LLPROXY:
   28.51  		mUseLLProxy = llclamp(value, 0L, 1L);
   28.52  		break;
   28.53  
   28.54 @@ -81,24 +79,23 @@
   28.55  		return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
   28.56  	}
   28.57  
   28.58 -	mSetMask |= 1UL << int(opt);
   28.59  	return HttpStatus();
   28.60  }
   28.61  
   28.62  
   28.63 -HttpStatus HttpPolicyGlobal::set(HttpRequest::EGlobalPolicy opt, const std::string & value)
   28.64 +HttpStatus HttpPolicyGlobal::set(HttpRequest::EPolicyOption opt, const std::string & value)
   28.65  {
   28.66  	switch (opt)
   28.67  	{
   28.68 -	case HttpRequest::GP_CA_PATH:
   28.69 +	case HttpRequest::PO_CA_PATH:
   28.70  		mCAPath = value;
   28.71  		break;
   28.72  
   28.73 -	case HttpRequest::GP_CA_FILE:
   28.74 +	case HttpRequest::PO_CA_FILE:
   28.75  		mCAFile = value;
   28.76  		break;
   28.77  
   28.78 -	case HttpRequest::GP_HTTP_PROXY:
   28.79 +	case HttpRequest::PO_HTTP_PROXY:
   28.80  		mCAFile = value;
   28.81  		break;
   28.82  
   28.83 @@ -106,69 +103,54 @@
   28.84  		return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
   28.85  	}
   28.86  	
   28.87 -	mSetMask |= 1UL << int(opt);
   28.88  	return HttpStatus();
   28.89  }
   28.90  
   28.91  
   28.92 -HttpStatus HttpPolicyGlobal::get(HttpRequest::EGlobalPolicy opt, long * value)
   28.93 +HttpStatus HttpPolicyGlobal::get(HttpRequest::EPolicyOption opt, long * value) const
   28.94  {
   28.95 -	static const HttpStatus not_set(HttpStatus::LLCORE, HE_OPT_NOT_SET);
   28.96 -	long * src(NULL);
   28.97 -	
   28.98  	switch (opt)
   28.99  	{
  28.100 -	case HttpRequest::GP_CONNECTION_LIMIT:
  28.101 -		src = &mConnectionLimit;
  28.102 +	case HttpRequest::PO_CONNECTION_LIMIT:
  28.103 +		*value = mConnectionLimit;
  28.104  		break;
  28.105  
  28.106 -	case HttpRequest::GP_TRACE:
  28.107 -		src = &mTrace;
  28.108 +	case HttpRequest::PO_TRACE:
  28.109 +		*value = mTrace;
  28.110  		break;
  28.111  
  28.112 -	case HttpRequest::GP_LLPROXY:
  28.113 -		src = &mUseLLProxy;
  28.114 +	case HttpRequest::PO_LLPROXY:
  28.115 +		*value = mUseLLProxy;
  28.116  		break;
  28.117  
  28.118  	default:
  28.119  		return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
  28.120  	}
  28.121  
  28.122 -	if (! (mSetMask & (1UL << int(opt))))
  28.123 -		return not_set;
  28.124 -
  28.125 -	*value = *src;
  28.126  	return HttpStatus();
  28.127  }
  28.128  
  28.129  
  28.130 -HttpStatus HttpPolicyGlobal::get(HttpRequest::EGlobalPolicy opt, const std::string ** value)
  28.131 +HttpStatus HttpPolicyGlobal::get(HttpRequest::EPolicyOption opt, std::string * value) const
  28.132  {
  28.133 -	static const HttpStatus not_set(HttpStatus::LLCORE, HE_OPT_NOT_SET);
  28.134 -	const std::string * src(NULL);
  28.135 -
  28.136  	switch (opt)
  28.137  	{
  28.138 -	case HttpRequest::GP_CA_PATH:
  28.139 -		src = &mCAPath;
  28.140 +	case HttpRequest::PO_CA_PATH:
  28.141 +		*value = mCAPath;
  28.142  		break;
  28.143  
  28.144 -	case HttpRequest::GP_CA_FILE:
  28.145 -		src = &mCAFile;
  28.146 +	case HttpRequest::PO_CA_FILE:
  28.147 +		*value = mCAFile;
  28.148  		break;
  28.149  
  28.150 -	case HttpRequest::GP_HTTP_PROXY:
  28.151 -		src = &mHttpProxy;
  28.152 +	case HttpRequest::PO_HTTP_PROXY:
  28.153 +		*value = mHttpProxy;
  28.154  		break;
  28.155  
  28.156  	default:
  28.157  		return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
  28.158  	}
  28.159  	
  28.160 -	if (! (mSetMask & (1UL << int(opt))))
  28.161 -		return not_set;
  28.162 -
  28.163 -	*value = src;
  28.164  	return HttpStatus();
  28.165  }
  28.166  
    29.1 --- a/indra/llcorehttp/_httppolicyglobal.h	Wed Feb 12 17:18:03 2014 -0800
    29.2 +++ b/indra/llcorehttp/_httppolicyglobal.h	Wed Feb 12 18:58:40 2014 -0800
    29.3 @@ -4,7 +4,7 @@
    29.4   *
    29.5   * $LicenseInfo:firstyear=2012&license=viewerlgpl$
    29.6   * Second Life Viewer Source Code
    29.7 - * Copyright (C) 2012, Linden Research, Inc.
    29.8 + * Copyright (C) 2012-2013, Linden Research, Inc.
    29.9   *
   29.10   * This library is free software; you can redistribute it and/or
   29.11   * modify it under the terms of the GNU Lesser General Public
   29.12 @@ -34,6 +34,18 @@
   29.13  namespace LLCore
   29.14  {
   29.15  
   29.16 +/// Options struct for global policy options.
   29.17 +///
   29.18 +/// Combines both raw blob data access with semantics-enforcing
   29.19 +/// set/get interfaces.  For internal operations by the worker
   29.20 +/// thread, just grab the setting directly from instance and test/use
   29.21 +/// as needed.  When attached to external APIs (the public API
   29.22 +/// options interfaces) the set/get methods are available to
   29.23 +/// enforce correct ranges, data types, contexts, etc. and suitable
   29.24 +/// status values are returned.
   29.25 +///
   29.26 +/// Threading:  Single-threaded.  In practice, init thread before
   29.27 +/// worker starts, worker thread after.
   29.28  class HttpPolicyGlobal
   29.29  {
   29.30  public:
   29.31 @@ -46,13 +58,12 @@
   29.32  	HttpPolicyGlobal(const HttpPolicyGlobal &);			// Not defined
   29.33  
   29.34  public:
   29.35 -	HttpStatus set(HttpRequest::EGlobalPolicy opt, long value);
   29.36 -	HttpStatus set(HttpRequest::EGlobalPolicy opt, const std::string & value);
   29.37 -	HttpStatus get(HttpRequest::EGlobalPolicy opt, long * value);
   29.38 -	HttpStatus get(HttpRequest::EGlobalPolicy opt, const std::string ** value);
   29.39 +	HttpStatus set(HttpRequest::EPolicyOption opt, long value);
   29.40 +	HttpStatus set(HttpRequest::EPolicyOption opt, const std::string & value);
   29.41 +	HttpStatus get(HttpRequest::EPolicyOption opt, long * value) const;
   29.42 +	HttpStatus get(HttpRequest::EPolicyOption opt, std::string * value) const;
   29.43  	
   29.44  public:
   29.45 -	unsigned long		mSetMask;
   29.46  	long				mConnectionLimit;
   29.47  	std::string			mCAPath;
   29.48  	std::string			mCAFile;
    30.1 --- a/indra/llcorehttp/_httpservice.cpp	Wed Feb 12 17:18:03 2014 -0800
    30.2 +++ b/indra/llcorehttp/_httpservice.cpp	Wed Feb 12 18:58:40 2014 -0800
    30.3 @@ -4,7 +4,7 @@
    30.4   *
    30.5   * $LicenseInfo:firstyear=2012&license=viewerlgpl$
    30.6   * Second Life Viewer Source Code
    30.7 - * Copyright (C) 2012, Linden Research, Inc.
    30.8 + * Copyright (C) 2012-2013, Linden Research, Inc.
    30.9   *
   30.10   * This library is free software; you can redistribute it and/or
   30.11   * modify it under the terms of the GNU Lesser General Public
   30.12 @@ -43,6 +43,18 @@
   30.13  namespace LLCore
   30.14  {
   30.15  
   30.16 +const HttpService::OptionDescriptor HttpService::sOptionDesc[] =
   30.17 +{ //    isLong     isDynamic  isGlobal    isClass
   30.18 +	{	true,		true,		true,		true	},		// PO_CONNECTION_LIMIT
   30.19 +	{	true,		true,		false,		true	},		// PO_PER_HOST_CONNECTION_LIMIT
   30.20 +	{	false,		false,		true,		false	},		// PO_CA_PATH
   30.21 +	{	false,		false,		true,		false	},		// PO_CA_FILE
   30.22 +	{	false,		true,		true,		false	},		// PO_HTTP_PROXY
   30.23 +	{	true,		true,		true,		false	},		// PO_LLPROXY
   30.24 +	{	true,		true,		true,		false	},		// PO_TRACE
   30.25 +	{	true,		true,		false,		true	},		// PO_ENABLE_PIPELINING
   30.26 +	{	true,		true,		false,		true	}		// PO_THROTTLE_RATE
   30.27 +};
   30.28  HttpService * HttpService::sInstance(NULL);
   30.29  volatile HttpService::EState HttpService::sState(NOT_INITIALIZED);
   30.30  
   30.31 @@ -51,15 +63,9 @@
   30.32  	  mExitRequested(0U),
   30.33  	  mThread(NULL),
   30.34  	  mPolicy(NULL),
   30.35 -	  mTransport(NULL)
   30.36 -{
   30.37 -	// Create the default policy class
   30.38 -	HttpPolicyClass pol_class;
   30.39 -	pol_class.set(HttpRequest::CP_CONNECTION_LIMIT, HTTP_CONNECTION_LIMIT_DEFAULT);
   30.40 -	pol_class.set(HttpRequest::CP_PER_HOST_CONNECTION_LIMIT, HTTP_CONNECTION_LIMIT_DEFAULT);
   30.41 -	pol_class.set(HttpRequest::CP_ENABLE_PIPELINING, 0L);
   30.42 -	mPolicyClasses.push_back(pol_class);
   30.43 -}
   30.44 +	  mTransport(NULL),
   30.45 +	  mLastPolicy(0)
   30.46 +{}
   30.47  
   30.48  
   30.49  HttpService::~HttpService()
   30.50 @@ -149,13 +155,8 @@
   30.51  
   30.52  HttpRequest::policy_t HttpService::createPolicyClass()
   30.53  {
   30.54 -	const HttpRequest::policy_t policy_class(mPolicyClasses.size());
   30.55 -	if (policy_class >= HTTP_POLICY_CLASS_LIMIT)
   30.56 -	{
   30.57 -		return 0;
   30.58 -	}
   30.59 -	mPolicyClasses.push_back(HttpPolicyClass());
   30.60 -	return policy_class;
   30.61 +	mLastPolicy = mPolicy->createPolicyClass();
   30.62 +	return mLastPolicy;
   30.63  }
   30.64  
   30.65  
   30.66 @@ -188,8 +189,8 @@
   30.67  	}
   30.68  
   30.69  	// Push current policy definitions, enable policy & transport components
   30.70 -	mPolicy->start(mPolicyGlobal, mPolicyClasses);
   30.71 -	mTransport->start(mPolicyClasses.size());
   30.72 +	mPolicy->start();
   30.73 +	mTransport->start(mLastPolicy + 1);
   30.74  
   30.75  	mThread = new LLCoreInt::HttpThread(boost::bind(&HttpService::threadRun, this, _1));
   30.76  	sState = RUNNING;
   30.77 @@ -322,7 +323,7 @@
   30.78  		{
   30.79  			// Setup for subsequent tracing
   30.80  			long tracing(HTTP_TRACE_OFF);
   30.81 -			mPolicy->getGlobalOptions().get(HttpRequest::GP_TRACE, &tracing);
   30.82 +			mPolicy->getGlobalOptions().get(HttpRequest::PO_TRACE, &tracing);
   30.83  			op->mTracing = (std::max)(op->mTracing, int(tracing));
   30.84  
   30.85  			if (op->mTracing > HTTP_TRACE_OFF)
   30.86 @@ -345,4 +346,137 @@
   30.87  }
   30.88  
   30.89  
   30.90 +HttpStatus HttpService::getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass,
   30.91 +										long * ret_value)
   30.92 +{
   30.93 +	if (opt < HttpRequest::PO_CONNECTION_LIMIT											// option must be in range
   30.94 +		|| opt >= HttpRequest::PO_LAST													// ditto
   30.95 +		|| (! sOptionDesc[opt].mIsLong)													// datatype is long
   30.96 +		|| (pclass != HttpRequest::GLOBAL_POLICY_ID && pclass > mLastPolicy)			// pclass in valid range
   30.97 +		|| (pclass == HttpRequest::GLOBAL_POLICY_ID && ! sOptionDesc[opt].mIsGlobal)	// global setting permitted
   30.98 +		|| (pclass != HttpRequest::GLOBAL_POLICY_ID && ! sOptionDesc[opt].mIsClass))	// class setting permitted
   30.99 +																						// can always get, no dynamic check
  30.100 +	{
  30.101 +		return HttpStatus(HttpStatus::LLCORE, LLCore::HE_INVALID_ARG);
  30.102 +	}
  30.103 +
  30.104 +	HttpStatus status;
  30.105 +	if (pclass == HttpRequest::GLOBAL_POLICY_ID)
  30.106 +	{
  30.107 +		HttpPolicyGlobal & opts(mPolicy->getGlobalOptions());
  30.108 +		
  30.109 +		status = opts.get(opt, ret_value);
  30.110 +	}
  30.111 +	else
  30.112 +	{
  30.113 +		HttpPolicyClass & opts(mPolicy->getClassOptions(pclass));
  30.114 +
  30.115 +		status = opts.get(opt, ret_value);
  30.116 +	}
  30.117 +
  30.118 +	return status;
  30.119 +}
  30.120 +
  30.121 +
  30.122 +HttpStatus HttpService::getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass,
  30.123 +										std::string * ret_value)
  30.124 +{
  30.125 +	HttpStatus status(HttpStatus::LLCORE, LLCore::HE_INVALID_ARG);
  30.126 +
  30.127 +	if (opt < HttpRequest::PO_CONNECTION_LIMIT											// option must be in range
  30.128 +		|| opt >= HttpRequest::PO_LAST													// ditto
  30.129 +		|| (sOptionDesc[opt].mIsLong)													// datatype is string
  30.130 +		|| (pclass != HttpRequest::GLOBAL_POLICY_ID && pclass > mLastPolicy)			// pclass in valid range
  30.131 +		|| (pclass == HttpRequest::GLOBAL_POLICY_ID && ! sOptionDesc[opt].mIsGlobal)	// global setting permitted
  30.132 +		|| (pclass != HttpRequest::GLOBAL_POLICY_ID && ! sOptionDesc[opt].mIsClass))	// class setting permitted
  30.133 +																						// can always get, no dynamic check
  30.134 +	{
  30.135 +		return status;
  30.136 +	}
  30.137 +
  30.138 +	// Only global has string values
  30.139 +	if (pclass == HttpRequest::GLOBAL_POLICY_ID)
  30.140 +	{
  30.141 +		HttpPolicyGlobal & opts(mPolicy->getGlobalOptions());
  30.142 +
  30.143 +		status = opts.get(opt, ret_value);
  30.144 +	}
  30.145 +
  30.146 +	return status;
  30.147 +}
  30.148 +
  30.149 +
  30.150 +HttpStatus HttpService::setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass,
  30.151 +										long value, long * ret_value)
  30.152 +{
  30.153 +	HttpStatus status(HttpStatus::LLCORE, LLCore::HE_INVALID_ARG);
  30.154 +	
  30.155 +	if (opt < HttpRequest::PO_CONNECTION_LIMIT											// option must be in range
  30.156 +		|| opt >= HttpRequest::PO_LAST													// ditto
  30.157 +		|| (! sOptionDesc[opt].mIsLong)													// datatype is long
  30.158 +		|| (pclass != HttpRequest::GLOBAL_POLICY_ID && pclass > mLastPolicy)			// pclass in valid range
  30.159 +		|| (pclass == HttpRequest::GLOBAL_POLICY_ID && ! sOptionDesc[opt].mIsGlobal)	// global setting permitted
  30.160 +		|| (pclass != HttpRequest::GLOBAL_POLICY_ID && ! sOptionDesc[opt].mIsClass)		// class setting permitted
  30.161 +		|| (RUNNING == sState && ! sOptionDesc[opt].mIsDynamic))						// dynamic setting permitted
  30.162 +	{
  30.163 +		return status;
  30.164 +	}
  30.165 +
  30.166 +	if (pclass == HttpRequest::GLOBAL_POLICY_ID)
  30.167 +	{
  30.168 +		HttpPolicyGlobal & opts(mPolicy->getGlobalOptions());
  30.169 +		
  30.170 +		status = opts.set(opt, value);
  30.171 +		if (status && ret_value)
  30.172 +		{
  30.173 +			status = opts.get(opt, ret_value);
  30.174 +		}
  30.175 +	}
  30.176 +	else
  30.177 +	{
  30.178 +		HttpPolicyClass & opts(mPolicy->getClassOptions(pclass));
  30.179 +
  30.180 +		status = opts.set(opt, value);
  30.181 +		if (status && ret_value)
  30.182 +		{
  30.183 +			status = opts.get(opt, ret_value);
  30.184 +		}
  30.185 +	}
  30.186 +
  30.187 +	return status;
  30.188 +}
  30.189 +
  30.190 +
  30.191 +HttpStatus HttpService::setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass,
  30.192 +										const std::string & value, std::string * ret_value)
  30.193 +{
  30.194 +	HttpStatus status(HttpStatus::LLCORE, LLCore::HE_INVALID_ARG);
  30.195 +	
  30.196 +	if (opt < HttpRequest::PO_CONNECTION_LIMIT											// option must be in range
  30.197 +		|| opt >= HttpRequest::PO_LAST													// ditto
  30.198 +		|| (sOptionDesc[opt].mIsLong)													// datatype is string
  30.199 +		|| (pclass != HttpRequest::GLOBAL_POLICY_ID && pclass > mLastPolicy)			// pclass in valid range
  30.200 +		|| (pclass == HttpRequest::GLOBAL_POLICY_ID && ! sOptionDesc[opt].mIsGlobal)	// global setting permitted
  30.201 +		|| (pclass != HttpRequest::GLOBAL_POLICY_ID && ! sOptionDesc[opt].mIsClass)		// class setting permitted
  30.202 +		|| (RUNNING == sState && ! sOptionDesc[opt].mIsDynamic))						// dynamic setting permitted
  30.203 +	{
  30.204 +		return status;
  30.205 +	}
  30.206 +
  30.207 +	// Only string values are global at this time
  30.208 +	if (pclass == HttpRequest::GLOBAL_POLICY_ID)
  30.209 +	{
  30.210 +		HttpPolicyGlobal & opts(mPolicy->getGlobalOptions());
  30.211 +		
  30.212 +		status = opts.set(opt, value);
  30.213 +		if (status && ret_value)
  30.214 +		{
  30.215 +			status = opts.get(opt, ret_value);
  30.216 +		}
  30.217 +	}
  30.218 +
  30.219 +	return status;
  30.220 +}
  30.221 +	
  30.222 +
  30.223  }  // end namespace LLCore
    31.1 --- a/indra/llcorehttp/_httpservice.h	Wed Feb 12 17:18:03 2014 -0800
    31.2 +++ b/indra/llcorehttp/_httpservice.h	Wed Feb 12 18:58:40 2014 -0800
    31.3 @@ -4,7 +4,7 @@
    31.4   *
    31.5   * $LicenseInfo:firstyear=2012&license=viewerlgpl$
    31.6   * Second Life Viewer Source Code
    31.7 - * Copyright (C) 2012, Linden Research, Inc.
    31.8 + * Copyright (C) 2012-2013, Linden Research, Inc.
    31.9   *
   31.10   * This library is free software; you can redistribute it and/or
   31.11   * modify it under the terms of the GNU Lesser General Public
   31.12 @@ -53,6 +53,7 @@
   31.13  class HttpRequestQueue;
   31.14  class HttpPolicy;
   31.15  class HttpLibcurl;
   31.16 +class HttpOpSetGet;
   31.17  
   31.18  
   31.19  /// The HttpService class does the work behind the request queue.  It
   31.20 @@ -106,7 +107,7 @@
   31.21  		NORMAL,					///< continuous polling of request, ready, active queues
   31.22  		REQUEST_SLEEP			///< can sleep indefinitely waiting for request queue write
   31.23  	};
   31.24 -		
   31.25 +
   31.26  	static void init(HttpRequestQueue *);
   31.27  	static void term();
   31.28  
   31.29 @@ -136,7 +137,7 @@
   31.30  	/// acquires its weaknesses.
   31.31  	static bool isStopped();
   31.32  
   31.33 -	/// Threading:  callable by consumer thread *once*.
   31.34 +	/// Threading:  callable by init thread *once*.
   31.35  	void startThread();
   31.36  
   31.37  	/// Threading:  callable by worker thread.
   31.38 @@ -181,27 +182,38 @@
   31.39  		}
   31.40  
   31.41  	/// Threading:  callable by consumer thread.
   31.42 -	HttpPolicyGlobal & getGlobalOptions()
   31.43 -		{
   31.44 -			return mPolicyGlobal;
   31.45 -		}
   31.46 -
   31.47 -	/// Threading:  callable by consumer thread.
   31.48  	HttpRequest::policy_t createPolicyClass();
   31.49  	
   31.50 -	/// Threading:  callable by consumer thread.
   31.51 -	HttpPolicyClass & getClassOptions(HttpRequest::policy_t policy_class)
   31.52 -		{
   31.53 -			llassert(policy_class >= 0 && policy_class < mPolicyClasses.size());
   31.54 -			return mPolicyClasses[policy_class];
   31.55 -		}
   31.56 -	
   31.57  protected:
   31.58  	void threadRun(LLCoreInt::HttpThread * thread);
   31.59  	
   31.60  	ELoopSpeed processRequestQueue(ELoopSpeed loop);
   31.61 +
   31.62 +protected:
   31.63 +	friend class HttpOpSetGet;
   31.64 +	friend class HttpRequest;
   31.65 +	
   31.66 +	// Used internally to describe what operations are allowed
   31.67 +	// on each policy option.
   31.68 +	struct OptionDescriptor
   31.69 +	{
   31.70 +		bool		mIsLong;
   31.71 +		bool		mIsDynamic;
   31.72 +		bool		mIsGlobal;
   31.73 +		bool		mIsClass;
   31.74 +	};
   31.75 +		
   31.76 +	HttpStatus getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t,
   31.77 +							   long * ret_value);
   31.78 +	HttpStatus getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t,
   31.79 +							   std::string * ret_value);
   31.80 +	HttpStatus setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t,
   31.81 +							   long value, long * ret_value);
   31.82 +	HttpStatus setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t,
   31.83 +							   const std::string & value, std::string * ret_value);
   31.84  	
   31.85  protected:
   31.86 +	static const OptionDescriptor		sOptionDesc[HttpRequest::PO_LAST];
   31.87  	static HttpService *				sInstance;
   31.88  	
   31.89  	// === shared data ===
   31.90 @@ -210,13 +222,13 @@
   31.91  	LLAtomicU32							mExitRequested;
   31.92  	LLCoreInt::HttpThread *				mThread;
   31.93  	
   31.94 -	// === consumer-thread-only data ===
   31.95 -	HttpPolicyGlobal					mPolicyGlobal;
   31.96 -	std::vector<HttpPolicyClass>		mPolicyClasses;
   31.97 -	
   31.98  	// === working-thread-only data ===
   31.99  	HttpPolicy *						mPolicy;		// Simple pointer, has ownership
  31.100  	HttpLibcurl *						mTransport;		// Simple pointer, has ownership
  31.101 +	
  31.102 +	// === main-thread-only data ===
  31.103 +	HttpRequest::policy_t				mLastPolicy;
  31.104 +	
  31.105  };  // end class HttpService
  31.106  
  31.107  }  // end namespace LLCore
    32.1 --- a/indra/llcorehttp/examples/http_texture_load.cpp	Wed Feb 12 17:18:03 2014 -0800
    32.2 +++ b/indra/llcorehttp/examples/http_texture_load.cpp	Wed Feb 12 18:58:40 2014 -0800
    32.3 @@ -4,7 +4,7 @@
    32.4   *
    32.5   * $LicenseInfo:firstyear=2012&license=viewerlgpl$
    32.6   * Second Life Viewer Source Code
    32.7 - * Copyright (C) 2012, Linden Research, Inc.
    32.8 + * Copyright (C) 2012-2013, Linden Research, Inc.
    32.9   * 
   32.10   * This library is free software; you can redistribute it and/or
   32.11   * modify it under the terms of the GNU Lesser General Public
   32.12 @@ -39,6 +39,7 @@
   32.13  #include "httprequest.h"
   32.14  #include "httphandler.h"
   32.15  #include "httpresponse.h"
   32.16 +#include "httpoptions.h"
   32.17  #include "httpheaders.h"
   32.18  #include "bufferarray.h"
   32.19  #include "_mutex.h"
   32.20 @@ -57,6 +58,7 @@
   32.21  
   32.22  // Default command line settings
   32.23  static int concurrency_limit(40);
   32.24 +static int highwater(100);
   32.25  static char url_format[1024] = "http://example.com/some/path?texture_id=%s.texture";
   32.26  
   32.27  #if defined(WIN32)
   32.28 @@ -79,11 +81,11 @@
   32.29  	WorkingSet();
   32.30  	~WorkingSet();
   32.31  
   32.32 -	bool reload(LLCore::HttpRequest *);
   32.33 +	bool reload(LLCore::HttpRequest *, LLCore::HttpOptions *);
   32.34  	
   32.35  	virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response);
   32.36  
   32.37 -	void loadTextureUuids(FILE * in);
   32.38 +	void loadAssetUuids(FILE * in);
   32.39  	
   32.40  public:
   32.41  	struct Spec
   32.42 @@ -93,24 +95,27 @@
   32.43  		int				mLength;
   32.44  	};
   32.45  	typedef std::set<LLCore::HttpHandle> handle_set_t;
   32.46 -	typedef std::vector<Spec> texture_list_t;
   32.47 +	typedef std::vector<Spec> asset_list_t;
   32.48  	
   32.49  public:
   32.50  	bool						mVerbose;
   32.51  	bool						mRandomRange;
   32.52 -	int							mMaxConcurrency;
   32.53 +	int							mRequestLowWater;
   32.54 +	int							mRequestHighWater;
   32.55  	handle_set_t				mHandles;
   32.56  	int							mRemaining;
   32.57  	int							mLimit;
   32.58  	int							mAt;
   32.59  	std::string					mUrl;
   32.60 -	texture_list_t				mTextures;
   32.61 +	asset_list_t				mAssets;
   32.62  	int							mErrorsApi;
   32.63  	int							mErrorsHttp;
   32.64  	int							mErrorsHttp404;
   32.65  	int							mErrorsHttp416;
   32.66  	int							mErrorsHttp500;
   32.67  	int							mErrorsHttp503;
   32.68 +	int							mRetries;
   32.69 +	int							mRetriesHttp503;
   32.70  	int							mSuccesses;
   32.71  	long						mByteCount;
   32.72  	LLCore::HttpHeaders *		mHeaders;
   32.73 @@ -158,7 +163,7 @@
   32.74  	bool do_verbose(false);
   32.75  	
   32.76  	int option(-1);
   32.77 -	while (-1 != (option = getopt(argc, argv, "u:c:h?Rv")))
   32.78 +	while (-1 != (option = getopt(argc, argv, "u:c:h?RvH:")))
   32.79  	{
   32.80  		switch (option)
   32.81  		{
   32.82 @@ -182,6 +187,21 @@
   32.83  			}
   32.84  			break;
   32.85  
   32.86 +		case 'H':
   32.87 +		    {
   32.88 +				unsigned long value;
   32.89 +				char * end;
   32.90 +
   32.91 +				value = strtoul(optarg, &end, 10);
   32.92 +				if (value < 1 || value > 100 || *end != '\0')
   32.93 +				{
   32.94 +					usage(std::cerr);
   32.95 +					return 1;
   32.96 +				}
   32.97 +				highwater = value;
   32.98 +			}
   32.99 +			break;
  32.100 +
  32.101  		case 'R':
  32.102  			do_random = true;
  32.103  			break;
  32.104 @@ -216,25 +236,32 @@
  32.105  	// Initialization
  32.106  	init_curl();
  32.107  	LLCore::HttpRequest::createService();
  32.108 -	LLCore::HttpRequest::setPolicyClassOption(LLCore::HttpRequest::DEFAULT_POLICY_ID,
  32.109 -											  LLCore::HttpRequest::CP_CONNECTION_LIMIT,
  32.110 -											  concurrency_limit);
  32.111 +	LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_CONNECTION_LIMIT,
  32.112 +											   LLCore::HttpRequest::DEFAULT_POLICY_ID,
  32.113 +											   concurrency_limit,
  32.114 +											   NULL);
  32.115  	LLCore::HttpRequest::startThread();
  32.116  	
  32.117  	// Get service point
  32.118  	LLCore::HttpRequest * hr = new LLCore::HttpRequest();
  32.119  
  32.120 +	// Get request options
  32.121 +	LLCore::HttpOptions * opt = new LLCore::HttpOptions();
  32.122 +	opt->setRetries(12);
  32.123 +	opt->setUseRetryAfter(true);
  32.124 +	
  32.125  	// Get a handler/working set
  32.126  	WorkingSet ws;
  32.127  
  32.128  	// Fill the working set with work
  32.129  	ws.mUrl = url_format;
  32.130 -	ws.loadTextureUuids(uuids);
  32.131 +	ws.loadAssetUuids(uuids);
  32.132  	ws.mRandomRange = do_random;
  32.133  	ws.mVerbose = do_verbose;
  32.134 -	ws.mMaxConcurrency = 100;
  32.135 +	ws.mRequestHighWater = highwater;
  32.136 +	ws.mRequestLowWater = ws.mRequestHighWater / 2;
  32.137  	
  32.138 -	if (! ws.mTextures.size())
  32.139 +	if (! ws.mAssets.size())
  32.140  	{
  32.141  		std::cerr << "No UUIDs found in file '" << argv[optind] << "'." << std::endl;
  32.142  		return 1;
  32.143 @@ -246,9 +273,9 @@
  32.144  	
  32.145  	// Run it
  32.146  	int passes(0);
  32.147 -	while (! ws.reload(hr))
  32.148 +	while (! ws.reload(hr, opt))
  32.149  	{
  32.150 -		hr->update(5000000);
  32.151 +		hr->update(0);
  32.152  		ms_sleep(2);
  32.153  		if (0 == (++passes % 200))
  32.154  		{
  32.155 @@ -265,6 +292,8 @@
  32.156  	std::cout << "HTTP 404 errors: " << ws.mErrorsHttp404 << "  HTTP 416 errors: " << ws.mErrorsHttp416
  32.157  			  << "  HTTP 500 errors:  " << ws.mErrorsHttp500 << "  HTTP 503 errors: " << ws.mErrorsHttp503 
  32.158  			  << std::endl;
  32.159 +	std::cout << "Retries: " << ws.mRetries << "  Retries on 503: " << ws.mRetriesHttp503
  32.160 +			  << std::endl;
  32.161  	std::cout << "User CPU: " << (metrics.mEndUTime - metrics.mStartUTime)
  32.162  			  << " uS  System CPU: " << (metrics.mEndSTime - metrics.mStartSTime)
  32.163  			  << " uS  Wall Time: "  << (metrics.mEndWallTime - metrics.mStartWallTime)
  32.164 @@ -275,6 +304,8 @@
  32.165  	// Clean up
  32.166  	hr->requestStopThread(NULL);
  32.167  	ms_sleep(1000);
  32.168 +	opt->release();
  32.169 +	opt = NULL;
  32.170  	delete hr;
  32.171  	LLCore::HttpRequest::destroyService();
  32.172  	term_curl();
  32.173 @@ -300,8 +331,10 @@
  32.174  		" -u <url_format>       printf-style format string for URL generation\n"
  32.175  		"                       Default:  " << url_format << "\n"
  32.176  		" -R                    Issue GETs with random Range: headers\n"
  32.177 -		" -c <limit>            Maximum request concurrency.  Range:  [1..100]\n"
  32.178 +		" -c <limit>            Maximum connection concurrency.  Range:  [1..100]\n"
  32.179  		"                       Default:  " << concurrency_limit << "\n"
  32.180 +		" -H <limit>            HTTP request highwater (requests fed to llcorehttp).\n"
  32.181 +		"                       Range:  [1..100]  Default:  " << highwater << "\n"
  32.182  		" -v                    Verbose mode.  Issue some chatter while running\n"
  32.183  		" -h                    print this help\n"
  32.184  		"\n"
  32.185 @@ -322,13 +355,15 @@
  32.186  	  mErrorsHttp416(0),
  32.187  	  mErrorsHttp500(0),
  32.188  	  mErrorsHttp503(0),
  32.189 +	  mRetries(0),
  32.190 +	  mRetriesHttp503(0),
  32.191  	  mSuccesses(0),
  32.192  	  mByteCount(0L)
  32.193  {
  32.194 -	mTextures.reserve(30000);
  32.195 +	mAssets.reserve(30000);
  32.196  
  32.197  	mHeaders = new LLCore::HttpHeaders;
  32.198 -	mHeaders->mHeaders.push_back("Accept: image/x-j2c");
  32.199 +	mHeaders->append("Accept", "image/x-j2c");
  32.200  }
  32.201  
  32.202  
  32.203 @@ -342,29 +377,35 @@
  32.204  }
  32.205  
  32.206  
  32.207 -bool WorkingSet::reload(LLCore::HttpRequest * hr)
  32.208 +bool WorkingSet::reload(LLCore::HttpRequest * hr, LLCore::HttpOptions * opt)
  32.209  {
  32.210 -	int to_do((std::min)(mRemaining, mMaxConcurrency - int(mHandles.size())));
  32.211 +	if (mRequestLowWater <= mHandles.size())
  32.212 +	{
  32.213 +		// Haven't fallen below low-water level yet.
  32.214 +		return false;
  32.215 +	}
  32.216 +	
  32.217 +	int to_do((std::min)(mRemaining, mRequestHighWater - int(mHandles.size())));
  32.218  
  32.219  	for (int i(0); i < to_do; ++i)
  32.220  	{
  32.221  		char buffer[1024];
  32.222  #if	defined(WIN32)
  32.223 -		_snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, mUrl.c_str(), mTextures[mAt].mUuid.c_str());
  32.224 +		_snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, mUrl.c_str(), mAssets[mAt].mUuid.c_str());
  32.225  #else
  32.226 -		snprintf(buffer, sizeof(buffer), mUrl.c_str(), mTextures[mAt].mUuid.c_str());
  32.227 +		snprintf(buffer, sizeof(buffer), mUrl.c_str(), mAssets[mAt].mUuid.c_str());
  32.228  #endif
  32.229 -		int offset(mRandomRange ? ((unsigned long) rand()) % 1000000UL : mTextures[mAt].mOffset);
  32.230 -		int length(mRandomRange ? ((unsigned long) rand()) % 1000000UL : mTextures[mAt].mLength);
  32.231 +		int offset(mRandomRange ? ((unsigned long) rand()) % 1000000UL : mAssets[mAt].mOffset);
  32.232 +		int length(mRandomRange ? ((unsigned long) rand()) % 1000000UL : mAssets[mAt].mLength);
  32.233  
  32.234  		LLCore::HttpHandle handle;
  32.235  		if (offset || length)
  32.236  		{
  32.237 -			handle = hr->requestGetByteRange(0, 0, buffer, offset, length, NULL, mHeaders, this);
  32.238 +			handle = hr->requestGetByteRange(0, 0, buffer, offset, length, opt, mHeaders, this);
  32.239  		}
  32.240  		else
  32.241  		{
  32.242 -			handle = hr->requestGet(0, 0, buffer, NULL, mHeaders, this);
  32.243 +			handle = hr->requestGet(0, 0, buffer, opt, mHeaders, this);
  32.244  		}
  32.245  		if (! handle)
  32.246  		{
  32.247 @@ -410,7 +451,7 @@
  32.248  		{
  32.249  			// More success
  32.250  			LLCore::BufferArray * data(response->getBody());
  32.251 -			mByteCount += data->size();
  32.252 +			mByteCount += data ? data->size() : 0;
  32.253  			++mSuccesses;
  32.254  		}
  32.255  		else
  32.256 @@ -446,6 +487,10 @@
  32.257  				++mErrorsApi;
  32.258  			}
  32.259  		}
  32.260 +		unsigned int retry(0U), retry_503(0U);
  32.261 +		response->getRetries(&retry, &retry_503);
  32.262 +		mRetries += int(retry);
  32.263 +		mRetriesHttp503 += int(retry_503);
  32.264  		mHandles.erase(it);
  32.265  	}
  32.266  
  32.267 @@ -459,21 +504,21 @@
  32.268  }
  32.269  
  32.270  
  32.271 -void WorkingSet::loadTextureUuids(FILE * in)
  32.272 +void WorkingSet::loadAssetUuids(FILE * in)
  32.273  {
  32.274  	char buffer[1024];
  32.275  
  32.276  	while (fgets(buffer, sizeof(buffer), in))
  32.277  	{
  32.278 -		WorkingSet::Spec texture;
  32.279 +		WorkingSet::Spec asset;
  32.280  		char * state(NULL);
  32.281  		char * token = strtok_r(buffer, " \t\n,", &state);
  32.282  		if (token && 36 == strlen(token))
  32.283  		{
  32.284  			// Close enough for this function
  32.285 -			texture.mUuid = token;
  32.286 -			texture.mOffset = 0;
  32.287 -			texture.mLength = 0;
  32.288 +			asset.mUuid = token;
  32.289 +			asset.mOffset = 0;
  32.290 +			asset.mLength = 0;
  32.291  			token = strtok_r(buffer, " \t\n,", &state);
  32.292  			if (token)
  32.293  			{
  32.294 @@ -482,14 +527,14 @@
  32.295  				if (token)
  32.296  				{
  32.297  					int length(atoi(token));
  32.298 -					texture.mOffset = offset;
  32.299 -					texture.mLength = length;
  32.300 +					asset.mOffset = offset;
  32.301 +					asset.mLength = length;
  32.302  				}
  32.303  			}
  32.304 -			mTextures.push_back(texture);
  32.305 +			mAssets.push_back(asset);
  32.306  		}
  32.307  	}
  32.308 -	mRemaining = mLimit = mTextures.size();
  32.309 +	mRemaining = mLimit = mAssets.size();
  32.310  }
  32.311  
  32.312  
    33.1 --- a/indra/llcorehttp/httpcommon.cpp	Wed Feb 12 17:18:03 2014 -0800
    33.2 +++ b/indra/llcorehttp/httpcommon.cpp	Wed Feb 12 18:58:40 2014 -0800
    33.3 @@ -70,7 +70,8 @@
    33.4  			"Invalid datatype for argument or option",
    33.5  			"Option has not been explicitly set",
    33.6  			"Option is not dynamic and must be set early",
    33.7 -			"Invalid HTTP status code received from server"
    33.8 +			"Invalid HTTP status code received from server",
    33.9 +			"Could not allocate required resource"
   33.10  		};
   33.11  	static const int llcore_errors_count(sizeof(llcore_errors) / sizeof(llcore_errors[0]));
   33.12  
   33.13 @@ -177,6 +178,44 @@
   33.14  }
   33.15  
   33.16  
   33.17 +std::string HttpStatus::toTerseString() const
   33.18 +{
   33.19 +	std::ostringstream result;
   33.20 +
   33.21 +	unsigned int error_value((unsigned short) mStatus);
   33.22 +	
   33.23 +	switch (mType)
   33.24 +	{
   33.25 +	case EXT_CURL_EASY:
   33.26 +		result << "Easy_";
   33.27 +		break;
   33.28 +		
   33.29 +	case EXT_CURL_MULTI:
   33.30 +		result << "Multi_";
   33.31 +		break;
   33.32 +		
   33.33 +	case LLCORE:
   33.34 +		result << "Core_";
   33.35 +		break;
   33.36 +
   33.37 +	default:
   33.38 +		if (isHttpStatus())
   33.39 +		{
   33.40 +			result << "Http_";
   33.41 +			error_value = mType;
   33.42 +		}
   33.43 +		else
   33.44 +		{
   33.45 +			result << "Unknown_";
   33.46 +		}
   33.47 +		break;
   33.48 +	}
   33.49 +	
   33.50 +	result << error_value;
   33.51 +	return result.str();
   33.52 +}
   33.53 +
   33.54 +
   33.55  // Pass true on statuses that might actually be cleared by a
   33.56  // retry.  Library failures, calling problems, etc. aren't
   33.57  // going to be fixed by squirting bits all over the Net.
   33.58 @@ -206,6 +245,5 @@
   33.59  			*this == inv_cont_range);	// Short data read disagrees with content-range
   33.60  }
   33.61  
   33.62 -		
   33.63  } // end namespace LLCore
   33.64  
    34.1 --- a/indra/llcorehttp/httpcommon.h	Wed Feb 12 17:18:03 2014 -0800
    34.2 +++ b/indra/llcorehttp/httpcommon.h	Wed Feb 12 18:58:40 2014 -0800
    34.3 @@ -29,9 +29,9 @@
    34.4  
    34.5  /// @package LLCore::HTTP
    34.6  ///
    34.7 -/// This library implements a high-level, Indra-code-free client interface to
    34.8 -/// HTTP services based on actual patterns found in the viewer and simulator.
    34.9 -/// Interfaces are similar to those supplied by the legacy classes
   34.10 +/// This library implements a high-level, Indra-code-free (somewhat) client
   34.11 +/// interface to HTTP services based on actual patterns found in the viewer
   34.12 +/// and simulator.  Interfaces are similar to those supplied by the legacy classes
   34.13  /// LLCurlRequest and LLHTTPClient.  To that is added a policy scheme that
   34.14  /// allows an application to specify connection behaviors:  limits on
   34.15  /// connections, HTTP keepalive, HTTP pipelining, retry-on-error limits, etc.
   34.16 @@ -52,7 +52,7 @@
   34.17  /// - "llcorehttp/httprequest.h"
   34.18  /// - "llcorehttp/httpresponse.h"
   34.19  ///
   34.20 -/// The library is still under early development and particular users
   34.21 +/// The library is still under development and particular users
   34.22  /// may need access to internal implementation details that are found
   34.23  /// in the _*.h header files.  But this is a crutch to be avoided if at
   34.24  /// all possible and probably indicates some interface work is neeeded.
   34.25 @@ -66,6 +66,8 @@
   34.26  ///   .  CRYPTO_set_id_callback(...)
   34.27  /// - HttpRequest::createService() called to instantiate singletons
   34.28  ///   and support objects.
   34.29 +/// - HttpRequest::startThread() to kick off the worker thread and
   34.30 +///   begin servicing requests.
   34.31  ///
   34.32  /// An HTTP consumer in an application, and an application may have many
   34.33  /// consumers, does a few things:
   34.34 @@ -91,10 +93,12 @@
   34.35  ///   objects.
   34.36  /// - Do completion processing in your onCompletion() method.
   34.37  ///
   34.38 -/// Code fragments:
   34.39 -/// Rather than a poorly-maintained example in comments, look in the
   34.40 -/// example subdirectory which is a minimal yet functional tool to do
   34.41 -/// GET request performance testing.  With four calls:
   34.42 +/// Code fragments.
   34.43 +///
   34.44 +/// Initialization.  Rather than a poorly-maintained example in
   34.45 +/// comments, look in the example subdirectory which is a minimal
   34.46 +/// yet functional tool to do GET request performance testing.
   34.47 +/// With four calls:
   34.48  ///
   34.49  ///   	init_curl();
   34.50  ///     LLCore::HttpRequest::createService();
   34.51 @@ -103,7 +107,85 @@
   34.52  ///
   34.53  /// the program is basically ready to issue requests.
   34.54  ///
   34.55 -
   34.56 +/// HttpHandler.  Having started life as a non-indra library,
   34.57 +/// this code broke away from the classic Responder model and
   34.58 +/// introduced a handler class to represent an interface for
   34.59 +/// request responses.  This is a non-reference-counted entity
   34.60 +/// which can be used as a base class or a mixin.  An instance
   34.61 +/// of a handler can be used for each request or can be shared
   34.62 +/// among any number of requests.  Your choice but expect to
   34.63 +/// code something like the following:
   34.64 +///
   34.65 +///     class AppHandler : public LLCore::HttpHandler
   34.66 +///     {
   34.67 +///     public:
   34.68 +///         virtual void onCompleted(HttpHandle handle,
   34.69 +///                                  HttpResponse * response)
   34.70 +///         {
   34.71 +///             ...
   34.72 +///         }
   34.73 +///         ...
   34.74 +///     };
   34.75 +///     ...
   34.76 +///     handler = new handler(...);
   34.77 +///
   34.78 +///
   34.79 +/// Issuing requests.  Using 'hr' above,
   34.80 +///
   34.81 +///     hr->requestGet(HttpRequest::DEFAULT_POLICY_ID,
   34.82 +///                    0,				// Priority, not used yet
   34.83 +///                    url,
   34.84 +///                    NULL,			// options
   34.85 +///                    NULL,            // additional headers
   34.86 +///                    handler);
   34.87 +///
   34.88 +/// If that returns a value other than LLCORE_HTTP_HANDLE_INVALID,
   34.89 +/// the request was successfully issued and there will eventally
   34.90 +/// be a status delivered to the handler.  If invalid is returnedd,
   34.91 +/// the actual status can be retrieved by calling hr->getStatus().
   34.92 +///
   34.93 +/// Completing requests and delivering notifications.  Operations
   34.94 +/// are all performed by the worker thread and will be driven to
   34.95 +/// completion regardless of caller actions.  Notification of
   34.96 +/// completion (success or failure) is done by calls to
   34.97 +/// HttpRequest::update() which will invoke handlers for completed
   34.98 +/// requests:
   34.99 +///
  34.100 +///     hr->update(0);
  34.101 +///       // Callbacks into handler->onCompleted()
  34.102 +///
  34.103 +///
  34.104 +/// Threads.
  34.105 +///
  34.106 +/// Threads are supported and used by this library.  The various
  34.107 +/// classes, methods and members are documented with thread
  34.108 +/// constraints which programmers must follow and which are
  34.109 +/// defined as follows:
  34.110 +///
  34.111 +/// consumer	Any thread that has instanced HttpRequest and is
  34.112 +///             issuing requests.  A particular instance can only
  34.113 +///             be used by one consumer thread but a consumer may
  34.114 +///             have many instances available to it.
  34.115 +/// init		Special consumer thread, usually the main thread,
  34.116 +///             involved in setting up the library at startup.
  34.117 +/// worker      Thread used internally by the library to perform
  34.118 +///             HTTP operations.  Consumers will not have to deal
  34.119 +///             with this thread directly but some APIs are reserved
  34.120 +///             to it.
  34.121 +/// any         Consumer or worker thread.
  34.122 +///
  34.123 +/// For the most part, API users will not have to do much in the
  34.124 +/// way of ensuring thread safely.  However, there is a tremendous
  34.125 +/// amount of sharing between threads of read-only data.  So when
  34.126 +/// documentation declares that an option or header instance
  34.127 +/// becomes shared between consumer and worker, the consumer must
  34.128 +/// not modify the shared object.
  34.129 +///
  34.130 +/// Internally, there is almost no thread synchronization.  During
  34.131 +/// normal operations (non-init, non-term), only the request queue
  34.132 +/// and the multiple reply queues are shared between threads and
  34.133 +/// only here are mutexes used.
  34.134 +///
  34.135  
  34.136  #include "linden_common.h"		// Modifies curl/curl.h interfaces
  34.137  
  34.138 @@ -164,7 +246,10 @@
  34.139  	HE_OPT_NOT_DYNAMIC = 8,
  34.140  	
  34.141  	// Invalid HTTP status code returned by server
  34.142 -	HE_INVALID_HTTP_STATUS = 9
  34.143 +	HE_INVALID_HTTP_STATUS = 9,
  34.144 +	
  34.145 +	// Couldn't allocate resource, typically libcurl handle
  34.146 +	HE_BAD_ALLOC = 10
  34.147  	
  34.148  }; // end enum HttpError
  34.149  
  34.150 @@ -239,9 +324,10 @@
  34.151  			return *this;
  34.152  		}
  34.153  	
  34.154 -	static const type_enum_t EXT_CURL_EASY = 0;
  34.155 -	static const type_enum_t EXT_CURL_MULTI = 1;
  34.156 -	static const type_enum_t LLCORE = 2;
  34.157 +	static const type_enum_t EXT_CURL_EASY = 0;			///< mStatus is an error from a curl_easy_*() call
  34.158 +	static const type_enum_t EXT_CURL_MULTI = 1;		///< mStatus is an error from a curl_multi_*() call
  34.159 +	static const type_enum_t LLCORE = 2;				///< mStatus is an HE_* error code
  34.160 +														///< 100-999 directly represent HTTP status codes
  34.161  	
  34.162  	type_enum_t			mType;
  34.163  	short				mStatus;
  34.164 @@ -297,6 +383,14 @@
  34.165  	/// LLCore itself).
  34.166  	std::string toString() const;
  34.167  
  34.168 +	/// Convert status to a compact string representation
  34.169 +	/// of the form:  "<type>_<value>".  The <type> will be
  34.170 +	/// one of:  Core, Http, Easy, Multi, Unknown.  And
  34.171 +	/// <value> will be an unsigned integer.  More easily
  34.172 +	/// interpreted than the hex representation, it's still
  34.173 +	/// compact and easily searched.
  34.174 +	std::string toTerseString() const;
  34.175 +
  34.176  	/// Returns true if the status value represents an
  34.177  	/// HTTP response status (100 - 999).
  34.178  	bool isHttpStatus() const
    35.1 --- a/indra/llcorehttp/httpheaders.cpp	Wed Feb 12 17:18:03 2014 -0800
    35.2 +++ b/indra/llcorehttp/httpheaders.cpp	Wed Feb 12 18:58:40 2014 -0800
    35.3 @@ -4,7 +4,7 @@
    35.4   *
    35.5   * $LicenseInfo:firstyear=2012&license=viewerlgpl$
    35.6   * Second Life Viewer Source Code
    35.7 - * Copyright (C) 2012, Linden Research, Inc.
    35.8 + * Copyright (C) 2012-2013, Linden Research, Inc.
    35.9   *
   35.10   * This library is free software; you can redistribute it and/or
   35.11   * modify it under the terms of the GNU Lesser General Public
   35.12 @@ -26,6 +26,8 @@
   35.13  
   35.14  #include "httpheaders.h"
   35.15  
   35.16 +#include "llstring.h"
   35.17 +
   35.18  
   35.19  namespace LLCore
   35.20  {
   35.21 @@ -40,5 +42,142 @@
   35.22  {}
   35.23  
   35.24  
   35.25 +void
   35.26 +HttpHeaders::clear()
   35.27 +{
   35.28 +	mHeaders.clear();
   35.29 +}
   35.30 +
   35.31 +
   35.32 +void HttpHeaders::append(const std::string & name, const std::string & value)
   35.33 +{
   35.34 +	mHeaders.push_back(value_type(name, value));
   35.35 +}
   35.36 +
   35.37 +
   35.38 +void HttpHeaders::append(const char * name, const char * value)
   35.39 +{
   35.40 +	mHeaders.push_back(value_type(name, value));
   35.41 +}
   35.42 +
   35.43 +
   35.44 +void HttpHeaders::appendNormal(const char * header, size_t size)
   35.45 +{
   35.46 +	std::string name;
   35.47 +	std::string value;
   35.48 +
   35.49 +	int col_pos(0);
   35.50 +	for (; col_pos < size; ++col_pos)
   35.51 +	{
   35.52 +		if (':' == header[col_pos])
   35.53 +			break;
   35.54 +	}
   35.55 +	
   35.56 +	if (col_pos < size)
   35.57 +	{
   35.58 +		// Looks like a header, split it and normalize.
   35.59 +		// Name is everything before the colon, may be zero-length.
   35.60 +		name.assign(header, col_pos);
   35.61 +
   35.62 +		// Value is everything after the colon, may also be zero-length.
   35.63 +		const size_t val_len(size - col_pos - 1);
   35.64 +		if (val_len)
   35.65 +		{
   35.66 +			value.assign(header + col_pos + 1, val_len);
   35.67 +		}
   35.68 +
   35.69 +		// Clean the strings
   35.70 +		LLStringUtil::toLower(name);
   35.71 +		LLStringUtil::trim(name);
   35.72 +		LLStringUtil::trimHead(value);
   35.73 +	}
   35.74 +	else
   35.75 +	{
   35.76 +		// Uncertain what this is, we'll pack it as
   35.77 +		// a name without a value.  Won't clean as we don't
   35.78 +		// know what it is...
   35.79 +		name.assign(header, size);
   35.80 +	}
   35.81 +
   35.82 +	mHeaders.push_back(value_type(name, value));
   35.83 +}
   35.84 +
   35.85 +
   35.86 +// Find from end to simulate a tradition of using single-valued
   35.87 +// std::map for this in the past.
   35.88 +const std::string * HttpHeaders::find(const char * name) const
   35.89 +{
   35.90 +	const_reverse_iterator iend(rend());
   35.91 +	for (const_reverse_iterator iter(rbegin()); iend != iter; ++iter)
   35.92 +	{
   35.93 +		if ((*iter).first == name)
   35.94 +		{
   35.95 +			return &(*iter).second;
   35.96 +		}
   35.97 +	}
   35.98 +	return NULL;
   35.99 +}
  35.100 +
  35.101 +
  35.102 +// Standard Iterators
  35.103 +HttpHeaders::iterator HttpHeaders::begin()
  35.104 +{
  35.105 +	return mHeaders.begin();
  35.106 +}
  35.107 +
  35.108 +
  35.109 +HttpHeaders::const_iterator HttpHeaders::begin() const
  35.110 +{
  35.111 +	return mHeaders.begin();
  35.112 +}
  35.113 +
  35.114 +
  35.115 +HttpHeaders::iterator HttpHeaders::end()
  35.116 +{
  35.117 +	return mHeaders.end();
  35.118 +}
  35.119 +
  35.120 +
  35.121 +HttpHeaders::const_iterator HttpHeaders::end() const
  35.122 +{
  35.123 +	return mHeaders.end();
  35.124 +}
  35.125 +
  35.126 +
  35.127 +// Standard Reverse Iterators
  35.128 +HttpHeaders::reverse_iterator HttpHeaders::rbegin()
  35.129 +{
  35.130 +	return mHeaders.rbegin();
  35.131 +}
  35.132 +
  35.133 +
  35.134 +HttpHeaders::const_reverse_iterator HttpHeaders::rbegin() const
  35.135 +{
  35.136 +	return mHeaders.rbegin();
  35.137 +}
  35.138 +
  35.139 +
  35.140 +HttpHeaders::reverse_iterator HttpHeaders::rend()
  35.141 +{
  35.142 +	return mHeaders.rend();
  35.143 +}
  35.144 +
  35.145 +
  35.146 +HttpHeaders::const_reverse_iterator HttpHeaders::rend() const
  35.147 +{
  35.148 +	return mHeaders.rend();
  35.149 +}
  35.150 +
  35.151 +
  35.152 +// Return the raw container to the caller.
  35.153 +//
  35.154 +// To be used FOR UNIT TESTS ONLY.
  35.155 +//
  35.156 +HttpHeaders::container_t & HttpHeaders::getContainerTESTONLY()
  35.157 +{
  35.158 +	return mHeaders;
  35.159 +}
  35.160 +
  35.161 +
  35.162  }   // end namespace LLCore
  35.163  
    36.1 --- a/indra/llcorehttp/httpheaders.h	Wed Feb 12 17:18:03 2014 -0800
    36.2 +++ b/indra/llcorehttp/httpheaders.h	Wed Feb 12 18:58:40 2014 -0800
    36.3 @@ -4,7 +4,7 @@
    36.4   *
    36.5   * $LicenseInfo:firstyear=2012&license=viewerlgpl$
    36.6   * Second Life Viewer Source Code
    36.7 - * Copyright (C) 2012, Linden Research, Inc.
    36.8 + * Copyright (C) 2012-2013, Linden Research, Inc.
    36.9   *
   36.10   * This library is free software; you can redistribute it and/or
   36.11   * modify it under the terms of the GNU Lesser General Public
   36.12 @@ -43,13 +43,26 @@
   36.13  /// caller has asked that headers be returned (not the default
   36.14  /// option).
   36.15  ///
   36.16 -/// @note
   36.17 -/// This is a minimally-functional placeholder at the moment
   36.18 -/// to fill out the class hierarchy.  The final class will be
   36.19 -/// something else, probably more pair-oriented.  It's also
   36.20 -/// an area where shared values are desirable so refcounting is
   36.21 -/// already specced and a copy-on-write scheme imagined.
   36.22 -/// Expect changes here.
   36.23 +/// Class is mostly a thin wrapper around a vector of pairs
   36.24 +/// of strings.  Methods provided are few and intended to
   36.25 +/// reflect actual use patterns.  These include:
   36.26 +/// - Clearing the list
   36.27 +/// - Appending a name/value pair to the vector
   36.28 +/// - Processing a raw byte string into a normalized name/value
   36.29 +///   pair and appending the result.
   36.30 +/// - Simple case-sensitive find-last-by-name search
   36.31 +/// - Forward and reverse iterators over all pairs
   36.32 +///
   36.33 +/// Container is ordered and multi-valued.  Headers are
   36.34 +/// written in the order in which they are appended and
   36.35 +/// are stored in the order in which they're received from
   36.36 +/// the wire.  The same header may appear two or more times
   36.37 +/// in any container.  Searches using the simple find()
   36.38 +/// interface will find only the last occurrence (somewhat
   36.39 +/// simulates the use of std::map).  Fuller searches require
   36.40 +/// the use of an iterator.  Headers received from the wire
   36.41 +/// are only returned from the last request when redirections
   36.42 +/// are involved.
   36.43  ///
   36.44  /// Threading:  Not intrinsically thread-safe.  It *is* expected
   36.45  /// that callers will build these objects and then share them
   36.46 @@ -64,6 +77,16 @@
   36.47  class HttpHeaders : public LLCoreInt::RefCounted
   36.48  {
   36.49  public:
   36.50 +	typedef std::pair<std::string, std::string> header_t;
   36.51 +	typedef std::vector<header_t> container_t;
   36.52 +	typedef container_t::iterator iterator;
   36.53 +	typedef container_t::const_iterator const_iterator;
   36.54 +	typedef container_t::reverse_iterator reverse_iterator;
   36.55 +	typedef container_t::const_reverse_iterator const_reverse_iterator;
   36.56 +	typedef container_t::value_type value_type;
   36.57 +	typedef container_t::size_type size_type;
   36.58 +
   36.59 +public:
   36.60  	/// @post In addition to the instance, caller has a refcount
   36.61  	/// to the instance.  A call to @see release() will destroy
   36.62  	/// the instance.
   36.63 @@ -76,7 +99,78 @@
   36.64  	void operator=(const HttpHeaders &);		// Not defined
   36.65  
   36.66  public:
   36.67 -	typedef std::vector<std::string> container_t;
   36.68 +	// Empty the list of headers.
   36.69 +	void clear();
   36.70 +
   36.71 +	// Append a name/value pair supplied as either std::strings
   36.72 +	// or NUL-terminated char * to the header list.  No normalization
   36.73 +	// is performed on the strings.  No conformance test is
   36.74 +	// performed (names may contain spaces, colons, etc.).
   36.75 +	//
   36.76 +	void append(const std::string & name, const std::string & value);
   36.77 +	void append(const char * name, const char * value);
   36.78 +
   36.79 +	// Extract a name/value pair from a raw byte array using
   36.80 +	// the first colon character as a separator.  Input string
   36.81 +	// does not need to be NUL-terminated.  Resulting name/value
   36.82 +	// pair is appended to the header list.
   36.83 +	//
   36.84 +	// Normalization is performed on the name/value pair as
   36.85 +	// follows:
   36.86 +	// - name is lower-cased according to mostly ASCII rules
   36.87 +	// - name is left- and right-trimmed of spaces and tabs
   36.88 +	// - value is left-trimmed of spaces and tabs
   36.89 +	// - either or both of name and value may be zero-length
   36.90 +	//
   36.91 +	// By convention, headers read from the wire will be normalized
   36.92 +	// in this fashion prior to delivery to any HttpHandler code.
   36.93 +	// Headers to be written to the wire are left as appended to
   36.94 +	// the list.
   36.95 +	void appendNormal(const char * header, size_t size);
   36.96 +
   36.97 +	// Perform a simple, case-sensitive search of the header list
   36.98 +	// returning a pointer to the value of the last matching header
   36.99 +	// in the header list.  If none is found, a NULL pointer is returned.
  36.100 +	//
  36.101 +	// Any pointer returned references objects in the container itself
  36.102 +	// and will have the same lifetime as this class.  If you want
  36.103 +	// the value beyond the lifetime of this instance, make a copy.
  36.104 +	//
  36.105 +	// @arg		name	C-style string giving the name of a header
  36.106 +	//					to search.  The comparison is case-sensitive
  36.107 +	//					though list entries may have been normalized
  36.108 +	//					to lower-case.
  36.109 +	//
  36.110 +	// @return			NULL if the header wasn't found otherwise
  36.111 +	//					a pointer to a std::string in the container.
  36.112 +	//					Pointer is valid only for the lifetime of
  36.113 +	//					the container or until container is modifed.
  36.114 +	//
  36.115 +	const std::string * find(const char * name) const;
  36.116 +
  36.117 +	// Count of headers currently in the list.
  36.118 +	size_type size() const
  36.119 +		{
  36.120 +			return mHeaders.size();
  36.121 +		}
  36.122 +
  36.123 +	// Standard std::vector-based forward iterators.
  36.124 +	iterator begin();
  36.125 +	const_iterator begin() const;
  36.126 +	iterator end();
  36.127 +	const_iterator end() const;
  36.128 +
  36.129 +	// Standard std::vector-based reverse iterators.
  36.130 +	reverse_iterator rbegin();
  36.131 +	const_reverse_iterator rbegin() const;
  36.132 +	reverse_iterator rend();
  36.133 +	const_reverse_iterator rend() const;
  36.134 +
  36.135 +public:
  36.136 +	// For unit tests only - not a public API
  36.137 +	container_t &		getContainerTESTONLY();
  36.138 +	
  36.139 +protected:
  36.140  	container_t			mHeaders;
  36.141  	
  36.142  }; // end class HttpHeaders
    37.1 --- a/indra/llcorehttp/httpoptions.cpp	Wed Feb 12 17:18:03 2014 -0800
    37.2 +++ b/indra/llcorehttp/httpoptions.cpp	Wed Feb 12 18:58:40 2014 -0800
    37.3 @@ -4,7 +4,7 @@
    37.4   *
    37.5   * $LicenseInfo:firstyear=2012&license=viewerlgpl$
    37.6   * Second Life Viewer Source Code
    37.7 - * Copyright (C) 2012, Linden Research, Inc.
    37.8 + * Copyright (C) 2012-2013, Linden Research, Inc.
    37.9   *
   37.10   * This library is free software; you can redistribute it and/or
   37.11   * modify it under the terms of the GNU Lesser General Public
   37.12 @@ -38,7 +38,9 @@
   37.13  	  mWantHeaders(false),
   37.14  	  mTracing(HTTP_TRACE_OFF),
   37.15  	  mTimeout(HTTP_REQUEST_TIMEOUT_DEFAULT),
   37.16 -	  mRetries(HTTP_RETRY_COUNT_DEFAULT)
   37.17 +	  mTransferTimeout(HTTP_REQUEST_XFER_TIMEOUT_DEFAULT),
   37.18 +	  mRetries(HTTP_RETRY_COUNT_DEFAULT),
   37.19 +	  mUseRetryAfter(HTTP_USE_RETRY_AFTER_DEFAULT)
   37.20  {}
   37.21  
   37.22  
   37.23 @@ -64,10 +66,21 @@
   37.24  }
   37.25  
   37.26  
   37.27 +void HttpOptions::setTransferTimeout(unsigned int timeout)
   37.28 +{
   37.29 +	mTransferTimeout = timeout;
   37.30 +}
   37.31 +
   37.32 +
   37.33  void HttpOptions::setRetries(unsigned int retries)
   37.34  {
   37.35  	mRetries = retries;
   37.36  }
   37.37  
   37.38 +void HttpOptions::setUseRetryAfter(bool use_retry)
   37.39 +{
   37.40 +	mUseRetryAfter = use_retry;
   37.41 +}
   37.42 +
   37.43  
   37.44  }   // end namespace LLCore
    38.1 --- a/indra/llcorehttp/httpoptions.h	Wed Feb 12 17:18:03 2014 -0800
    38.2 +++ b/indra/llcorehttp/httpoptions.h	Wed Feb 12 18:58:40 2014 -0800
    38.3 @@ -4,7 +4,7 @@
    38.4   *
    38.5   * $LicenseInfo:firstyear=2012&license=viewerlgpl$
    38.6   * Second Life Viewer Source Code
    38.7 - * Copyright (C) 2012, Linden Research, Inc.
    38.8 + * Copyright (C) 2012-2013, Linden Research, Inc.
    38.9   *
   38.10   * This library is free software; you can redistribute it and/or
   38.11   * modify it under the terms of the GNU Lesser General Public
   38.12 @@ -68,36 +68,55 @@
   38.13  	void operator=(const HttpOptions &);		// Not defined
   38.14  
   38.15  public:
   38.16 +	// Default:   false
   38.17  	void				setWantHeaders(bool wanted);
   38.18  	bool				getWantHeaders() const
   38.19  		{
   38.20  			return mWantHeaders;
   38.21  		}
   38.22 -	
   38.23 +
   38.24 +	// Default:  0
   38.25  	void				setTrace(int long);
   38.26  	int					getTrace() const
   38.27  		{
   38.28  			return mTracing;
   38.29  		}
   38.30  
   38.31 +	// Default:  30
   38.32  	void				setTimeout(unsigned int timeout);
   38.33  	unsigned int		getTimeout() const
   38.34  		{
   38.35  			return mTimeout;
   38.36  		}
   38.37  
   38.38 +	// Default:  0
   38.39 +	void				setTransferTimeout(unsigned int timeout);
   38.40 +	unsigned int		getTransferTimeout() const
   38.41 +		{
   38.42 +			return mTransferTimeout;
   38.43 +		}
   38.44 +
   38.45 +	// Default:  8
   38.46  	void				setRetries(unsigned int retries);
   38.47  	unsigned int		getRetries() const
   38.48  		{
   38.49  			return mRetries;
   38.50  		}
   38.51 +
   38.52 +	// Default:  true
   38.53 +	void				setUseRetryAfter(bool use_retry);
   38.54 +	bool				getUseRetryAfter() const
   38.55 +		{
   38.56 +			return mUseRetryAfter;
   38.57 +		}
   38.58  	
   38.59  protected:
   38.60  	bool				mWantHeaders;
   38.61  	int					mTracing;
   38.62  	unsigned int		mTimeout;
   38.63 +	unsigned int		mTransferTimeout;
   38.64  	unsigned int		mRetries;
   38.65 -	
   38.66 +	bool				mUseRetryAfter;
   38.67  }; // end class HttpOptions
   38.68  
   38.69  
    39.1 --- a/indra/llcorehttp/httprequest.cpp	Wed Feb 12 17:18:03 2014 -0800
    39.2 +++ b/indra/llcorehttp/httprequest.cpp	Wed Feb 12 18:58:40 2014 -0800
    39.3 @@ -4,7 +4,7 @@
    39.4   *
    39.5   * $LicenseInfo:firstyear=2012&license=viewerlgpl$
    39.6   * Second Life Viewer Source Code
    39.7 - * Copyright (C) 2012, Linden Research, Inc.
    39.8 + * Copyright (C) 2012-2013, Linden Research, Inc.
    39.9   *
   39.10   * This library is free software; you can redistribute it and/or
   39.11   * modify it under the terms of the GNU Lesser General Public
   39.12 @@ -54,12 +54,8 @@
   39.13  // ====================================
   39.14  
   39.15  
   39.16 -HttpRequest::policy_t HttpRequest::sNextPolicyID(1);
   39.17 -
   39.18 -
   39.19  HttpRequest::HttpRequest()
   39.20 -	: //HttpHandler(),
   39.21 -	  mReplyQueue(NULL),
   39.22 +	: mReplyQueue(NULL),
   39.23  	  mRequestQueue(NULL)
   39.24  {
   39.25  	mRequestQueue = HttpRequestQueue::instanceOf();
   39.26 @@ -90,26 +86,6 @@
   39.27  // ====================================
   39.28  
   39.29  
   39.30 -HttpStatus HttpRequest::setPolicyGlobalOption(EGlobalPolicy opt, long value)
   39.31 -{
   39.32 -	if (HttpService::RUNNING == HttpService::instanceOf()->getState())
   39.33 -	{
   39.34 -		return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC);
   39.35 -	}
   39.36 -	return HttpService::instanceOf()->getGlobalOptions().set(opt, value);
   39.37 -}
   39.38 -
   39.39 -
   39.40 -HttpStatus HttpRequest::setPolicyGlobalOption(EGlobalPolicy opt, const std::string & value)
   39.41 -{
   39.42 -	if (HttpService::RUNNING == HttpService::instanceOf()->getState())
   39.43 -	{
   39.44 -		return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC);
   39.45 -	}
   39.46 -	return HttpService::instanceOf()->getGlobalOptions().set(opt, value);
   39.47 -}
   39.48 -
   39.49 -
   39.50  HttpRequest::policy_t HttpRequest::createPolicyClass()
   39.51  {
   39.52  	if (HttpService::RUNNING == HttpService::instanceOf()->getState())
   39.53 @@ -120,15 +96,81 @@
   39.54  }
   39.55  
   39.56  
   39.57 -HttpStatus HttpRequest::setPolicyClassOption(policy_t policy_id,
   39.58 -											 EClassPolicy opt,
   39.59 -											 long value)
   39.60 +HttpStatus HttpRequest::setStaticPolicyOption(EPolicyOption opt, policy_t pclass,
   39.61 +											  long value, long * ret_value)
   39.62  {
   39.63  	if (HttpService::RUNNING == HttpService::instanceOf()->getState())
   39.64  	{
   39.65  		return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC);
   39.66  	}
   39.67 -	return HttpService::instanceOf()->getClassOptions(policy_id).set(opt, value);
   39.68 +	return HttpService::instanceOf()->setPolicyOption(opt, pclass, value, ret_value);
   39.69 +}
   39.70 +
   39.71 +
   39.72 +HttpStatus HttpRequest::setStaticPolicyOption(EPolicyOption opt, policy_t pclass,
   39.73 +											  const std::string & value, std::string * ret_value)
   39.74 +{
   39.75 +	if (HttpService::RUNNING == HttpService::instanceOf()->getState())
   39.76 +	{
   39.77 +		return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC);
   39.78 +	}
   39.79 +	return HttpService::instanceOf()->setPolicyOption(opt, pclass, value, ret_value);
   39.80 +}
   39.81 +
   39.82 +
   39.83 +HttpHandle HttpRequest::setPolicyOption(EPolicyOption opt, policy_t pclass,
   39.84 +										long value, HttpHandler * handler)
   39.85 +{
   39.86 +	HttpStatus status;
   39.87 +	HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID);
   39.88 +
   39.89 +	HttpOpSetGet * op = new HttpOpSetGet();
   39.90 +	if (! (status = op->setupSet(opt, pclass, value)))
   39.91 +	{
   39.92 +		op->release();
   39.93 +		mLastReqStatus = status;
   39.94 +		return handle;
   39.95 +	}
   39.96 +	op->setReplyPath(mReplyQueue, handler);
   39.97 +	if (! (status = mRequestQueue->addOp(op)))			// transfers refcount
   39.98 +	{
   39.99 +		op->release();
  39.100 +		mLastReqStatus = status;
  39.101 +		return handle;
  39.102 +	}
  39.103 +	
  39.104 +	mLastReqStatus = status;
  39.105 +	handle = static_cast<HttpHandle>(op);
  39.106 +	
  39.107 +	return handle;
  39.108 +}
  39.109 +
  39.110 +
  39.111 +HttpHandle HttpRequest::setPolicyOption(EPolicyOption opt, policy_t pclass,
  39.112 +										const std::string & value, HttpHandler * handler)
  39.113 +{
  39.114 +	HttpStatus status;
  39.115 +	HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID);
  39.116 +
  39.117 +	HttpOpSetGet * op = new HttpOpSetGet();
  39.118 +	if (! (status = op->setupSet(opt, pclass, value)))
  39.119 +	{
  39.120 +		op->release();
  39.121 +		mLastReqStatus = status;
  39.122 +		return handle;
  39.123 +	}
  39.124 +	op->setReplyPath(mReplyQueue, handler);
  39.125 +	if (! (status = mRequestQueue->addOp(op)))			// transfers refcount
  39.126 +	{
  39.127 +		op->release();
  39.128 +		mLastReqStatus = status;
  39.129 +		return handle;
  39.130 +	}
  39.131 +	
  39.132 +	mLastReqStatus = status;
  39.133 +	handle = static_cast<HttpHandle>(op);
  39.134 +	
  39.135 +	return handle;
  39.136  }
  39.137  
  39.138  
  39.139 @@ -474,31 +516,6 @@
  39.140  	return handle;
  39.141  }
  39.142  
  39.143 -// ====================================
  39.144 -// Dynamic Policy Methods
  39.145 -// ====================================
  39.146 -
  39.147 -HttpHandle HttpRequest::requestSetHttpProxy(const std::string & proxy, HttpHandler * handler)
  39.148 -{
  39.149 -	HttpStatus status;
  39.150 -	HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID);
  39.151 -
  39.152 -	HttpOpSetGet * op = new HttpOpSetGet();
  39.153 -	op->setupSet(GP_HTTP_PROXY, proxy);
  39.154 -	op->setReplyPath(mReplyQueue, handler);
  39.155 -	if (! (status = mRequestQueue->addOp(op)))			// transfers refcount
  39.156 -	{
  39.157 -		op->release();
  39.158 -		mLastReqStatus = status;
  39.159 -		return handle;
  39.160 -	}
  39.161 -
  39.162 -	mLastReqStatus = status;
  39.163 -	handle = static_cast<HttpHandle>(op);
  39.164 -
  39.165 -	return handle;
  39.166 -}
  39.167 -
  39.168  
  39.169  }   // end namespace LLCore
  39.170  
    40.1 --- a/indra/llcorehttp/httprequest.h	Wed Feb 12 17:18:03 2014 -0800
    40.2 +++ b/indra/llcorehttp/httprequest.h	Wed Feb 12 18:58:40 2014 -0800
    40.3 @@ -4,7 +4,7 @@
    40.4   *
    40.5   * $LicenseInfo:firstyear=2012&license=viewerlgpl$
    40.6   * Second Life Viewer Source Code
    40.7 - * Copyright (C) 2012, Linden Research, Inc.
    40.8 + * Copyright (C) 2012-2013, Linden Research, Inc.
    40.9   *
   40.10   * This library is free software; you can redistribute it and/or
   40.11   * modify it under the terms of the GNU Lesser General Public
   40.12 @@ -56,6 +56,9 @@
   40.13  /// The class supports the current HTTP request operations:
   40.14  ///
   40.15  /// - requestGetByteRange:  GET with Range header for a single range of bytes
   40.16 +/// - requestGet:
   40.17 +/// - requestPost:
   40.18 +/// - requestPut:
   40.19  ///
   40.20  /// Policy Classes
   40.21  ///
   40.22 @@ -100,9 +103,26 @@
   40.23  
   40.24  	/// Represents a default, catch-all policy class that guarantees
   40.25  	/// eventual service for any HTTP request.
   40.26 -	static const int DEFAULT_POLICY_ID = 0;
   40.27 +	static const policy_t DEFAULT_POLICY_ID = 0;
   40.28 +	static const policy_t INVALID_POLICY_ID = 0xFFFFFFFFU;
   40.29 +	static const policy_t GLOBAL_POLICY_ID = 0xFFFFFFFEU;
   40.30  
   40.31 -	enum EGlobalPolicy
   40.32 +	/// Create a new policy class into which requests can be made.
   40.33 +	///
   40.34 +	/// All class creation must occur before threads are started and
   40.35 +	/// transport begins.  Policy classes are limited to a small value.
   40.36 +	/// Currently that limit is the default class + 1.
   40.37 +	///
   40.38 +	/// @return			If positive, the policy_id used to reference
   40.39 +	///					the class in other methods.  If 0, requests
   40.40 +	///					for classes have exceeded internal limits
   40.41 +	///					or caller has tried to create a class after
   40.42 +	///					threads have been started.  Caller must fallback
   40.43 +	///					and recover.
   40.44 +	///
   40.45 +	static policy_t createPolicyClass();
   40.46 +
   40.47 +	enum EPolicyOption
   40.48  	{
   40.49  		/// Maximum number of connections the library will use to
   40.50  		/// perform operations.  This is somewhat soft as the underlying
   40.51 @@ -113,24 +133,40 @@
   40.52  		/// a somewhat soft value.  There may be an additional five
   40.53  		/// connections per policy class depending upon runtime
   40.54  		/// behavior.
   40.55 -		GP_CONNECTION_LIMIT,
   40.56 +		///
   40.57 +		/// Both global and per-class
   40.58 +		PO_CONNECTION_LIMIT,
   40.59 +
   40.60 +		/// Limits the number of connections used for a single
   40.61 +		/// literal address/port pair within the class.
   40.62 +		///
   40.63 +		/// Per-class only
   40.64 +		PO_PER_HOST_CONNECTION_LIMIT,
   40.65  
   40.66  		/// String containing a system-appropriate directory name
   40.67  		/// where SSL certs are stored.
   40.68 -		GP_CA_PATH,
   40.69 +		///
   40.70 +		/// Global only
   40.71 +		PO_CA_PATH,
   40.72  
   40.73  		/// String giving a full path to a file containing SSL certs.
   40.74 -		GP_CA_FILE,
   40.75 +		///
   40.76 +		/// Global only
   40.77 +		PO_CA_FILE,
   40.78  
   40.79  		/// String of host/port to use as simple HTTP proxy.  This is
   40.80  		/// going to change in the future into something more elaborate
   40.81  		/// that may support richer schemes.
   40.82 -		GP_HTTP_PROXY,
   40.83 +		///
   40.84 +		/// Global only
   40.85 +		PO_HTTP_PROXY,
   40.86  
   40.87  		/// Long value that if non-zero enables the use of the
   40.88  		/// traditional LLProxy code for http/socks5 support.  If
   40.89 -		/// enabled, has priority over GP_HTTP_PROXY.
   40.90 -		GP_LLPROXY,
   40.91 +		// enabled, has priority over GP_HTTP_PROXY.
   40.92 +		///
   40.93 +		/// Global only
   40.94 +		PO_LLPROXY,
   40.95  
   40.96  		/// Long value setting the logging trace level for the
   40.97  		/// library.  Possible values are:
   40.98 @@ -143,50 +179,59 @@
   40.99  		/// These values are also used in the trace modes for
  40.100  		/// individual requests in HttpOptions.  Also be aware that
  40.101  		/// tracing tends to impact performance of the viewer.
  40.102 -		GP_TRACE
  40.103 -	};
  40.104 -
  40.105 -	/// Set a parameter on a global policy option.  Calls
  40.106 -	/// made after the start of the servicing thread are
  40.107 -	/// not honored and return an error status.
  40.108 -	///
  40.109 -	/// @param opt		Enum of option to be set.
  40.110 -	/// @param value	Desired value of option.
  40.111 -	/// @return			Standard status code.
  40.112 -	static HttpStatus setPolicyGlobalOption(EGlobalPolicy opt, long value);
  40.113 -	static HttpStatus setPolicyGlobalOption(EGlobalPolicy opt, const std::string & value);
  40.114 -
  40.115 -	/// Create a new policy class into which requests can be made.
  40.116 -	///
  40.117 -	/// @return			If positive, the policy_id used to reference
  40.118 -	///					the class in other methods.  If 0, an error
  40.119 -	///					occurred and @see getStatus() may provide more
  40.120 -	///					detail on the reason.
  40.121 -	static policy_t createPolicyClass();
  40.122 -
  40.123 -	enum EClassPolicy
  40.124 -	{
  40.125 -		/// Limits the number of connections used for the class.
  40.126 -		CP_CONNECTION_LIMIT,
  40.127 -
  40.128 -		/// Limits the number of connections used for a single
  40.129 -		/// literal address/port pair within the class.
  40.130 -		CP_PER_HOST_CONNECTION_LIMIT,
  40.131 +		///
  40.132 +		/// Global only
  40.133 +		PO_TRACE,
  40.134  
  40.135  		/// Suitable requests are allowed to pipeline on their
  40.136  		/// connections when they ask for it.
  40.137 -		CP_ENABLE_PIPELINING
  40.138 +		///
  40.139 +		/// Per-class only
  40.140 +		PO_ENABLE_PIPELINING,
  40.141 +
  40.142 +		/// Controls whether client-side throttling should be
  40.143 +		/// performed on this policy class.  Positive values
  40.144 +		/// enable throttling and specify the request rate
  40.145 +		/// (requests per second) that should be targetted.
  40.146 +		/// A value of zero, the default, specifies no throttling.
  40.147 +		///
  40.148 +		/// Per-class only
  40.149 +		PO_THROTTLE_RATE,
  40.150 +		
  40.151 +		PO_LAST  // Always at end
  40.152  	};
  40.153 -	
  40.154 +
  40.155 +	/// Set a policy option for a global or class parameter at
  40.156 +	/// startup time (prior to thread start).
  40.157 +	///
  40.158 +	/// @param opt			Enum of option to be set.
  40.159 +	/// @param pclass		For class-based options, the policy class ID to
  40.160 +	///					    be changed.  For globals, specify GLOBAL_POLICY_ID.
  40.161 +	/// @param value		Desired value of option.
  40.162 +	/// @param ret_value	Pointer to receive effective set value
  40.163 +	///						if successful.  May be NULL if effective
  40.164 +	///						value not wanted.
  40.165 +	/// @return				Standard status code.
  40.166 +	static HttpStatus setStaticPolicyOption(EPolicyOption opt, policy_t pclass,
  40.167 +											long value, long * ret_value);
  40.168 +	static HttpStatus setStaticPolicyOption(EPolicyOption opt, policy_t pclass,
  40.169 +											const std::string & value, std::string * ret_value);
  40.170 +
  40.171  	/// Set a parameter on a class-based policy option.  Calls
  40.172  	/// made after the start of the servicing thread are
  40.173  	/// not honored and return an error status.
  40.174  	///
  40.175 -	/// @param policy_id		ID of class as returned by @see createPolicyClass().
  40.176 -	/// @param opt				Enum of option to be set.
  40.177 -	/// @param value			Desired value of option.
  40.178 -	/// @return					Standard status code.
  40.179 -	static HttpStatus setPolicyClassOption(policy_t policy_id, EClassPolicy opt, long value);
  40.180 +	/// @param opt			Enum of option to be set.
  40.181 +	/// @param pclass		For class-based options, the policy class ID to
  40.182 +	///					    be changed.  Ignored for globals but recommend
  40.183 +	///					    using INVALID_POLICY_ID in this case.
  40.184 +	/// @param value		Desired value of option.
  40.185 +	/// @return				Handle of dynamic request.  Use @see getStatus() if
  40.186 +	///						the returned handle is invalid.
  40.187 +	HttpHandle setPolicyOption(EPolicyOption opt, policy_t pclass, long value,
  40.188 +							   HttpHandler * handler);
  40.189 +	HttpHandle setPolicyOption(EPolicyOption opt, policy_t pclass, const std::string & value,
  40.190 +							   HttpHandler * handler);
  40.191  
  40.192  	/// @}
  40.193  
  40.194 @@ -488,16 +533,6 @@
  40.195  
  40.196  	/// @}
  40.197  	
  40.198 -	/// @name DynamicPolicyMethods
  40.199 -	///
  40.200 -	/// @{
  40.201 -
  40.202 -	/// Request that a running transport pick up a new proxy setting.
  40.203 -	/// An empty string will indicate no proxy is to be used.
  40.204 -	HttpHandle requestSetHttpProxy(const std::string & proxy, HttpHandler * handler);
  40.205 -
  40.206 -    /// @}
  40.207 -
  40.208  protected:
  40.209  	void generateNotification(HttpOperation * op);
  40.210  
  40.211 @@ -519,7 +554,6 @@
  40.212  	/// Must be established before any threading is allowed to
  40.213  	/// start.
  40.214  	///
  40.215 -	static policy_t		sNextPolicyID;
  40.216  	
  40.217  	/// @}
  40.218  	// End Global State
    41.1 --- a/indra/llcorehttp/httpresponse.cpp	Wed Feb 12 17:18:03 2014 -0800
    41.2 +++ b/indra/llcorehttp/httpresponse.cpp	Wed Feb 12 18:58:40 2014 -0800
    41.3 @@ -4,7 +4,7 @@
    41.4   *
    41.5   * $LicenseInfo:firstyear=2012&license=viewerlgpl$
    41.6   * Second Life Viewer Source Code
    41.7 - * Copyright (C) 2012, Linden Research, Inc.
    41.8 + * Copyright (C) 2012-2013, Linden Research, Inc.
    41.9   *
   41.10   * This library is free software; you can redistribute it and/or
   41.11   * modify it under the terms of the GNU Lesser General Public
   41.12 @@ -39,7 +39,9 @@
   41.13  	  mReplyLength(0U),
   41.14  	  mReplyFullLength(0U),
   41.15  	  mBufferArray(NULL),
   41.16 -	  mHeaders(NULL)
   41.17 +	  mHeaders(NULL),
   41.18 +	  mRetries(0U),
   41.19 +	  m503Retries(0U)
   41.20  {}
   41.21  
   41.22  
    42.1 --- a/indra/llcorehttp/httpresponse.h	Wed Feb 12 17:18:03 2014 -0800
    42.2 +++ b/indra/llcorehttp/httpresponse.h	Wed Feb 12 18:58:40 2014 -0800
    42.3 @@ -4,7 +4,7 @@
    42.4   *
    42.5   * $LicenseInfo:firstyear=2012&license=viewerlgpl$
    42.6   * Second Life Viewer Source Code
    42.7 - * Copyright (C) 2012, Linden Research, Inc.
    42.8 + * Copyright (C) 2012-2013, Linden Research, Inc.
    42.9   *
   42.10   * This library is free software; you can redistribute it and/or
   42.11   * modify it under the terms of the GNU Lesser General Public
   42.12 @@ -149,6 +149,25 @@
   42.13  			mContentType = con_type;
   42.14  		}
   42.15  
   42.16 +	/// Get and set retry attempt information on the request.
   42.17 +	void getRetries(unsigned int * retries, unsigned int * retries_503) const
   42.18 +		{
   42.19 +			if (retries)
   42.20 +			{
   42.21 +				*retries = mRetries;
   42.22 +			}
   42.23 +			if (retries_503)
   42.24 +			{
   42.25 +				*retries_503 = m503Retries;
   42.26 +			}
   42.27 +		}
   42.28 +
   42.29 +	void setRetries(unsigned int retries, unsigned int retries_503)
   42.30 +		{
   42.31 +			mRetries = retries;
   42.32 +			m503Retries = retries_503;
   42.33 +		}
   42.34 +
   42.35  protected:
   42.36  	// Response data here
   42.37  	HttpStatus			mStatus;
   42.38 @@ -158,6 +177,8 @@
   42.39  	BufferArray *		mBufferArray;
   42.40  	HttpHeaders *		mHeaders;
   42.41  	std::string			mContentType;
   42.42 +	unsigned int		mRetries;
   42.43 +	unsigned int		m503Retries;
   42.44  };
   42.45  
   42.46  
    43.1 --- a/indra/llcorehttp/tests/test_httpheaders.hpp	Wed Feb 12 17:18:03 2014 -0800
    43.2 +++ b/indra/llcorehttp/tests/test_httpheaders.hpp	Wed Feb 12 18:58:40 2014 -0800
    43.3 @@ -4,7 +4,7 @@
    43.4   *
    43.5   * $LicenseInfo:firstyear=2012&license=viewerlgpl$
    43.6   * Second Life Viewer Source Code
    43.7 - * Copyright (C) 2012, Linden Research, Inc.
    43.8 + * Copyright (C) 2012-2013, Linden Research, Inc.
    43.9   *
   43.10   * This library is free software; you can redistribute it and/or
   43.11   * modify it under the terms of the GNU Lesser General Public
   43.12 @@ -36,7 +36,6 @@
   43.13  using namespace LLCoreInt;
   43.14  
   43.15  
   43.16 -
   43.17  namespace tut
   43.18  {
   43.19  
   43.20 @@ -63,7 +62,7 @@
   43.21  	HttpHeaders * headers = new HttpHeaders();
   43.22  	ensure("One ref on construction of HttpHeaders", headers->getRefCount() == 1);
   43.23  	ensure("Memory being used", mMemTotal < GetMemTotal());
   43.24 -	ensure("Nothing in headers", 0 == headers->mHeaders.size());
   43.25 +	ensure("Nothing in headers", 0 == headers->size());
   43.26  
   43.27  	// release the implicit reference, causing the object to be released
   43.28  	headers->release();
   43.29 @@ -85,14 +84,340 @@
   43.30  	
   43.31  	{
   43.32  		// Append a few strings
   43.33 -		std::string str1("Pragma:");
   43.34 -		headers->mHeaders.push_back(str1);
   43.35 -		std::string str2("Accept: application/json");
   43.36 -		headers->mHeaders.push_back(str2);
   43.37 +		std::string str1n("Pragma");
   43.38 +		std::string str1v("");
   43.39 +		headers->append(str1n, str1v);
   43.40 +		std::string str2n("Accept");
   43.41 +		std::string str2v("application/json");
   43.42 +		headers->append(str2n, str2v);
   43.43  	
   43.44 -		ensure("Headers retained", 2 == headers->mHeaders.size());
   43.45 -		ensure("First is first", headers->mHeaders[0] == str1);
   43.46 -		ensure("Second is second", headers->mHeaders[1] == str2);
   43.47 +		ensure("Headers retained", 2 == headers->size());
   43.48 +		HttpHeaders::container_t & c(headers->getContainerTESTONLY());
   43.49 +		
   43.50 +		ensure("First name is first name", c[0].first == str1n);
   43.51 +		ensure("First value is first value", c[0].second == str1v);
   43.52 +		ensure("Second name is second name", c[1].first == str2n);
   43.53 +		ensure("Second value is second value", c[1].second == str2v);
   43.54 +	}
   43.55 +	
   43.56 +	// release the implicit reference, causing the object to be released
   43.57 +	headers->release();
   43.58 +
   43.59 +	// make sure we didn't leak any memory
   43.60 +	ensure(mMemTotal == GetMemTotal());
   43.61 +}
   43.62 +
   43.63 +template <> template <>
   43.64 +void HttpHeadersTestObjectType::test<3>()
   43.65 +{
   43.66 +	set_test_name("HttpHeaders basic find");
   43.67 +
   43.68 +	// record the total amount of dynamically allocated memory
   43.69 +	mMemTotal = GetMemTotal();
   43.70 +
   43.71 +	// create a new ref counted object with an implicit reference
   43.72 +	HttpHeaders * headers = new HttpHeaders();
   43.73 +	
   43.74 +	{
   43.75 +		// Append a few strings
   43.76 +		std::string str1n("Uno");
   43.77 +		std::string str1v("1");
   43.78 +		headers->append(str1n, str1v);
   43.79 +		std::string str2n("doS");
   43.80 +		std::string str2v("2-2-2-2");
   43.81 +		headers->append(str2n, str2v);
   43.82 +		std::string str3n("TRES");
   43.83 +		std::string str3v("trois gymnopedie");
   43.84 +		headers->append(str3n, str3v);
   43.85 +	
   43.86 +		ensure("Headers retained", 3 == headers->size());
   43.87 +
   43.88 +		const std::string * result(NULL);
   43.89 +
   43.90 +		// Find a header
   43.91 +		result = headers->find("TRES");
   43.92 +		ensure("Found the last item", result != NULL);
   43.93 +		ensure("Last item is a nice", result != NULL && str3v == *result);
   43.94 +
   43.95 +		// appends above are raw and find is case sensitive
   43.96 +		result = headers->find("TReS");
   43.97 +		ensure("Last item not found due to case", result == NULL);
   43.98 +
   43.99 +		result = headers->find("TRE");
  43.100 +		ensure("Last item not found due to prefixing (1)", result == NULL);
  43.101 +
  43.102 +		result = headers->find("TRESS");
  43.103 +		ensure("Last item not found due to prefixing (2)", result == NULL);
  43.104 +	}
  43.105 +	
  43.106 +	// release the implicit reference, causing the object to be released
  43.107 +	headers->release();
  43.108 +
  43.109 +	// make sure we didn't leak any memory
  43.110 +	ensure(mMemTotal == GetMemTotal());
  43.111 +}
  43.112 +
  43.113 +template <> template <>
  43.114 +void HttpHeadersTestObjectType::test<4>()
  43.115 +{
  43.116 +	set_test_name("HttpHeaders normalized header entry");
  43.117 +
  43.118 +	// record the total amount of dynamically allocated memory
  43.119 +	mMemTotal = GetMemTotal();
  43.120 +
  43.121 +	// create a new ref counted object with an implicit reference
  43.122 +	HttpHeaders * headers = new HttpHeaders();
  43.123 +	
  43.124 +	{
  43.125 +		static char line1[] = " AcCePT : image/yourfacehere";
  43.126 +		static char line1v[] = "image/yourfacehere";
  43.127 +		headers->appendNormal(line1, sizeof(line1) - 1);
  43.128 +		
  43.129 +		ensure("First append worked in some fashion", 1 == headers->size());
  43.130 +
  43.131 +		const std::string * result(NULL);
  43.132 +
  43.133 +		// Find a header
  43.134 +		result = headers->find("accept");
  43.135 +		ensure("Found 'accept'", result != NULL);
  43.136 +		ensure("accept value has face", result != NULL && *result == line1v);
  43.137 +
  43.138 +		// Left-clean on value
  43.139 +		static char line2[] = " next : \t\tlinejunk \t";
  43.140 +		headers->appendNormal(line2, sizeof(line2) - 1);
  43.141 +		ensure("Second append worked", 2 == headers->size());
  43.142 +		result = headers->find("next");
  43.143 +		ensure("Found 'next'", result != NULL);
  43.144 +		ensure("next value is left-clean", result != NULL &&
  43.145 +			   *result == "linejunk \t");
  43.146 +
  43.147 +		// First value unmolested
  43.148 +		result = headers->find("accept");
  43.149 +		ensure("Found 'accept' again", result != NULL);
  43.150 +		ensure("accept value has face", result != NULL && *result == line1v);
  43.151 +
  43.152 +		// Colons in value are okay
  43.153 +		static char line3[] = "FancY-PANTs::plop:-neuf-=vleem=";
  43.154 +		static char line3v[] = ":plop:-neuf-=vleem=";
  43.155 +		headers->appendNormal(line3, sizeof(line3) - 1);
  43.156 +		ensure("Third append worked", 3 == headers->size());
  43.157 +		result = headers->find("fancy-pants");
  43.158 +		ensure("Found 'fancy-pants'", result != NULL);
  43.159 +		ensure("fancy-pants value has colons", result != NULL && *result == line3v);
  43.160 +
  43.161 +		// Zero-length value
  43.162 +		static char line4[] = "all-talk-no-walk:";
  43.163 +		headers->appendNormal(line4, sizeof(line4) - 1);
  43.164 +		ensure("Fourth append worked", 4 == headers->size());
  43.165 +		result = headers->find("all-talk-no-walk");
  43.166 +		ensure("Found 'all-talk'", result != NULL);
  43.167 +		ensure("al-talk value is zero-length", result != NULL && result->size() == 0);
  43.168 +
  43.169 +		// Zero-length name
  43.170 +		static char line5[] = ":all-talk-no-walk";
  43.171 +		static char line5v[] = "all-talk-no-walk";
  43.172 +		headers->appendNormal(line5, sizeof(line5) - 1);
  43.173 +		ensure("Fifth append worked", 5 == headers->size());
  43.174 +		result = headers->find("");
  43.175 +		ensure("Found no-name", result != NULL);
  43.176 +		ensure("no-name value is something", result != NULL && *result == line5v);
  43.177 +
  43.178 +		// Lone colon is still something
  43.179 +		headers->clear();
  43.180 +		static char line6[] = "  :";
  43.181 +		headers->appendNormal(line6, sizeof(line6) - 1);
  43.182 +		ensure("Sixth append worked", 1 == headers->size());
  43.183 +		result = headers->find("");
  43.184 +		ensure("Found 2nd no-name", result != NULL);
  43.185 +		ensure("2nd no-name value is nothing", result != NULL && result->size() == 0);
  43.186 +
  43.187 +		// Line without colons is taken as-is and unstripped in name
  43.188 +		static char line7[] = " \toskdgioasdghaosdghoowg28342908tg8902hg0hwedfhqew890v7qh0wdebv78q0wdevbhq>?M>BNM<ZV>?NZ? \t";
  43.189 +		headers->appendNormal(line7, sizeof(line7) - 1);
  43.190 +		ensure("Seventh append worked", 2 == headers->size());
  43.191 +		result = headers->find(line7);
  43.192 +		ensure("Found whatsit line", result != NULL);
  43.193 +		ensure("Whatsit line has no value", result != NULL && result->size() == 0);
  43.194 +
  43.195 +		// Normaling interface heeds the byte count, doesn't look for NUL-terminator
  43.196 +		static char line8[] = "binary:ignorestuffontheendofthis";
  43.197 +		headers->appendNormal(line8, 13);
  43.198 +		ensure("Eighth append worked", 3 == headers->size());
  43.199 +		result = headers->find("binary");
  43.200 +		ensure("Found 'binary'", result != NULL);
  43.201 +		ensure("binary value was limited to 'ignore'", result != NULL &&
  43.202 +			   *result == "ignore");
  43.203 +
  43.204 +	}
  43.205 +	
  43.206 +	// release the implicit reference, causing the object to be released
  43.207 +	headers->release();
  43.208 +
  43.209 +	// make sure we didn't leak any memory
  43.210 +	ensure(mMemTotal == GetMemTotal());
  43.211 +}
  43.212 +
  43.213 +// Verify forward iterator finds everything as expected
  43.214 +template <> template <>
  43.215 +void HttpHeadersTestObjectType::test<5>()
  43.216 +{
  43.217 +	set_test_name("HttpHeaders iterator tests");
  43.218 +
  43.219 +	// record the total amount of dynamically allocated memory
  43.220 +	mMemTotal = GetMemTotal();
  43.221 +
  43.222 +	// create a new ref counted object with an implicit reference
  43.223 +	HttpHeaders * headers = new HttpHeaders();
  43.224 +
  43.225 +	HttpHeaders::iterator end(headers->end()), begin(headers->begin());
  43.226 +	ensure("Empty container has equal begin/end const iterators", end == begin);
  43.227 +	HttpHeaders::const_iterator cend(headers->end()), cbegin(headers->begin());
  43.228 +	ensure("Empty container has equal rbegin/rend const iterators", cend == cbegin);
  43.229 +
  43.230 +	ensure("Empty container has equal begin/end iterators", headers->end() == headers->begin());
  43.231 +	
  43.232 +	{
  43.233 +		static char line1[] = " AcCePT : image/yourfacehere";
  43.234 +		static char line1v[] = "image/yourfacehere";
  43.235 +		headers->appendNormal(line1, sizeof(line1) - 1);
  43.236 +
  43.237 +		static char line2[] = " next : \t\tlinejunk \t";
  43.238 +		static char line2v[] = "linejunk \t";
  43.239 +		headers->appendNormal(line2, sizeof(line2) - 1);
  43.240 +
  43.241 +		static char line3[] = "FancY-PANTs::plop:-neuf-=vleem=";
  43.242 +		static char line3v[] = ":plop:-neuf-=vleem=";
  43.243 +		headers->appendNormal(line3, sizeof(line3) - 1);
  43.244 +
  43.245 +		static char line4[] = "all-talk-no-walk:";
  43.246 +		static char line4v[] = "";
  43.247 +		headers->appendNormal(line4, sizeof(line4) - 1);
  43.248 +
  43.249 +		static char line5[] = ":all-talk-no-walk";
  43.250 +		static char line5v[] = "all-talk-no-walk";
  43.251 +		headers->appendNormal(line5, sizeof(line5) - 1);
  43.252 +
  43.253 +		static char line6[] = "  :";
  43.254 +		static char line6v[] = "";
  43.255 +		headers->appendNormal(line6, sizeof(line6) - 1);
  43.256 +
  43.257 +		ensure("All entries accounted for", 6 == headers->size());
  43.258 +
  43.259 +		static char * values[] = {
  43.260 +			line1v,
  43.261 +			line2v,
  43.262 +			line3v,
  43.263 +			line4v,
  43.264 +			line5v,
  43.265 +			line6v
  43.266 +		};
  43.267 +			
  43.268 +		int i(0);
  43.269 +		HttpHeaders::const_iterator cend(headers->end());
  43.270 +		for (HttpHeaders::const_iterator it(headers->begin());
  43.271 +			 cend != it;
  43.272 +			 ++it, ++i)
  43.273 +		{
  43.274 +			std::ostringstream str;
  43.275 +			str << "Const Iterator value # " << i << " was " << values[i];
  43.276 +			ensure(str.str(), (*it).second == values[i]);
  43.277 +		}
  43.278 +
  43.279 +		// Rewind, do non-consts
  43.280 +		i = 0;
  43.281 +		HttpHeaders::iterator end(headers->end());
  43.282 +		for (HttpHeaders::iterator it(headers->begin());
  43.283 +			 end != it;
  43.284 +			 ++it, ++i)
  43.285 +		{
  43.286 +			std::ostringstream str;
  43.287 +			str << "Const Iterator value # " << i << " was " << values[i];
  43.288 +			ensure(str.str(), (*it).second == values[i]);
  43.289 +		}
  43.290 +	}
  43.291 +	
  43.292 +	// release the implicit reference, causing the object to be released
  43.293 +	headers->release();
  43.294 +
  43.295 +	// make sure we didn't leak any memory
  43.296 +	ensure(mMemTotal == GetMemTotal());
  43.297 +}
  43.298 +
  43.299 +// Reverse iterators find everything as expected
  43.300 +template <> template <>
  43.301 +void HttpHeadersTestObjectType::test<6>()
  43.302 +{
  43.303 +	set_test_name("HttpHeaders reverse iterator tests");
  43.304 +
  43.305 +	// record the total amount of dynamically allocated memory
  43.306 +	mMemTotal = GetMemTotal();
  43.307 +
  43.308 +	// create a new ref counted object with an implicit reference
  43.309 +	HttpHeaders * headers = new HttpHeaders();
  43.310 +
  43.311 +	HttpHeaders::reverse_iterator rend(headers->rend()), rbegin(headers->rbegin());
  43.312 +	ensure("Empty container has equal rbegin/rend const iterators", rend == rbegin);
  43.313 +	HttpHeaders::const_reverse_iterator crend(headers->rend()), crbegin(headers->rbegin());
  43.314 +	ensure("Empty container has equal rbegin/rend const iterators", crend == crbegin);
  43.315 +	
  43.316 +	{
  43.317 +		static char line1[] = " AcCePT : image/yourfacehere";
  43.318 +		static char line1v[] = "image/yourfacehere";
  43.319 +		headers->appendNormal(line1, sizeof(line1) - 1);
  43.320 +
  43.321 +		static char line2[] = " next : \t\tlinejunk \t";
  43.322 +		static char line2v[] = "linejunk \t";
  43.323 +		headers->appendNormal(line2, sizeof(line2) - 1);
  43.324 +
  43.325 +		static char line3[] = "FancY-PANTs::plop:-neuf-=vleem=";
  43.326 +		static char line3v[] = ":plop:-neuf-=vleem=";
  43.327 +		headers->appendNormal(line3, sizeof(line3) - 1);
  43.328 +
  43.329 +		static char line4[] = "all-talk-no-walk:";
  43.330 +		static char line4v[] = "";
  43.331 +		headers->appendNormal(line4, sizeof(line4) - 1);
  43.332 +
  43.333 +		static char line5[] = ":all-talk-no-walk";
  43.334 +		static char line5v[] = "all-talk-no-walk";
  43.335 +		headers->appendNormal(line5, sizeof(line5) - 1);
  43.336 +
  43.337 +		static char line6[] = "  :";
  43.338 +		static char line6v[] = "";
  43.339 +		headers->appendNormal(line6, sizeof(line6) - 1);
  43.340 +
  43.341 +		ensure("All entries accounted for", 6 == headers->size());
  43.342 +
  43.343 +		static char * values[] = {
  43.344 +			line6v,
  43.345 +			line5v,
  43.346 +			line4v,
  43.347 +			line3v,
  43.348 +			line2v,
  43.349 +			line1v
  43.350 +		};
  43.351 +			
  43.352 +		int i(0);
  43.353 +		HttpHeaders::const_reverse_iterator cend(headers->rend());
  43.354 +		for (HttpHeaders::const_reverse_iterator it(headers->rbegin());
  43.355 +			 cend != it;
  43.356 +			 ++it, ++i)
  43.357 +		{
  43.358 +			std::ostringstream str;
  43.359 +			str << "Const Iterator value # " << i << " was " << values[i];
  43.360 +			ensure(str.str(), (*it).second == values[i]);
  43.361 +		}
  43.362 +
  43.363 +		// Rewind, do non-consts
  43.364 +		i = 0;
  43.365 +		HttpHeaders::reverse_iterator end(headers->rend());
  43.366 +		for (HttpHeaders::reverse_iterator it(headers->rbegin());
  43.367 +			 end != it;
  43.368 +			 ++it, ++i)
  43.369 +		{
  43.370 +			std::ostringstream str;
  43.371 +			str << "Iterator value # " << i << " was " << values[i];
  43.372 +			ensure(str.str(), (*it).second == values[i]);
  43.373 +		}
  43.374  	}
  43.375  	
  43.376  	// release the implicit reference, causing the object to be released
    44.1 --- a/indra/llcorehttp/tests/test_httprequest.hpp	Wed Feb 12 17:18:03 2014 -0800
    44.2 +++ b/indra/llcorehttp/tests/test_httprequest.hpp	Wed Feb 12 18:58:40 2014 -0800
    44.3 @@ -69,6 +69,8 @@
    44.4  namespace tut
    44.5  {
    44.6  
    44.7 +typedef std::vector<std::pair<boost::regex, boost::regex> > regex_container_t;
    44.8 +
    44.9  struct HttpRequestTestData
   44.10  {
   44.11  	// the test objects inherit from this so the member functions and variables
   44.12 @@ -118,11 +120,17 @@
   44.13  					for (int i(0); i < mHeadersRequired.size(); ++i)
   44.14  					{
   44.15  						bool found = false;
   44.16 -						for (HttpHeaders::container_t::const_iterator iter(header->mHeaders.begin());
   44.17 -							 header->mHeaders.end() != iter;
   44.18 +						for (HttpHeaders::const_iterator iter(header->begin());
   44.19 +							 header->end() != iter;
   44.20  							 ++iter)
   44.21  						{
   44.22 -							if (boost::regex_match(*iter, mHeadersRequired[i]))
   44.23 +							// std::cerr << "Header: " << (*iter).first
   44.24 +							//		  << ": " << (*iter).second << std::endl;
   44.25 +							
   44.26 +							if (boost::regex_match((*iter).first,
   44.27 +												   mHeadersRequired[i].first) &&
   44.28 +								boost::regex_match((*iter).second,
   44.29 +												   mHeadersRequired[i].second))
   44.30  							{
   44.31  								found = true;
   44.32  								break;
   44.33 @@ -138,11 +146,14 @@
   44.34  				{
   44.35  					for (int i(0); i < mHeadersDisallowed.size(); ++i)
   44.36  					{
   44.37 -						for (HttpHeaders::container_t::const_iterator iter(header->mHeaders.begin());
   44.38 -							 header->mHeaders.end() != iter;
   44.39 +						for (HttpHeaders::const_iterator iter(header->begin());
   44.40 +							 header->end() != iter;
   44.41  							 ++iter)
   44.42  						{
   44.43 -							if (boost::regex_match(*iter, mHeadersDisallowed[i]))
   44.44 +							if (boost::regex_match((*iter).first,
   44.45 +												   mHeadersDisallowed[i].first) &&
   44.46 +								boost::regex_match((*iter).second,
   44.47 +												   mHeadersDisallowed[i].second))
   44.48  							{
   44.49  								std::ostringstream str;
   44.50  								str << "Disallowed header # " << i << " not found in response";
   44.51 @@ -168,8 +179,8 @@
   44.52  	std::string mName;
   44.53  	HttpHandle mExpectHandle;
   44.54  	std::string mCheckContentType;
   44.55 -	std::vector<boost::regex> mHeadersRequired;
   44.56 -	std::vector<boost::regex> mHeadersDisallowed;
   44.57 +	regex_container_t mHeadersRequired;
   44.58 +	regex_container_t mHeadersDisallowed;
   44.59  };
   44.60  
   44.61  typedef test_group<HttpRequestTestData> HttpRequestTestGroupType;
   44.62 @@ -1211,7 +1222,7 @@
   44.63  		HttpRequest::createService();
   44.64  
   44.65  		// Enable tracing
   44.66 -		HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_TRACE, 2);
   44.67 +		HttpRequest::setStaticPolicyOption(HttpRequest::PO_TRACE, HttpRequest::DEFAULT_POLICY_ID, 2, NULL);
   44.68  
   44.69  		// Start threading early so that thread memory is invariant
   44.70  		// over the test.
   44.71 @@ -1329,7 +1340,7 @@
   44.72  		HttpRequest::createService();
   44.73  
   44.74  		// Enable tracing
   44.75 -		HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_TRACE, 2);
   44.76 +		HttpRequest::setStaticPolicyOption(HttpRequest::PO_TRACE, HttpRequest::DEFAULT_POLICY_ID, 2, NULL);
   44.77  
   44.78  		// Start threading early so that thread memory is invariant
   44.79  		// over the test.
   44.80 @@ -1344,7 +1355,9 @@
   44.81  		
   44.82  		// Issue a GET that succeeds
   44.83  		mStatus = HttpStatus(200);
   44.84 -		handler.mHeadersRequired.push_back(boost::regex("\\W*X-LL-Special:.*", boost::regex::icase));
   44.85 +		handler.mHeadersRequired.push_back(
   44.86 +			regex_container_t::value_type(boost::regex("X-LL-Special", boost::regex::icase),
   44.87 +										  boost::regex(".*", boost::regex::icase)));
   44.88  		HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID,
   44.89  													 0U,
   44.90  													 url_base,
   44.91 @@ -1711,18 +1724,54 @@
   44.92  		
   44.93  		// Issue a GET that *can* connect
   44.94  		mStatus = HttpStatus(200);
   44.95 -		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase));
   44.96 -		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*\\*/\\*", boost::regex::icase));
   44.97 -		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough
   44.98 -		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*\\d+", boost::regex::icase));
   44.99 -		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase));
  44.100 -		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-cache-control:.*", boost::regex::icase));
  44.101 -		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase));
  44.102 -		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-range:.*", boost::regex::icase));
  44.103 -		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:.*", boost::regex::icase));
  44.104 -		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase));
  44.105 -		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-type:.*", boost::regex::icase));
  44.106 -		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase));
  44.107 +		handler.mHeadersRequired.push_back(
  44.108 +			regex_container_t::value_type(
  44.109 +				boost::regex("X-Reflect-connection", boost::regex::icase),
  44.110 +				boost::regex("keep-alive", boost::regex::icase)));
  44.111 +		handler.mHeadersRequired.push_back(
  44.112 +			regex_container_t::value_type(
  44.113 +				boost::regex("X-Reflect-accept", boost::regex::icase),
  44.114 +				boost::regex("\\*/\\*", boost::regex::icase)));
  44.115 +		handler.mHeadersRequired.push_back(
  44.116 +			regex_container_t::value_type(
  44.117 +				boost::regex("X-Reflect-accept-encoding", boost::regex::icase),
  44.118 +				boost::regex("((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase))); // close enough
  44.119 +		handler.mHeadersRequired.push_back(
  44.120 +			regex_container_t::value_type(
  44.121 +				boost::regex("X-Reflect-keep-alive", boost::regex::icase),
  44.122 +				boost::regex("\\d+", boost::regex::icase)));
  44.123 +		handler.mHeadersRequired.push_back(
  44.124 +			regex_container_t::value_type(
  44.125 +				boost::regex("X-Reflect-host", boost::regex::icase),
  44.126 +				boost::regex(".*", boost::regex::icase)));
  44.127 +		handler.mHeadersDisallowed.push_back(
  44.128 +			regex_container_t::value_type(
  44.129 +				boost::regex("X-Reflect-cache-control", boost::regex::icase),
  44.130 +				boost::regex(".*", boost::regex::icase)));
  44.131 +		handler.mHeadersDisallowed.push_back(
  44.132 +			regex_container_t::value_type(
  44.133 +				boost::regex("X-Reflect-pragma", boost::regex::icase),
  44.134 +				boost::regex(".*", boost::regex::icase)));
  44.135 +		handler.mHeadersDisallowed.push_back(
  44.136 +			regex_container_t::value_type(
  44.137 +				boost::regex("X-Reflect-range", boost::regex::icase),
  44.138 +				boost::regex(".*", boost::regex::icase)));
  44.139 +		handler.mHeadersDisallowed.push_back(
  44.140 +			regex_container_t::value_type(
  44.141 +				boost::regex("X-Reflect-transfer-encoding", boost::regex::icase),
  44.142 +				boost::regex(".*", boost::regex::icase)));
  44.143 +		handler.mHeadersDisallowed.push_back(
  44.144 +			regex_container_t::value_type(
  44.145 +				boost::regex("X-Reflect-referer", boost::regex::icase),
  44.146 +				boost::regex(".*", boost::regex::icase)));
  44.147 +		handler.mHeadersDisallowed.push_back(
  44.148 +			regex_container_t::value_type(
  44.149 +				boost::regex("X-Reflect-content-type", boost::regex::icase),
  44.150 +				boost::regex(".*", boost::regex::icase)));
  44.151 +		handler.mHeadersDisallowed.push_back(
  44.152 +			regex_container_t::value_type(
  44.153 +				boost::regex("X-Reflect-content-encoding", boost::regex::icase),
  44.154 +				boost::regex(".*", boost::regex::icase)));
  44.155  		HttpHandle handle = req->requestGet(HttpRequest::DEFAULT_POLICY_ID,
  44.156  											0U,
  44.157  											url_base + "reflect/",
  44.158 @@ -1744,23 +1793,60 @@
  44.159  
  44.160  		// Do a texture-style fetch
  44.161  		headers = new HttpHeaders;
  44.162 -		headers->mHeaders.push_back("Accept: image/x-j2c");
  44.163 +		headers->append("Accept", "image/x-j2c");
  44.164  		
  44.165  		mStatus = HttpStatus(200);
  44.166  		handler.mHeadersRequired.clear();
  44.167  		handler.mHeadersDisallowed.clear();
  44.168 -		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase));
  44.169 -		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*image/x-j2c", boost::regex::icase));
  44.170 -		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough
  44.171 -		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*\\d+", boost::regex::icase));
  44.172 -		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase));
  44.173 -		handler.mHeadersRequired.push_back(boost::regex("\\W*X-Reflect-range:.*", boost::regex::icase));
  44.174 -		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-cache-control:.*", boost::regex::icase));
  44.175 -		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase));
  44.176 -		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:.*", boost::regex::icase));
  44.177 -		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase));
  44.178 -		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-type:.*", boost::regex::icase));
  44.179 -		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase));
  44.180 +		handler.mHeadersRequired.push_back(
  44.181 +			regex_container_t::value_type(
  44.182 +				boost::regex("X-Reflect-connection", boost::regex::icase),
  44.183 +				boost::regex("keep-alive", boost::regex::icase)));
  44.184 +		handler.mHeadersRequired.push_back(
  44.185 +			regex_container_t::value_type(
  44.186 +				boost::regex("X-Reflect-accept", boost::regex::icase),
  44.187 +				boost::regex("image/x-j2c", boost::regex::icase)));
  44.188 +		handler.mHeadersRequired.push_back(
  44.189 +			regex_container_t::value_type(
  44.190 +				boost::regex("X-Reflect-accept-encoding", boost::regex::icase),
  44.191 +				boost::regex("((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase))); // close enough
  44.192 +		handler.mHeadersRequired.push_back(
  44.193 +			regex_container_t::value_type(
  44.194 +				boost::regex("X-Reflect-keep-alive", boost::regex::icase),
  44.195 +				boost::regex("\\d+", boost::regex::icase)));
  44.196 +		handler.mHeadersRequired.push_back(
  44.197 +			regex_container_t::value_type(
  44.198 +				boost::regex("X-Reflect-host", boost::regex::icase),
  44.199 +				boost::regex(".*", boost::regex::icase)));
  44.200 +		handler.mHeadersRequired.push_back(
  44.201 +			regex_container_t::value_type(
  44.202 +				boost::regex("\\W*X-Reflect-range", boost::regex::icase),
  44.203 +				boost::regex(".*", boost::regex::icase)));
  44.204 +
  44.205 +		handler.mHeadersDisallowed.push_back(
  44.206 +			regex_container_t::value_type(
  44.207 +				boost::regex("X-Reflect-cache-control", boost::regex::icase),
  44.208 +				boost::regex(".*", boost::regex::icase)));
  44.209 +		handler.mHeadersDisallowed.push_back(
  44.210 +			regex_container_t::value_type(
  44.211 +				boost::regex("X-Reflect-pragma", boost::regex::icase),
  44.212 +				boost::regex(".*", boost::regex::icase)));
  44.213 +		handler.mHeadersDisallowed.push_back(
  44.214 +			regex_container_t::value_type(
  44.215 +				boost::regex("X-Reflect-transfer-encoding", boost::regex::icase),
  44.216 +				boost::regex(".*", boost::regex::icase)));
  44.217 +		handler.mHeadersDisallowed.push_back(
  44.218 +			regex_container_t::value_type(
  44.219 +				boost::regex("X-Reflect-referer", boost::regex::icase),
  44.220 +				boost::regex(".*", boost::regex::icase)));
  44.221 +		handler.mHeadersDisallowed.push_back(
  44.222 +			regex_container_t::value_type(
  44.223 +				boost::regex("X-Reflect-content-type", boost::regex::icase),
  44.224 +				boost::regex(".*", boost::regex::icase)));
  44.225 +		handler.mHeadersDisallowed.push_back(
  44.226 +			regex_container_t::value_type(
  44.227 +				boost::regex("X-Reflect-content-encoding", boost::regex::icase),
  44.228 +				boost::regex(".*", boost::regex::icase)));
  44.229  		handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID,
  44.230  										  0U,
  44.231  										  url_base + "reflect/",
  44.232 @@ -1901,20 +1987,63 @@
  44.233  			
  44.234  		// Issue a default POST
  44.235  		mStatus = HttpStatus(200);
  44.236 -		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase));
  44.237 -		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*\\*/\\*", boost::regex::icase));
  44.238 -		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough
  44.239 -		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*\\d+", boost::regex::icase));
  44.240 -		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase));
  44.241 -		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-length:\\s*\\d+", boost::regex::icase));
  44.242 -		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-type:\\s*application/x-www-form-urlencoded", boost::regex::icase));
  44.243 -		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-cache-control:.*", boost::regex::icase));
  44.244 -		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase));
  44.245 -		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-range:.*", boost::regex::icase));
  44.246 -		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase));
  44.247 -		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase));
  44.248 -		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-expect:.*", boost::regex::icase));
  44.249 -		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:\\s*.*chunked.*", boost::regex::icase));
  44.250 +		handler.mHeadersRequired.push_back(
  44.251 +			regex_container_t::value_type(
  44.252 +				boost::regex("X-Reflect-connection", boost::regex::icase),
  44.253 +				boost::regex("keep-alive", boost::regex::icase)));
  44.254 +		handler.mHeadersRequired.push_back(
  44.255 +			regex_container_t::value_type(
  44.256 +				boost::regex("X-Reflect-accept", boost::regex::icase),
  44.257 +				boost::regex("\\*/\\*", boost::regex::icase)));
  44.258 +		handler.mHeadersRequired.push_back(
  44.259 +			regex_container_t::value_type(
  44.260 +				boost::regex("X-Reflect-accept-encoding", boost::regex::icase),
  44.261 +				boost::regex("((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase))); // close enough
  44.262 +		handler.mHeadersRequired.push_back(
  44.263 +			regex_container_t::value_type(
  44.264 +				boost::regex("X-Reflect-keep-alive", boost::regex::icase),
  44.265 +				boost::regex("\\d+", boost::regex::icase)));
  44.266 +		handler.mHeadersRequired.push_back(
  44.267 +			regex_container_t::value_type(
  44.268 +				boost::regex("X-Reflect-host", boost::regex::icase),
  44.269 +				boost::regex(".*", boost::regex::icase)));
  44.270 +		handler.mHeadersRequired.push_back(
  44.271 +			regex_container_t::value_type(
  44.272 +				boost::regex("X-Reflect-content-length", boost::regex::icase),
  44.273 +				boost::regex("\\d+", boost::regex::icase)));
  44.274 +		handler.mHeadersRequired.push_back(
  44.275 +			regex_container_t::value_type(
  44.276 +				boost::regex("X-Reflect-content-type", boost::regex::icase),
  44.277 +				boost::regex("application/x-www-form-urlencoded", boost::regex::icase)));
  44.278 +
  44.279 +		handler.mHeadersDisallowed.push_back(
  44.280 +			regex_container_t::value_type(
  44.281 +				boost::regex("X-Reflect-cache-control", boost::regex::icase),
  44.282 +				boost::regex(".*", boost::regex::icase)));
  44.283 +		handler.mHeadersDisallowed.push_back(
  44.284 +			regex_container_t::value_type(
  44.285 +				boost::regex("X-Reflect-pragma", boost::regex::icase),
  44.286 +				boost::regex(".*", boost::regex::icase)));
  44.287 +		handler.mHeadersDisallowed.push_back(
  44.288 +			regex_container_t::value_type(
  44.289 +				boost::regex("X-Reflect-range", boost::regex::icase),
  44.290 +				boost::regex(".*", boost::regex::icase)));
  44.291 +		handler.mHeadersDisallowed.push_back(
  44.292 +			regex_container_t::value_type(
  44.293 +				boost::regex("X-Reflect-referer", boost::regex::icase),
  44.294 +				boost::regex(".*", boost::regex::icase)));
  44.295 +		handler.mHeadersDisallowed.push_back(
  44.296 +			regex_container_t::value_type(
  44.297 +				boost::regex("X-Reflect-content-encoding", boost::regex::icase),
  44.298 +				boost::regex(".*", boost::regex::icase)));
  44.299 +		handler.mHeadersDisallowed.push_back(
  44.300 +			regex_container_t::value_type(
  44.301 +				boost::regex("X-Reflect-expect", boost::regex::icase),
  44.302 +				boost::regex(".*", boost::regex::icase)));
  44.303 +		handler.mHeadersDisallowed.push_back(
  44.304 +			regex_container_t::value_type(
  44.305 +				boost::regex("X-Reflect-transfer_encoding", boost::regex::icase),
  44.306 +				boost::regex(".*chunked.*", boost::regex::icase)));
  44.307  		HttpHandle handle = req->requestPost(HttpRequest::DEFAULT_POLICY_ID,
  44.308  											 0U,
  44.309  											 url_base + "reflect/",
  44.310 @@ -2061,20 +2190,64 @@
  44.311  			
  44.312  		// Issue a default PUT
  44.313  		mStatus = HttpStatus(200);
  44.314 -		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase));
  44.315 -		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*\\*/\\*", boost::regex::icase));
  44.316 -		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough
  44.317 -		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*\\d+", boost::regex::icase));
  44.318 -		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase));
  44.319 -		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-length:\\s*\\d+", boost::regex::icase));
  44.320 -		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-cache-control:.*", boost::regex::icase));
  44.321 -		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase));
  44.322 -		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-range:.*", boost::regex::icase));
  44.323 -		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase));
  44.324 -		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase));
  44.325 -		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-expect:.*", boost::regex::icase));
  44.326 -		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:\\s*.*chunked.*", boost::regex::icase));
  44.327 -		handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-content-type:.*", boost::regex::icase));
  44.328 +		handler.mHeadersRequired.push_back(
  44.329 +			regex_container_t::value_type(
  44.330 +				boost::regex("X-Reflect-connection", boost::regex::icase),
  44.331 +				boost::regex("keep-alive", boost::regex::icase)));
  44.332 +		handler.mHeadersRequired.push_back(
  44.333 +			regex_container_t::value_type(
  44.334 +				boost::regex("X-Reflect-accept", boost::regex::icase),
  44.335 +				boost::regex("\\*/\\*", boost::regex::icase)));
  44.336 +		handler.mHeadersRequired.push_back(
  44.337 +			regex_container_t::value_type(
  44.338 +				boost::regex("X-Reflect-accept-encoding", boost::regex::icase),
  44.339 +				boost::regex("((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase))); // close enough
  44.340 +		handler.mHeadersRequired.push_back(
  44.341 +			regex_container_t::value_type(
  44.342 +				boost::regex("X-Reflect-keep-alive", boost::regex::icase),
  44.343 +				boost::regex("\\d+", boost::regex::icase)));
  44.344 +		handler.mHeadersRequired.push_back(
  44.345 +			regex_container_t::value_type(
  44.346 +				boost::regex("X-Reflect-host", boost::regex::icase),
  44.347 +				boost::regex(".*", boost::regex::icase)));
  44.348 +		handler.mHeadersRequired.push_back(
  44.349 +			regex_container_t::value_type(
  44.350 +				boost::regex("X-Reflect-content-length", boost::regex::icase),
  44.351 +				boost::regex("\\d+", boost::regex::icase)));
  44.352 +
  44.353 +		handler.mHeadersDisallowed.push_back(
  44.354 +			regex_container_t::value_type(
  44.355 +				boost::regex("X-Reflect-cache-control", boost::regex::icase),
  44.356 +				boost::regex(".*", boost::regex::icase)));
  44.357 +		handler.mHeadersDisallowed.push_back(
  44.358 +			regex_container_t::value_type(
  44.359 +				boost::regex("X-Reflect-pragma", boost::regex::icase),
  44.360 +				boost::regex(".*", boost::regex::icase)));
  44.361 +		handler.mHeadersDisallowed.push_back(
  44.362 +			regex_container_t::value_type(
  44.363 +				boost::regex("X-Reflect-range", boost::regex::icase),
  44.364 +				boost::regex(".*", boost::regex::icase)));
  44.365 +		handler.mHeadersDisallowed.push_back(
  44.366 +			regex_container_t::value_type(
  44.367 +				boost::regex("X-Reflect-referer", boost::regex::icase),
  44.368 +				boost::regex(".*", boost::regex::icase)));
  44.369 +		handler.mHeadersDisallowed.push_back(
  44.370 +			regex_container_t::value_type(
  44.371 +				boost::regex("X-Reflect-content-encoding", boost::regex::icase),
  44.372 +				boost::regex(".*", boost::regex::icase)));
  44.373 +		handler.mHeadersDisallowed.push_back(
  44.374 +			regex_container_t::value_type(
  44.375 +				boost::regex("X-Reflect-expect", boost::regex::icase),
  44.376 +				boost::regex(".*", boost::regex::icase)));
  44.377 +		handler.mHeadersDisallowed.push_back(
  44.378 +			regex_container_t::value_type(
  44.379 +				boost::regex("X-Reflect-transfer-encoding", boost::regex::icase),
  44.380 +				boost::regex(".*chunked.*", boost::regex::icase)));
  44.381 +		handler.mHeadersDisallowed.push_back(
  44.382 +			regex_container_t::value_type(
  44.383 +				boost::regex("X-Reflect-content-type", boost::regex::icase),
  44.384 +				boost::regex(".*", boost::regex::icase)));
  44.385 +
  44.386  		HttpHandle handle = req->requestPut(HttpRequest::DEFAULT_POLICY_ID,
  44.387  											0U,
  44.388  											url_base + "reflect/",
  44.389 @@ -2215,27 +2388,73 @@
  44.390  
  44.391  		// headers
  44.392  		headers = new HttpHeaders;
  44.393 -		headers->mHeaders.push_back("Keep-Alive: 120");
  44.394 -		headers->mHeaders.push_back("Accept-encoding: deflate");
  44.395 -		headers->mHeaders.push_back("Accept: text/plain");
  44.396 +		headers->append("Keep-Alive", "120");
  44.397 +		headers->append("Accept-encoding", "deflate");
  44.398 +		headers->append("Accept", "text/plain");
  44.399  
  44.400  		// Issue a GET with modified headers
  44.401  		mStatus = HttpStatus(200);
  44.402 -		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase));
  44.403 -		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*text/plain", boost::regex::icase));
  44.404 -		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*deflate", boost::regex::icase)); // close enough
  44.405 -		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*120", boost::regex::icase));
  44.406 -		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase));
  44.407 -		handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough
  44.408 -		handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-keep-alive:\\s*300", boost::regex::icase));
  44.409 -		handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-accept:\\s*\\*/\\*", boost::regex::icase));
  44.410 -		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-cache-control:.*", boost::regex::icase));
  44.411 -		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase));
  44.412 -		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-range:.*", boost::regex::icase));
  44.413 -		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:.*", boost::regex::icase));
  44.414 -		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase));
  44.415 -		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-type:.*", boost::regex::icase));
  44.416 -		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase));
  44.417 +		handler.mHeadersRequired.push_back(
  44.418 +			regex_container_t::value_type(
  44.419 +				boost::regex("X-Reflect-connection", boost::regex::icase),
  44.420 +				boost::regex("keep-alive", boost::regex::icase)));
  44.421 +		handler.mHeadersRequired.push_back(
  44.422 +			regex_container_t::value_type(
  44.423 +				boost::regex("X-Reflect-accept", boost::regex::icase),
  44.424 +				boost::regex("text/plain", boost::regex::icase)));
  44.425 +		handler.mHeadersRequired.push_back(
  44.426 +			regex_container_t::value_type(
  44.427 +				boost::regex("X-Reflect-accept-encoding", boost::regex::icase),
  44.428 +				boost::regex("deflate", boost::regex::icase))); // close enough
  44.429 +		handler.mHeadersRequired.push_back(
  44.430 +			regex_container_t::value_type(
  44.431 +				boost::regex("X-Reflect-keep-alive", boost::regex::icase),
  44.432 +				boost::regex("120", boost::regex::icase)));
  44.433 +		handler.mHeadersRequired.push_back(
  44.434 +			regex_container_t::value_type(
  44.435 +				boost::regex("X-Reflect-host", boost::regex::icase),
  44.436 +				boost::regex(".*", boost::regex::icase)));
  44.437 +
  44.438 +		handler.mHeadersDisallowed.push_back(
  44.439 +			regex_container_t::value_type(
  44.440 +				boost::regex("X-Reflect-accept-encoding", boost::regex::icase),
  44.441 +				boost::regex("((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase))); // close enough
  44.442 +		handler.mHeadersDisallowed.push_back(
  44.443 +			regex_container_t::value_type(
  44.444 +				boost::regex("X-Reflect-keep-alive", boost::regex::icase),
  44.445 +				boost::regex("300", boost::regex::icase)));
  44.446 +		handler.mHeadersDisallowed.push_back(
  44.447 +			regex_container_t::value_type(
  44.448 +				boost::regex("X-Reflect-accept", boost::regex::icase),
  44.449 +				boost::regex("\\*/\\*", boost::regex::icase)));
  44.450 +		handler.mHeadersDisallowed.push_back(
  44.451 +			regex_container_t::value_type(
  44.452 +				boost::regex("X-Reflect-cache-control", boost::regex::icase),
  44.453 +				boost::regex(".*", boost::regex::icase)));
  44.454 +		handler.mHeadersDisallowed.push_back(
  44.455 +			regex_container_t::value_type(
  44.456 +				boost::regex("X-Reflect-pragma", boost::regex::icase),
  44.457 +				boost::regex(".*", boost::regex::icase)));
  44.458 +		handler.mHeadersDisallowed.push_back(
  44.459 +			regex_container_t::value_type(
  44.460 +				boost::regex("X-Reflect-range", boost::regex::icase),
  44.461 +				boost::regex(".*", boost::regex::icase)));
  44.462 +		handler.mHeadersDisallowed.push_back(
  44.463 +			regex_container_t::value_type(
  44.464 +				boost::regex("X-Reflect-transfer-encoding", boost::regex::icase),
  44.465 +				boost::regex(".*", boost::regex::icase)));
  44.466 +		handler.mHeadersDisallowed.push_back(
  44.467 +			regex_container_t::value_type(
  44.468 +				boost::regex("X-Reflect-referer", boost::regex::icase),
  44.469 +				boost::regex(".*", boost::regex::icase)));
  44.470 +		handler.mHeadersDisallowed.push_back(
  44.471 +			regex_container_t::value_type(
  44.472 +				boost::regex("X-Reflect-content-type", boost::regex::icase),
  44.473 +				boost::regex(".*", boost::regex::icase)));
  44.474 +		handler.mHeadersDisallowed.push_back(
  44.475 +			regex_container_t::value_type(
  44.476 +				boost::regex("X-Reflect-content-encoding", boost::regex::icase),
  44.477 +				boost::regex(".*", boost::regex::icase)));
  44.478  		HttpHandle handle = req->requestGet(HttpRequest::DEFAULT_POLICY_ID,
  44.479  											0U,
  44.480  											url_base + "reflect/",
  44.481 @@ -2368,10 +2587,10 @@
  44.482  
  44.483  		// headers
  44.484  		headers = new HttpHeaders();
  44.485 -		headers->mHeaders.push_back("keep-Alive: 120");
  44.486 -		headers->mHeaders.push_back("Accept:  text/html");
  44.487 -		headers->mHeaders.push_back("content-type:  application/llsd+xml");
  44.488 -		headers->mHeaders.push_back("cache-control: no-store");
  44.489 +		headers->append("keep-Alive", "120");
  44.490 +		headers->append("Accept", "text/html");
  44.491 +		headers->append("content-type", "application/llsd+xml");
  44.492 +		headers->append("cache-control", "no-store");
  44.493  		
  44.494  		// And a buffer array
  44.495  		const char * msg("<xml><llsd><string>It was the best of times, it was the worst of times.</string></llsd></xml>");
  44.496 @@ -2380,23 +2599,76 @@
  44.497  			
  44.498  		// Issue a default POST
  44.499  		mStatus = HttpStatus(200);
  44.500 -		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase));
  44.501 -		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*text/html", boost::regex::icase));
  44.502 -		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough
  44.503 -		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*120", boost::regex::icase));
  44.504 -		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase));
  44.505 -		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-length:\\s*\\d+", boost::regex::icase));
  44.506 -		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-type:\\s*application/llsd\\+xml", boost::regex::icase));
  44.507 -		handler.mHeadersRequired.push_back(boost::regex("\\s*X-Reflect-cache-control:\\s*no-store", boost::regex::icase));
  44.508 -		handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-content-type:\\s*application/x-www-form-urlencoded", boost::regex::icase));
  44.509 -		handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-accept:\\s*\\*/\\*", boost::regex::icase));
  44.510 -		handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-keep-alive:\\s*300", boost::regex::icase));
  44.511 -		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase));
  44.512 -		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-range:.*", boost::regex::icase));
  44.513 -		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase));
  44.514 -		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase));
  44.515 -		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-expect:.*", boost::regex::icase));
  44.516 -		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:\\s*.*chunked.*", boost::regex::icase));
  44.517 +		handler.mHeadersRequired.push_back(
  44.518 +			regex_container_t::value_type(
  44.519 +				boost::regex("X-Reflect-connection", boost::regex::icase),
  44.520 +				boost::regex("keep-alive", boost::regex::icase)));
  44.521 +		handler.mHeadersRequired.push_back(
  44.522 +			regex_container_t::value_type(
  44.523 +				boost::regex("X-Reflect-accept", boost::regex::icase),
  44.524 +				boost::regex("text/html", boost::regex::icase)));
  44.525 +		handler.mHeadersRequired.push_back(
  44.526 +			regex_container_t::value_type(
  44.527 +				boost::regex("X-Reflect-accept-encoding", boost::regex::icase),
  44.528 +				boost::regex("((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase))); // close enough
  44.529 +		handler.mHeadersRequired.push_back(
  44.530 +			regex_container_t::value_type(
  44.531 +				boost::regex("X-Reflect-keep-alive", boost::regex::icase),
  44.532 +				boost::regex("120", boost::regex::icase)));
  44.533 +		handler.mHeadersRequired.push_back(
  44.534 +			regex_container_t::value_type(
  44.535 +				boost::regex("X-Reflect-host", boost::regex::icase),
  44.536 +				boost::regex(".*", boost::regex::icase)));
  44.537 +		handler.mHeadersRequired.push_back(
  44.538 +			regex_container_t::value_type(
  44.539 +				boost::regex("X-Reflect-content-length", boost::regex::icase),
  44.540 +				boost::regex("\\d+", boost::regex::icase)));
  44.541 +		handler.mHeadersRequired.push_back(
  44.542 +			regex_container_t::value_type(
  44.543 +				boost::regex("X-Reflect-content-type", boost::regex::icase),
  44.544 +				boost::regex("application/llsd\\+xml", boost::regex::icase)));
  44.545 +		handler.mHeadersRequired.push_back(
  44.546 +			regex_container_t::value_type(
  44.547 +				boost::regex("X-Reflect-cache-control", boost::regex::icase),
  44.548 +				boost::regex("no-store", boost::regex::icase)));
  44.549 +
  44.550 +		handler.mHeadersDisallowed.push_back(
  44.551 +			regex_container_t::value_type(
  44.552 +				boost::regex("X-Reflect-content-type", boost::regex::icase),
  44.553 +				boost::regex("application/x-www-form-urlencoded", boost::regex::icase)));
  44.554 +		handler.mHeadersDisallowed.push_back(
  44.555 +			regex_container_t::value_type(
  44.556 +				boost::regex("X-Reflect-accept", boost::regex::icase),
  44.557 +				boost::regex("\\*/\\*", boost::regex::icase)));
  44.558 +		handler.mHeadersDisallowed.push_back(
  44.559 +			regex_container_t::value_type(
  44.560 +				boost::regex("X-Reflect-keep-alive", boost::regex::icase),
  44.561 +				boost::regex("300", boost::regex::icase)));
  44.562 +		handler.mHeadersDisallowed.push_back(
  44.563 +			regex_container_t::value_type(
  44.564 +				boost::regex("X-Reflect-pragma", boost::regex::icase),
  44.565 +				boost::regex(".*", boost::regex::icase)));
  44.566 +		handler.mHeadersDisallowed.push_back(
  44.567 +			regex_container_t::value_type(
  44.568 +				boost::regex("X-Reflect-range", boost::regex::icase),
  44.569 +				boost::regex(".*", boost::regex::icase)));
  44.570 +		handler.mHeadersDisallowed.push_back(
  44.571 +			regex_container_t::value_type(
  44.572 +				boost::regex("X-Reflect-referer", boost::regex::icase),
  44.573 +				boost::regex(".*", boost::regex::icase)));
  44.574 +		handler.mHeadersDisallowed.push_back(
  44.575 +			regex_container_t::value_type(
  44.576 +				boost::regex("X-Reflect-content-encoding", boost::regex::icase),
  44.577 +				boost::regex(".*", boost::regex::icase)));
  44.578 +		handler.mHeadersDisallowed.push_back(
  44.579 +			regex_container_t::value_type(
  44.580 +				boost::regex("X-Reflect-expect", boost::regex::icase),
  44.581 +				boost::regex(".*", boost::regex::icase)));
  44.582 +		handler.mHeadersDisallowed.push_back(
  44.583 +			regex_container_t::value_type(
  44.584 +				boost::regex("X-Reflect-transfer-encoding", boost::regex::icase),
  44.585 +				boost::regex(".*", boost::regex::icase)));
  44.586 +
  44.587  		HttpHandle handle = req->requestPost(HttpRequest::DEFAULT_POLICY_ID,
  44.588  											 0U,
  44.589  											 url_base + "reflect/",
  44.590 @@ -2538,9 +2810,9 @@
  44.591  
  44.592  		// headers
  44.593  		headers = new HttpHeaders;
  44.594 -		headers->mHeaders.push_back("content-type:  text/plain");
  44.595 -		headers->mHeaders.push_back("content-type:  text/html");
  44.596 -		headers->mHeaders.push_back("content-type:  application/llsd+xml");
  44.597 +		headers->append("content-type", "text/plain");
  44.598 +		headers->append("content-type", "text/html");
  44.599 +		headers->append("content-type", "application/llsd+xml");
  44.600  		
  44.601  		// And a buffer array
  44.602  		const char * msg("<xml><llsd><string>It was the best of times, it was the worst of times.</string></llsd></xml>");
  44.603 @@ -2549,22 +2821,71 @@
  44.604  			
  44.605  		// Issue a default PUT
  44.606  		mStatus = HttpStatus(200);
  44.607 -		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase));
  44.608 -		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*\\*/\\*", boost::regex::icase));
  44.609 -		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough
  44.610 -		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*\\d+", boost::regex::icase));
  44.611 -		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase));
  44.612 -		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-length:\\s*\\d+", boost::regex::icase));
  44.613 -		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-type:\\s*application/llsd\\+xml", boost::regex::icase));
  44.614 -		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-cache-control:.*", boost::regex::icase));
  44.615 -		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase));
  44.616 -		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-range:.*", boost::regex::icase));
  44.617 -		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase));
  44.618 -		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase));
  44.619 -		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-expect:.*", boost::regex::icase));
  44.620 -		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:\\s*.*chunked.*", boost::regex::icase));
  44.621 -		handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-content-type:\\s*text/plain", boost::regex::icase));
  44.622 -		handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-content-type:\\s*text/html", boost::regex::icase));
  44.623 +		handler.mHeadersRequired.push_back(
  44.624 +			regex_container_t::value_type(
  44.625 +				boost::regex("X-Reflect-connection", boost::regex::icase),
  44.626 +				boost::regex("keep-alive", boost::regex::icase)));
  44.627 +		handler.mHeadersRequired.push_back(
  44.628 +			regex_container_t::value_type(
  44.629 +				boost::regex("X-Reflect-accept", boost::regex::icase),
  44.630 +				boost::regex("\\*/\\*", boost::regex::icase)));
  44.631 +		handler.mHeadersRequired.push_back(
  44.632 +			regex_container_t::value_type(
  44.633 +				boost::regex("X-Reflect-accept-encoding", boost::regex::icase),
  44.634 +				boost::regex("((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase))); // close enough
  44.635 +		handler.mHeadersRequired.push_back(
  44.636 +			regex_container_t::value_type(
  44.637 +				boost::regex("X-Reflect-keep-alive", boost::regex::icase),
  44.638 +				boost::regex("\\d+", boost::regex::icase)));
  44.639 +		handler.mHeadersRequired.push_back(
  44.640 +			regex_container_t::value_type(
  44.641 +				boost::regex("X-Reflect-host", boost::regex::icase),
  44.642 +				boost::regex(".*", boost::regex::icase)));
  44.643 +		handler.mHeadersRequired.push_back(
  44.644 +			regex_container_t::value_type(
  44.645 +				boost::regex("X-Reflect-content-length", boost::regex::icase),
  44.646 +				boost::regex("\\d+", boost::regex::icase)));
  44.647 +		handler.mHeadersRequired.push_back(
  44.648 +			regex_container_t::value_type(
  44.649 +				boost::regex("X-Reflect-content-type", boost::regex::icase),
  44.650 +				boost::regex("application/llsd\\+xml", boost::regex::icase)));
  44.651 +
  44.652 +		handler.mHeadersDisallowed.push_back(
  44.653 +			regex_container_t::value_type(
  44.654 +				boost::regex("X-Reflect-cache-control", boost::regex::icase),
  44.655 +				boost::regex(".*", boost::regex::icase)));
  44.656 +		handler.mHeadersDisallowed.push_back(
  44.657 +			regex_container_t::value_type(
  44.658 +				boost::regex("X-Reflect-pragma", boost::regex::icase),
  44.659 +				boost::regex(".*", boost::regex::icase)));
  44.660 +		handler.mHeadersDisallowed.push_back(
  44.661 +			regex_container_t::value_type(
  44.662 +				boost::regex("X-Reflect-range", boost::regex::icase),
  44.663 +				boost::regex(".*", boost::regex::icase)));
  44.664 +		handler.mHeadersDisallowed.push_back(
  44.665 +			regex_container_t::value_type(
  44.666 +				boost::regex("X-Reflect-referer", boost::regex::icase),
  44.667 +				boost::regex(".*", boost::regex::icase)));
  44.668 +		handler.mHeadersDisallowed.push_back(
  44.669 +			regex_container_t::value_type(
  44.670 +				boost::regex("X-Reflect-content-encoding", boost::regex::icase),
  44.671 +				boost::regex(".*", boost::regex::icase)));
  44.672 +		handler.mHeadersDisallowed.push_back(
  44.673 +			regex_container_t::value_type(
  44.674 +				boost::regex("X-Reflect-expect", boost::regex::icase),
  44.675 +				boost::regex(".*", boost::regex::icase)));
  44.676 +		handler.mHeadersDisallowed.push_back(
  44.677 +			regex_container_t::value_type(
  44.678 +				boost::regex("X-Reflect-transfer-encoding", boost::regex::icase),
  44.679 +				boost::regex(".*", boost::regex::icase)));
  44.680 +		handler.mHeadersDisallowed.push_back(
  44.681 +			regex_container_t::value_type(
  44.682 +				boost::regex("X-Reflect-content-type", boost::regex::icase),
  44.683 +				boost::regex("text/plain", boost::regex::icase)));
  44.684 +		handler.mHeadersDisallowed.push_back(
  44.685 +			regex_container_t::value_type(
  44.686 +				boost::regex("X-Reflect-content-type", boost::regex::icase),
  44.687 +				boost::regex("text/html", boost::regex::icase)));
  44.688  		HttpHandle handle = req->requestPut(HttpRequest::DEFAULT_POLICY_ID,
  44.689  											0U,
  44.690  											url_base + "reflect/",
  44.691 @@ -2854,6 +3175,142 @@
  44.692  	}
  44.693  }
  44.694  
  44.695 +template <> template <>
  44.696 +void HttpRequestTestObjectType::test<23>()
  44.697 +{
  44.698 +	ScopedCurlInit ready;
  44.699 +
  44.700 +	set_test_name("HttpRequest GET 503s with 'Retry-After'");
  44.701 +
  44.702 +	// This tests mainly that the code doesn't fall over if
  44.703 +	// various well- and mis-formed Retry-After headers are
  44.704 +	// sent along with the response.  Direct inspection of
  44.705 +	// the parsing result isn't supported.
  44.706 +	
  44.707 +	// Handler can be stack-allocated *if* there are no dangling
  44.708 +	// references to it after completion of this method.
  44.709 +	// Create before memory record as the string copy will bump numbers.
  44.710 +	TestHandler2 handler(this, "handler");
  44.711 +	std::string url_base(get_base_url() + "/503/");	// path to 503 generators
  44.712 +		
  44.713 +	// record the total amount of dynamically allocated memory
  44.714 +	mMemTotal = GetMemTotal();
  44.715 +	mHandlerCalls = 0;
  44.716 +
  44.717 +	HttpRequest * req = NULL;
  44.718 +	HttpOptions * opts = NULL;
  44.719 +	
  44.720 +	try
  44.721 +	{
  44.722 +		// Get singletons created
  44.723 +		HttpRequest::createService();
  44.724 +		
  44.725 +		// Start threading early so that thread memory is invariant
  44.726 +		// over the test.
  44.727 +		HttpRequest::startThread();
  44.728 +
  44.729 +		// create a new ref counted object with an implicit reference
  44.730 +		req = new HttpRequest();
  44.731 +		ensure("Memory allocated on construction", mMemTotal < GetMemTotal());
  44.732 +
  44.733 +		opts = new HttpOptions();
  44.734 +		opts->setRetries(1);			// Retry once only
  44.735 +		opts->setUseRetryAfter(true);	// Try to parse the retry-after header
  44.736 +		
  44.737 +		// Issue a GET that 503s with valid retry-after
  44.738 +		mStatus = HttpStatus(503);
  44.739 +		int url_limit(6);
  44.740 +		for (int i(0); i < url_limit; ++i)
  44.741 +		{
  44.742 +			std::ostringstream url;
  44.743 +			url << url_base << i << "/";
  44.744 +			HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID,
  44.745 +														 0U,
  44.746 +														 url.str(),
  44.747 +														 0,
  44.748 +														 0,
  44.749 +														 opts,
  44.750 +														 NULL,
  44.751 +														 &handler);
  44.752 +
  44.753 +			std::ostringstream testtag;
  44.754 +			testtag << "Valid handle returned for 503 request #" << i;
  44.755 +			ensure(testtag.str(), handle != LLCORE_HTTP_HANDLE_INVALID);
  44.756 +		}
  44.757 +		
  44.758 +
  44.759 +		// Run the notification pump.
  44.760 +		int count(0);
  44.761 +		int limit(LOOP_COUNT_LONG);
  44.762 +		while (count++ < limit && mHandlerCalls < url_limit)
  44.763 +		{
  44.764 +			req->update(0);
  44.765 +			usleep(LOOP_SLEEP_INTERVAL);
  44.766 +		}
  44.767 +		ensure("Request executed in reasonable time", count < limit);
  44.768 +		ensure("One handler invocation for request", mHandlerCalls == url_limit);
  44.769 +
  44.770 +		// Okay, request a shutdown of the servicing thread
  44.771 +		mStatus = HttpStatus();
  44.772 +		mHandlerCalls = 0;
  44.773 +		HttpHandle handle = req->requestStopThread(&handler);
  44.774 +		ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID);
  44.775 +	
  44.776 +		// Run the notification pump again
  44.777 +		count = 0;
  44.778 +		limit = LOOP_COUNT_LONG;
  44.779 +		while (count++ < limit && mHandlerCalls < 1)
  44.780 +		{
  44.781 +			req->update(1000000);
  44.782 +			usleep(LOOP_SLEEP_INTERVAL);
  44.783 +		}
  44.784 +		ensure("Second request executed in reasonable time", count < limit);
  44.785 +		ensure("Second handler invocation", mHandlerCalls == 1);
  44.786 +
  44.787 +		// See that we actually shutdown the thread
  44.788 +		count = 0;
  44.789 +		limit = LOOP_COUNT_SHORT;
  44.790 +		while (count++ < limit && ! HttpService::isStopped())
  44.791 +		{
  44.792 +			usleep(LOOP_SLEEP_INTERVAL);
  44.793 +		}
  44.794 +		ensure("Thread actually stopped running", HttpService::isStopped());
  44.795 +
  44.796 +		// release options
  44.797 +		opts->release();
  44.798 +		opts = NULL;
  44.799 +		
  44.800 +		// release the request object
  44.801 +		delete req;
  44.802 +		req = NULL;
  44.803 +
  44.804 +		// Shut down service
  44.805 +		HttpRequest::destroyService();
  44.806 +	
  44.807 +#if defined(WIN32)
  44.808 +		// Can only do this memory test on Windows.  On other platforms,
  44.809 +		// the LL logging system holds on to memory and produces what looks
  44.810 +		// like memory leaks...
  44.811 +	
  44.812 +		// printf("Old mem:  %d, New mem:  %d\n", mMemTotal, GetMemTotal());
  44.813 +		ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal());
  44.814 +#endif
  44.815 +	}
  44.816 +	catch (...)
  44.817 +	{
  44.818 +		stop_thread(req);
  44.819 +		if (opts)
  44.820 +		{
  44.821 +			opts->release();
  44.822 +			opts = NULL;
  44.823 +		}
  44.824 +		delete req;
  44.825 +		HttpRequest::destroyService();
  44.826 +		throw;
  44.827 +	}
  44.828 +}
  44.829 +
  44.830 +
  44.831  }  // end namespace tut
  44.832  
  44.833  namespace
    45.1 --- a/indra/llcorehttp/tests/test_httpstatus.hpp	Wed Feb 12 17:18:03 2014 -0800
    45.2 +++ b/indra/llcorehttp/tests/test_httpstatus.hpp	Wed Feb 12 18:58:40 2014 -0800
    45.3 @@ -259,6 +259,65 @@
    45.4  	ensure(msg == "Unknown error");
    45.5  }
    45.6  
    45.7 +
    45.8 +template <> template <>
    45.9 +void HttpStatusTestObjectType::test<8>()
   45.10 +{
   45.11 +	set_test_name("HttpStatus toHex() nominal function");
   45.12 +	
   45.13 +	HttpStatus status(404);
   45.14 +	std::string msg = status.toHex();
   45.15 +	// std::cout << "Result:  " << msg << std::endl;
   45.16 +	ensure(msg == "01940001");
   45.17 +}
   45.18 +
   45.19 +
   45.20 +template <> template <>
   45.21 +void HttpStatusTestObjectType::test<9>()
   45.22 +{
   45.23 +	set_test_name("HttpStatus toTerseString() nominal function");
   45.24 +	
   45.25 +	HttpStatus status(404);
   45.26 +	std::string msg = status.toTerseString();
   45.27 +	// std::cout << "Result:  " << msg << std::endl;
   45.28 +	ensure("Normal HTTP 404", msg == "Http_404");
   45.29 +
   45.30 +	status = HttpStatus(200);
   45.31 +	msg = status.toTerseString();
   45.32 +	// std::cout << "Result:  " << msg << std::endl;
   45.33 +	ensure("Normal HTTP 200", msg == "Http_200");
   45.34 +
   45.35 +	status = HttpStatus(200, HE_REPLY_ERROR);
   45.36 +	msg = status.toTerseString();
   45.37 +	// std::cout << "Result:  " << msg << std::endl;
   45.38 +	ensure("Unsuccessful HTTP 200", msg == "Http_200");			// No distinction for error
   45.39 +
   45.40 +	status = HttpStatus(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT);
   45.41 +	msg = status.toTerseString();
   45.42 +	// std::cout << "Result:  " << msg << std::endl;
   45.43 +	ensure("Easy couldn't connect error", msg == "Easy_7");
   45.44 +
   45.45 +	status = HttpStatus(HttpStatus::EXT_CURL_MULTI, CURLM_OUT_OF_MEMORY);
   45.46 +	msg = status.toTerseString();
   45.47 +	// std::cout << "Result:  " << msg << std::endl;
   45.48 +	ensure("Multi out-of-memory error", msg == "Multi_3");
   45.49 +
   45.50 +	status = HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_SET);
   45.51 +	msg = status.toTerseString();
   45.52 +	// std::cout << "Result:  " << msg << std::endl;
   45.53 +	ensure("Core option not set error", msg == "Core_7");
   45.54 +
   45.55 +	status = HttpStatus(22000, 1);
   45.56 +	msg = status.toTerseString();
   45.57 +	// std::cout << "Result:  " << msg << std::endl;
   45.58 +	ensure("Undecodable error", msg == "Unknown_1");
   45.59 +
   45.60 +	status = HttpStatus(22000, -1);
   45.61 +	msg = status.toTerseString();
   45.62 +	// std::cout << "Result:  " << msg << std::endl;
   45.63 +	ensure("Undecodable error 65535", msg == "Unknown_65535");
   45.64 +}
   45.65 +
   45.66  } // end namespace tut
   45.67  
   45.68  #endif	// TEST_HTTP_STATUS_H
    46.1 --- a/indra/llcorehttp/tests/test_llcorehttp_peer.py	Wed Feb 12 17:18:03 2014 -0800
    46.2 +++ b/indra/llcorehttp/tests/test_llcorehttp_peer.py	Wed Feb 12 18:58:40 2014 -0800
    46.3 @@ -69,6 +69,15 @@
    46.4                             "Content-Range: bytes 0-75/2983",
    46.5                             "Content-Length: 76"
    46.6      -- '/bug2295/inv_cont_range/0/'  Generates HE_INVALID_CONTENT_RANGE error in llcorehttp.
    46.7 +    - '/503/'           Generate 503 responses with various kinds
    46.8 +                        of 'retry-after' headers
    46.9 +    -- '/503/0/'            "Retry-After: 2"   
   46.10 +    -- '/503/1/'            "Retry-After: Thu, 31 Dec 2043 23:59:59 GMT"
   46.11 +    -- '/503/2/'            "Retry-After: Fri, 31 Dec 1999 23:59:59 GMT"
   46.12 +    -- '/503/3/'            "Retry-After: "
   46.13 +    -- '/503/4/'            "Retry-After: (*#*(@*(@(")"
   46.14 +    -- '/503/5/'            "Retry-After: aklsjflajfaklsfaklfasfklasdfklasdgahsdhgasdiogaioshdgo"
   46.15 +    -- '/503/6/'            "Retry-After: 1 2 3 4 5 6 7 8 9 10"
   46.16  
   46.17      Some combinations make no sense, there's no effort to protect
   46.18      you from that.
   46.19 @@ -143,22 +152,40 @@
   46.20          if "/sleep/" in self.path:
   46.21              time.sleep(30)
   46.22  
   46.23 -        if "fail" in self.path:
   46.24 -            status = data.get("status", 500)
   46.25 -            # self.responses maps an int status to a (short, long) pair of
   46.26 -            # strings. We want the longer string. That's why we pass a string
   46.27 -            # pair to get(): the [1] will select the second string, whether it
   46.28 -            # came from self.responses or from our default pair.
   46.29 -            reason = data.get("reason",
   46.30 -                               self.responses.get(status,
   46.31 -                                                  ("fail requested",
   46.32 -                                                   "Your request specified failure status %s "
   46.33 -                                                   "without providing a reason" % status))[1])
   46.34 -            debug("fail requested: %s: %r", status, reason)
   46.35 -            self.send_error(status, reason)
   46.36 +        if "/503/" in self.path:
   46.37 +            # Tests for various kinds of 'Retry-After' header parsing
   46.38 +            body = None
   46.39 +            if "/503/0/" in self.path:
   46.40 +                self.send_response(503)
   46.41 +                self.send_header("retry-after", "2")
   46.42 +            elif "/503/1/" in self.path:
   46.43 +                self.send_response(503)
   46.44 +                self.send_header("retry-after", "Thu, 31 Dec 2043 23:59:59 GMT")
   46.45 +            elif "/503/2/" in self.path:
   46.46 +                self.send_response(503)
   46.47 +                self.send_header("retry-after", "Fri, 31 Dec 1999 23:59:59 GMT")
   46.48 +            elif "/503/3/" in self.path:
   46.49 +                self.send_response(503)
   46.50 +                self.send_header("retry-after", "")
   46.51 +            elif "/503/4/" in self.path:
   46.52 +                self.send_response(503)
   46.53 +                self.send_header("retry-after", "(*#*(@*(@(")
   46.54 +            elif "/503/5/" in self.path:
   46.55 +                self.send_response(503)
   46.56 +                self.send_header("retry-after", "aklsjflajfaklsfaklfasfklasdfklasdgahsdhgasdiogaioshdgo")
   46.57 +            elif "/503/6/" in self.path:
   46.58 +                self.send_response(503)
   46.59 +                self.send_header("retry-after", "1 2 3 4 5 6 7 8 9 10")
   46.60 +            else:
   46.61 +                # Unknown request
   46.62 +                self.send_response(400)
   46.63 +                body = "Unknown /503/ path in server"
   46.64              if "/reflect/" in self.path:
   46.65                  self.reflect_headers()
   46.66 +            self.send_header("Content-type", "text/plain")
   46.67              self.end_headers()
   46.68 +            if body:
   46.69 +                self.wfile.write(body)
   46.70          elif "/bug2295/" in self.path:
   46.71              # Test for https://jira.secondlife.com/browse/BUG-2295
   46.72              #
   46.73 @@ -194,8 +221,7 @@
   46.74              self.end_headers()
   46.75              if body:
   46.76                  self.wfile.write(body)
   46.77 -        else:
   46.78 -            # Normal response path
   46.79 +        elif "fail" not in self.path:
   46.80              data = data.copy()          # we're going to modify
   46.81              # Ensure there's a "reply" key in data, even if there wasn't before
   46.82              data["reply"] = data.get("reply", llsd.LLSD("success"))
   46.83 @@ -210,6 +236,22 @@
   46.84              self.end_headers()
   46.85              if withdata:
   46.86                  self.wfile.write(response)
   46.87 +        else:                           # fail requested
   46.88 +            status = data.get("status", 500)
   46.89 +            # self.responses maps an int status to a (short, long) pair of
   46.90 +            # strings. We want the longer string. That's why we pass a string
   46.91 +            # pair to get(): the [1] will select the second string, whether it
   46.92 +            # came from self.responses or from our default pair.
   46.93 +            reason = data.get("reason",
   46.94 +                               self.responses.get(status,
   46.95 +                                                  ("fail requested",
   46.96 +                                                   "Your request specified failure status %s "
   46.97 +                                                   "without providing a reason" % status))[1])
   46.98 +            debug("fail requested: %s: %r", status, reason)
   46.99 +            self.send_error(status, reason)
  46.100 +            if "/reflect/" in self.path:
  46.101 +                self.reflect_headers()
  46.102 +            self.end_headers()
  46.103  
  46.104      def reflect_headers(self):
  46.105          for name in self.headers.keys():
    47.1 --- a/indra/llmessage/llcurl.cpp	Wed Feb 12 17:18:03 2014 -0800
    47.2 +++ b/indra/llmessage/llcurl.cpp	Wed Feb 12 18:58:40 2014 -0800
    47.3 @@ -6,7 +6,7 @@
    47.4   *
    47.5   * $LicenseInfo:firstyear=2006&license=viewerlgpl$
    47.6   * Second Life Viewer Source Code
    47.7 - * Copyright (C) 2010, Linden Research, Inc.
    47.8 + * Copyright (C) 2010-2013, Linden Research, Inc.
    47.9   * 
   47.10   * This library is free software; you can redistribute it and/or
   47.11   * modify it under the terms of the GNU Lesser General Public
   47.12 @@ -312,9 +312,12 @@
   47.13  		return NULL;
   47.14  	}
   47.15  	
   47.16 -	// set no DNS caching as default for all easy handles. This prevents them adopting a
   47.17 -	// multi handles cache if they are added to one.
   47.18 -	CURLcode result = curl_easy_setopt(easy->mCurlEasyHandle, CURLOPT_DNS_CACHE_TIMEOUT, 0);
   47.19 +	// Enable a brief cache period for now.  This was zero for the longest time
   47.20 +	// which caused some routers grief and generated unneeded traffic.  For the
   47.21 +	// threaded resolver, we're using system resolution libraries and non-zero values
   47.22 +	// are preferred.  The c-ares resolver is another matter and it might not
   47.23 +	// track server changes as well.
   47.24 +	CURLcode result = curl_easy_setopt(easy->mCurlEasyHandle, CURLOPT_DNS_CACHE_TIMEOUT, 15);
   47.25  	check_curl_code(result);
   47.26  	result = curl_easy_setopt(easy->mCurlEasyHandle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
   47.27  	check_curl_code(result);
    48.1 --- a/indra/newview/app_settings/settings.xml	Wed Feb 12 17:18:03 2014 -0800
    48.2 +++ b/indra/newview/app_settings/settings.xml	Wed Feb 12 18:58:40 2014 -0800
    48.3 @@ -12620,11 +12620,21 @@
    48.4      <key>Backup</key>
    48.5      <integer>0</integer>
    48.6    </map>
    48.7 -
    48.8 +  <key>Mesh2MaxConcurrentRequests</key>
    48.9 +  <map>
   48.10 +    <key>Comment</key>
   48.11 +    <string>Number of connections to use for loading meshes.</string>
   48.12 +    <key>Persist</key>
   48.13 +    <integer>1</integer>
   48.14 +    <key>Type</key>
   48.15 +    <string>U32</string>
   48.16 +    <key>Value</key>
   48.17 +    <integer>8</integer>
   48.18 +  </map>
   48.19    <key>MeshMaxConcurrentRequests</key>
   48.20    <map>
   48.21      <key>Comment</key>
   48.22 -    <string>Number of threads to use for loading meshes.</string>
   48.23 +    <string>Number of connections to use for loading meshes (legacy system).</string>
   48.24      <key>Persist</key>
   48.25      <integer>1</integer>
   48.26      <key>Type</key>
   48.27 @@ -12632,6 +12642,28 @@
   48.28      <key>Value</key>
   48.29      <integer>32</integer>
   48.30    </map>
   48.31 +  <key>MeshUseHttpRetryAfter</key>
   48.32 +  <map>
   48.33 +    <key>Comment</key>
   48.34 +    <string>If TRUE, use Retry-After response headers when rescheduling a mesh request that fails with an HTTP 503 status.  Static.</string>
   48.35 +    <key>Persist</key>
   48.36 +    <integer>1</integer>
   48.37 +    <key>Type</key>
   48.38 +    <string>Boolean</string>
   48.39 +    <key>Value</key>
   48.40 +    <boolean>1</boolean>
   48.41 +  </map>
   48.42 +  <key>MeshUseGetMesh1</key>
   48.43 +  <map>
   48.44 +    <key>Comment</key>
   48.45 +    <string>If TRUE, use the legacy GetMesh capability for mesh download requests.  Semi-dynamic (read at region crossings).</string>
   48.46 +    <key>Persist</key>
   48.47 +    <integer>1</integer>
   48.48 +    <key>Type</key>
   48.49 +    <string>Boolean</string>
   48.50 +    <key>Value</key>
   48.51 +    <boolean>0</boolean>
   48.52 +  </map>
   48.53     <key>RunMultipleThreads</key>
   48.54      <map>
   48.55        <key>Comment</key>
    49.1 --- a/indra/newview/llappcorehttp.cpp	Wed Feb 12 17:18:03 2014 -0800
    49.2 +++ b/indra/newview/llappcorehttp.cpp	Wed Feb 12 18:58:40 2014 -0800
    49.3 @@ -4,7 +4,7 @@
    49.4   *
    49.5   * $LicenseInfo:firstyear=2012&license=viewerlgpl$
    49.6   * Second Life Viewer Source Code
    49.7 - * Copyright (C) 2012, Linden Research, Inc.
    49.8 + * Copyright (C) 2012-2013, Linden Research, Inc.
    49.9   *
   49.10   * This library is free software; you can redistribute it and/or
   49.11   * modify it under the terms of the GNU Lesser General Public
   49.12 @@ -28,18 +28,81 @@
   49.13  
   49.14  #include "llappcorehttp.h"
   49.15  
   49.16 +#include "llappviewer.h"
   49.17  #include "llviewercontrol.h"
   49.18  
   49.19  
   49.20 +// Here is where we begin to get our connection usage under control.
   49.21 +// This establishes llcorehttp policy classes that, among other
   49.22 +// things, limit the maximum number of connections to outside
   49.23 +// services.  Each of the entries below maps to a policy class and
   49.24 +// has a limit, sometimes configurable, of how many connections can
   49.25 +// be open at a time.
   49.26 +
   49.27  const F64 LLAppCoreHttp::MAX_THREAD_WAIT_TIME(10.0);
   49.28 +static const struct
   49.29 +{
   49.30 +	LLAppCoreHttp::EAppPolicy	mPolicy;
   49.31 +	U32							mDefault;
   49.32 +	U32							mMin;
   49.33 +	U32							mMax;
   49.34 +	U32							mRate;
   49.35 +	std::string					mKey;
   49.36 +	const char *				mUsage;
   49.37 +} init_data[] =					//  Default and dynamic values for classes
   49.38 +{
   49.39 +	{
   49.40 +		LLAppCoreHttp::AP_DEFAULT,			8,		8,		8,		0,
   49.41 +		"",
   49.42 +		"other"
   49.43 +	},
   49.44 +	{
   49.45 +		LLAppCoreHttp::AP_TEXTURE,			8,		1,		12,		0,
   49.46 +		"TextureFetchConcurrency",
   49.47 +		"texture fetch"
   49.48 +	},
   49.49 +	{
   49.50 +		LLAppCoreHttp::AP_MESH1,			32,		1,		128,	100,
   49.51 +		"MeshMaxConcurrentRequests",
   49.52 +		"mesh fetch"
   49.53 +	},
   49.54 +	{
   49.55 +		LLAppCoreHttp::AP_MESH2,			8,		1,		32,		100,
   49.56 +		"Mesh2MaxConcurrentRequests",
   49.57 +		"mesh2 fetch"
   49.58 +	},
   49.59 +	{
   49.60 +		LLAppCoreHttp::AP_LARGE_MESH,		2,		1,		8,		0,
   49.61 +		"",
   49.62 +		"large mesh fetch"
   49.63 +	},
   49.64 +	{
   49.65 +		LLAppCoreHttp::AP_UPLOADS,			2,		1,		8,		0,
   49.66 +		"",
   49.67 +		"asset upload"
   49.68 +	},
   49.69 +	{
   49.70 +		LLAppCoreHttp::AP_LONG_POLL,		32,		32,		32,		0,
   49.71 +		"",
   49.72 +		"long poll"
   49.73 +	}
   49.74 +};
   49.75 +
   49.76 +static void setting_changed();
   49.77 +
   49.78  
   49.79  LLAppCoreHttp::LLAppCoreHttp()
   49.80  	: mRequest(NULL),
   49.81  	  mStopHandle(LLCORE_HTTP_HANDLE_INVALID),
   49.82  	  mStopRequested(0.0),
   49.83 -	  mStopped(false),
   49.84 -	  mPolicyDefault(-1)
   49.85 -{}
   49.86 +	  mStopped(false)
   49.87 +{
   49.88 +	for (int i(0); i < LL_ARRAY_SIZE(mPolicies); ++i)
   49.89 +	{
   49.90 +		mPolicies[i] = LLCore::HttpRequest::DEFAULT_POLICY_ID;
   49.91 +		mSettings[i] = 0U;
   49.92 +	}
   49.93 +}
   49.94  
   49.95  
   49.96  LLAppCoreHttp::~LLAppCoreHttp()
   49.97 @@ -54,30 +117,28 @@
   49.98  	LLCore::HttpStatus status = LLCore::HttpRequest::createService();
   49.99  	if (! status)
  49.100  	{
  49.101 -		LL_ERRS("Init") << "Failed to initialize HTTP services.  Reason:  "
  49.102 -						<< status.toString()
  49.103 +		LL_ERRS("Init") << "Failed to initialize HTTP services.  Reason:  " << status.toString()
  49.104  						<< LL_ENDL;
  49.105  	}
  49.106  
  49.107  	// Point to our certs or SSH/https: will fail on connect
  49.108 -	status = LLCore::HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_CA_FILE,
  49.109 -														gDirUtilp->getCAFile());
  49.110 +	status = LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_CA_FILE,
  49.111 +														LLCore::HttpRequest::GLOBAL_POLICY_ID,
  49.112 +														gDirUtilp->getCAFile(), NULL);
  49.113  	if (! status)
  49.114  	{
  49.115 -		LL_ERRS("Init") << "Failed to set CA File for HTTP services.  Reason:  "
  49.116 -						<< status.toString()
  49.117 +		LL_ERRS("Init") << "Failed to set CA File for HTTP services.  Reason:  " << status.toString()
  49.118  						<< LL_ENDL;
  49.119  	}
  49.120  
  49.121 -	// Establish HTTP Proxy.  "LLProxy" is a special string which directs
  49.122 -	// the code to use LLProxy::applyProxySettings() to establish any
  49.123 -	// HTTP or SOCKS proxy for http operations.
  49.124 -	status = LLCore::HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_LLPROXY, 1);
  49.125 +	// Establish HTTP Proxy, if desired.
  49.126 +	status = LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_LLPROXY,
  49.127 +														LLCore::HttpRequest::GLOBAL_POLICY_ID,
  49.128 +														1, NULL);
  49.129  	if (! status)
  49.130  	{
  49.131 -		LL_ERRS("Init") << "Failed to set HTTP proxy for HTTP services.  Reason:  "
  49.132 -						<< status.toString()
  49.133 -						<< LL_ENDL;
  49.134 +		LL_WARNS("Init") << "Failed to set HTTP proxy for HTTP services.  Reason:  " << status.toString()
  49.135 +						 << LL_ENDL;
  49.136  	}
  49.137  
  49.138  	// Tracing levels for library & libcurl (note that 2 & 3 are beyond spammy):
  49.139 @@ -90,47 +151,74 @@
  49.140  	{
  49.141  		long trace_level(0L);
  49.142  		trace_level = long(gSavedSettings.getU32(http_trace));
  49.143 -		status = LLCore::HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_TRACE, trace_level);
  49.144 +		status = LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_TRACE,
  49.145 +															LLCore::HttpRequest::GLOBAL_POLICY_ID,
  49.146 +															trace_level, NULL);
  49.147  	}
  49.148  	
  49.149  	// Setup default policy and constrain if directed to
  49.150 -	mPolicyDefault = LLCore::HttpRequest::DEFAULT_POLICY_ID;
  49.151 -	static const std::string texture_concur("TextureFetchConcurrency");
  49.152 -	if (gSavedSettings.controlExists(texture_concur))
  49.153 +	mPolicies[AP_DEFAULT] = LLCore::HttpRequest::DEFAULT_POLICY_ID;
  49.154 +
  49.155 +	// Setup additional policies based on table and some special rules
  49.156 +	for (int i(0); i < LL_ARRAY_SIZE(init_data); ++i)
  49.157  	{
  49.158 -		U32 concur(llmin(gSavedSettings.getU32(texture_concur), U32(12)));
  49.159 +		const EAppPolicy policy(init_data[i].mPolicy);
  49.160  
  49.161 -		if (concur > 0)
  49.162 +		if (AP_DEFAULT == policy)
  49.163  		{
  49.164 -			LLCore::HttpStatus status;
  49.165 -			status = LLCore::HttpRequest::setPolicyClassOption(mPolicyDefault,
  49.166 -															   LLCore::HttpRequest::CP_CONNECTION_LIMIT,
  49.167 -															   concur);
  49.168 -			if (! status)
  49.169 -			{
  49.170 -				LL_WARNS("Init") << "Unable to set texture fetch concurrency.  Reason:  "
  49.171 -								 << status.toString()
  49.172 -								 << LL_ENDL;
  49.173 -			}
  49.174 -			else
  49.175 -			{
  49.176 -				LL_INFOS("Init") << "Application settings overriding default texture fetch concurrency.  New value:  "
  49.177 -								 << concur
  49.178 -								 << LL_ENDL;
  49.179 -			}
  49.180 +			// Pre-created
  49.181 +			continue;
  49.182 +		}
  49.183 +
  49.184 +		mPolicies[policy] = LLCore::HttpRequest::createPolicyClass();
  49.185 +		if (! mPolicies[policy])
  49.186 +		{
  49.187 +			// Use default policy (but don't accidentally modify default)
  49.188 +			LL_WARNS("Init") << "Failed to create HTTP policy class for " << init_data[i].mUsage
  49.189 +							 << ".  Using default policy."
  49.190 +							 << LL_ENDL;
  49.191 +			mPolicies[policy] = mPolicies[AP_DEFAULT];
  49.192 +			continue;
  49.193  		}
  49.194  	}
  49.195 +
  49.196 +	// Need a request object to handle dynamic options before setting them
  49.197 +	mRequest = new LLCore::HttpRequest;
  49.198 +
  49.199 +	// Apply initial settings
  49.200 +	refreshSettings(true);
  49.201  	
  49.202  	// Kick the thread
  49.203  	status = LLCore::HttpRequest::startThread();
  49.204  	if (! status)
  49.205  	{
  49.206 -		LL_ERRS("Init") << "Failed to start HTTP servicing thread.  Reason:  "
  49.207 -						<< status.toString()
  49.208 +		LL_ERRS("Init") << "Failed to start HTTP servicing thread.  Reason:  " << status.toString()
  49.209  						<< LL_ENDL;
  49.210  	}
  49.211  
  49.212 -	mRequest = new LLCore::HttpRequest;
  49.213 +	// Register signals for settings and state changes
  49.214 +	for (int i(0); i < LL_ARRAY_SIZE(init_data); ++i)
  49.215 +	{
  49.216 +		if (! init_data[i].mKey.empty() && gSavedSettings.controlExists(init_data[i].mKey))
  49.217 +		{
  49.218 +			LLPointer<LLControlVariable> cntrl_ptr = gSavedSettings.getControl(init_data[i].mKey);
  49.219 +			if (cntrl_ptr.isNull())
  49.220 +			{
  49.221 +				LL_WARNS("Init") << "Unable to set signal on global setting '" << init_data[i].mKey
  49.222 +								 << "'" << LL_ENDL;
  49.223 +			}
  49.224 +			else
  49.225 +			{
  49.226 +				mSettingsSignal[i] = cntrl_ptr->getCommitSignal()->connect(boost::bind(&setting_changed));
  49.227 +			}
  49.228 +		}
  49.229 +	}
  49.230 +}
  49.231 +
  49.232 +
  49.233 +void setting_changed()
  49.234 +{
  49.235 +	LLAppViewer::instance()->getAppCoreHttp().refreshSettings(false);
  49.236  }
  49.237  
  49.238  
  49.239 @@ -173,6 +261,11 @@
  49.240  		}
  49.241  	}
  49.242  
  49.243 +	for (int i(0); i < LL_ARRAY_SIZE(init_data); ++i)
  49.244 +	{
  49.245 +		mSettingsSignal[i].disconnect();
  49.246 +	}
  49.247 +	
  49.248  	delete mRequest;
  49.249  	mRequest = NULL;
  49.250  
  49.251 @@ -185,6 +278,78 @@
  49.252  	}
  49.253  }
  49.254  
  49.255 +void LLAppCoreHttp::refreshSettings(bool initial)
  49.256 +{
  49.257 +	LLCore::HttpStatus status;
  49.258 +	
  49.259 +	for (int i(0); i < LL_ARRAY_SIZE(init_data); ++i)
  49.260 +	{
  49.261 +		const EAppPolicy policy(init_data[i].mPolicy);
  49.262 +
  49.263 +		// Set any desired throttle
  49.264 +		if (initial && init_data[i].mRate)
  49.265 +		{
  49.266 +			// Init-time only, can use the static setters here
  49.267 +			status = LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_THROTTLE_RATE,
  49.268 +																mPolicies[policy],
  49.269 +																init_data[i].mRate,
  49.270 +																NULL);
  49.271 +			if (! status)
  49.272 +			{
  49.273 +				LL_WARNS("Init") << "Unable to set " << init_data[i].mUsage
  49.274 +								 << " throttle rate.  Reason:  " << status.toString()
  49.275 +								 << LL_ENDL;
  49.276 +			}
  49.277 +		}
  49.278 +
  49.279 +		// Get target connection concurrency value
  49.280 +		U32 setting(init_data[i].mDefault);
  49.281 +		if (! init_data[i].mKey.empty() && gSavedSettings.controlExists(init_data[i].mKey))
  49.282 +		{
  49.283 +			U32 new_setting(gSavedSettings.getU32(init_data[i].mKey));
  49.284 +			if (new_setting)
  49.285 +			{
  49.286 +				// Treat zero settings as an ask for default
  49.287 +				setting = llclamp(new_setting, init_data[i].mMin, init_data[i].mMax);
  49.288 +			}
  49.289 +		}
  49.290 +
  49.291 +		if (! initial && setting == mSettings[policy])
  49.292 +		{
  49.293 +			// Unchanged, try next setting
  49.294 +			continue;
  49.295 +		}
  49.296 +		
  49.297 +		// Set it and report
  49.298 +		// *TODO:  These are intended to be per-host limits when we can
  49.299 +		// support that in llcorehttp/libcurl.
  49.300 +		LLCore::HttpHandle handle;
  49.301 +		handle = mRequest->setPolicyOption(LLCore::HttpRequest::PO_CONNECTION_LIMIT,
  49.302 +										   mPolicies[policy],
  49.303 +										   setting, NULL);
  49.304 +		if (LLCORE_HTTP_HANDLE_INVALID == handle)
  49.305 +		{
  49.306 +			status = mRequest->getStatus();
  49.307 +			LL_WARNS("Init") << "Unable to set " << init_data[i].mUsage
  49.308 +							 << " concurrency.  Reason:  " << status.toString()
  49.309 +							 << LL_ENDL;
  49.310 +		}
  49.311 +		else
  49.312 +		{
  49.313 +			LL_DEBUGS("Init") << "Changed " << init_data[i].mUsage
  49.314 +							  << " concurrency.  New value:  " << setting
  49.315 +							  << LL_ENDL;
  49.316 +			mSettings[policy] = setting;
  49.317 +			if (initial && setting != init_data[i].mDefault)
  49.318 +			{
  49.319 +				LL_INFOS("Init") << "Application settings overriding default " << init_data[i].mUsage
  49.320 +								 << " concurrency.  New value:  " << setting
  49.321 +								 << LL_ENDL;
  49.322 +			}
  49.323 +		}
  49.324 +	}
  49.325 +}
  49.326 +
  49.327  
  49.328  void LLAppCoreHttp::onCompleted(LLCore::HttpHandle, LLCore::HttpResponse *)
  49.329  {
    50.1 --- a/indra/newview/llappcorehttp.h	Wed Feb 12 17:18:03 2014 -0800
    50.2 +++ b/indra/newview/llappcorehttp.h	Wed Feb 12 18:58:40 2014 -0800
    50.3 @@ -4,7 +4,7 @@
    50.4   *
    50.5   * $LicenseInfo:firstyear=2012&license=viewerlgpl$
    50.6   * Second Life Viewer Source Code
    50.7 - * Copyright (C) 2012, Linden Research, Inc.
    50.8 + * Copyright (C) 2012-2013, Linden Research, Inc.
    50.9   *
   50.10   * This library is free software; you can redistribute it and/or
   50.11   * modify it under the terms of the GNU Lesser General Public
   50.12 @@ -41,6 +41,117 @@
   50.13  class LLAppCoreHttp : public LLCore::HttpHandler
   50.14  {
   50.15  public:
   50.16 +	typedef LLCore::HttpRequest::policy_t policy_t;
   50.17 +
   50.18 +	enum EAppPolicy
   50.19 +	{
   50.20 +		/// Catchall policy class.  Not used yet
   50.21 +		/// but will have a generous concurrency
   50.22 +		/// limit.  Deep queueing possible by having
   50.23 +		/// a chatty HTTP user.
   50.24 +		///
   50.25 +		/// Destination:     anywhere
   50.26 +		/// Protocol:        http: or https:
   50.27 +		/// Transfer size:   KB-MB
   50.28 +		/// Long poll:       no
   50.29 +		/// Concurrency:     high 
   50.30 +		/// Request rate:    unknown
   50.31 +		/// Pipelined:       no
   50.32 +		AP_DEFAULT,
   50.33 +
   50.34 +		/// Texture fetching policy class.  Used to
   50.35 +		/// download textures via capability or SSA
   50.36 +		/// baking service.  Deep queueing of requests.
   50.37 +		/// Do not share.
   50.38 +		///
   50.39 +		/// Destination:     simhost:12046 & bake-texture:80
   50.40 +		/// Protocol:        http:
   50.41 +		/// Transfer size:   KB-MB
   50.42 +		/// Long poll:       no
   50.43 +		/// Concurrency:     high
   50.44 +		/// Request rate:    high
   50.45 +		/// Pipelined:       soon
   50.46 +		AP_TEXTURE,
   50.47 +
   50.48 +		/// Legacy mesh fetching policy class.  Used to
   50.49 +		/// download textures via 'GetMesh' capability.
   50.50 +		/// To be deprecated.  Do not share.
   50.51 +		///
   50.52 +		/// Destination:     simhost:12046
   50.53 +		/// Protocol:        http:
   50.54 +		/// Transfer size:   KB-MB
   50.55 +		/// Long poll:       no
   50.56 +		/// Concurrency:     dangerously high
   50.57 +		/// Request rate:    high
   50.58 +		/// Pipelined:       no
   50.59 +		AP_MESH1,
   50.60 +
   50.61 +		/// New mesh fetching policy class.  Used to
   50.62 +		/// download textures via 'GetMesh2' capability.
   50.63 +		/// Used when fetch request (typically one LOD)
   50.64 +		/// is 'small', currently defined as 2MB.
   50.65 +		/// Very deeply queued.  Do not share.
   50.66 +		///
   50.67 +		/// Destination:     simhost:12046
   50.68 +		/// Protocol:        http:
   50.69 +		/// Transfer size:   KB-MB
   50.70 +		/// Long poll:       no
   50.71 +		/// Concurrency:     high
   50.72 +		/// Request rate:    high
   50.73 +		/// Pipelined:       soon
   50.74 +		AP_MESH2,
   50.75 +
   50.76 +		/// Large mesh fetching policy class.  Used to
   50.77 +		/// download textures via 'GetMesh' or 'GetMesh2'
   50.78 +		/// capability.  Used when fetch request
   50.79 +		/// is not small to avoid head-of-line problem
   50.80 +		/// when large requests block a sequence of small,
   50.81 +		/// fast requests.  Can be shared with similar
   50.82 +		/// traffic that can wait for longish stalls
   50.83 +		/// (default timeout 600S).
   50.84 +		///
   50.85 +		/// Destination:     simhost:12046
   50.86 +		/// Protocol:        http:
   50.87 +		/// Transfer size:   MB
   50.88 +		/// Long poll:       no
   50.89 +		/// Concurrency:     low
   50.90 +		/// Request rate:    low
   50.91 +		/// Pipelined:       soon
   50.92 +		AP_LARGE_MESH,
   50.93 +
   50.94 +		/// Asset upload policy class.  Used to store
   50.95 +		/// assets (mesh only at the moment) via
   50.96 +		/// changeable URL.  Responses may take some
   50.97 +		/// time (default timeout 240S).
   50.98 +		///
   50.99 +		/// Destination:     simhost:12043
  50.100 +		/// Protocol:        https:
  50.101 +		/// Transfer size:   KB-MB
  50.102 +		/// Long poll:       no
  50.103 +		/// Concurrency:     low
  50.104 +		/// Request rate:    low
  50.105 +		/// Pipelined:       no
  50.106 +		AP_UPLOADS,
  50.107 +
  50.108 +		/// Long-poll-type HTTP requests.  Not
  50.109 +		/// bound by a connection limit.  Requests
  50.110 +		/// will typically hang around for a long
  50.111 +		/// time (~30S).  Only shareable with other
  50.112 +		/// long-poll requests.
  50.113 +		///
  50.114 +		/// Destination:     simhost:12043
  50.115 +		/// Protocol:        https:
  50.116 +		/// Transfer size:   KB
  50.117 +		/// Long poll:       yes
  50.118 +		/// Concurrency:     unlimited but low in practice
  50.119 +		/// Request rate:    low
  50.120 +		/// Pipelined:       no
  50.121 +		AP_LONG_POLL,
  50.122 +
  50.123 +		AP_COUNT						// Must be last
  50.124 +	};
  50.125 +	
  50.126 +public:
  50.127  	LLAppCoreHttp();
  50.128  	~LLAppCoreHttp();
  50.129  	
  50.130 @@ -65,21 +176,27 @@
  50.131  	// Notification when the stop request is complete.
  50.132  	virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response);
  50.133  
  50.134 -	// Retrieve the policy class for default operations.
  50.135 -	int getPolicyDefault() const
  50.136 +	// Retrieve a policy class identifier for desired
  50.137 +	// application function.
  50.138 +	policy_t getPolicy(EAppPolicy policy) const
  50.139  		{
  50.140 -			return mPolicyDefault;
  50.141 +			return mPolicies[policy];
  50.142  		}
  50.143 +
  50.144 +	// Apply initial or new settings from the environment.
  50.145 +	void refreshSettings(bool initial);
  50.146  	
  50.147  private:
  50.148  	static const F64			MAX_THREAD_WAIT_TIME;
  50.149  	
  50.150  private:
  50.151 -	LLCore::HttpRequest *		mRequest;
  50.152 +	LLCore::HttpRequest *		mRequest;						// Request queue to issue shutdowns
  50.153  	LLCore::HttpHandle			mStopHandle;
  50.154  	F64							mStopRequested;
  50.155  	bool						mStopped;
  50.156 -	int							mPolicyDefault;
  50.157 +	policy_t					mPolicies[AP_COUNT];			// Policy class id for each connection set
  50.158 +	U32							mSettings[AP_COUNT];
  50.159 +	boost::signals2::connection mSettingsSignal[AP_COUNT];		// Signals to global settings that affect us
  50.160  };
  50.161  
  50.162  
    51.1 --- a/indra/newview/llmeshrepository.cpp	Wed Feb 12 17:18:03 2014 -0800
    51.2 +++ b/indra/newview/llmeshrepository.cpp	Wed Feb 12 18:58:40 2014 -0800
    51.3 @@ -5,7 +5,7 @@
    51.4   *
    51.5   * $LicenseInfo:firstyear=2005&license=viewerlgpl$
    51.6   * Second Life Viewer Source Code
    51.7 - * Copyright (C) 2010, Linden Research, Inc.
    51.8 + * Copyright (C) 2010-2014, Linden Research, Inc.
    51.9   * 
   51.10   * This library is free software; you can redistribute it and/or
   51.11   * modify it under the terms of the GNU Lesser General Public
   51.12 @@ -38,11 +38,13 @@
   51.13  #include "llcallbacklist.h"
   51.14  #include "llcurl.h"
   51.15  #include "lldatapacker.h"
   51.16 +#include "lldeadmantimer.h"
   51.17  #include "llfloatermodelpreview.h"
   51.18  #include "llfloaterperms.h"
   51.19  #include "lleconomy.h"
   51.20  #include "llimagej2c.h"
   51.21  #include "llhost.h"
   51.22 +#include "llmath.h"
   51.23  #include "llnotificationsutil.h"
   51.24  #include "llsd.h"
   51.25  #include "llsdutil_math.h"
   51.26 @@ -52,6 +54,7 @@
   51.27  #include "llviewercontrol.h"
   51.28  #include "llviewerinventory.h"
   51.29  #include "llviewermenufile.h"
   51.30 +#include "llviewermessage.h"
   51.31  #include "llviewerobjectlist.h"
   51.32  #include "llviewerregion.h"
   51.33  #include "llviewertexturelist.h"
   51.34 @@ -65,6 +68,9 @@
   51.35  #include "llfoldertype.h"
   51.36  #include "llviewerparcelmgr.h"
   51.37  #include "lluploadfloaterobservers.h"
   51.38 +#include "bufferarray.h"
   51.39 +#include "bufferstream.h"
   51.40 +#include "llfasttimer.h"
   51.41  
   51.42  #include "boost/lexical_cast.hpp"
   51.43  
   51.44 @@ -72,11 +78,296 @@
   51.45  #include "netdb.h"
   51.46  #endif
   51.47  
   51.48 -#include <queue>
   51.49 +
   51.50 +// Purpose
   51.51 +//
   51.52 +//   The purpose of this module is to provide access between the viewer
   51.53 +//   and the asset system as regards to mesh objects.
   51.54 +//
   51.55 +//   * High-throughput download of mesh assets from servers while
   51.56 +//     following best industry practices for network profile.
   51.57 +//   * Reliable expensing and upload of new mesh assets.
   51.58 +//   * Recovery and retry from errors when appropriate.
   51.59 +//   * Decomposition of mesh assets for preview and uploads.
   51.60 +//   * And most important:  all of the above without exposing the
   51.61 +//     main thread to stalls due to deep processing or thread
   51.62 +//     locking actions.  In particular, the following operations
   51.63 +//     on LLMeshRepository are very averse to any stalls:
   51.64 +//     * loadMesh
   51.65 +//     * getMeshHeader (For structural details, see:
   51.66 +//       http://wiki.secondlife.com/wiki/Mesh/Mesh_Asset_Format)
   51.67 +//     * notifyLoadedMeshes
   51.68 +//     * getSkinInfo
   51.69 +//
   51.70 +// Threads
   51.71 +//
   51.72 +//   main     Main rendering thread, very sensitive to locking and other stalls
   51.73 +//   repo     Overseeing worker thread associated with the LLMeshRepoThread class
   51.74 +//   decom    Worker thread for mesh decomposition requests
   51.75 +//   core     HTTP worker thread:  does the work but doesn't intrude here
   51.76 +//   uploadN  0-N temporary mesh upload threads (0-1 in practice)
   51.77 +//
   51.78 +// Sequence of Operations
   51.79 +//
   51.80 +//   What follows is a description of the retrieval of one LOD for
   51.81 +//   a new mesh object.  Work is performed by a series of short, quick
   51.82 +//   actions distributed over a number of threads.  Each is meant
   51.83 +//   to proceed without stalling and the whole forms a deep request
   51.84 +//   pipeline to achieve throughput.  Ellipsis indicates a return
   51.85 +//   or break in processing which is resumed elsewhere.
   51.86 +//
   51.87 +//         main thread         repo thread (run() method)
   51.88 +//
   51.89 +//         loadMesh() invoked to request LOD
   51.90 +//           append LODRequest to mPendingRequests
   51.91 +//         ...
   51.92 +//         other mesh requests may be made
   51.93 +//         ...
   51.94 +//         notifyLoadedMeshes() invoked to stage work
   51.95 +//           append HeaderRequest to mHeaderReqQ
   51.96 +//         ...
   51.97 +//                             scan mHeaderReqQ
   51.98 +//                             issue 4096-byte GET for header
   51.99 +//                             ...
  51.100 +//                             onCompleted() invoked for GET
  51.101 +//                               data copied
  51.102 +//                               headerReceived() invoked
  51.103 +//                                 LLSD parsed
  51.104 +//                                 mMeshHeader, mMeshHeaderSize updated
  51.105 +//                                 scan mPendingLOD for LOD request
  51.106 +//                                 push LODRequest to mLODReqQ
  51.107 +//                             ...
  51.108 +//                             scan mLODReqQ
  51.109 +//                             fetchMeshLOD() invoked
  51.110 +//                               issue Byte-Range GET for LOD
  51.111 +//                             ...
  51.112 +//                             onCompleted() invoked for GET
  51.113 +//                               data copied
  51.114 +//                               lodReceived() invoked
  51.115 +//                                 unpack data into LLVolume
  51.116 +//                                 append LoadedMesh to mLoadedQ
  51.117 +//                             ...
  51.118 +//         notifyLoadedMeshes() invoked again
  51.119 +//           scan mLoadedQ
  51.120 +//           notifyMeshLoaded() for LOD
  51.121 +//             setMeshAssetLoaded() invoked for system volume
  51.122 +//             notifyMeshLoaded() invoked for each interested object
  51.123 +//         ...
  51.124 +//
  51.125 +// Mutexes
  51.126 +//
  51.127 +//   LLMeshRepository::mMeshMutex
  51.128 +//   LLMeshRepoThread::mMutex
  51.129 +//   LLMeshRepoThread::mHeaderMutex
  51.130 +//   LLMeshRepoThread::mSignal (LLCondition)
  51.131 +//   LLPhysicsDecomp::mSignal (LLCondition)
  51.132 +//   LLPhysicsDecomp::mMutex
  51.133 +//   LLMeshUploadThread::mMutex
  51.134 +//
  51.135 +// Mutex Order Rules
  51.136 +//
  51.137 +//   1.  LLMeshRepoThread::mMutex before LLMeshRepoThread::mHeaderMutex
  51.138 +//   2.  LLMeshRepository::mMeshMutex before LLMeshRepoThread::mMutex
  51.139 +//   (There are more rules, haven't been extracted.)
  51.140 +//
  51.141 +// Data Member Access/Locking
  51.142 +//
  51.143 +//   Description of how shared access to static and instance data
  51.144 +//   members is performed.  Each member is followed by the name of
  51.145 +//   the mutex, if any, covering the data and then a list of data
  51.146 +//   access models each of which is a triplet of the following form:
  51.147 +//
  51.148 +//     {ro, wo, rw}.{main, repo, any}.{mutex, none}
  51.149 +//     Type of access:  read-only, write-only, read-write.
  51.150 +//     Accessing thread or 'any'
  51.151 +//     Relevant mutex held during access (several may be held) or 'none'
  51.152 +//
  51.153 +//   A careful eye will notice some unsafe operations.  Many of these
  51.154 +//   have an alibi of some form.  Several types of alibi are identified
  51.155 +//   and listed here:
  51.156 +//
  51.157 +//     [0]  No alibi.  Probably unsafe.
  51.158 +//     [1]  Single-writer, self-consistent readers.  Old data must
  51.159 +//          be tolerated by any reader but data will come true eventually.
  51.160 +//     [2]  Like [1] but provides a hint about thread state.  These
  51.161 +//          may be unsafe.
  51.162 +//     [3]  empty() check outside of lock.  Can me made safish when
  51.163 +//          done in double-check lock style.  But this depends on
  51.164 +//          std:: implementation and memory model.
  51.165 +//     [4]  Appears to be covered by a mutex but doesn't need one.
  51.166 +//     [5]  Read of a double-checked lock.
  51.167 +//