Merge branch 'curlthreading3'

This commit is contained in:
Aleric Inglewood
2012-11-02 18:54:07 +01:00
126 changed files with 5178 additions and 4321 deletions

View File

@@ -46,6 +46,7 @@ endif(NOT STANDALONE)
add_custom_target(prepare DEPENDS ${prepare_depends})
add_subdirectory(cmake)
add_subdirectory(${LIBS_OPEN_PREFIX}aistatemachine)
add_subdirectory(${LIBS_OPEN_PREFIX}llaudio)
add_subdirectory(${LIBS_OPEN_PREFIX}llcharacter)
add_subdirectory(${LIBS_OPEN_PREFIX}llcommon)

View File

@@ -0,0 +1,39 @@
# -*- cmake -*-
project(aistatemachine)
include(00-Common)
include(LLCommon)
include(LLMessage)
include(LLMath)
include(LLXML)
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
include_directories(
${LLCOMMON_INCLUDE_DIRS}
${LLMESSAGE_INCLUDE_DIRS}
${LLMATH_INCLUDE_DIRS}
${LLXML_INCLUDE_DIRS}
)
set(aistatemachine_SOURCE_FILES
aistatemachine.cpp
aitimer.cpp
)
set(aistatemachine_HEADER_FILES
CMakeLists.txt
aistatemachine.h
aitimer.h
)
set_source_files_properties(${aistatemachine_HEADER_FILES}
PROPERTIES HEADER_FILE_ONLY TRUE)
list(APPEND aistatemachine_SOURCE_FILES ${aistatemachine_HEADER_FILES})
add_library (aistatemachine ${aistatemachine_SOURCE_FILES})
add_dependencies(aistatemachine prepare)

View File

@@ -32,14 +32,11 @@
#include <algorithm>
#include "llcallbacklist.h"
#include "llcontrol.h"
#include "llfasttimer.h"
#include "aithreadsafe.h"
#include "aistatemachine.h"
extern LLControlGroup gSavedSettings;
// Local variables.
namespace {
struct QueueElementComp;
@@ -64,28 +61,18 @@ namespace {
typedef std::vector<QueueElement> active_statemachines_type;
active_statemachines_type active_statemachines;
typedef std::vector<AIStateMachine*> continued_statemachines_type;
struct cscm_type
{
continued_statemachines_type continued_statemachines;
bool calling_mainloop;
};
AIThreadSafeDC<cscm_type> continued_statemachines_and_calling_mainloop;
}
// static
AIThreadSafeSimpleDC<U64> AIStateMachine::sMaxCount;
U64 AIStateMachine::sMaxCount;
AIThreadSafeDC<AIStateMachine::csme_type> AIStateMachine::sContinuedStateMachinesAndMainloopEnabled;
void AIStateMachine::updateSettings(void)
// static
void AIStateMachine::setMaxCount(F32 StateMachineMaxTime)
{
static const LLCachedControl<U32> StateMachineMaxTime("StateMachineMaxTime", 20);
static U32 last_StateMachineMaxTime = 0;
if (last_StateMachineMaxTime != StateMachineMaxTime)
{
Dout(dc::statemachine, "Initializing AIStateMachine::sMaxCount");
*AIAccess<U64>(sMaxCount) = calc_clock_frequency() * StateMachineMaxTime / 1000;
last_StateMachineMaxTime = StateMachineMaxTime;
}
llassert(is_main_thread());
Dout(dc::statemachine, "(Re)calculating AIStateMachine::sMaxCount");
sMaxCount = calc_clock_frequency() * StateMachineMaxTime / 1000;
}
//----------------------------------------------------------------------------
@@ -219,24 +206,23 @@ void AIStateMachine::locked_cont(void)
// If not_active is true then main-thread is not running this statemachine.
// It might call cont() (or set_state()) but never locked_cont(), and will never
// start actually running until we are done here and release the lock on
// continued_statemachines_and_calling_mainloop again. It is therefore safe
// sContinuedStateMachinesAndMainloopEnabled again. It is therefore safe
// to release mSetStateLock here, with as advantage that if we're not the main-
// thread and not_active is true, then the main-thread won't block when it has
// a timer running that times out and calls set_state().
mSetStateLock.unlock();
if (not_active)
{
AIWriteAccess<cscm_type> cscm_w(continued_statemachines_and_calling_mainloop);
AIWriteAccess<csme_type> csme_w(sContinuedStateMachinesAndMainloopEnabled);
// See above: it is not possible that mActive was changed since not_active
// was set to true above.
llassert_always(mActive == as_idle);
Dout(dc::statemachine, "Adding " << (void*)this << " to continued_statemachines");
cscm_w->continued_statemachines.push_back(this);
if (!cscm_w->calling_mainloop)
csme_w->continued_statemachines.push_back(this);
if (!csme_w->mainloop_enabled)
{
Dout(dc::statemachine, "Adding AIStateMachine::mainloop to gIdleCallbacks");
cscm_w->calling_mainloop = true;
gIdleCallbacks.addFunction(&AIStateMachine::mainloop);
Dout(dc::statemachine, "Activating AIStateMachine::mainloop.");
csme_w->mainloop_enabled = true;
}
mActive = as_queued;
llassert_always(!mIdle); // It should never happen that the main thread calls idle(), while another thread calls cont() concurrently.
@@ -497,11 +483,10 @@ void AIStateMachine::multiplex(U64 current_time)
}
//static
void AIStateMachine::add_continued_statemachines(void)
void AIStateMachine::add_continued_statemachines(AIReadAccess<csme_type>& csme_r)
{
AIReadAccess<cscm_type> cscm_r(continued_statemachines_and_calling_mainloop);
bool nonempty = false;
for (continued_statemachines_type::const_iterator iter = cscm_r->continued_statemachines.begin(); iter != cscm_r->continued_statemachines.end(); ++iter)
for (continued_statemachines_type::const_iterator iter = csme_r->continued_statemachines.begin(); iter != csme_r->continued_statemachines.end(); ++iter)
{
nonempty = true;
active_statemachines.push_back(QueueElement(*iter));
@@ -509,19 +494,15 @@ void AIStateMachine::add_continued_statemachines(void)
(*iter)->mActive = as_active;
}
if (nonempty)
AIWriteAccess<cscm_type>(cscm_r)->continued_statemachines.clear();
AIWriteAccess<csme_type>(csme_r)->continued_statemachines.clear();
}
static LLFastTimer::DeclareTimer FTM_STATEMACHINE("State Machine");
// static
void AIStateMachine::mainloop(void*)
void AIStateMachine::dowork(void)
{
LLFastTimer t(FTM_STATEMACHINE);
add_continued_statemachines();
llassert(!active_statemachines.empty());
// Run one or more state machines.
U64 total_clocks = 0;
U64 max_count = *AIAccess<U64>(sMaxCount);
for (active_statemachines_type::iterator iter = active_statemachines.begin(); iter != active_statemachines.end(); ++iter)
{
AIStateMachine& statemachine(iter->statemachine());
@@ -535,7 +516,7 @@ void AIStateMachine::mainloop(void*)
U64 delta = get_clock_count() - start;
iter->add(delta);
total_clocks += delta;
if (total_clocks >= max_count)
if (total_clocks >= sMaxCount)
{
#ifndef LL_RELEASE_FOR_DOWNLOAD
llwarns << "AIStateMachine::mainloop did run for " << (total_clocks * 1000 / calc_clock_frequency()) << " ms." << llendl;
@@ -588,12 +569,11 @@ void AIStateMachine::mainloop(void*)
if (active_statemachines.empty())
{
// If this was the last state machine, remove mainloop from the IdleCallbacks.
AIReadAccess<cscm_type> cscm_r(continued_statemachines_and_calling_mainloop);
if (cscm_r->continued_statemachines.empty() && cscm_r->calling_mainloop)
AIReadAccess<csme_type> csme_r(sContinuedStateMachinesAndMainloopEnabled, true);
if (csme_r->continued_statemachines.empty() && csme_r->mainloop_enabled)
{
Dout(dc::statemachine, "Removing AIStateMachine::mainloop from gIdleCallbacks");
AIWriteAccess<cscm_type>(cscm_r)->calling_mainloop = false;
gIdleCallbacks.deleteFunction(&AIStateMachine::mainloop);
Dout(dc::statemachine, "Deactivating AIStateMachine::mainloop: no active state machines left.");
AIWriteAccess<csme_type>(csme_r)->mainloop_enabled = false;
}
}
}
@@ -602,7 +582,10 @@ void AIStateMachine::mainloop(void*)
void AIStateMachine::flush(void)
{
DoutEntering(dc::curl, "AIStateMachine::flush(void)");
add_continued_statemachines();
{
AIReadAccess<csme_type> csme_r(sContinuedStateMachinesAndMainloopEnabled);
add_continued_statemachines(csme_r);
}
// Abort all state machines.
for (active_statemachines_type::iterator iter = active_statemachines.begin(); iter != active_statemachines.end(); ++iter)
{
@@ -624,15 +607,18 @@ void AIStateMachine::flush(void)
for(;;)
{
{
AIReadAccess<cscm_type> cscm_r(continued_statemachines_and_calling_mainloop);
if (!cscm_r->calling_mainloop)
AIReadAccess<csme_type> csme_r(sContinuedStateMachinesAndMainloopEnabled);
if (!csme_r->mainloop_enabled)
break;
}
mainloop(NULL);
mainloop();
}
if (batch == 1)
break;
add_continued_statemachines();
{
AIReadAccess<csme_type> csme_r(sContinuedStateMachinesAndMainloopEnabled);
add_continued_statemachines(csme_r);
}
// Kill all state machines.
for (active_statemachines_type::iterator iter = active_statemachines.begin(); iter != active_statemachines.end(); ++iter)
{

View File

@@ -192,6 +192,15 @@ class AIStateMachine {
as_active // State machine is on active_statemachines list.
};
//! Type of continued_statemachines.
typedef std::vector<AIStateMachine*> continued_statemachines_type;
//! Type of sContinuedStateMachinesAndMainloopEnabled.
struct csme_type
{
continued_statemachines_type continued_statemachines;
bool mainloop_enabled;
};
public:
typedef U32 state_type; //!< The type of mRunState
@@ -230,7 +239,8 @@ class AIStateMachine {
};
callback_type* mCallback; //!< Pointer to signal/connection, or NULL when not connected.
static AIThreadSafeSimpleDC<U64> sMaxCount; //!< Number of cpu clocks below which we start a new state machine within the same frame.
static U64 sMaxCount; //!< Number of cpu clocks below which we start a new state machine within the same frame.
static AIThreadSafeDC<csme_type> sContinuedStateMachinesAndMainloopEnabled; //!< Read/write locked variable pair.
protected:
LLMutex mSetStateLock; //!< For critical areas in set_state() and locked_cont().
@@ -244,7 +254,7 @@ class AIStateMachine {
#ifdef SHOW_ASSERT
, mContThread(AIThreadID::none), mCalledThreadUnsafeIdle(false)
#endif
{ updateSettings(); }
{ }
protected:
//! The user should call 'kill()', not delete a AIStateMachine (derived) directly.
@@ -339,7 +349,7 @@ class AIStateMachine {
// Other.
//! Called whenever the StateMachineMaxTime setting is changed.
static void updateSettings(void);
static void setMaxCount(F32 StateMachineMaxTime);
//---------------------------------------
// Accessors.
@@ -365,11 +375,24 @@ class AIStateMachine {
char const* state_str(state_type state);
private:
static void add_continued_statemachines(void);
static void mainloop(void*);
static void add_continued_statemachines(AIReadAccess<csme_type>& csme_r);
static void dowork(void);
void multiplex(U64 current_time);
public:
//! Call this once per frame to give the statemachines CPU cycles.
static void mainloop(void)
{
{
AIReadAccess<csme_type> csme_r(sContinuedStateMachinesAndMainloopEnabled, true);
if (!csme_r->mainloop_enabled)
return;
if (!csme_r->continued_statemachines.empty())
add_continued_statemachines(csme_r);
}
dowork();
}
//! Abort all running state machines and then run mainloop until all state machines are idle (called when application is exiting).
static void flush(void);

View File

@@ -1,4 +1,4 @@
# -*- cmake -*-
set(AISTATEMACHINE_INCLUDE_DIRS statemachine)
set(AISTATEMACHINE_LIBRARIES statemachine)
set(AISTATEMACHINE_INCLUDE_DIRS ${LIBS_OPEN_DIR}/aistatemachine)
set(AISTATEMACHINE_LIBRARIES aistatemachine)

View File

@@ -4,12 +4,14 @@ include(CARes)
include(CURL)
include(OpenSSL)
include(XmlRpcEpi)
include(AIStateMachine)
set(LLMESSAGE_INCLUDE_DIRS
${LIBS_OPEN_DIR}/llmessage
${CARES_INCLUDE_DIRS}
${CURL_INCLUDE_DIRS}
${OPENSSL_INCLUDE_DIRS}
${AISTATEMACHINE_INCLUDE_DIRS}
)
set(LLMESSAGE_LIBRARIES llmessage)
set(LLMESSAGE_LIBRARIES llmessage aistatemachine)

View File

@@ -0,0 +1,4 @@
# -*- cmake -*-
set(STATEMACHINE_INCLUDE_DIRS statemachine)
set(STATEMACHINE_LIBRARIES statemachine)

View File

@@ -49,6 +49,8 @@
namespace debug {
#if CWDEBUG_LOCATION
ll_thread_local size_t BackTrace::S_number;
void BackTrace::dump_backtrace(void) const
{
for (int frame = 0; frame < frames(); ++frame)
@@ -67,6 +69,67 @@ void BackTrace::dump_backtrace(void) const
Dout(dc::finish, mangled_function_name);
}
}
void BackTraces::store_trace(size_t trace)
{
mBackTraces.push_back(trace);
}
void BackTraces::remove_trace(size_t trace)
{
trace_container_type::iterator iter = mBackTraces.begin();
while (iter != mBackTraces.end())
{
if (*iter == trace)
{
*iter = mBackTraces.back();
mBackTraces.pop_back();
return;
}
++iter;
}
DoutFatal(dc::core, "Trace doesn't exist!");
}
void BackTraces::dump(void) const
{
Dout(dc::backtrace|continued_cf, "Dump for (BackTraces*)" << (void*)this << " (" << mBackTraces.size() << " backtraces): ");
for (trace_container_type::const_iterator iter = mBackTraces.begin(); iter != mBackTraces.end(); ++iter)
{
Dout(dc::continued|nonewline_cf, *iter << ' ');
}
Dout(dc::finish, "");
}
BackTraceTracker::BackTraceTracker(BackTraces* back_traces) : mBackTraces(back_traces)
{
BACKTRACE;
mTrace = BackTrace::S_number;
mBackTraces->store_trace(mTrace);
}
BackTraceTracker::~BackTraceTracker()
{
mBackTraces->remove_trace(mTrace);
}
BackTraceTracker::BackTraceTracker(BackTraceTracker const& orig) : mBackTraces(orig.mBackTraces)
{
BACKTRACE;
mTrace = BackTrace::S_number;
mBackTraces->store_trace(mTrace);
}
BackTraceTracker& BackTraceTracker::operator=(BackTraceTracker const& orig)
{
mBackTraces->remove_trace(mTrace);
mBackTraces = orig.mBackTraces;
BACKTRACE;
mTrace = BackTrace::S_number;
mBackTraces->store_trace(mTrace);
return *this;
}
#endif // CWDEBUG_LOCATION
#if CWDEBUG_ALLOC && CWDEBUG_LOCATION

View File

@@ -173,7 +173,9 @@ extern LL_COMMON_API fake_channel const notice;
#include <boost/shared_array.hpp>
#if CWDEBUG_LOCATION
#include <execinfo.h> // Needed for 'backtrace'.
#include "llpreprocessor.h"
#endif
#include <set>
#define CWD_API __attribute__ ((visibility("default")))
@@ -273,6 +275,8 @@ class BackTrace {
private:
boost::shared_array<void*> M_buffer;
int M_frames;
public:
static ll_thread_local size_t S_number;
public:
BackTrace(void** buffer, int frames) : M_buffer(new void* [frames]), M_frames(frames) { std::memcpy(M_buffer.get(), buffer, sizeof(void*) * frames); }
@@ -299,19 +303,81 @@ extern pthread_mutex_t backtrace_mutex;
using namespace debug; \
void* buffer[32]; \
int frames = backtrace(buffer, 32); \
size_t size; \
{ \
pthread_mutex_lock(&backtrace_mutex); \
backtraces.push_back(BackTrace(buffer, frames)); \
size = backtraces.size(); \
BackTrace::S_number = backtraces.size(); \
pthread_mutex_unlock(&backtrace_mutex); \
} \
Dout(dc::backtrace, "Stored backtrace #" << size); \
Dout(dc::backtrace, "Stored backtrace #" << BackTrace::S_number); \
} while(0)
class LL_COMMON_API BackTraces {
private:
typedef std::vector<size_t> trace_container_type;
trace_container_type mBackTraces;
public:
void store_trace(size_t trace);
void remove_trace(size_t trace);
void dump(void) const;
};
class LL_COMMON_API BackTraceTracker {
private:
BackTraces* mBackTraces;
size_t mTrace;
public:
BackTraceTracker(BackTraces* back_traces);
~BackTraceTracker();
BackTraceTracker(BackTraceTracker const&);
BackTraceTracker& operator=(BackTraceTracker const&);
void dump(void) const { mBackTraces->dump(); }
};
#else
#define BACKTRACE do { } while(0)
#endif // CWDEBUG_LOCATION
template<class T>
class LL_COMMON_API InstanceTracker {
private:
T const* mInstance;
static pthread_mutex_t sInstancesMutex;
static std::set<T const*> sInstances;
static void remember(T const* instance) { pthread_mutex_lock(&sInstancesMutex); sInstances.insert(instance); pthread_mutex_unlock(&sInstancesMutex); }
static void forget(T const* instance) { pthread_mutex_lock(&sInstancesMutex); sInstances.erase(instance); pthread_mutex_unlock(&sInstancesMutex); }
public:
InstanceTracker(T const* instance) : mInstance(instance) { remember(mInstance); }
~InstanceTracker() { forget(mInstance); }
InstanceTracker& operator=(InstanceTracker const& orig) { forget(mInstance); mInstance = orig.mInstance; remember(mInstance); return *this; }
static void dump(void);
private:
// Non-copyable. Instead of copying, call InstanceTracker(T const*) with the this pointer of the new instance.
InstanceTracker(InstanceTracker const& orig);
};
template<class T>
pthread_mutex_t InstanceTracker<T>::sInstancesMutex = PTHREAD_MUTEX_INITIALIZER;
template<class T>
std::set<T const*> InstanceTracker<T>::sInstances;
template<class T>
void InstanceTracker<T>::dump(void)
{
pthread_mutex_lock(&sInstancesMutex);
for (typename std::set<T const*>::iterator iter = sInstances.begin(); iter != sInstances.end(); ++iter)
{
std::cout << *iter << std::endl;
}
pthread_mutex_unlock(&sInstancesMutex);
}
} // namespace debug
//! Debugging macro.

View File

@@ -329,14 +329,14 @@ struct AIReadAccessConst
};
//! Construct a AIReadAccessConst from a constant AIThreadSafe.
AIReadAccessConst(AIThreadSafe<T> const& wrapper)
AIReadAccessConst(AIThreadSafe<T> const& wrapper, bool high_priority = false)
: mWrapper(const_cast<AIThreadSafe<T>&>(wrapper)),
mState(readlocked)
#if AI_NEED_ACCESS_CC
, mIsCopyConstructed(false)
#endif
{
mWrapper.mRWLock.rdlock();
mWrapper.mRWLock.rdlock(high_priority);
}
//! Destruct the AI*Access object.
@@ -393,7 +393,7 @@ struct AIReadAccess : public AIReadAccessConst<T>
using AIReadAccessConst<T>::readlocked;
//! Construct a AIReadAccess from a non-constant AIThreadSafe.
AIReadAccess(AIThreadSafe<T>& wrapper) : AIReadAccessConst<T>(wrapper, readlocked) { this->mWrapper.mRWLock.rdlock(); }
AIReadAccess(AIThreadSafe<T>& wrapper, bool high_priority = false) : AIReadAccessConst<T>(wrapper, readlocked) { this->mWrapper.mRWLock.rdlock(high_priority); }
protected:
//! Constructor used by AIWriteAccess.

View File

@@ -227,7 +227,7 @@ public:
#endif
/**
* @breif filesize helpers.
* @brief filesize helpers.
*
* The file size helpers are not considered particularly efficient,
* and should only be used for config files and the like -- not in a

View File

@@ -179,7 +179,6 @@ LLMemType::DeclareMemType LLMemType::MTYPE_IO_BUFFER("IoBuffer");
LLMemType::DeclareMemType LLMemType::MTYPE_IO_HTTP_SERVER("IoHttpServer");
LLMemType::DeclareMemType LLMemType::MTYPE_IO_SD_SERVER("IoSDServer");
LLMemType::DeclareMemType LLMemType::MTYPE_IO_SD_CLIENT("IoSDClient");
LLMemType::DeclareMemType LLMemType::MTYPE_IO_URL_REQUEST("IOUrlRequest");
LLMemType::DeclareMemType LLMemType::MTYPE_DIRECTX_INIT("DirectXInit");

View File

@@ -223,7 +223,6 @@ public:
static DeclareMemType MTYPE_IO_HTTP_SERVER;
static DeclareMemType MTYPE_IO_SD_SERVER;
static DeclareMemType MTYPE_IO_SD_CLIENT;
static DeclareMemType MTYPE_IO_URL_REQUEST;
static DeclareMemType MTYPE_DIRECTX_INIT;

View File

@@ -368,6 +368,7 @@ public:
* should work.
*/
static void _makeASCII(string_type& string);
static bool _isASCII(std::basic_string<T> const& string);
// Conversion to other data types
static BOOL convertToBOOL(const string_type& string, BOOL& value);
@@ -1473,6 +1474,19 @@ void LLStringUtilBase<T>::_makeASCII(string_type& string)
}
}
template<class T>
bool LLStringUtilBase<T>::_isASCII(std::basic_string<T> const& string)
{
size_type const len = string.length();
T bit_collector = 0;
for (size_type i = 0; i < len; ++i)
{
bit_collector |= string[i];
}
T const ascii_bits = 0x7f;
return !(bit_collector & ~ascii_bits);
}
// static
template<class T>
void LLStringUtilBase<T>::copy( T* dst, const T* src, size_type dst_size )

View File

@@ -113,12 +113,11 @@ void *APR_THREAD_FUNC LLThread::staticRun(apr_thread_t *apr_threadp, void *datap
// Only now print this info [doing that before setting mStatus
// to STOPPED makes it much more likely that another thread runs
// after the LLCurl::Multi::run() function exits and we actually
// change this variable (which really SHOULD have been inside
// the critical area of the mSignal lock)].
// after the AICurlPrivate::curlthread::AICurlThread::run() function
// exits and we actually change this variable (which really SHOULD
// have been inside the critical area of the mSignal lock)].
lldebugs << "LLThread::staticRun() Exiting: " << name << llendl;
--sRunning; // Would be better to do this after joining with the thread, but we don't join :/
return NULL;
}

View File

@@ -48,14 +48,20 @@
#include "llpumpio.h"
#include "llhttpclient.h"
#include "llsdserialize.h"
#include "llcurl.h"
LLPumpIO* gServicePump;
BOOL gBreak = false;
BOOL gSent = false;
class LLCrashLoggerResponder : public LLHTTPClient::Responder
class AIHTTPTimeoutPolicy;
extern AIHTTPTimeoutPolicy crashLoggerResponder_timeout;
class LLCrashLoggerResponder : public LLHTTPClient::ResponderWithResult
{
public:
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return crashLoggerResponder_timeout; }
LLCrashLoggerResponder()
{
}
@@ -308,14 +314,14 @@ bool LLCrashLogger::saveCrashBehaviorSetting(S32 crash_behavior)
return true;
}
bool LLCrashLogger::runCrashLogPost(std::string host, LLSD data, std::string msg, int retries, int timeout)
bool LLCrashLogger::runCrashLogPost(std::string host, LLSD data, std::string msg, int retries)
{
gBreak = false;
std::string status_message;
for(int i = 0; i < retries; ++i)
{
status_message = llformat("%s, try %d...", msg.c_str(), i+1);
LLHTTPClient::post(host, data, new LLCrashLoggerResponder(), timeout);
LLHTTPClient::post(host, data, new LLCrashLoggerResponder);
while(!gBreak)
{
updateApplication(status_message);
@@ -350,12 +356,12 @@ bool LLCrashLogger::sendCrashLogs()
// *TODO: Translate
if(mCrashHost != "")
{
sent = runCrashLogPost(mCrashHost, post_data, std::string("Sending to server"), 3, 5);
sent = runCrashLogPost(mCrashHost, post_data, std::string("Sending to server"), 3);
}
if(!sent)
{
sent = runCrashLogPost(mAltCrashHost, post_data, std::string("Sending to alternate server"), 3, 5);
sent = runCrashLogPost(mAltCrashHost, post_data, std::string("Sending to alternate server"), 3);
}
mSentCrashLogs = sent;
@@ -367,6 +373,7 @@ void LLCrashLogger::updateApplication(const std::string& message)
{
gServicePump->pump();
gServicePump->callback();
//FIXME: AIStateMachine::mainloop(); needs CPU cycles. Can't call it from here though, because it uses gSavedSettings which is part of newview.
}
bool LLCrashLogger::init()
@@ -394,7 +401,6 @@ bool LLCrashLogger::init()
}
gServicePump = new LLPumpIO;
LLHTTPClient::setPump(*gServicePump);
//If we've opened the crash logger, assume we can delete the marker file if it exists
if( gDirUtilp )

View File

@@ -40,6 +40,8 @@
#include "llsd.h"
#include "llcontrol.h"
class AIHTTPTimeoutPolicy;
class LLCrashLogger : public LLApp
{
public:
@@ -57,7 +59,7 @@ public:
virtual bool cleanup() { return true; }
void setUserText(const std::string& text) { mCrashInfo["UserNotes"] = text; }
S32 getCrashBehavior() { return mCrashBehavior; }
bool runCrashLogPost(std::string host, LLSD data, std::string msg, int retries, int timeout);
bool runCrashLogPost(std::string host, LLSD data, std::string msg, int retries);
protected:
S32 mCrashBehavior;
BOOL mCrashInPreviousExec;

View File

@@ -4,20 +4,31 @@ project(llmessage)
include(00-Common)
include(LLCommon)
include(AIStateMachine)
include(LLMath)
include(LLMessage)
include(LLVFS)
include(LLXML)
include_directories (${CMAKE_CURRENT_SOURCE_DIR})
include_directories(
${LLCOMMON_INCLUDE_DIRS}
${AISTATEMACHINE_INCLUDE_DIRS}
${LLMATH_INCLUDE_DIRS}
${LLMESSAGE_INCLUDE_DIRS}
${LLVFS_INCLUDE_DIRS}
${LLXML_INCLUDE_DIRS}
)
set(llmessage_SOURCE_FILES
aicurl.cpp
aicurleasyrequeststatemachine.cpp
aicurlthread.cpp
aihttpheaders.cpp
aihttptimeoutpolicy.cpp
debug_libcurl.cpp
llhttpclient.cpp
llares.cpp
llareslistener.cpp
llassetstorage.cpp
@@ -29,15 +40,10 @@ set(llmessage_SOURCE_FILES
llchainio.cpp
llcircuit.cpp
llclassifiedflags.cpp
aicurl.cpp
debug_libcurl.cpp
aicurlthread.cpp
lldatapacker.cpp
lldispatcher.cpp
llfiltersd2xmlrpc.cpp
llhost.cpp
llhttpclient.cpp
llhttpclientadapter.cpp
llhttpnode.cpp
llhttpsender.cpp
llinstantmessage.cpp
@@ -104,6 +110,13 @@ set(llmessage_SOURCE_FILES
set(llmessage_HEADER_FILES
CMakeLists.txt
aicurl.h
aicurleasyrequeststatemachine.h
aicurlprivate.h
aicurlthread.h
aihttpheaders.h
aihttptimeoutpolicy.h
debug_libcurl.h
llares.h
llareslistener.h
llassetstorage.h
@@ -117,10 +130,6 @@ set(llmessage_HEADER_FILES
llcircuit.h
llclassifiedflags.h
llcurl.h
aicurl.h
debug_libcurl.h
aicurlprivate.h
aicurlthread.h
lldatapacker.h
lldbstrings.h
lldispatcher.h
@@ -129,8 +138,6 @@ set(llmessage_HEADER_FILES
llfollowcamparams.h
llhost.h
llhttpclient.h
llhttpclientinterface.h
llhttpclientadapter.h
llhttpnode.h
llhttpnodeadapter.h
llhttpsender.h
@@ -227,7 +234,6 @@ if (LL_TESTS)
include(Tut)
SET(llmessage_TEST_SOURCE_FILES
# llhttpclientadapter.cpp
llmime.cpp
llnamevalue.cpp
lltrustedmessageservice.cpp
@@ -243,7 +249,8 @@ if (LL_TESTS)
${LLVFS_LIBRARIES}
${LLMATH_LIBRARIES}
${LLCOMMON_LIBRARIES}
${GOOGLEMOCK_LIBRARIES}
${GOOGLEMOCK_LIBRARIES}
${LLXML_LIBRARIES}
)
LL_ADD_INTEGRATION_TEST(

View File

@@ -55,9 +55,15 @@
#include "lltimer.h" // ms_sleep
#include "llproxy.h"
#include "llhttpstatuscodes.h"
#ifdef CWDEBUG
#include <libcwd/buf2str.h>
#endif
#include "aihttpheaders.h"
#include "aihttptimeoutpolicy.h"
#include "aicurleasyrequeststatemachine.h"
//==================================================================================
// Debug Settings
//
bool gNoVerifySSLCert;
//==================================================================================
// Local variables.
@@ -416,123 +422,20 @@ void setCAPath(std::string const& path)
CertificateAuthority_w->path = path;
}
// THREAD-SAFE
std::string strerror(CURLcode errorcode)
{
// libcurl is thread safe, no locking needed.
return curl_easy_strerror(errorcode);
}
//-----------------------------------------------------------------------------
// class Responder
//
Responder::Responder(void) : mReferenceCount(0)
{
DoutEntering(dc::curl, "AICurlInterface::Responder() with this = " << (void*)this);
}
Responder::~Responder()
{
DoutEntering(dc::curl, "AICurlInterface::Responder::~Responder() with this = " << (void*)this << "; mReferenceCount = " << mReferenceCount);
llassert(mReferenceCount == 0);
}
void Responder::setURL(std::string const& url)
{
// setURL is called from llhttpclient.cpp (request()), before calling any of the below (of course).
// We don't need locking here therefore; it's a case of initializing before use.
mURL = url;
}
// Called with HTML header.
// virtual
void Responder::completedHeader(U32, std::string const&, LLSD const&)
{
// Nothing.
}
// Called with HTML body.
// virtual
void Responder::completedRaw(U32 status, std::string const& reason, LLChannelDescriptors const& channels, LLIOPipe::buffer_ptr_t const& buffer)
{
LLSD content;
LLBufferStream istr(channels, buffer.get());
if (!LLSDSerialize::fromXML(content, istr))
{
llinfos << "Failed to deserialize LLSD. " << mURL << " [" << status << "]: " << reason << llendl;
}
// Allow derived class to override at this point.
completed(status, reason, content);
}
void Responder::fatalError(std::string const& reason)
{
llwarns << "Responder::fatalError(\"" << reason << "\") is called (" << mURL << "). Passing it to Responder::completed with fake HTML error status and empty HTML body!" << llendl;
completed(U32_MAX, reason, LLSD());
}
// virtual
void Responder::completed(U32 status, std::string const& reason, LLSD const& content)
{
// HTML status good?
if (200 <= status && status < 300)
{
// Allow derived class to override at this point.
result(content);
}
else
{
// Allow derived class to override at this point.
errorWithContent(status, reason, content);
}
}
// virtual
void Responder::errorWithContent(U32 status, std::string const& reason, LLSD const&)
{
// Allow derived class to override at this point.
error(status, reason);
}
// virtual
void Responder::error(U32 status, std::string const& reason)
{
llinfos << mURL << " [" << status << "]: " << reason << llendl;
}
// virtual
void Responder::result(LLSD const&)
{
// Nothing.
}
// Friend functions.
void intrusive_ptr_add_ref(Responder* responder)
{
responder->mReferenceCount++;
}
void intrusive_ptr_release(Responder* responder)
{
if (--responder->mReferenceCount == 0)
{
delete responder;
}
}
} // namespace AICurlInterface
//==================================================================================
//==================================================================================
// Local implementation.
//
namespace AICurlPrivate {
#if defined(CWDEBUG) || defined(DEBUG_CURLIO)
// CURLOPT_DEBUGFUNCTION function.
extern int debug_callback(CURL*, curl_infotype infotype, char* buf, size_t size, void* user_ptr);
#endif
//static
LLAtomicU32 Stats::easy_calls;
LLAtomicU32 Stats::easy_errors;
@@ -640,7 +543,7 @@ char* CurlEasyHandle::getTLErrorBuffer(void)
return tldata.mCurlErrorBuffer;
}
void CurlEasyHandle::setErrorBuffer(void)
void CurlEasyHandle::setErrorBuffer(void) const
{
char* error_buffer = getTLErrorBuffer();
if (mErrorBuffer != error_buffer)
@@ -659,7 +562,7 @@ void CurlEasyHandle::setErrorBuffer(void)
}
}
CURLcode CurlEasyHandle::getinfo_priv(CURLINFO info, void* data)
CURLcode CurlEasyHandle::getinfo_priv(CURLINFO info, void* data) const
{
setErrorBuffer();
return check_easy_code(curl_easy_getinfo(mEasyHandle, info, data));
@@ -707,12 +610,12 @@ CURLMcode CurlEasyHandle::remove_handle_from_multi(AICurlEasyRequest_wat& curl_e
return res;
}
void intrusive_ptr_add_ref(ThreadSafeCurlEasyRequest* threadsafe_curl_easy_request)
void intrusive_ptr_add_ref(ThreadSafeBufferedCurlEasyRequest* threadsafe_curl_easy_request)
{
threadsafe_curl_easy_request->mReferenceCount++;
}
void intrusive_ptr_release(ThreadSafeCurlEasyRequest* threadsafe_curl_easy_request)
void intrusive_ptr_release(ThreadSafeBufferedCurlEasyRequest* threadsafe_curl_easy_request)
{
if (--threadsafe_curl_easy_request->mReferenceCount == 0)
{
@@ -799,22 +702,22 @@ void CurlEasyRequest::setoptString(CURLoption option, std::string const& value)
setopt(option, value.c_str());
}
void CurlEasyRequest::setPost(AIPostFieldPtr const& postdata, S32 size)
void CurlEasyRequest::setPost(AIPostFieldPtr const& postdata, U32 size)
{
llassert_always(postdata->data());
Dout(dc::curl, "POST size is " << size << " bytes: \"" << libcwd::buf2str(postdata->data(), size) << "\".");
DoutCurl("POST size is " << size << " bytes: \"" << libcwd::buf2str(postdata->data(), size) << "\".");
setPostField(postdata); // Make sure the data stays around until we don't need it anymore.
setPost_raw(size, postdata->data());
}
void CurlEasyRequest::setPost_raw(S32 size, char const* data)
void CurlEasyRequest::setPost_raw(U32 size, char const* data)
{
if (!data)
{
// data == NULL when we're going to read the data using CURLOPT_READFUNCTION.
Dout(dc::curl, "POST size is " << size << " bytes.");
DoutCurl("POST size is " << size << " bytes.");
}
// The server never replies with 100-continue, so suppress the "Expect: 100-continue" header that libcurl adds by default.
@@ -825,19 +728,14 @@ void CurlEasyRequest::setPost_raw(S32 size, char const* data)
addHeader("Keep-alive: 300");
}
setopt(CURLOPT_POSTFIELDSIZE, size);
setopt(CURLOPT_POSTFIELDS, data);
}
ThreadSafeCurlEasyRequest* CurlEasyRequest::get_lockobj(void)
{
return static_cast<ThreadSafeCurlEasyRequest*>(AIThreadSafeSimpleDC<CurlEasyRequest>::wrapper_cast(this));
setopt(CURLOPT_POSTFIELDS, data); // Implies CURLOPT_POST
}
//static
size_t CurlEasyRequest::headerCallback(char* ptr, size_t size, size_t nmemb, void* userdata)
{
CurlEasyRequest* self = static_cast<CurlEasyRequest*>(userdata);
ThreadSafeCurlEasyRequest* lockobj = self->get_lockobj();
ThreadSafeBufferedCurlEasyRequest* lockobj = self->get_lockobj();
AICurlEasyRequest_wat lock_self(*lockobj);
return self->mHeaderCallback(ptr, size, nmemb, self->mHeaderCallbackUserData);
}
@@ -854,7 +752,7 @@ void CurlEasyRequest::setHeaderCallback(curl_write_callback callback, void* user
size_t CurlEasyRequest::writeCallback(char* ptr, size_t size, size_t nmemb, void* userdata)
{
CurlEasyRequest* self = static_cast<CurlEasyRequest*>(userdata);
ThreadSafeCurlEasyRequest* lockobj = self->get_lockobj();
ThreadSafeBufferedCurlEasyRequest* lockobj = self->get_lockobj();
AICurlEasyRequest_wat lock_self(*lockobj);
return self->mWriteCallback(ptr, size, nmemb, self->mWriteCallbackUserData);
}
@@ -871,7 +769,7 @@ void CurlEasyRequest::setWriteCallback(curl_write_callback callback, void* userd
size_t CurlEasyRequest::readCallback(char* ptr, size_t size, size_t nmemb, void* userdata)
{
CurlEasyRequest* self = static_cast<CurlEasyRequest*>(userdata);
ThreadSafeCurlEasyRequest* lockobj = self->get_lockobj();
ThreadSafeBufferedCurlEasyRequest* lockobj = self->get_lockobj();
AICurlEasyRequest_wat lock_self(*lockobj);
return self->mReadCallback(ptr, size, nmemb, self->mReadCallbackUserData);
}
@@ -888,7 +786,7 @@ void CurlEasyRequest::setReadCallback(curl_read_callback callback, void* userdat
CURLcode CurlEasyRequest::SSLCtxCallback(CURL* curl, void* sslctx, void* userdata)
{
CurlEasyRequest* self = static_cast<CurlEasyRequest*>(userdata);
ThreadSafeCurlEasyRequest* lockobj = self->get_lockobj();
ThreadSafeBufferedCurlEasyRequest* lockobj = self->get_lockobj();
AICurlEasyRequest_wat lock_self(*lockobj);
return self->mSSLCtxCallback(curl, sslctx, self->mSSLCtxCallbackUserData);
}
@@ -956,7 +854,7 @@ CurlEasyRequest::~CurlEasyRequest()
// If the CurlEasyRequest object is destructed then we need to revoke all callbacks, because
// we can't set the lock anymore, and neither will mHeaderCallback, mWriteCallback etc,
// be available anymore.
send_events_to(NULL);
send_handle_events_to(NULL);
revokeCallbacks();
// This wasn't freed yet if the request never finished.
curl_slist_free_all(mHeaders);
@@ -969,124 +867,24 @@ void CurlEasyRequest::resetState(void)
reset();
curl_slist_free_all(mHeaders);
mHeaders = NULL;
mRequestFinalized = false;
mEventsTarget = NULL;
mTimeoutPolicy = NULL;
mTimeout = NULL;
mHandleEventsTarget = NULL;
mResult = CURLE_FAILED_INIT;
applyDefaultOptions();
}
void CurlEasyRequest::addHeader(char const* header)
{
llassert(!mRequestFinalized);
llassert(!mTimeoutPolicy); // Cannot add a header after calling finalizeRequest.
mHeaders = curl_slist_append(mHeaders, header);
}
#if defined(CWDEBUG) || defined(DEBUG_CURLIO)
static int curl_debug_cb(CURL*, curl_infotype infotype, char* buf, size_t size, void* user_ptr)
void CurlEasyRequest::addHeaders(AIHTTPHeaders const& headers)
{
#ifdef CWDEBUG
using namespace ::libcwd;
CurlEasyRequest* request = (CurlEasyRequest*)user_ptr;
std::ostringstream marker;
marker << (void*)request->get_lockobj();
libcw_do.push_marker();
libcw_do.marker().assign(marker.str().data(), marker.str().size());
if (!debug::channels::dc::curlio.is_on())
debug::channels::dc::curlio.on();
LibcwDoutScopeBegin(LIBCWD_DEBUGCHANNELS, libcw_do, dc::curlio|cond_nonewline_cf(infotype == CURLINFO_TEXT))
#else
if (infotype == CURLINFO_TEXT)
{
while (size > 0 && (buf[size - 1] == '\r' || buf[size - 1] == '\n'))
--size;
}
LibcwDoutScopeBegin(LIBCWD_DEBUGCHANNELS, libcw_do, dc::curlio)
#endif
switch (infotype)
{
case CURLINFO_TEXT:
LibcwDoutStream << "* ";
break;
case CURLINFO_HEADER_IN:
LibcwDoutStream << "H> ";
break;
case CURLINFO_HEADER_OUT:
LibcwDoutStream << "H< ";
break;
case CURLINFO_DATA_IN:
LibcwDoutStream << "D> ";
break;
case CURLINFO_DATA_OUT:
LibcwDoutStream << "D< ";
break;
case CURLINFO_SSL_DATA_IN:
LibcwDoutStream << "S> ";
break;
case CURLINFO_SSL_DATA_OUT:
LibcwDoutStream << "S< ";
break;
default:
LibcwDoutStream << "?? ";
}
if (infotype == CURLINFO_TEXT)
LibcwDoutStream.write(buf, size);
else if (infotype == CURLINFO_HEADER_IN || infotype == CURLINFO_HEADER_OUT)
LibcwDoutStream << libcwd::buf2str(buf, size);
else if (infotype == CURLINFO_DATA_IN)
{
LibcwDoutStream << size << " bytes";
bool finished = false;
size_t i = 0;
while (i < size)
{
char c = buf[i];
if (!('0' <= c && c <= '9') && !('a' <= c && c <= 'f'))
{
if (0 < i && i + 1 < size && buf[i] == '\r' && buf[i + 1] == '\n')
{
// Binary output: "[0-9a-f]*\r\n ...binary data..."
LibcwDoutStream << ": \"" << libcwd::buf2str(buf, i + 2) << "\"...";
finished = true;
}
break;
}
++i;
}
if (!finished && size > 9 && buf[0] == '<')
{
// Human readable output: html, xml or llsd.
if (!strncmp(buf, "<!DOCTYPE", 9) || !strncmp(buf, "<?xml", 5) || !strncmp(buf, "<llsd>", 6))
{
LibcwDoutStream << ": \"" << libcwd::buf2str(buf, size) << '"';
finished = true;
}
}
if (!finished)
{
// Unknown format. Only print the first and last 20 characters.
if (size > 40UL)
{
LibcwDoutStream << ": \"" << libcwd::buf2str(buf, 20) << "\"...\"" << libcwd::buf2str(&buf[size - 20], 20) << '"';
}
else
{
LibcwDoutStream << ": \"" << libcwd::buf2str(buf, size) << '"';
}
}
}
else if (infotype == CURLINFO_DATA_OUT)
LibcwDoutStream << size << " bytes: \"" << libcwd::buf2str(buf, size) << '"';
else
LibcwDoutStream << size << " bytes";
LibcwDoutScopeEnd;
#ifdef CWDEBUG
libcw_do.pop_marker();
#endif
return 0;
llassert(!mTimeoutPolicy); // Cannot add headers after calling finalizeRequest.
headers.append_to(mHeaders);
}
#endif
void CurlEasyRequest::applyProxySettings(void)
{
@@ -1124,6 +922,7 @@ void CurlEasyRequest::applyProxySettings(void)
//static
CURLcode CurlEasyRequest::curlCtxCallback(CURL* curl, void* sslctx, void* parm)
{
DoutEntering(dc::curl, "CurlEasyRequest::curlCtxCallback((CURL*)" << (void*)curl << ", " << sslctx << ", " << parm << ")");
SSL_CTX* ctx = (SSL_CTX*)sslctx;
// Turn off TLS v1.1 (which is not supported anyway by Linden Lab) because otherwise we fail to connect.
// Also turn off SSL v2, which is highly broken and strongly discouraged[1].
@@ -1156,8 +955,18 @@ void CurlEasyRequest::applyDefaultOptions(void)
setoptString(CURLOPT_CAINFO, CertificateAuthority_r->file);
setSSLCtxCallback(&curlCtxCallback, NULL);
setopt(CURLOPT_NOSIGNAL, 1);
// The old code did this for the 'buffered' version, but I think it's nonsense.
//setopt(CURLOPT_DNS_CACHE_TIMEOUT, 0);
// Cache DNS look ups an hour. If we set it smaller we risk frequent connect timeouts in cases where DNS look ups are slow.
setopt(CURLOPT_DNS_CACHE_TIMEOUT, 3600);
// Only resolve to IPV4.
// Rationale: if a host resolves to both, ipv4 and ipv6, then this stops libcurl from
// using the ipv6 address. If we don't do that then libcurl first attempts to connect
// to the ipv4 IP number (using only HALF the connect timeout we passed to it!) and if
// that fails try the ipv6 IP number, which then most likely fails with "network unreachable".
// Then libcurl immediately returns with just the ipv6 error as result masking the real problem.
// Since the viewer doesn't support IPv6 at least for UDP services, and there are no
// transition plans to IPv6 anywhere at this moment, the easiest way to get rid of this
// problem is by simply not falling back to ipv6.
setopt(CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
// Disable SSL/TLS session caching; some servers (aka id.secondlife.com) refuse connections when session ids are enabled.
setopt(CURLOPT_SSL_SESSIONID_CACHE, 0);
// Set the CURL options for either SOCKS or HTTP proxy.
@@ -1167,17 +976,67 @@ void CurlEasyRequest::applyDefaultOptions(void)
if (dc::curlio.is_on())
{
setopt(CURLOPT_VERBOSE, 1);
setopt(CURLOPT_DEBUGFUNCTION, &curl_debug_cb);
setopt(CURLOPT_DEBUGFUNCTION, &debug_callback);
setopt(CURLOPT_DEBUGDATA, this);
}
);
}
void CurlEasyRequest::finalizeRequest(std::string const& url)
// url must be of the form
// (see http://www.ietf.org/rfc/rfc3986.txt Appendix A for definitions not given here):
//
// url = sheme ":" hier-part [ "?" query ] [ "#" fragment ]
// hier-part = "//" authority path-abempty
// authority = [ userinfo "@" ] host [ ":" port ]
// path-abempty = *( "/" segment )
//
// That is, a hier-part of the form '/ path-absolute', '/ path-rootless' or
// '/ path-empty' is NOT allowed here. This should be safe because we only
// call this function for curl access, any file access would use APR.
//
// However, as a special exception, this function allows:
//
// url = authority path-abempty
//
// without the 'sheme ":" "//"' parts.
//
// As follows from the ABNF (see RFC, Appendix A):
// - authority is either terminated by a '/' or by the end of the string because
// neither userinfo, host nor port may contain a '/'.
// - userinfo does not contain a '@', and if it exists, is always terminated by a '@'.
// - port does not contain a ':', and if it exists is always prepended by a ':'.
//
// Only called by CurlEasyRequest::finalizeRequest.
static std::string extract_canonical_hostname(std::string const& url)
{
llassert(!mRequestFinalized);
mResult = CURLE_FAILED_INIT; // General error code, the final code is written here in MultiHandle::check_run_count when msg is CURLMSG_DONE.
lldebugs << url << llendl;
std::string::size_type pos;
std::string::size_type authority = 0; // Default if there is no sheme.
if ((pos = url.find("://")) != url.npos && pos < url.find('/')) authority = pos + 3; // Skip the "sheme://" if any, the second find is to avoid finding a "://" as part of path-abempty.
std::string::size_type host = authority; // Default if there is no userinfo.
if ((pos = url.find('@', authority)) != url.npos) host = pos + 1; // Skip the "userinfo@" if any.
authority = url.length() - 1; // Default last character of host if there is no path-abempty.
if ((pos = url.find('/', host)) != url.npos) authority = pos - 1; // Point to last character of host.
std::string::size_type len = url.find_last_not_of(":0123456789", authority) - host + 1; // Skip trailing ":port", if any.
std::string hostname(url, host, len);
#if APR_CHARSET_EBCDIC
#error Not implemented
#else
// Convert hostname to lowercase in a way that we compare two hostnames equal iff libcurl does.
for (std::string::iterator iter = hostname.begin(); iter != hostname.end(); ++iter)
{
int c = *iter;
if (c >= 'A' && c <= 'Z')
*iter = c + ('a' - 'A');
}
#endif
return hostname;
}
void CurlEasyRequest::finalizeRequest(std::string const& url, AIHTTPTimeoutPolicy const& policy, AICurlEasyRequestStateMachine* state_machine)
{
DoutCurlEntering("CurlEasyRequest::finalizeRequest(\"" << url << "\", " << policy.name() << ", " << (void*)state_machine << ")");
llassert(!mTimeoutPolicy); // May only call finalizeRequest once!
mResult = CURLE_FAILED_INIT; // General error code; the final result code is stored here by MultiHandle::check_run_count when msg is CURLMSG_DONE.
#ifdef SHOW_ASSERT
// Do a sanity check on the headers.
int content_type_count = 0;
@@ -1190,12 +1049,14 @@ void CurlEasyRequest::finalizeRequest(std::string const& url)
}
if (content_type_count > 1)
{
llwarns << content_type_count << " Content-Type: headers!" << llendl;
llwarns << "Requesting: \"" << url << "\": " << content_type_count << " Content-Type: headers!" << llendl;
}
#endif
mRequestFinalized = true;
setopt(CURLOPT_HTTPHEADER, mHeaders);
setoptString(CURLOPT_URL, url);
mLowercaseHostname = extract_canonical_hostname(url);
mTimeoutPolicy = &policy;
state_machine->setTotalDelayTimeout(policy.getTotalDelay());
// The following line is a bit tricky: we store a pointer to the object without increasing its reference count.
// Of course we could increment the reference count, but that doesn't help much: if then this pointer would
// get "lost" we'd have a memory leak. Either way we must make sure that it is impossible that this pointer
@@ -1213,7 +1074,51 @@ void CurlEasyRequest::finalizeRequest(std::string const& url)
setopt(CURLOPT_PRIVATE, get_lockobj());
}
void CurlEasyRequest::getTransferInfo(AICurlInterface::TransferInfo* info)
// AIFIXME: Doing this only when it is actually being added assures that the first curl easy handle that is
// // being added for a particular host will be the one getting extra 'DNS lookup' connect time.
// // However, if another curl easy handle for the same host is added immediately after, it will
// // get less connect time, while it still (also) has to wait for this DNS lookup.
void CurlEasyRequest::set_timeout_opts(void)
{
setopt(CURLOPT_CONNECTTIMEOUT, mTimeoutPolicy->getConnectTimeout(mLowercaseHostname));
setopt(CURLOPT_TIMEOUT, mTimeoutPolicy->getCurlTransaction());
}
void CurlEasyRequest::create_timeout_object(ThreadSafeBufferedCurlEasyRequest* lockobj)
{
mTimeout = new curlthread::HTTPTimeout(mTimeoutPolicy, lockobj);
}
LLPointer<curlthread::HTTPTimeout>& CurlEasyRequest::get_timeout_object(ThreadSafeBufferedCurlEasyRequest* lockobj)
{
if (mTimeoutIsOrphan)
{
mTimeoutIsOrphan = false;
llassert_always(mTimeout);
}
else
{
create_timeout_object(lockobj);
}
return mTimeout;
}
void CurlEasyRequest::print_curl_timings(void) const
{
double t;
getinfo(CURLINFO_NAMELOOKUP_TIME, &t);
DoutCurl("CURLINFO_NAMELOOKUP_TIME = " << t);
getinfo(CURLINFO_CONNECT_TIME, &t);
DoutCurl("CURLINFO_CONNECT_TIME = " << t);
getinfo(CURLINFO_APPCONNECT_TIME, &t);
DoutCurl("CURLINFO_APPCONNECT_TIME = " << t);
getinfo(CURLINFO_PRETRANSFER_TIME, &t);
DoutCurl("CURLINFO_PRETRANSFER_TIME = " << t);
getinfo(CURLINFO_STARTTRANSFER_TIME, &t);
DoutCurl("CURLINFO_STARTTRANSFER_TIME = " << t);
}
void CurlEasyRequest::getTransferInfo(AITransferInfo* info)
{
// Curl explicitly demands a double for these info's.
double size, total_time, speed;
@@ -1226,7 +1131,7 @@ void CurlEasyRequest::getTransferInfo(AICurlInterface::TransferInfo* info)
info->mSpeedDownload = speed;
}
void CurlEasyRequest::getResult(CURLcode* result, AICurlInterface::TransferInfo* info)
void CurlEasyRequest::getResult(CURLcode* result, AITransferInfo* info)
{
*result = mResult;
if (info && mResult != CURLE_FAILED_INIT)
@@ -1237,60 +1142,63 @@ void CurlEasyRequest::getResult(CURLcode* result, AICurlInterface::TransferInfo*
void CurlEasyRequest::added_to_multi_handle(AICurlEasyRequest_wat& curl_easy_request_w)
{
if (mEventsTarget)
mEventsTarget->added_to_multi_handle(curl_easy_request_w);
if (mHandleEventsTarget)
mHandleEventsTarget->added_to_multi_handle(curl_easy_request_w);
}
void CurlEasyRequest::finished(AICurlEasyRequest_wat& curl_easy_request_w)
{
if (mEventsTarget)
mEventsTarget->finished(curl_easy_request_w);
if (mHandleEventsTarget)
mHandleEventsTarget->finished(curl_easy_request_w);
}
void CurlEasyRequest::removed_from_multi_handle(AICurlEasyRequest_wat& curl_easy_request_w)
{
if (mEventsTarget)
mEventsTarget->removed_from_multi_handle(curl_easy_request_w);
if (mHandleEventsTarget)
mHandleEventsTarget->removed_from_multi_handle(curl_easy_request_w);
}
void CurlEasyRequest::print_diagnostics(CURLcode code)
{
if (code == CURLE_OPERATION_TIMEDOUT)
{
// mTimeout SHOULD always be set, but I see no reason not to test it, as
// this is far from the code that guaranteeds that it is set.
if (mTimeout)
{
mTimeout->print_diagnostics(this);
}
}
}
//-----------------------------------------------------------------------------
// CurlResponderBuffer
// BufferedCurlEasyRequest
static unsigned int const MAX_REDIRECTS = 5;
static S32 const CURL_REQUEST_TIMEOUT = 30; // Seconds per operation.
static int const HTTP_REDIRECTS_DEFAULT = 10;
LLChannelDescriptors const CurlResponderBuffer::sChannels;
LLChannelDescriptors const BufferedCurlEasyRequest::sChannels;
CurlResponderBuffer::CurlResponderBuffer()
BufferedCurlEasyRequest::BufferedCurlEasyRequest() : mRequestTransferedBytes(0), mResponseTransferedBytes(0), mBufferEventsTarget(NULL)
{
ThreadSafeBufferedCurlEasyRequest* lockobj = get_lockobj();
AICurlEasyRequest_wat curl_easy_request_w(*lockobj);
curl_easy_request_w->send_events_to(this);
}
#define llmaybeerrs lllog(LLApp::isRunning() ? LLError::LEVEL_ERROR : LLError::LEVEL_WARN, NULL, NULL, false, true)
// The callbacks need to be revoked when the CurlResponderBuffer is destructed (because that is what the callbacks use).
// The AIThreadSafeSimple<CurlResponderBuffer> is destructed first (right to left), so when we get here then the
// ThreadSafeCurlEasyRequest base class of ThreadSafeBufferedCurlEasyRequest is still intact and we can create
// and use curl_easy_request_w.
CurlResponderBuffer::~CurlResponderBuffer()
BufferedCurlEasyRequest::~BufferedCurlEasyRequest()
{
ThreadSafeBufferedCurlEasyRequest* lockobj = get_lockobj();
AICurlEasyRequest_wat curl_easy_request_w(*lockobj); // Wait 'til possible callbacks have returned.
curl_easy_request_w->send_events_to(NULL);
curl_easy_request_w->revokeCallbacks();
send_buffer_events_to(NULL);
revokeCallbacks();
if (mResponder)
{
// If the responder is still alive, then that means that CurlResponderBuffer::processOutput was
// If the responder is still alive, then that means that BufferedCurlEasyRequest::processOutput was
// never called, which means that the removed_from_multi_handle event never happened.
// This is definitely an internal error as it can only happen when libcurl is too slow,
// in which case AICurlEasyRequestStateMachine::mTimer times out, but that already
// calls CurlResponderBuffer::timed_out().
llmaybeerrs << "Calling ~CurlResponderBuffer() with active responder!" << llendl;
// calls BufferedCurlEasyRequest::timed_out().
llmaybeerrs << "Calling ~BufferedCurlEasyRequest() with active responder!" << llendl;
if (!LLApp::isRunning())
{
// It might happen if some CurlResponderBuffer escaped clean up somehow :/
// It might happen if some BufferedCurlEasyRequest escaped clean up somehow :/
mResponder = NULL;
}
else
@@ -1301,38 +1209,39 @@ CurlResponderBuffer::~CurlResponderBuffer()
}
}
void CurlResponderBuffer::timed_out(void)
void BufferedCurlEasyRequest::timed_out(void)
{
mResponder->completedRaw(HTTP_INTERNAL_ERROR, "Request timeout, aborted.", sChannels, mOutput);
mResponder = NULL;
mResponder->finished(CURLE_OK, HTTP_INTERNAL_ERROR, "Request timeout, aborted.", sChannels, mOutput);
if (mResponder->needsHeaders())
{
send_buffer_events_to(NULL); // Revoke buffer events: we sent them to the responder.
}
mResponder = NULL;
}
void CurlResponderBuffer::resetState(AICurlEasyRequest_wat& curl_easy_request_w)
void BufferedCurlEasyRequest::resetState(void)
{
llassert(!mResponder);
curl_easy_request_w->resetState();
// Call base class implementation.
CurlEasyRequest::resetState();
mOutput.reset();
mInput.reset();
mHeaderOutput.str("");
mHeaderOutput.clear();
}
ThreadSafeBufferedCurlEasyRequest* CurlResponderBuffer::get_lockobj(void)
ThreadSafeBufferedCurlEasyRequest* BufferedCurlEasyRequest::get_lockobj(void)
{
return static_cast<ThreadSafeBufferedCurlEasyRequest*>(AIThreadSafeSimple<CurlResponderBuffer>::wrapper_cast(this));
return static_cast<ThreadSafeBufferedCurlEasyRequest*>(AIThreadSafeSimple<BufferedCurlEasyRequest>::wrapper_cast(this));
}
void CurlResponderBuffer::prepRequest(AICurlEasyRequest_wat& curl_easy_request_w, std::vector<std::string> const& headers, AICurlInterface::ResponderPtr responder, S32 time_out, bool post)
ThreadSafeBufferedCurlEasyRequest const* BufferedCurlEasyRequest::get_lockobj(void) const
{
if (post)
{
// Accept everything (send an Accept-Encoding header containing all encodings we support (zlib and gzip)).
curl_easy_request_w->setoptString(CURLOPT_ENCODING, ""); // CURLOPT_ACCEPT_ENCODING
}
return static_cast<ThreadSafeBufferedCurlEasyRequest const*>(AIThreadSafeSimple<BufferedCurlEasyRequest>::wrapper_cast(this));
}
void BufferedCurlEasyRequest::prepRequest(AICurlEasyRequest_wat& curl_easy_request_w, AIHTTPHeaders const& headers, LLHTTPClient::ResponderPtr responder)
{
mInput.reset(new LLBufferArray);
mInput->setThreaded(true);
mLastRead = NULL;
@@ -1345,126 +1254,23 @@ void CurlResponderBuffer::prepRequest(AICurlEasyRequest_wat& curl_easy_request_w
curl_easy_request_w->setReadCallback(&curlReadCallback, lockobj);
curl_easy_request_w->setHeaderCallback(&curlHeaderCallback, lockobj);
// Allow up to five redirects.
if (responder && responder->followRedir())
// Allow up to ten redirects.
if (responder->followRedir())
{
curl_easy_request_w->setopt(CURLOPT_FOLLOWLOCATION, 1);
curl_easy_request_w->setopt(CURLOPT_MAXREDIRS, MAX_REDIRECTS);
curl_easy_request_w->setopt(CURLOPT_MAXREDIRS, HTTP_REDIRECTS_DEFAULT);
}
curl_easy_request_w->setopt(CURLOPT_SSL_VERIFYPEER, true);
// Don't verify host name so urls with scrubbed host names will work (improves DNS performance).
curl_easy_request_w->setopt(CURLOPT_SSL_VERIFYHOST, 0);
curl_easy_request_w->setopt(CURLOPT_TIMEOUT, llmax(time_out, CURL_REQUEST_TIMEOUT));
// Keep responder alive.
mResponder = responder;
if (!post)
// Send header events to responder if needed.
if (mResponder->needsHeaders())
{
// Add extra headers.
for (std::vector<std::string>::const_iterator iter = headers.begin(); iter != headers.end(); ++iter)
{
curl_easy_request_w->addHeader((*iter).c_str());
}
}
}
//static
size_t CurlResponderBuffer::curlWriteCallback(char* data, size_t size, size_t nmemb, void* user_data)
{
ThreadSafeBufferedCurlEasyRequest* lockobj = static_cast<ThreadSafeBufferedCurlEasyRequest*>(user_data);
// We need to lock the curl easy request object too, because that lock is used
// to make sure that callbacks and destruction aren't done simultaneously.
AICurlEasyRequest_wat buffered_easy_request_w(*lockobj);
AICurlResponderBuffer_wat buffer_w(*lockobj);
S32 n = size * nmemb;
buffer_w->getOutput()->append(sChannels.in(), (U8 const*)data, n);
return n;
}
//static
size_t CurlResponderBuffer::curlReadCallback(char* data, size_t size, size_t nmemb, void* user_data)
{
ThreadSafeBufferedCurlEasyRequest* lockobj = static_cast<ThreadSafeBufferedCurlEasyRequest*>(user_data);
// We need to lock the curl easy request object too, because that lock is used
// to make sure that callbacks and destruction aren't done simultaneously.
AICurlEasyRequest_wat buffered_easy_request_w(*lockobj);
S32 bytes = size * nmemb; // The maximum amount to read.
AICurlResponderBuffer_wat buffer_w(*lockobj);
buffer_w->mLastRead = buffer_w->getInput()->readAfter(sChannels.out(), buffer_w->mLastRead, (U8*)data, bytes);
return bytes; // Return the amount actually read.
}
//static
size_t CurlResponderBuffer::curlHeaderCallback(char* data, size_t size, size_t nmemb, void* user_data)
{
ThreadSafeBufferedCurlEasyRequest* lockobj = static_cast<ThreadSafeBufferedCurlEasyRequest*>(user_data);
// We need to lock the curl easy request object too, because that lock is used
// to make sure that callbacks and destruction aren't done simultaneously.
AICurlEasyRequest_wat buffered_easy_request_w(*lockobj);
AICurlResponderBuffer_wat buffer_w(*lockobj);
size_t n = size * nmemb;
buffer_w->getHeaderOutput().write(data, n);
return n;
}
void CurlResponderBuffer::added_to_multi_handle(AICurlEasyRequest_wat& curl_easy_request_w)
{
Dout(dc::curl, "Calling CurlResponderBuffer::added_to_multi_handle(@" << (void*)&*curl_easy_request_w << ") for this = " << (void*)this);
}
void CurlResponderBuffer::finished(AICurlEasyRequest_wat& curl_easy_request_w)
{
Dout(dc::curl, "Calling CurlResponderBuffer::finished(@" << (void*)&*curl_easy_request_w << ") for this = " << (void*)this);
}
void CurlResponderBuffer::removed_from_multi_handle(AICurlEasyRequest_wat& curl_easy_request_w)
{
Dout(dc::curl, "Calling CurlResponderBuffer::removed_from_multi_handle(@" << (void*)&*curl_easy_request_w << ") for this = " << (void*)this);
// Lock self.
ThreadSafeBufferedCurlEasyRequest* lockobj = get_lockobj();
llassert(dynamic_cast<ThreadSafeBufferedCurlEasyRequest*>(static_cast<ThreadSafeCurlEasyRequest*>(ThreadSafeCurlEasyRequest::wrapper_cast(&*curl_easy_request_w))) == lockobj);
AICurlResponderBuffer_wat buffer_w(*lockobj);
llassert(&*buffer_w == this);
processOutput(curl_easy_request_w);
}
void CurlResponderBuffer::processOutput(AICurlEasyRequest_wat& curl_easy_request_w)
{
U32 responseCode = 0;
std::string responseReason;
CURLcode code;
curl_easy_request_w->getResult(&code);
if (code == CURLE_OK)
{
curl_easy_request_w->getinfo(CURLINFO_RESPONSE_CODE, &responseCode);
//*TODO: get reason from first line of mHeaderOutput
}
else
{
responseCode = 499;
responseReason = AICurlInterface::strerror(code);
curl_easy_request_w->setopt(CURLOPT_FRESH_CONNECT, TRUE);
send_buffer_events_to(mResponder.get());
}
if (mResponder)
{
mResponder->completedRaw(responseCode, responseReason, sChannels, mOutput);
mResponder = NULL;
}
resetState(curl_easy_request_w);
// Add extra headers.
curl_easy_request_w->addHeaders(headers);
}
//-----------------------------------------------------------------------------

View File

@@ -36,6 +36,7 @@
#include <set>
#include <stdexcept>
#include <boost/intrusive_ptr.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/utility.hpp>
#include "llpreprocessor.h"
@@ -48,12 +49,51 @@
#undef CURLOPT_DNS_USE_GLOBAL_CACHE
#define CURLOPT_DNS_USE_GLOBAL_CACHE do_not_use_CURLOPT_DNS_USE_GLOBAL_CACHE
#include "stdtypes.h" // U32
#include "lliopipe.h" // LLIOPipe::buffer_ptr_t
#include "stdtypes.h" // U16, S32, U32, F64
#include "llatomic.h" // LLAtomicU32
#include "aithreadsafe.h"
#include "llhttpstatuscodes.h"
#include "llhttpclient.h"
// Debug Settings.
extern bool gNoVerifySSLCert;
class LLSD;
class LLBufferArray;
class LLChannelDescriptors;
class AIHTTPTimeoutPolicy;
// Some pretty printing for curl easy handle related things:
// Print the lock object related to the current easy handle in every debug output.
#ifdef CWDEBUG
#include <libcwd/buf2str.h>
#include <sstream>
#define DoutCurl(x) do { \
using namespace libcwd; \
std::ostringstream marker; \
marker << (void*)this->get_lockobj(); \
libcw_do.push_marker(); \
libcw_do.marker().assign(marker.str().data(), marker.str().size()); \
libcw_do.inc_indent(2); \
Dout(dc::curl, x); \
libcw_do.dec_indent(2); \
libcw_do.pop_marker(); \
} while(0)
#define DoutCurlEntering(x) do { \
using namespace libcwd; \
std::ostringstream marker; \
marker << (void*)this->get_lockobj(); \
libcw_do.push_marker(); \
libcw_do.marker().assign(marker.str().data(), marker.str().size()); \
libcw_do.inc_indent(2); \
DoutEntering(dc::curl, x); \
libcw_do.dec_indent(2); \
libcw_do.pop_marker(); \
} while(0)
#else // !CWDEBUG
#define DoutCurl(x) Dout(dc::curl, x << " [" << (void*)this->get_lockobj() << ']')
#define DoutCurlEntering(x) DoutEntering(dc::curl, x << " [" << (void*)this->get_lockobj() << ']')
#endif // CWDEBUG
//-----------------------------------------------------------------------------
// Exceptions.
@@ -76,40 +116,35 @@ class AICurlNoMultiHandle : public AICurlError {
AICurlNoMultiHandle(std::string const& message) : AICurlError(message) { }
};
class AICurlNoBody : public AICurlError {
public:
AICurlNoBody(std::string const& message) : AICurlError(message) { }
};
// End Exceptions.
//-----------------------------------------------------------------------------
// Things defined in this namespace are called from elsewhere in the viewer code.
namespace AICurlInterface {
// Output parameter of AICurlPrivate::CurlEasyRequest::getResult.
// Only used by LLXMLRPCTransaction::Impl.
struct TransferInfo {
TransferInfo() : mSizeDownload(0.0), mTotalTime(0.0), mSpeedDownload(0.0) { }
F64 mSizeDownload;
F64 mTotalTime;
F64 mSpeedDownload;
};
//-----------------------------------------------------------------------------
// Global functions.
// Called to handle changes in Debug Settings.
bool handleCurlConcurrentConnections(LLSD const& newvalue);
bool handleNoVerifySSLCert(LLSD const& newvalue);
// Called once at start of application (from newview/llappviewer.cpp by main thread (before threads are created)),
// with main purpose to initialize curl.
void initCurl(void (*)(void) = NULL);
// Called once at start of application (from LLAppViewer::initThreads), starts AICurlThread.
void startCurlThread(U32 CurlConcurrentConnections);
void startCurlThread(U32 CurlConcurrentConnections, bool NoVerifySSLCert);
// Called once at end of application (from newview/llappviewer.cpp by main thread),
// with purpose to stop curl threads, free curl resources and deinitialize curl.
void cleanupCurl(void);
// Called from indra/llmessage/llurlrequest.cpp to print debug output regarding
// an error code returned by EasyRequest::getResult.
// Just returns curl_easy_strerror(errorcode).
std::string strerror(CURLcode errorcode);
// Called from indra/newview/llfloaterabout.cpp for the About floater, and
// from newview/llappviewer.cpp in behalf of debug output.
// Just returns curl_version().
@@ -123,105 +158,11 @@ void setCAFile(std::string const& file);
// Can be used to set the path to the Certificate Authority file.
void setCAPath(std::string const& file);
//-----------------------------------------------------------------------------
// Global classes.
// Responder - base class for Request::get* and Request::post API.
//
// The life cycle of classes derived from this class is as follows:
// They are allocated with new on the line where get(), getByteRange() or post() is called,
// and the pointer to the allocated object is then put in a reference counting ResponderPtr.
// This ResponderPtr is passed to CurlResponderBuffer::prepRequest which stores it in its
// member mResponder. Hence, the life time of a Responder is never longer than its
// associated CurlResponderBuffer, however, if everything works correctly, then normally a
// responder is deleted in CurlResponderBuffer::removed_from_multi_handle by setting
// mReponder to NULL.
//
// Note that the lifetime of CurlResponderBuffer is (a bit) shorter than the associated
// CurlEasyRequest (because of the order of base classes of ThreadSafeBufferedCurlEasyRequest)
// and the callbacks, as set by prepRequest, only use those two.
// A callback locks the CurlEasyRequest before actually making the callback, and the
// destruction of CurlResponderBuffer also first locks the CurlEasyRequest, and then revokes
// the callbacks. This assures that a Responder is never used when the objects it uses are
// destructed. Also, if any of those are destructed then the Responder is automatically
// destructed too.
//
class Responder {
protected:
Responder(void);
virtual ~Responder();
private:
// Associated URL, used for debug output.
std::string mURL;
public:
// Called to set the URL of the current request for this Responder,
// used only when printing debug output regarding activity of the Responder.
void setURL(std::string const& url);
public:
// Called from LLHTTPClientURLAdaptor::complete():
// Derived classes can override this to get the HTML header that was received, when the message is completed.
// The default does nothing.
virtual void completedHeader(U32 status, std::string const& reason, LLSD const& content);
// Derived classes can override this to get the raw data of the body of the HTML message that was received.
// The default is to interpret the content as LLSD and call completed().
virtual void completedRaw(U32 status, std::string const& reason, LLChannelDescriptors const& channels, LLIOPipe::buffer_ptr_t const& buffer);
// Called from LLHTTPClient request calls, if an error occurs even before we can call one of the above.
// It calls completed() with a fake status U32_MAX, as that is what some derived clients expect (bad design).
// This means that if a derived class overrides completedRaw() it now STILL has to override completed() to catch this error.
void fatalError(std::string const& reason);
// A derived class should return true if curl should follow redirections.
// The default is not to follow redirections.
virtual bool followRedir(void) { return false; }
protected:
// ... or, derived classes can override this to get the LLSD content when the message is completed.
// The default is to call result() (or errorWithContent() in case of a HTML status indicating an error).
virtual void completed(U32 status, std::string const& reason, LLSD const& content);
// ... or, derived classes can override this to received the content of a body upon success.
// The default does nothing.
virtual void result(LLSD const& content);
// Derived classes can override this to get informed when a bad HTML status code is received.
// The default calls error().
virtual void errorWithContent(U32 status, std::string const& reason, LLSD const& content);
// ... or, derived classes can override this to get informed when a bad HTML statis code is received.
// The default prints the error to llinfos.
virtual void error(U32 status, std::string const& reason);
public:
// Called from LLSDMessage::ResponderAdapter::listener.
// LLSDMessage::ResponderAdapter is a hack, showing among others by fact that these functions need to be public.
void pubErrorWithContent(U32 status, std::string const& reason, LLSD const& content) { errorWithContent(status, reason, content); }
void pubResult(LLSD const& content) { result(content); }
private:
// Used by ResponderPtr. Object is deleted when reference count reaches zero.
LLAtomicU32 mReferenceCount;
friend void intrusive_ptr_add_ref(Responder* p); // Called by boost::intrusive_ptr when a new copy of a boost::intrusive_ptr<Responder> is made.
friend void intrusive_ptr_release(Responder* p); // Called by boost::intrusive_ptr when a boost::intrusive_ptr<Responder> is destroyed.
// This function must delete the Responder object when the reference count reaches zero.
};
// A Responder is passed around as ResponderPtr, which causes it to automatically
// destruct when there are no pointers left pointing to it.
typedef boost::intrusive_ptr<Responder> ResponderPtr;
} // namespace AICurlInterface
// Forward declaration (see aicurlprivate.h).
namespace AICurlPrivate {
class CurlEasyRequest;
class BufferedCurlEasyRequest;
} // namespace AICurlPrivate
// Define access types (_crat = Const Read Access Type, _rat = Read Access Type, _wat = Write Access Type).
@@ -229,13 +170,13 @@ namespace AICurlPrivate {
// AICurlEasyRequest h1; // Create easy handle.
// AICurlEasyRequest h2(h1); // Make lightweight copies.
// AICurlEasyRequest_wat h2_w(*h2); // Lock and obtain write access to the easy handle.
// Use *h2_w, which is a reference to the locked CurlEasyRequest instance.
// Use *h2_w, which is a reference to the locked BufferedCurlEasyRequest instance.
// Note: As it is not allowed to use curl easy handles in any way concurrently,
// read access would at most give access to a CURL const*, which will turn out
// to be completely useless; therefore it is sufficient and efficient to use
// an AIThreadSafeSimple and it's unlikely that AICurlEasyRequest_rat will be used.
typedef AIAccessConst<AICurlPrivate::CurlEasyRequest> AICurlEasyRequest_rat;
typedef AIAccess<AICurlPrivate::CurlEasyRequest> AICurlEasyRequest_wat;
typedef AIAccessConst<AICurlPrivate::BufferedCurlEasyRequest> AICurlEasyRequest_rat;
typedef AIAccess<AICurlPrivate::BufferedCurlEasyRequest> AICurlEasyRequest_wat;
// Events generated by AICurlPrivate::CurlEasyHandle.
struct AICurlEasyHandleEvents {
@@ -264,32 +205,38 @@ typedef LLPointer<AIPostField> AIPostFieldPtr;
#include "aicurlprivate.h"
// AICurlPrivate::CurlEasyRequestPtr, a boost::intrusive_ptr, is no more threadsafe than a
// AICurlPrivate::BufferedCurlEasyRequestPtr, a boost::intrusive_ptr, is no more threadsafe than a
// builtin type, but wrapping it in AIThreadSafe is obviously not going to help here.
// Therefore we use the following trick: we wrap CurlEasyRequestPtr too, and only allow
// Therefore we use the following trick: we wrap BufferedCurlEasyRequestPtr too, and only allow
// read accesses on it.
// AICurlEasyRequest: a thread safe, reference counting, auto-cleaning curl easy handle.
// AICurlEasyRequest: a thread safe, reference counting, buffered, auto-cleaning curl easy handle.
class AICurlEasyRequest {
public:
private:
// Use AICurlEasyRequestStateMachine, not AICurlEasyRequest.
friend class AICurlEasyRequestStateMachine;
// Initial construction is allowed (thread-safe).
// Note: If ThreadSafeCurlEasyRequest() throws then the memory allocated is still freed.
// 'new' never returned however and neither the constructor nor destructor of mCurlEasyRequest is called in this case.
// Note: If ThreadSafeBufferedCurlEasyRequest() throws then the memory allocated is still freed.
// 'new' never returned however and neither the constructor nor destructor of mBufferedCurlEasyRequest is called in this case.
// This might throw AICurlNoEasyHandle.
AICurlEasyRequest(bool buffered) :
mCurlEasyRequest(buffered ? new AICurlPrivate::ThreadSafeBufferedCurlEasyRequest : new AICurlPrivate::ThreadSafeCurlEasyRequest) { }
AICurlEasyRequest(AICurlEasyRequest const& orig) : mCurlEasyRequest(orig.mCurlEasyRequest) { }
AICurlEasyRequest(void) :
mBufferedCurlEasyRequest(new AICurlPrivate::ThreadSafeBufferedCurlEasyRequest) { }
public:
// Used for storing this object in a standard container (see MultiHandle::add_easy_request).
AICurlEasyRequest(AICurlEasyRequest const& orig) : mBufferedCurlEasyRequest(orig.mBufferedCurlEasyRequest) { }
// For the rest, only allow read operations.
AIThreadSafeSimple<AICurlPrivate::CurlEasyRequest>& operator*(void) const { llassert(mCurlEasyRequest.get()); return *mCurlEasyRequest; }
AIThreadSafeSimple<AICurlPrivate::CurlEasyRequest>* operator->(void) const { llassert(mCurlEasyRequest.get()); return mCurlEasyRequest.get(); }
AIThreadSafeSimple<AICurlPrivate::CurlEasyRequest>* get(void) const { return mCurlEasyRequest.get(); }
AIThreadSafeSimple<AICurlPrivate::BufferedCurlEasyRequest>& operator*(void) const { llassert(mBufferedCurlEasyRequest.get()); return *mBufferedCurlEasyRequest; }
AIThreadSafeSimple<AICurlPrivate::BufferedCurlEasyRequest>* operator->(void) const { llassert(mBufferedCurlEasyRequest.get()); return mBufferedCurlEasyRequest.get(); }
AIThreadSafeSimple<AICurlPrivate::BufferedCurlEasyRequest>* get(void) const { return mBufferedCurlEasyRequest.get(); }
// Returns true if this object points to the same CurlEasyRequest object.
bool operator==(AICurlEasyRequest const& cer) const { return mCurlEasyRequest == cer.mCurlEasyRequest; }
// Returns true if this object points to the same BufferedCurlEasyRequest object.
bool operator==(AICurlEasyRequest const& cer) const { return mBufferedCurlEasyRequest == cer.mBufferedCurlEasyRequest; }
// Returns true if this object points to a different CurlEasyRequest object.
bool operator!=(AICurlEasyRequest const& cer) const { return mCurlEasyRequest != cer.mCurlEasyRequest; }
// Returns true if this object points to a different BufferedCurlEasyRequest object.
bool operator!=(AICurlEasyRequest const& cer) const { return mBufferedCurlEasyRequest != cer.mBufferedCurlEasyRequest; }
// Queue this request for insertion in the multi session.
void addRequest(void);
@@ -297,12 +244,9 @@ class AICurlEasyRequest {
// Queue a command to remove this request from the multi session (or cancel a queued command to add it).
void removeRequest(void);
// Returns true when this AICurlEasyRequest wraps a AICurlPrivate::ThreadSafeBufferedCurlEasyRequest.
bool isBuffered(void) const { return mCurlEasyRequest->isBuffered(); }
private:
// The actual pointer to the ThreadSafeCurlEasyRequest instance.
AICurlPrivate::CurlEasyRequestPtr mCurlEasyRequest;
// The actual pointer to the ThreadSafeBufferedCurlEasyRequest instance.
AICurlPrivate::BufferedCurlEasyRequestPtr mBufferedCurlEasyRequest;
private:
// Assignment would not be thread-safe; we may create this object and read from it.
@@ -312,7 +256,7 @@ class AICurlEasyRequest {
public:
// Instead of assignment, it might be helpful to use swap.
void swap(AICurlEasyRequest& cer) { mCurlEasyRequest.swap(cer.mCurlEasyRequest); }
void swap(AICurlEasyRequest& cer) { mBufferedCurlEasyRequest.swap(cer.mBufferedCurlEasyRequest); }
public:
// The more exotic member functions of this class, to deal with passing this class
@@ -320,26 +264,18 @@ class AICurlEasyRequest {
// For "internal use" only; don't use things from AICurlPrivate yourself.
// It's thread-safe to give read access the underlaying boost::intrusive_ptr.
// It's not OK to then call get() on that and store the AICurlPrivate::ThreadSafeCurlEasyRequest* separately.
AICurlPrivate::CurlEasyRequestPtr const& get_ptr(void) const { return mCurlEasyRequest; }
// It's not OK to then call get() on that and store the AICurlPrivate::ThreadSafeBufferedCurlEasyRequest* separately.
AICurlPrivate::BufferedCurlEasyRequestPtr const& get_ptr(void) const { return mBufferedCurlEasyRequest; }
// If we have a correct (with regard to reference counting) AICurlPrivate::CurlEasyRequestPtr,
// If we have a correct (with regard to reference counting) AICurlPrivate::BufferedCurlEasyRequestPtr,
// then it's OK to construct a AICurlEasyRequest from it.
// Note that the external AICurlPrivate::CurlEasyRequestPtr needs its own locking, because
// Note that the external AICurlPrivate::BufferedCurlEasyRequestPtr needs its own locking, because
// it's not thread-safe in itself.
AICurlEasyRequest(AICurlPrivate::CurlEasyRequestPtr const& ptr) : mCurlEasyRequest(ptr) { }
AICurlEasyRequest(AICurlPrivate::BufferedCurlEasyRequestPtr const& ptr) : mBufferedCurlEasyRequest(ptr) { }
// This one is obviously dangerous. It's for use only in MultiHandle::check_run_count.
// See also the long comment in CurlEasyRequest::finalizeRequest with regard to CURLOPT_PRIVATE.
explicit AICurlEasyRequest(AICurlPrivate::ThreadSafeCurlEasyRequest* ptr) : mCurlEasyRequest(ptr) { }
};
// Write Access Type for the buffer.
struct AICurlResponderBuffer_wat : public AIAccess<AICurlPrivate::CurlResponderBuffer> {
explicit AICurlResponderBuffer_wat(AICurlPrivate::ThreadSafeBufferedCurlEasyRequest& lockobj) :
AIAccess<AICurlPrivate::CurlResponderBuffer>(lockobj) { }
AICurlResponderBuffer_wat(AIThreadSafeSimple<AICurlPrivate::CurlEasyRequest>& lockobj) :
AIAccess<AICurlPrivate::CurlResponderBuffer>(static_cast<AICurlPrivate::ThreadSafeBufferedCurlEasyRequest&>(lockobj)) { }
// See also the long comment in BufferedCurlEasyRequest::finalizeRequest with regard to CURLOPT_PRIVATE.
explicit AICurlEasyRequest(AICurlPrivate::ThreadSafeBufferedCurlEasyRequest* ptr) : mBufferedCurlEasyRequest(ptr) { }
};
#define AICurlPrivate DONTUSE_AICurlPrivate

View File

@@ -30,6 +30,7 @@
#include "linden_common.h"
#include "aicurleasyrequeststatemachine.h"
#include "aihttptimeoutpolicy.h"
#include "llcontrol.h"
enum curleasyrequeststatemachine_state_type {
@@ -61,8 +62,8 @@ void AICurlEasyRequestStateMachine::initialize_impl(void)
{
{
AICurlEasyRequest_wat curlEasyRequest_w(*mCurlEasyRequest);
llassert(curlEasyRequest_w->is_finalized()); // Call finalizeRequest(url) before calling run().
curlEasyRequest_w->send_events_to(this);
llassert(curlEasyRequest_w->is_finalized()); // Call finalizeRequest() before calling run().
curlEasyRequest_w->send_handle_events_to(this);
}
mAdded = false;
mTimedOut = false;
@@ -87,6 +88,9 @@ void AICurlEasyRequestStateMachine::finished(AICurlEasyRequest_wat&)
// CURL-THREAD
void AICurlEasyRequestStateMachine::removed_from_multi_handle(AICurlEasyRequest_wat&)
{
llassert(mFinished || mTimedOut); // If we neither finished nor timed out, then why is this being removed?
// Note that allowing this would cause an assertion later on for removing
// a BufferedCurlEasyRequest with a still active Responder.
set_state(mFinished ? AICurlEasyRequestStateMachine_removed_after_finished : AICurlEasyRequestStateMachine_removed);
}
@@ -116,13 +120,15 @@ void AICurlEasyRequestStateMachine::multiplex_impl(void)
// 3) AICurlEasyRequestStateMachine_finished (running)
// 4) AICurlEasyRequestStateMachine_removed_after_finished (running)
// Set an inactivity timer.
// This shouldn't really be necessary, except in the case of a bug
// in libcurl; but lets be sure and set a timer for inactivity.
static LLCachedControl<F32> CurlRequestTimeOut("CurlRequestTimeOut", 40.f);
mTimer = new AIPersistentTimer; // Do not delete timer upon expiration.
mTimer->setInterval(CurlRequestTimeOut);
mTimer->run(this, AICurlEasyRequestStateMachine_timedOut, false, false);
if (mTotalDelayTimeout > 0.f)
{
// Set an inactivity timer.
// This shouldn't really be necessary, except in the case of a bug
// in libcurl; but lets be sure and set a timer for inactivity.
mTimer = new AIPersistentTimer; // Do not delete timer upon expiration.
mTimer->setInterval(mTotalDelayTimeout);
mTimer->run(this, AICurlEasyRequestStateMachine_timedOut, false, false);
}
break;
}
case AICurlEasyRequestStateMachine_added:
@@ -161,17 +167,16 @@ void AICurlEasyRequestStateMachine::multiplex_impl(void)
// Only do this once.
mHandled = true;
// Stop the timer. Note that it's the main thread that generates timer events,
// so we're certain that there will be no time out anymore if we reach this point.
mTimer->abort();
if (mTimer)
{
// Stop the timer. Note that it's the main thread that generates timer events,
// so we're certain that there will be no time out anymore if we reach this point.
mTimer->abort();
}
// The request finished and either data or an error code is available.
if (mBuffered)
{
AICurlEasyRequest_wat easy_request_w(*mCurlEasyRequest);
AICurlResponderBuffer_wat buffered_easy_request_w(*mCurlEasyRequest);
buffered_easy_request_w->processOutput(easy_request_w);
}
AICurlEasyRequest_wat easy_request_w(*mCurlEasyRequest);
easy_request_w->processOutput();
}
if (current_state == AICurlEasyRequestStateMachine_finished)
@@ -187,11 +192,10 @@ void AICurlEasyRequestStateMachine::multiplex_impl(void)
case AICurlEasyRequestStateMachine_removed:
{
// The request was removed from the multi handle.
if (mBuffered && mTimedOut)
if (mTimedOut)
{
AICurlEasyRequest_wat easy_request_w(*mCurlEasyRequest);
AICurlResponderBuffer_wat buffered_easy_request_w(*mCurlEasyRequest);
buffered_easy_request_w->timed_out();
easy_request_w->timed_out();
}
// We're done. If we timed out, abort -- or else the application will
@@ -226,20 +230,30 @@ void AICurlEasyRequestStateMachine::finish_impl(void)
// Revoke callbacks.
{
AICurlEasyRequest_wat curl_easy_request_w(*mCurlEasyRequest);
curl_easy_request_w->send_events_to(NULL);
curl_easy_request_w->send_buffer_events_to(NULL);
curl_easy_request_w->send_handle_events_to(NULL);
curl_easy_request_w->revokeCallbacks();
}
// Note that even if the timer expired, it wasn't deleted because we used AIPersistentTimer; so mTimer is still valid.
// Stop the timer, if it's still running.
if (!mHandled)
mTimer->abort();
if (mTimer)
{
// Note that even if the timer expired, it wasn't deleted because we used AIPersistentTimer; so mTimer is still valid.
// Stop the timer, if it's still running.
if (!mHandled)
mTimer->abort();
}
// Auto clean up ourselves.
kill();
}
AICurlEasyRequestStateMachine::AICurlEasyRequestStateMachine(bool buffered) : mBuffered(buffered), mCurlEasyRequest(buffered)
AICurlEasyRequestStateMachine::AICurlEasyRequestStateMachine(void) :
mTimer(NULL), mTotalDelayTimeout(AIHTTPTimeoutPolicy::getDebugSettingsCurlTimeout().getTotalDelay())
{
Dout(dc::statemachine, "Calling AICurlEasyRequestStateMachine(" << (buffered ? "true" : "false") << ") [" << (void*)this << "] [" << (void*)mCurlEasyRequest.get() << "]");
Dout(dc::statemachine, "Calling AICurlEasyRequestStateMachine(void) [" << (void*)this << "] [" << (void*)mCurlEasyRequest.get() << "]");
}
void AICurlEasyRequestStateMachine::setTotalDelayTimeout(F32 totalDelayTimeout)
{
mTotalDelayTimeout = totalDelayTimeout;
}
AICurlEasyRequestStateMachine::~AICurlEasyRequestStateMachine()

View File

@@ -40,7 +40,7 @@
// Before calling cersm.run() initialize the object (cersm) as follows:
//
// AICurlEasyRequest_wat cersm_w(cersm);
// cersm_w->setopt(...); // etc, see the interface of AICurlPrivate::CurlEasyRequest and it's base class AICurlPrivate::CurlEasyHandle.
// cersm_w->setopt(...); // etc, see the interface of AICurlPrivate::CurlEasyRequest.
//
// When the state machine finishes, call aborted() to check
// whether or not the statemachine succeeded in fetching
@@ -52,18 +52,22 @@
// Construction of a AICurlEasyRequestStateMachine might throw AICurlNoEasyHandle.
class AICurlEasyRequestStateMachine : public AIStateMachine, public AICurlEasyHandleEvents {
public:
AICurlEasyRequestStateMachine(bool buffered);
AICurlEasyRequestStateMachine(void);
// Transparent access.
AICurlEasyRequest mCurlEasyRequest;
private:
bool mBuffered; // Argument used for construction of mCurlEasyRequest.
bool mAdded; // Set when the last command to the curl thread was to add the request.
bool mTimedOut; // Set if the expiration timer timed out.
bool mFinished; // Set by the curl thread to signal it finished.
bool mHandled; // Set when we processed the received data.
AITimer* mTimer; // Expiration timer.
bool mAdded; // Set when the last command to the curl thread was to add the request.
bool mTimedOut; // Set if the expiration timer timed out.
bool mFinished; // Set by the curl thread to signal it finished.
bool mHandled; // Set when we processed the received data.
AITimer* mTimer; // Expiration timer.
F32 mTotalDelayTimeout; // The time out value for mTimer.
public:
// Called to set a specific time out, instead of the default one.
void setTotalDelayTimeout(F32 totalDelayTimeout);
protected:
// AICurlEasyRequest Events.

View File

@@ -33,9 +33,83 @@
#include <sstream>
#include "llatomic.h"
#include "llrefcount.h"
class AIHTTPHeaders;
class AIHTTPTimeoutPolicy;
class AICurlEasyRequestStateMachine;
namespace AICurlPrivate {
namespace curlthread { class MultiHandle; }
class CurlEasyRequest;
class ThreadSafeBufferedCurlEasyRequest;
namespace curlthread {
class MultiHandle;
// A class that keeps track of timeout administration per connection.
class HTTPTimeout : public LLRefCount {
private:
AIHTTPTimeoutPolicy const* mPolicy; // A pointer to the used timeout policy.
std::vector<U32> mBuckets; // An array with the number of bytes transfered in each second.
U16 mBucket; // The bucket corresponding to mLastSecond.
bool mNothingReceivedYet; // Set when created, reset when the HTML reply header from the server is received.
bool mLowSpeedOn; // Set while uploading or downloading data.
bool mUploadFinished; // Used to keep track of whether upload_finished was called yet.
S32 mLastSecond; // The time at which lowspeed() was last called, in seconds since mLowSpeedClock.
U32 mTotalBytes; // The sum of all bytes in mBuckets.
U64 mLowSpeedClock; // Clock count at which low speed detection (re)started.
U64 mStalled; // The clock count at which this transaction is considered to be stalling if nothing is transfered anymore.
public:
static F64 const sClockWidth; // Time between two clock ticks in seconds.
static U64 sClockCount; // Clock count used as 'now' during one loop of the main loop.
#if defined(CWDEBUG) || defined(DEBUG_CURLIO)
ThreadSafeBufferedCurlEasyRequest* mLockObj;
#endif
public:
HTTPTimeout(AIHTTPTimeoutPolicy const* policy, ThreadSafeBufferedCurlEasyRequest* lock_obj) :
mPolicy(policy), mNothingReceivedYet(true), mLowSpeedOn(false), mUploadFinished(false), mStalled((U64)-1)
#if defined(CWDEBUG) || defined(DEBUG_CURLIO)
, mLockObj(lock_obj)
#endif
{ }
// Called after sending all headers, when body data is written the first time.
void connected(void);
// Called when everything we had to send to the server has been sent.
void upload_finished(void);
// Called when data is sent. Returns true if transfer timed out.
bool data_sent(size_t n);
// Called when data is received. Returns true if transfer timed out.
bool data_received(size_t n);
// Called immediately before done() after curl finished, with code.
void done(AICurlEasyRequest_wat const& curlEasyRequest_w, CURLcode code);
// Accessor.
bool has_stalled(void) const { return mStalled < sClockCount; }
// Called from CurlResponderBuffer::processOutput if a timeout occurred.
void print_diagnostics(CurlEasyRequest const* curl_easy_request);
#if defined(CWDEBUG) || defined(DEBUG_CURLIO)
void* get_lockobj(void) const { return mLockObj; }
#endif
private:
// (Re)start low speed transer rate detection.
void reset_lowspeed(void);
// Common low speed detection, Called from data_sent or data_received.
bool lowspeed(size_t bytes);
};
} // namespace curlthread
struct Stats {
static LLAtomicU32 easy_calls;
@@ -56,11 +130,8 @@ bool curlThreadIsRunning(void);
void wakeUpCurlThread(void);
void stopCurlThread(void);
class ThreadSafeCurlEasyRequest;
class ThreadSafeBufferedCurlEasyRequest;
#define DECLARE_SETOPT(param_type) \
CURLcode setopt(CURLoption option, param_type parameter)
CURLcode setopt(CURLoption option, param_type parameter)
// This class wraps CURL*'s.
// It guarantees that a pointer is cleaned up when no longer needed, as required by libcurl.
@@ -112,20 +183,20 @@ class CurlEasyHandle : public boost::noncopyable, protected AICurlEasyHandleEven
// Extract information from a curl handle.
private:
CURLcode getinfo_priv(CURLINFO info, void* data);
CURLcode getinfo_priv(CURLINFO info, void* data) const;
public:
// The rest are inlines to provide some type-safety.
CURLcode getinfo(CURLINFO info, char** data) { return getinfo_priv(info, data); }
CURLcode getinfo(CURLINFO info, curl_slist** data) { return getinfo_priv(info, data); }
CURLcode getinfo(CURLINFO info, double* data) { return getinfo_priv(info, data); }
CURLcode getinfo(CURLINFO info, long* data) { return getinfo_priv(info, data); }
CURLcode getinfo(CURLINFO info, char** data) const { return getinfo_priv(info, data); }
CURLcode getinfo(CURLINFO info, curl_slist** data) const { return getinfo_priv(info, data); }
CURLcode getinfo(CURLINFO info, double* data) const { return getinfo_priv(info, data); }
CURLcode getinfo(CURLINFO info, long* data) const { return getinfo_priv(info, data); }
#ifdef __LP64__ // sizeof(long) > sizeof(int) ?
// Overload for integer types that are too small (libcurl demands a long).
CURLcode getinfo(CURLINFO info, S32* data) { long ldata; CURLcode res = getinfo_priv(info, &ldata); *data = static_cast<S32>(ldata); return res; }
CURLcode getinfo(CURLINFO info, U32* data) { long ldata; CURLcode res = getinfo_priv(info, &ldata); *data = static_cast<U32>(ldata); return res; }
CURLcode getinfo(CURLINFO info, S32* data) const { long ldata; CURLcode res = getinfo_priv(info, &ldata); *data = static_cast<S32>(ldata); return res; }
CURLcode getinfo(CURLINFO info, U32* data) const { long ldata; CURLcode res = getinfo_priv(info, &ldata); *data = static_cast<U32>(ldata); return res; }
#else // sizeof(long) == sizeof(int)
CURLcode getinfo(CURLINFO info, S32* data) { return getinfo_priv(info, reinterpret_cast<long*>(data)); }
CURLcode getinfo(CURLINFO info, U32* data) { return getinfo_priv(info, reinterpret_cast<long*>(data)); }
CURLcode getinfo(CURLINFO info, S32* data) const { return getinfo_priv(info, reinterpret_cast<long*>(data)); }
CURLcode getinfo(CURLINFO info, U32* data) const { return getinfo_priv(info, reinterpret_cast<long*>(data)); }
#endif
// Perform a file transfer (blocking).
@@ -142,7 +213,7 @@ class CurlEasyHandle : public boost::noncopyable, protected AICurlEasyHandleEven
private:
CURL* mEasyHandle;
CURLM* mActiveMultiHandle;
char* mErrorBuffer;
mutable char* mErrorBuffer;
AIPostFieldPtr mPostField; // This keeps the POSTFIELD data alive for as long as the easy handle exists.
bool mQueuedForRemoval; // Set if the easy handle is (probably) added to the multi handle, but is queued for removal.
#ifdef SHOW_ASSERT
@@ -170,7 +241,7 @@ class CurlEasyHandle : public boost::noncopyable, protected AICurlEasyHandleEven
private:
// Call this prior to every curl_easy function whose return value is passed to check_easy_code.
void setErrorBuffer(void);
void setErrorBuffer(void) const;
static void handle_easy_error(CURLcode code);
@@ -202,19 +273,20 @@ class CurlEasyHandle : public boost::noncopyable, protected AICurlEasyHandleEven
// the data exchange. Use getResult() to determine if an error occurred.
//
// Note that the life cycle of a CurlEasyRequest is controlled by AICurlEasyRequest:
// a CurlEasyRequest is only ever created as base class of a ThreadSafeCurlEasyRequest,
// a CurlEasyRequest is only ever created as base class of a ThreadSafeBufferedCurlEasyRequest,
// which is only created by creating a AICurlEasyRequest. When the last copy of such
// AICurlEasyRequest is deleted, then also the ThreadSafeCurlEasyRequest is deleted
// AICurlEasyRequest is deleted, then also the ThreadSafeBufferedCurlEasyRequest is deleted
// and the CurlEasyRequest destructed.
class CurlEasyRequest : public CurlEasyHandle {
private:
void setPost_raw(S32 size, char const* data);
void setPost_raw(U32 size, char const* data);
public:
void setPost(S32 size) { setPost_raw(size, NULL); }
void setPost(AIPostFieldPtr const& postdata, S32 size);
void setPost(char const* data, S32 size) { setPost(new AIPostField(data), size); }
void setPost(U32 size) { setPost_raw(size, NULL); }
void setPost(AIPostFieldPtr const& postdata, U32 size);
void setPost(char const* data, U32 size) { setPost(new AIPostField(data), size); }
void setoptString(CURLoption option, std::string const& value);
void addHeader(char const* str);
void addHeaders(AIHTTPHeaders const& headers);
private:
// Callback stubs.
@@ -241,61 +313,106 @@ class CurlEasyRequest : public CurlEasyHandle {
// Call this if the set callbacks are about to be invalidated.
void revokeCallbacks(void);
protected:
// Reset everything to the state it was in when this object was just created.
// Called by BufferedCurlEasyRequest::resetState.
void resetState(void);
private:
// Called from applyDefaultOptions.
void applyProxySettings(void);
// Used in applyProxySettings.
// Used in applyDefaultOptions.
static CURLcode curlCtxCallback(CURL* curl, void* sslctx, void* parm);
// Called from get_timeout_object and httptimeout.
void create_timeout_object(ThreadSafeBufferedCurlEasyRequest* lockobj);
public:
// Set default options that we want applied to all curl easy handles.
void applyDefaultOptions(void);
// Prepare the request for adding it to a multi session, or calling perform.
// This actually adds the headers that were collected with addHeader.
void finalizeRequest(std::string const& url);
void finalizeRequest(std::string const& url, AIHTTPTimeoutPolicy const& policy, AICurlEasyRequestStateMachine* state_machine);
// Called by MultiHandle::check_run_count() to store result code that is returned by getResult.
void store_result(CURLcode result) { mResult = result; }
// Last second initialization. Called from MultiHandle::add_easy_request.
void set_timeout_opts(void);
// Called by MultiHandle::check_run_count() when the curl easy handle is done.
void done(AICurlEasyRequest_wat& curl_easy_request_w) { finished(curl_easy_request_w); }
public:
// Called by MultiHandle::finish_easy_request() to store result code that is returned by getResult.
void storeResult(CURLcode result) { mResult = result; }
// Called by MultiHandle::finish_easy_request() when the curl easy handle is done.
void done(AICurlEasyRequest_wat& curl_easy_request_w, CURLcode result)
{
if (mTimeout)
{
// Update timeout administration.
mTimeout->done(curl_easy_request_w, result);
}
finished(curl_easy_request_w);
}
// Called by in case of an error.
void print_diagnostics(CURLcode code);
// Called by MultiHandle::check_run_count() to fill info with the transfer info.
void getTransferInfo(AICurlInterface::TransferInfo* info);
void getTransferInfo(AITransferInfo* info);
// If result != CURLE_FAILED_INIT then also info was filled.
void getResult(CURLcode* result, AICurlInterface::TransferInfo* info = NULL);
void getResult(CURLcode* result, AITransferInfo* info = NULL);
// For debugging purposes.
void print_curl_timings(void) const;
private:
curl_slist* mHeaders;
bool mRequestFinalized;
AICurlEasyHandleEvents* mEventsTarget;
CURLcode mResult;
AICurlEasyHandleEvents* mHandleEventsTarget;
CURLcode mResult; //AIFIXME: this does not belong in the request object, but belongs in the response object.
private:
// This class may only be created by constructing a ThreadSafeCurlEasyRequest.
friend class ThreadSafeCurlEasyRequest;
AIHTTPTimeoutPolicy const* mTimeoutPolicy;
std::string mLowercaseHostname; // Lowercase hostname (canonicalized) extracted from the url.
LLPointer<curlthread::HTTPTimeout> mTimeout;// Timeout administration object associated with last created CurlSocketInfo.
bool mTimeoutIsOrphan; // Set to true when mTimeout is not (yet) associated with a CurlSocketInfo.
#if defined(CWDEBUG) || defined(DEBUG_CURLIO)
public:
bool mDebugIsHeadOrGetMethod;
#endif
public:
// These two are only valid after finalizeRequest.
AIHTTPTimeoutPolicy const* getTimeoutPolicy(void) const { return mTimeoutPolicy; }
std::string const& getLowercaseHostname(void) const { return mLowercaseHostname; }
// Called by CurlSocketInfo to allow access to the last (after a redirect) HTTPTimeout object related to this request.
// This creates mTimeout (unless mTimeoutIsOrphan is set in which case it adopts the orphan).
LLPointer<curlthread::HTTPTimeout>& get_timeout_object(ThreadSafeBufferedCurlEasyRequest* lockobj);
// Accessor for mTimeout with optional creation of orphaned object (if lockobj != NULL).
LLPointer<curlthread::HTTPTimeout>& httptimeout(ThreadSafeBufferedCurlEasyRequest* lockobj = NULL) { if (lockobj && !mTimeout) create_timeout_object(lockobj); return mTimeout; }
// Return true if no data has been received on the latest socket (if any) for too long.
bool has_stalled(void) const { return mTimeout && mTimeout->has_stalled(); }
protected:
// This class may only be created as base class of BufferedCurlEasyRequest.
// Throws AICurlNoEasyHandle.
CurlEasyRequest(void) :
mHeaders(NULL), mRequestFinalized(false), mEventsTarget(NULL), mResult(CURLE_FAILED_INIT)
{ applyDefaultOptions(); }
CurlEasyRequest(void) : mHeaders(NULL), mHandleEventsTarget(NULL), mResult(CURLE_FAILED_INIT), mTimeoutPolicy(NULL), mTimeoutIsOrphan(false)
#if defined(CWDEBUG) || defined(DEBUG_CURLIO)
, mDebugIsHeadOrGetMethod(false)
#endif
{ applyDefaultOptions(); }
public:
~CurlEasyRequest();
public:
// Post-initialization, set the parent to pass the events to.
void send_events_to(AICurlEasyHandleEvents* target) { mEventsTarget = target; }
void send_handle_events_to(AICurlEasyHandleEvents* target) { mHandleEventsTarget = target; }
// For debugging purposes
bool is_finalized(void) const { return mRequestFinalized; }
bool is_finalized(void) const { return mTimeoutPolicy; }
// Return pointer to the ThreadSafe (wrapped) version of this object.
ThreadSafeCurlEasyRequest* get_lockobj(void);
inline ThreadSafeBufferedCurlEasyRequest* get_lockobj(void);
inline ThreadSafeBufferedCurlEasyRequest const* get_lockobj(void) const;
protected:
// Pass events to parent.
@@ -304,47 +421,48 @@ class CurlEasyRequest : public CurlEasyHandle {
/*virtual*/ void removed_from_multi_handle(AICurlEasyRequest_wat& curl_easy_request_w);
};
// Buffers used by the AICurlInterface::Request API.
// Curl callbacks write into and read from these buffers.
// The interface with the rest of the code is through AICurlInterface::Responder.
//
// The lifetime of a CurlResponderBuffer is slightly shorter than its
// associated CurlEasyRequest; this class can only be created as base class
// of ThreadSafeBufferedCurlEasyRequest, and is therefore constructed after
// the construction of the associated CurlEasyRequest and destructed before it.
// Hence, it's safe to use get_lockobj() and through that access the CurlEasyRequest
// object at all times.
//
// A CurlResponderBuffer is thus created when a ThreadSafeBufferedCurlEasyRequest
// is created which only happens by creating a AICurlEasyRequest(true) instance,
// and when the last AICurlEasyRequest is deleted, then the ThreadSafeBufferedCurlEasyRequest
// is deleted and the CurlResponderBuffer destructed.
class CurlResponderBuffer : protected AICurlEasyHandleEvents {
// This class adds input/output buffers to the request and hooks up the libcurl callbacks to use those buffers.
// Received data is partially decoded and made available through various member functions.
class BufferedCurlEasyRequest : public CurlEasyRequest {
public:
void resetState(AICurlEasyRequest_wat& curl_easy_request_w);
void prepRequest(AICurlEasyRequest_wat& buffered_curl_easy_request_w, std::vector<std::string> const& headers, AICurlInterface::ResponderPtr responder, S32 time_out = 0, bool post = false);
// The type of the used buffers.
typedef boost::shared_ptr<LLBufferArray> buffer_ptr_t;
LLIOPipe::buffer_ptr_t& getInput(void) { return mInput; }
std::stringstream& getHeaderOutput(void) { return mHeaderOutput; }
LLIOPipe::buffer_ptr_t& getOutput(void) { return mOutput; }
void resetState(void);
void prepRequest(AICurlEasyRequest_wat& buffered_curl_easy_request_w, AIHTTPHeaders const& headers, LLHTTPClient::ResponderPtr responder);
// Called if libcurl doesn't deliver within CurlRequestTimeOut seconds.
buffer_ptr_t& getInput(void) { return mInput; }
buffer_ptr_t& getOutput(void) { return mOutput; }
// Called if libcurl doesn't deliver within AIHTTPTimeoutPolicy::mMaximumTotalDelay seconds.
void timed_out(void);
// Called after removed_from_multi_handle was called.
void processOutput(AICurlEasyRequest_wat& curl_easy_request_w);
void processOutput(void);
// Do not write more than this amount.
//void setBodyLimit(U32 size) { mBodyLimit = size; }
// Post-initialization, set the parent to pass the events to.
void send_buffer_events_to(AICurlResponderBufferEvents* target) { mBufferEventsTarget = target; }
protected:
/*virtual*/ void added_to_multi_handle(AICurlEasyRequest_wat& curl_easy_request_w);
/*virtual*/ void finished(AICurlEasyRequest_wat& curl_easy_request_w);
/*virtual*/ void removed_from_multi_handle(AICurlEasyRequest_wat& curl_easy_request_w);
// Events from this class.
/*virtual*/ void received_HTTP_header(void);
/*virtual*/ void received_header(std::string const& key, std::string const& value);
/*virtual*/ void completed_headers(U32 status, std::string const& reason, AITransferInfo* info);
private:
LLIOPipe::buffer_ptr_t mInput;
buffer_ptr_t mInput;
U8* mLastRead; // Pointer into mInput where we last stopped reading (or NULL to start at the beginning).
std::stringstream mHeaderOutput;
LLIOPipe::buffer_ptr_t mOutput;
AICurlInterface::ResponderPtr mResponder;
buffer_ptr_t mOutput;
LLHTTPClient::ResponderPtr mResponder;
//U32 mBodyLimit; // From the old LLURLRequestDetail::mBodyLimit, but never used.
U32 mStatus; // HTTP status, decoded from the first header line.
std::string mReason; // The "reason" from the same header line.
S32 mRequestTransferedBytes;
S32 mResponseTransferedBytes;
AICurlResponderBufferEvents* mBufferEventsTarget;
public:
static LLChannelDescriptors const sChannels; // Channel object for mInput (channel out()) and mOutput (channel in()).
@@ -352,64 +470,60 @@ class CurlResponderBuffer : protected AICurlEasyHandleEvents {
private:
// This class may only be created by constructing a ThreadSafeBufferedCurlEasyRequest.
friend class ThreadSafeBufferedCurlEasyRequest;
CurlResponderBuffer(void);
BufferedCurlEasyRequest(void);
public:
~CurlResponderBuffer();
~BufferedCurlEasyRequest();
private:
static size_t curlWriteCallback(char* data, size_t size, size_t nmemb, void* user_data);
static size_t curlReadCallback(char* data, size_t size, size_t nmemb, void* user_data);
static size_t curlHeaderCallback(char* data, size_t size, size_t nmemb, void* user_data);
// Called from curlHeaderCallback.
void setStatusAndReason(U32 status, std::string const& reason);
public:
// Return pointer to the ThreadSafe (wrapped) version of this object.
ThreadSafeBufferedCurlEasyRequest* get_lockobj(void);
ThreadSafeBufferedCurlEasyRequest const* get_lockobj(void) const;
// Return true when prepRequest was already called and the object has not been
// invalidated as a result of calling timed_out().
bool isValid(void) const { return mResponder; }
};
// This class wraps CurlEasyRequest for thread-safety and adds a reference counter so we can
inline ThreadSafeBufferedCurlEasyRequest* CurlEasyRequest::get_lockobj(void)
{
return static_cast<BufferedCurlEasyRequest*>(this)->get_lockobj();
}
inline ThreadSafeBufferedCurlEasyRequest const* CurlEasyRequest::get_lockobj(void) const
{
return static_cast<BufferedCurlEasyRequest const*>(this)->get_lockobj();
}
// This class wraps BufferedCurlEasyRequest for thread-safety and adds a reference counter so we can
// copy it around cheaply and it gets destructed automatically when the last instance is deleted.
// It guarantees that the CURL* handle is never used concurrently, which is not allowed by libcurl.
// As AIThreadSafeSimpleDC contains a mutex, it cannot be copied. Therefore we need a reference counter for this object.
class ThreadSafeCurlEasyRequest : public AIThreadSafeSimple<CurlEasyRequest> {
class ThreadSafeBufferedCurlEasyRequest : public AIThreadSafeSimple<BufferedCurlEasyRequest> {
public:
// Throws AICurlNoEasyHandle.
ThreadSafeCurlEasyRequest(void) : mReferenceCount(0)
{ new (ptr()) CurlEasyRequest;
Dout(dc::curl, "Creating ThreadSafeCurlEasyRequest with this = " << (void*)this); }
virtual ~ThreadSafeCurlEasyRequest()
{ Dout(dc::curl, "Destructing ThreadSafeCurlEasyRequest with this = " << (void*)this); }
// Returns true if this is a base class of ThreadSafeBufferedCurlEasyRequest.
virtual bool isBuffered(void) const { return false; }
ThreadSafeBufferedCurlEasyRequest(void) : mReferenceCount(0)
{ new (ptr()) BufferedCurlEasyRequest;
Dout(dc::curl, "Creating ThreadSafeBufferedCurlEasyRequest with this = " << (void*)this); }
virtual ~ThreadSafeBufferedCurlEasyRequest()
{ Dout(dc::curl, "Destructing ThreadSafeBufferedCurlEasyRequest with this = " << (void*)this); }
private:
LLAtomicU32 mReferenceCount;
friend void intrusive_ptr_add_ref(ThreadSafeCurlEasyRequest* p); // Called by boost::intrusive_ptr when a new copy of a boost::intrusive_ptr<ThreadSafeCurlEasyRequest> is made.
friend void intrusive_ptr_release(ThreadSafeCurlEasyRequest* p); // Called by boost::intrusive_ptr when a boost::intrusive_ptr<ThreadSafeCurlEasyRequest> is destroyed.
};
// Same as the above but adds a CurlResponderBuffer. The latter has its own locking in order to
// allow casting the underlying CurlEasyRequest to ThreadSafeCurlEasyRequest, independent of
// what class it is part of: ThreadSafeCurlEasyRequest or ThreadSafeBufferedCurlEasyRequest.
// The virtual destructor of ThreadSafeCurlEasyRequest allows to treat each easy handle transparently
// as a ThreadSafeCurlEasyRequest object, or optionally dynamic_cast it to a ThreadSafeBufferedCurlEasyRequest.
// Note: the order of these base classes is important: AIThreadSafeSimple<CurlResponderBuffer> is now
// destructed before ThreadSafeCurlEasyRequest is.
class ThreadSafeBufferedCurlEasyRequest : public ThreadSafeCurlEasyRequest, public AIThreadSafeSimple<CurlResponderBuffer> {
public:
// Throws AICurlNoEasyHandle.
ThreadSafeBufferedCurlEasyRequest(void) { new (AIThreadSafeSimple<CurlResponderBuffer>::ptr()) CurlResponderBuffer; }
/*virtual*/ bool isBuffered(void) const { return true; }
friend void intrusive_ptr_add_ref(ThreadSafeBufferedCurlEasyRequest* p); // Called by boost::intrusive_ptr when a new copy of a boost::intrusive_ptr<ThreadSafeBufferedCurlEasyRequest> is made.
friend void intrusive_ptr_release(ThreadSafeBufferedCurlEasyRequest* p); // Called by boost::intrusive_ptr when a boost::intrusive_ptr<ThreadSafeBufferedCurlEasyRequest> is destroyed.
};
// The curl easy request type wrapped in a reference counting pointer.
typedef boost::intrusive_ptr<AICurlPrivate::ThreadSafeCurlEasyRequest> CurlEasyRequestPtr;
typedef boost::intrusive_ptr<AICurlPrivate::ThreadSafeBufferedCurlEasyRequest> BufferedCurlEasyRequestPtr;
// This class wraps CURLM*'s.
// It guarantees that a pointer is cleaned up when no longer needed, as required by libcurl.

File diff suppressed because it is too large Load Diff

View File

@@ -74,14 +74,17 @@ class MultiHandle : public CurlMultiHandle
private:
typedef std::set<AICurlEasyRequest, AICurlEasyRequestCompare> addedEasyRequests_type;
addedEasyRequests_type mAddedEasyRequests;
bool mHandleAddedOrRemoved; // Set when an easy handle was added or removed, reset in check_run_count().
int mPrevRunningHandles; // The last value of mRunningHandles that check_run_count() was called with.
int mRunningHandles; // The last value returned by curl_multi_socket_action.
long mTimeOut; // The last time out in ms as set by the callback CURLMOPT_TIMERFUNCTION.
addedEasyRequests_type mAddedEasyRequests; // All easy requests currently added to the multi handle.
int mRunningHandles; // The last value returned by curl_multi_socket_action.
long mTimeout; // The last timeout in ms as set by the callback CURLMOPT_TIMERFUNCTION.
private:
// Store result and trigger events for easy request.
void finish_easy_request(AICurlEasyRequest const& easy_request, CURLcode result);
// Remove easy request at iter (must exist).
// Note that it's possible that a new request from mQueuedRequests is inserted before iter.
CURLMcode remove_easy_request(addedEasyRequests_type::iterator const& iter, bool as_per_command);
static int socket_callback(CURL* easy, curl_socket_t s, int action, void* userp, void* socketp);
static int timer_callback(CURLM* multi, long timeout_ms, void* userp);
@@ -90,11 +93,14 @@ class MultiHandle : public CurlMultiHandle
int getRunningHandles(void) const { return mRunningHandles; }
// Returns how long to wait for socket action before calling socket_action(CURL_SOCKET_TIMEOUT, 0), in ms.
int getTimeOut(void) const { return mTimeOut; }
int getTimeout(void) const { return mTimeout; }
// This is called before sleeping, after calling (one or more times) socket_action.
void check_run_count(void);
// Called from the main loop every time select() timed out.
void handle_stalls(void);
public:
//-----------------------------------------------------------------------------
// Curl socket administration:

View File

@@ -0,0 +1,153 @@
/**
* @file aihttpheaders.cpp
* @brief Implementation of AIHTTPHeaders
*
* Copyright (c) 2012, Aleric Inglewood.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution.
*
* CHANGELOG
* and additional copyright holders.
*
* 15/08/2012
* Initial version, written by Aleric Inglewood @ SL
*/
#include "sys.h"
#include "aihttpheaders.h"
#include <curl/curl.h>
#ifdef DEBUG_CURLIO
#include "debug_libcurl.h"
#endif
AIHTTPHeaders::AIHTTPHeaders(std::string const& key, std::string const& value) : mContainer(new Container)
{
addHeader(key, value);
}
bool AIHTTPHeaders::addHeader(std::string const& key, std::string const& value, op_type op)
{
if (!mContainer)
{
mContainer = new Container;
}
insert_t res = mContainer->mKeyValuePairs.insert(container_t::value_type(key, value));
bool key_already_exists = !res.second;
if (key_already_exists)
{
llassert_always(op != new_header);
if (op == replace_if_exists)
res.first->second = value;
}
return key_already_exists;
}
void AIHTTPHeaders::append_to(curl_slist*& slist) const
{
if (!mContainer)
return;
container_t::const_iterator const end = mContainer->mKeyValuePairs.end();
for (container_t::const_iterator iter = mContainer->mKeyValuePairs.begin(); iter != end; ++iter)
{
slist = curl_slist_append(slist, llformat("%s: %s", iter->first.c_str(), iter->second.c_str()).c_str());
}
}
bool AIHTTPHeaders::hasHeader(std::string const& key) const
{
return !mContainer ? false : (mContainer->mKeyValuePairs.find(key) != mContainer->mKeyValuePairs.end());
}
bool AIHTTPHeaders::getValue(std::string const& key, std::string& value_out) const
{
AIHTTPHeaders::container_t::const_iterator iter;
if (!mContainer || (iter = mContainer->mKeyValuePairs.find(key)) == mContainer->mKeyValuePairs.end())
return false;
value_out = iter->second;
return true;
}
std::ostream& operator<<(std::ostream& os, AIHTTPHeaders const& headers)
{
os << '{';
if (headers.mContainer)
{
bool first = true;
AIHTTPHeaders::container_t::const_iterator const end = headers.mContainer->mKeyValuePairs.end();
for (AIHTTPHeaders::container_t::const_iterator iter = headers.mContainer->mKeyValuePairs.begin(); iter != end; ++iter)
{
if (!first)
os << ", ";
os << '"' << iter->first << ": " << iter->second << '"';
first = false;
}
}
os << '}';
return os;
}
void AIHTTPReceivedHeaders::addHeader(std::string const& key, std::string const& value)
{
if (!mContainer)
{
mContainer = new Container;
}
mContainer->mKeyValuePairs.insert(container_t::value_type(key, value));
}
bool AIHTTPReceivedHeaders::hasHeader(std::string const& key) const
{
return !mContainer ? false : (mContainer->mKeyValuePairs.find(key) != mContainer->mKeyValuePairs.end());
}
bool AIHTTPReceivedHeaders::getFirstValue(std::string const& key, std::string& value_out) const
{
AIHTTPReceivedHeaders::container_t::const_iterator iter;
if (!mContainer || (iter = mContainer->mKeyValuePairs.find(key)) == mContainer->mKeyValuePairs.end())
return false;
value_out = iter->second;
return true;
}
bool AIHTTPReceivedHeaders::getValues(std::string const& key, range_type& value_out) const
{
if (!mContainer)
return false;
value_out = mContainer->mKeyValuePairs.equal_range(key);
return value_out.first != value_out.second;
}
std::ostream& operator<<(std::ostream& os, AIHTTPReceivedHeaders const& headers)
{
os << '{';
if (headers.mContainer)
{
bool first = true;
AIHTTPReceivedHeaders::container_t::const_iterator const end = headers.mContainer->mKeyValuePairs.end();
for (AIHTTPReceivedHeaders::container_t::const_iterator iter = headers.mContainer->mKeyValuePairs.begin(); iter != end; ++iter)
{
if (!first)
os << ", ";
os << '"' << iter->first << ": " << iter->second << '"';
first = false;
}
}
os << '}';
return os;
}

View File

@@ -0,0 +1,158 @@
/**
* @file aihttpheaders.h
* @brief Keep a list of HTTP headers.
*
* Copyright (c) 2012, Aleric Inglewood.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution.
*
* CHANGELOG
* and additional copyright holders.
*
* 15/08/2012
* Initial version, written by Aleric Inglewood @ SL
* 21/10/2012
* Added AIHTTPReceivedHeaders
*/
#ifndef AIHTTPHEADERS_H
#define AIHTTPHEADERS_H
#include <string>
#include <map>
#include <iosfwd>
#include <algorithm>
#include "llpointer.h"
#include "llthread.h" // LLThreadSafeRefCount
extern "C" struct curl_slist;
class AIHTTPHeaders {
public:
enum op_type
{
new_header, // The inserted header must be the first one.
replace_if_exists, // If a header of this type already exists, replace it. Otherwise add the header.
keep_existing_header // If a header of this type already exists, do nothing.
};
// Construct an empty container.
AIHTTPHeaders(void) { }
// Construct a container with a single header.
AIHTTPHeaders(std::string const& key, std::string const& value);
// Clear all headers.
void clear(void) { if (mContainer) mContainer->mKeyValuePairs.clear(); }
// Add a header. Returns true if the header already existed.
bool addHeader(std::string const& key, std::string const& value, op_type op = new_header);
// Return true if there are no headers associated with this object.
bool empty(void) const { return !mContainer || mContainer->mKeyValuePairs.empty(); }
// Return true if the header already exists.
bool hasHeader(std::string const& key) const;
// Return true if key exists and fill value_out with the value. Return false otherwise.
bool getValue(std::string const& key, std::string& value_out) const;
// Append the headers to slist.
void append_to(curl_slist*& slist) const;
// For debug purposes.
friend std::ostream& operator<<(std::ostream& os, AIHTTPHeaders const& headers);
private:
typedef std::map<std::string, std::string> container_t;
typedef std::pair<container_t::iterator, bool> insert_t;
struct Container : public LLThreadSafeRefCount {
container_t mKeyValuePairs;
};
LLPointer<Container> mContainer;
};
// Functor that returns true if c1 is less than c2, ignoring bit 5.
// The effect is that characters in the range a-z equivalent ordering with A-Z.
// This function assumes UTF-8 or ASCII encoding!
//
// Note that other characters aren't important in the case of HTTP header keys;
// however if one considers all printable ASCII characters, then this functor
// also compares "@[\]^" equal to "`{|}~" (any other is either not printable or
// would be equal to a not printable character).
struct AIHTTPReceivedHeadersCharCompare {
bool operator()(std::string::value_type c1, std::string::value_type c2) const
{
static std::string::value_type const bit5 = 0x20;
return (c1 | bit5) < (c2 | bit5);
}
};
// Functor to lexiographically compare two HTTP header keys using the above predicate.
// This means that for example "Content-Type" and "content-type" will have equivalent ordering.
struct AIHTTPReceivedHeadersCompare {
bool operator()(std::string const& h1, std::string const& h2) const
{
static AIHTTPReceivedHeadersCharCompare const predicate;
return std::lexicographical_compare(h1.begin(), h1.end(), h2.begin(), h2.end(), predicate);
}
};
class AIHTTPReceivedHeaders {
private:
typedef std::multimap<std::string, std::string, AIHTTPReceivedHeadersCompare> container_t;
public:
typedef container_t::const_iterator iterator_type;
typedef std::pair<iterator_type, iterator_type> range_type;
// Construct an empty container.
AIHTTPReceivedHeaders(void) { }
// Clear all headers.
void clear(void) { if (mContainer) mContainer->mKeyValuePairs.clear(); }
// Add a header.
void addHeader(std::string const& key, std::string const& value);
// Return true if there are no headers associated with this object.
bool empty(void) const { return !mContainer || mContainer->mKeyValuePairs.empty(); }
// Return true if the header exists.
bool hasHeader(std::string const& key) const;
// Return true if key exists and fill value_out with the value. Return false otherwise.
bool getFirstValue(std::string const& key, std::string& value_out) const;
// Return true if key exists and fill value_out with all values. Return false otherwise.
bool getValues(std::string const& key, range_type& value_out) const;
// For debug purposes.
friend std::ostream& operator<<(std::ostream& os, AIHTTPReceivedHeaders const& headers);
private:
struct Container : public LLThreadSafeRefCount {
container_t mKeyValuePairs;
};
LLPointer<Container> mContainer;
};
#endif // AIHTTPHEADERS_H

View File

@@ -0,0 +1,885 @@
/**
* @file aihttptimeoutpolicy.cpp
* @brief Implementation of AIHTTPTimeoutPolicy
*
* Copyright (c) 2012, Aleric Inglewood.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution.
*
* CHANGELOG
* and additional copyright holders.
*
* 24/08/2012
* Initial version, written by Aleric Inglewood @ SL
*/
#include "sys.h"
#include "aihttptimeoutpolicy.h"
#define NOMINMAX
#include "llerror.h"
#include "lldefs.h"
#include "v3math.h"
#include <vector>
//!
// Timing of a HTML connection.
//
// Request call
// |
// v ... <--low speed time--> ... ... <--low speed time--> ...
// <--request queued--><--DNS lookup--><--connect margin--><--data transfer to server--><--reply delay--><--data transfer from server-->
// <------------------------------------------curl transaction----------------------------------------------------->
// <--------------------------------------------------------------total delay---------------------------------------------------------->
// |
// v
// finished
// For now, low speed limit is the same for up and download: usually download is (much) higher, but we have to take into account that
// there might also be multiple downloads at the same time, more than simultaneous uploads.
// Absolute maxima (min/max range):
// These values are intuitively determined and rather arbitrary.
namespace {
U16 const ABS_min_DNS_lookup = 0; // Rationale: If a FAST roundtrip is demanded, then setting the DNS lookup grace time
// at 0 seconds will not make a connection fail when the lookup takes 1 second, it
// just means that no EXTRA time is added to the connect time.
U16 const ABS_max_DNS_lookup = 300; // Waiting longer than 5 minutes never makes sense.
U16 const ABS_min_connect_time = 1; // Can't demand 0 seconds, and we deal with integer numbers here.
U16 const ABS_max_connect_time = 30; // Making a TCP/IP connection should REALLY succeed within 30 seconds or we rather try again.
U16 const ABS_min_reply_delay = 1; // Can't demand 0 seconds, and we deal with integer numbers here.
U16 const ABS_max_reply_delay = 120; // If the server needs more than 2 minutes to find the reply then something just HAS to be wrong :/.
U16 const ABS_min_low_speed_time = 4; // Intuitively, I think it makes no sense to average a download speed over less than 4 seconds.
U16 const ABS_max_low_speed_time = 120; // Averaging it over a time considerably larger than the normal timeout periods makes no sense either.
U32 const ABS_min_low_speed_limit = 1; // In case you don't want to timeout when there is any data received at all.
U32 const ABS_max_low_speed_limit = 1000000; // This limit is almost certainly higher than the maximum speed you get from the server.
U16 const ABS_min_transaction = 60; // This is an absurd low value for experimentation. In reality, you should control
// termination of really slow connections through the low_speed settings.
U16 const ABS_max_transaction = 1200; // Insane long time. Downloads a texture of 4 MB at 3.5 kB/s. Textures are compressed though ;).
U16 const ABS_min_total_delay = 60; // This is an absurd low value for experimentation. In reality, you should control
// termination of really slow connections through the low_speed settings.
U16 const ABS_max_total_delay = 3000; // Insane long time, for when someone wants to be ABSOLUTELY sure this isn't the bottleneck.
using namespace AIHTTPTimeoutPolicyOperators;
// Default policy values.
U16 const AITP_default_DNS_lookup_grace = 60; // Allow for 60 seconds long DNS look ups.
U16 const AITP_default_maximum_connect_time = 10; // Allow the SSL/TLS connection through a proxy, including handshakes, to take up to 10 seconds.
U16 const AITP_default_maximum_reply_delay = 60; // Allow the server 60 seconds to do whatever it has to do before starting to send data.
U16 const AITP_default_low_speed_time = 30; // If a transfer speed drops below AITP_default_low_speed_limit bytes/s for 30 seconds, terminate the transfer.
U32 const AITP_default_low_speed_limit = 56000; // In bytes per second (use for CURLOPT_LOW_SPEED_LIMIT).
U16 const AITP_default_maximum_curl_transaction = 300; // Allow large files to be transfered over slow connections.
U16 const AITP_default_maximum_total_delay = 600; // Avoid "leaking" by terminating anything that wasn't completed after 10 minutes.
} // namespace
AIHTTPTimeoutPolicy& AIHTTPTimeoutPolicy::operator=(AIHTTPTimeoutPolicy const& rhs)
{
// You're not allowed to assign to a policy that is based on another policy.
llassert(!mBase);
mDNSLookupGrace = rhs.mDNSLookupGrace;
mMaximumConnectTime = rhs.mMaximumConnectTime;
mMaximumReplyDelay = rhs.mMaximumReplyDelay;
mLowSpeedTime = rhs.mLowSpeedTime;
mLowSpeedLimit = rhs.mLowSpeedLimit;
mMaximumCurlTransaction = rhs.mMaximumCurlTransaction;
mMaximumTotalDelay = rhs.mMaximumTotalDelay;
return *this;
}
AIHTTPTimeoutPolicy::AIHTTPTimeoutPolicy(char const* name,
U16 dns_lookup_grace, U16 subsequent_connects, U16 reply_delay,
U16 low_speed_time, U32 low_speed_limit,
U16 curl_transaction, U16 total_delay) :
mName(name),
mBase(NULL),
mDNSLookupGrace(dns_lookup_grace),
mMaximumConnectTime(subsequent_connects),
mMaximumReplyDelay(reply_delay),
mLowSpeedTime(low_speed_time),
mLowSpeedLimit(low_speed_limit),
mMaximumCurlTransaction(curl_transaction),
mMaximumTotalDelay(total_delay)
{
sanity_checks();
}
struct PolicyOp {
PolicyOp* mNext;
PolicyOp(void) : mNext(NULL) { }
PolicyOp(PolicyOp& op) : mNext(&op) { }
virtual void perform(AIHTTPTimeoutPolicy* policy) const { }
void nextOp(AIHTTPTimeoutPolicy* policy) const { if (mNext) mNext->perform(policy); }
};
class AIHTTPTimeoutPolicyBase : public AIHTTPTimeoutPolicy {
private:
std::vector<AIHTTPTimeoutPolicy*> mDerived; // Policies derived from this one.
public:
AIHTTPTimeoutPolicyBase(U16 dns_lookup_grace, U16 subsequent_connects, U16 reply_delay,
U16 low_speed_time, U32 low_speed_limit,
U16 curl_transaction, U16 total_delay) :
AIHTTPTimeoutPolicy(NULL, dns_lookup_grace, subsequent_connects, reply_delay, low_speed_time, low_speed_limit, curl_transaction, total_delay) { }
// Derive base from base.
AIHTTPTimeoutPolicyBase(AIHTTPTimeoutPolicyBase& rhs, PolicyOp const& op = PolicyOp()) : AIHTTPTimeoutPolicy(rhs) { op.perform(this); }
// Called for every derived policy.
void derived(AIHTTPTimeoutPolicy* derived) { mDerived.push_back(derived); }
// Provide public acces to sDebugSettingsCurlTimeout for this compilation unit.
static AIHTTPTimeoutPolicyBase& getDebugSettingsCurlTimeout(void) { return sDebugSettingsCurlTimeout; }
protected:
friend void AIHTTPTimeoutPolicy::setDefaultCurlTimeout(AIHTTPTimeoutPolicy const& timeout);
AIHTTPTimeoutPolicyBase& operator=(AIHTTPTimeoutPolicy const& rhs);
};
AIHTTPTimeoutPolicy::AIHTTPTimeoutPolicy(AIHTTPTimeoutPolicy& base) :
mName(NULL),
mBase(static_cast<AIHTTPTimeoutPolicyBase*>(&base)),
mDNSLookupGrace(base.mDNSLookupGrace),
mMaximumConnectTime(base.mMaximumConnectTime),
mMaximumReplyDelay(base.mMaximumReplyDelay),
mLowSpeedTime(base.mLowSpeedTime),
mLowSpeedLimit(base.mLowSpeedLimit),
mMaximumCurlTransaction(base.mMaximumCurlTransaction),
mMaximumTotalDelay(base.mMaximumTotalDelay)
{
}
AIHTTPTimeoutPolicyBase& AIHTTPTimeoutPolicyBase::operator=(AIHTTPTimeoutPolicy const& rhs)
{
AIHTTPTimeoutPolicy::operator=(rhs);
return *this;
}
AIHTTPTimeoutPolicy::AIHTTPTimeoutPolicy(char const* name, AIHTTPTimeoutPolicyBase& base) :
mName(name),
mBase(&base),
mDNSLookupGrace(mBase->mDNSLookupGrace),
mMaximumConnectTime(mBase->mMaximumConnectTime),
mMaximumReplyDelay(mBase->mMaximumReplyDelay),
mLowSpeedTime(mBase->mLowSpeedTime),
mLowSpeedLimit(mBase->mLowSpeedLimit),
mMaximumCurlTransaction(mBase->mMaximumCurlTransaction),
mMaximumTotalDelay(mBase->mMaximumTotalDelay)
{
sNameMap.insert(namemap_t::value_type(name, this));
// Register for changes to the base policy.
mBase->derived(this);
}
//static
void AIHTTPTimeoutPolicy::setDefaultCurlTimeout(AIHTTPTimeoutPolicy const& timeout)
{
sDebugSettingsCurlTimeout = timeout;
llinfos << "CurlTimeout Debug Settings now"
": DNSLookup: " << sDebugSettingsCurlTimeout.mDNSLookupGrace <<
"; Connect: " << sDebugSettingsCurlTimeout.mMaximumConnectTime <<
"; ReplyDelay: " << sDebugSettingsCurlTimeout.mMaximumReplyDelay <<
"; LowSpeedTime: " << sDebugSettingsCurlTimeout.mLowSpeedTime <<
"; LowSpeedLimit: " << sDebugSettingsCurlTimeout.mLowSpeedLimit <<
"; MaxTransaction: " << sDebugSettingsCurlTimeout.mMaximumCurlTransaction <<
"; MaxTotalDelay: " << sDebugSettingsCurlTimeout.mMaximumTotalDelay << llendl;
if (sDebugSettingsCurlTimeout.mDNSLookupGrace < AITP_default_DNS_lookup_grace)
{
llwarns << "CurlTimeoutDNSLookup (" << sDebugSettingsCurlTimeout.mDNSLookupGrace << ") is lower than the built-in default value (" << AITP_default_DNS_lookup_grace << ")." << llendl;
}
if (sDebugSettingsCurlTimeout.mMaximumConnectTime < AITP_default_maximum_connect_time)
{
llwarns << "CurlTimeoutConnect (" << sDebugSettingsCurlTimeout.mMaximumConnectTime << ") is lower than the built-in default value (" << AITP_default_maximum_connect_time << ")." << llendl;
}
if (sDebugSettingsCurlTimeout.mMaximumReplyDelay < AITP_default_maximum_reply_delay)
{
llwarns << "CurlTimeoutReplyDelay (" << sDebugSettingsCurlTimeout.mMaximumReplyDelay << ") is lower than the built-in default value (" << AITP_default_maximum_reply_delay << ")." << llendl;
}
if (sDebugSettingsCurlTimeout.mLowSpeedTime < AITP_default_low_speed_time)
{
llwarns << "CurlTimeoutLowSpeedTime (" << sDebugSettingsCurlTimeout.mLowSpeedTime << ") is lower than the built-in default value (" << AITP_default_low_speed_time << ")." << llendl;
}
if (sDebugSettingsCurlTimeout.mLowSpeedLimit > AITP_default_low_speed_limit)
{
llwarns << "CurlTimeoutLowSpeedLimit (" << sDebugSettingsCurlTimeout.mLowSpeedLimit << ") is higher than the built-in default value (" << AITP_default_low_speed_limit << ")." << llendl;
}
if (sDebugSettingsCurlTimeout.mMaximumCurlTransaction < AITP_default_maximum_curl_transaction)
{
llwarns << "CurlTimeoutMaxTransaction (" << sDebugSettingsCurlTimeout.mMaximumCurlTransaction << ") is lower than the built-in default value (" << AITP_default_maximum_curl_transaction<< ")." << llendl;
}
if (sDebugSettingsCurlTimeout.mMaximumTotalDelay < AITP_default_maximum_total_delay)
{
llwarns << "CurlTimeoutMaxTotalDelay (" << sDebugSettingsCurlTimeout.mMaximumTotalDelay << ") is lower than the built-in default value (" << AITP_default_maximum_total_delay << ")." << llendl;
}
}
//static
AIHTTPTimeoutPolicy const& AIHTTPTimeoutPolicy::getDebugSettingsCurlTimeout(void)
{
return sDebugSettingsCurlTimeout;
}
#ifdef SHOW_ASSERT
#include "aithreadid.h"
static AIThreadID curlthread(AIThreadID::none); // Initialized by getConnectTimeout.
#endif
static std::set<std::string> gSeenHostnames;
U16 AIHTTPTimeoutPolicy::getConnectTimeout(std::string const& hostname) const
{
#ifdef SHOW_ASSERT
// Only the CURL-THREAD may access gSeenHostnames.
if (curlthread.is_no_thread())
curlthread.reset();
llassert(curlthread.equals_current_thread());
#endif
U16 connect_timeout = mMaximumConnectTime;
// Add the hostname to the list of seen hostnames, if not already there.
if (gSeenHostnames.insert(hostname).second)
connect_timeout += mDNSLookupGrace; // If the host is not in the list, increase the connect timeout with mDNSLookupGrace.
return connect_timeout;
}
//static
bool AIHTTPTimeoutPolicy::connect_timed_out(std::string const& hostname)
{
llassert(curlthread.equals_current_thread());
// This is called when a connect to hostname timed out on connect.
// If the hostname is currently in the list, remove it and return true
// so that subsequent connects will get more time to connect.
// Otherwise return false.
return gSeenHostnames.erase(hostname) > 0;
}
//=======================================================================================================
// Start of policy operation definitions.
namespace AIHTTPTimeoutPolicyOperators {
// Note: Policies are applied in the order First(x, Second(y, Third(z))) etc,
// where the last (Third) has the highest priority.
// For example: Transaction(5, Connect(40)) would first enforce a transaction time of 5 seconds,
// and then a connect time of 40 seconds, even if that would mean increasing the transaction
// time again.
struct DNS : PolicyOp {
int mSeconds;
DNS(int seconds) : mSeconds(seconds) { }
DNS(int seconds, PolicyOp& op) : PolicyOp(op), mSeconds(seconds) { }
static void fix(AIHTTPTimeoutPolicy* policy);
static U16 min(void) { return ABS_min_DNS_lookup; }
static U16 max(void) { return ABS_max_DNS_lookup; }
virtual void perform(AIHTTPTimeoutPolicy* policy) const;
};
struct Connect : PolicyOp {
int mSeconds;
Connect(int seconds) : mSeconds(seconds) { }
Connect(int seconds, PolicyOp& op) : PolicyOp(op), mSeconds(seconds) { }
static void fix(AIHTTPTimeoutPolicy* policy);
static U16 min(void) { return ABS_min_connect_time; }
static U16 max(void) { return ABS_max_connect_time; }
virtual void perform(AIHTTPTimeoutPolicy* policy) const;
};
struct Reply : PolicyOp {
int mSeconds;
Reply(int seconds) : mSeconds(seconds) { }
Reply(int seconds, PolicyOp& op) : PolicyOp(op), mSeconds(seconds) { }
static void fix(AIHTTPTimeoutPolicy* policy);
static U16 min(void) { return ABS_min_reply_delay; }
static U16 max(void) { return ABS_max_reply_delay; }
virtual void perform(AIHTTPTimeoutPolicy* policy) const;
};
struct Speed : PolicyOp {
int mSeconds;
int mRate;
Speed(int seconds, int rate) : mSeconds(seconds), mRate(rate) { }
Speed(int seconds, int rate, PolicyOp& op) : PolicyOp(op), mSeconds(seconds), mRate(rate) { }
static void fix(AIHTTPTimeoutPolicy* policy);
static U16 min(void) { return ABS_min_low_speed_time; }
static U16 max(AIHTTPTimeoutPolicy const* policy) { return llmin(ABS_max_low_speed_time, (U16)(policy->mMaximumCurlTransaction / 2)); }
static U32 lmin(void) { return ABS_min_low_speed_limit; }
static U32 lmax(void) { return ABS_max_low_speed_limit; }
virtual void perform(AIHTTPTimeoutPolicy* policy) const;
};
struct Transaction : PolicyOp {
int mSeconds;
Transaction(int seconds) : mSeconds(seconds) { }
Transaction(int seconds, PolicyOp& op) : PolicyOp(op), mSeconds(seconds) { }
static void fix(AIHTTPTimeoutPolicy* policy);
static U16 min(AIHTTPTimeoutPolicy const* policy) { return llmax(ABS_min_transaction, (U16)(policy->mMaximumConnectTime + policy->mMaximumReplyDelay + 4 * policy->mLowSpeedTime)); }
static U16 max(void) { return ABS_max_transaction; }
virtual void perform(AIHTTPTimeoutPolicy* policy) const;
};
struct Total : PolicyOp {
int mSeconds;
Total(int seconds) : mSeconds(seconds) { }
Total(int seconds, PolicyOp& op) : PolicyOp(op), mSeconds(seconds) { }
static void fix(AIHTTPTimeoutPolicy* policy);
static U16 min(AIHTTPTimeoutPolicy const* policy) { return llmax(ABS_min_total_delay, (U16)(policy->mMaximumCurlTransaction + 1)); }
static U16 max(void) { return ABS_max_total_delay; }
virtual void perform(AIHTTPTimeoutPolicy* policy) const;
};
void DNS::perform(AIHTTPTimeoutPolicy* policy) const
{
policy->mDNSLookupGrace = mSeconds;
fix(policy);
nextOp(policy);
}
void Connect::perform(AIHTTPTimeoutPolicy* policy) const
{
policy->mMaximumConnectTime = mSeconds;
fix(policy);
nextOp(policy);
}
void Reply::perform(AIHTTPTimeoutPolicy* policy) const
{
policy->mMaximumReplyDelay = mSeconds;
fix(policy);
nextOp(policy);
}
void Speed::perform(AIHTTPTimeoutPolicy* policy) const
{
policy->mLowSpeedTime = mSeconds;
policy->mLowSpeedLimit = mRate;
fix(policy);
nextOp(policy);
}
void Transaction::perform(AIHTTPTimeoutPolicy* policy) const
{
policy->mMaximumCurlTransaction = mSeconds;
fix(policy);
nextOp(policy);
}
void Total::perform(AIHTTPTimeoutPolicy* policy) const
{
policy->mMaximumTotalDelay = mSeconds;
fix(policy);
nextOp(policy);
}
void DNS::fix(AIHTTPTimeoutPolicy* policy)
{
if (policy->mDNSLookupGrace > max())
{
policy->mDNSLookupGrace = max();
}
else if (policy->mDNSLookupGrace < min())
{
policy->mDNSLookupGrace = min();
}
}
void Connect::fix(AIHTTPTimeoutPolicy* policy)
{
bool changed = false;
if (policy->mMaximumConnectTime > max())
{
policy->mMaximumConnectTime = max();
changed = true;
}
else if (policy->mMaximumConnectTime < min())
{
policy->mMaximumConnectTime = min();
changed = true;
}
if (changed)
{
// Transaction limits depend on Connect.
Transaction::fix(policy);
}
}
void Reply::fix(AIHTTPTimeoutPolicy* policy)
{
bool changed = false;
if (policy->mMaximumReplyDelay > max())
{
policy->mMaximumReplyDelay = max();
changed = true;
}
else if (policy->mMaximumReplyDelay < min())
{
policy->mMaximumReplyDelay = min();
changed = true;
}
if (changed)
{
// Transaction limits depend on Reply.
Transaction::fix(policy);
}
}
void Speed::fix(AIHTTPTimeoutPolicy* policy)
{
bool changed = false;
if (policy->mLowSpeedTime > ABS_max_low_speed_time)
{
policy->mLowSpeedTime = ABS_max_low_speed_time;
changed = true;
}
else if (policy->mLowSpeedTime != 0 && policy->mLowSpeedTime < min())
{
policy->mLowSpeedTime = min();
changed = true;
}
if (changed)
{
// Transaction limits depend on Speed time.
Transaction::fix(policy);
}
if (policy->mLowSpeedTime > max(policy))
{
policy->mLowSpeedTime = max(policy);
}
if (policy->mLowSpeedLimit > lmax())
{
policy->mLowSpeedLimit = lmax();
}
else if (policy->mLowSpeedLimit != 0 && policy->mLowSpeedLimit < lmin())
{
policy->mLowSpeedLimit = lmin();
}
}
void Transaction::fix(AIHTTPTimeoutPolicy* policy)
{
bool changed = false;
if (policy->mMaximumCurlTransaction > max())
{
policy->mMaximumCurlTransaction = max();
changed = true;
}
else if (policy->mMaximumCurlTransaction < ABS_min_transaction)
{
policy->mMaximumCurlTransaction = ABS_min_transaction;
changed = true;
}
if (changed)
{
// Totals minimum limit depends on Transaction.
Total::fix(policy);
// Transaction limits depend on Connect, Reply and Speed time.
if (policy->mMaximumCurlTransaction < min(policy))
{
// We need to achieve the following (from Transaction::min()):
// policy->mMaximumCurlTransaction >= policy->mMaximumConnectTime + policy->mMaximumReplyDelay + 4 * policy->mLowSpeedTime
// There isn't a single way to fix this, so we just do something randomly intuitive.
// We consider the vector space <connect_time, reply_delay, low_speed_time>;
// In other words, we need to compare with the dot product of <1, 1, 4>.
LLVector3 const ref(1, 1, 4);
// The shortest allowed vector is:
LLVector3 const vec_min(ABS_min_connect_time, ABS_min_reply_delay, ABS_min_low_speed_time);
// Initialize the result vector to (0, 0, 0) (in the vector space with shifted origin).
LLVector3 vec_res;
// Check if there is a solution at all:
if (policy->mMaximumCurlTransaction > ref * vec_min) // Is vec_min small enough?
{
// The current point is:
LLVector3 vec_cur(policy->mMaximumConnectTime, policy->mMaximumReplyDelay, policy->mLowSpeedTime);
// The default point is:
LLVector3 vec_def(AITP_default_maximum_connect_time, AITP_default_maximum_reply_delay, AITP_default_low_speed_time);
// Move the origin.
vec_cur -= vec_min;
vec_def -= vec_min;
// Normalize the default vector (we only need it's direction).
vec_def.normalize();
// Project the current vector onto the default vector (dp = default projection):
LLVector3 vec_dp = vec_def * (vec_cur * vec_def);
// Check if the projection is a solution and choose the vectors between which the result lays.
LLVector3 a; // vec_min is too small (a = (0, 0, 0) which corresponds to vec_min).
LLVector3 b = vec_cur; // vec_cur is too large.
if (policy->mMaximumCurlTransaction > ref * (vec_dp + vec_min)) // Is vec_dp small enough too?
{
a = vec_dp; // New lower bound.
}
else
{
b = vec_dp; // New upper bound.
}
// Find vec_res = a + lambda * (b - a), where 0 <= lambda <= 1, such that
// policy->mMaximumCurlTransaction == ref * (vec_res + vec_min).
//
// Note that ref * (b - a) must be non-zero because if it wasn't then changing lambda wouldn't have
// any effect on right-hand side of the equation (ref * (vec_res + vec_min)) which in contradiction
// with the fact that a is a solution and b is not.
F32 lambda = (policy->mMaximumCurlTransaction - ref * (a + vec_min)) / (ref * (b - a));
vec_res = a + lambda * (b - a);
}
// Shift origin back and fill in the result.
vec_res += vec_min;
policy->mMaximumConnectTime = vec_res[VX];
policy->mMaximumReplyDelay = vec_res[VY];
policy->mLowSpeedTime = vec_res[VZ];
}
}
if (policy->mMaximumCurlTransaction < min(policy))
{
policy->mMaximumCurlTransaction = min(policy);
}
}
void Total::fix(AIHTTPTimeoutPolicy* policy)
{
bool changed = false;
if (policy->mMaximumTotalDelay > max())
{
policy->mMaximumTotalDelay = max();
changed = true;
}
else if (policy->mMaximumTotalDelay < ABS_min_total_delay)
{
policy->mMaximumTotalDelay = ABS_min_total_delay;
changed = true;
}
if (changed)
{
// Totals minimum limit depends on Transaction.
// We have to correct mMaximumCurlTransaction such that (from Total::min)
// mMaximumTotalDelay >= llmax((int)ABS_min_total_delay, policy->mMaximumCurlTransaction + 1)
if (policy->mMaximumTotalDelay < policy->mMaximumCurlTransaction + 1)
{
policy->mMaximumCurlTransaction = policy->mMaximumTotalDelay - 1;
}
}
if (policy->mMaximumTotalDelay < min(policy))
{
policy->mMaximumTotalDelay = min(policy);
}
}
} // namespace AIHTTPTimeoutPolicyOperators
void AIHTTPTimeoutPolicy::sanity_checks(void) const
{
// Sanity checks.
llassert( DNS::min() <= mDNSLookupGrace && mDNSLookupGrace <= DNS::max());
llassert( Connect::min() <= mMaximumConnectTime && mMaximumConnectTime <= Connect::max());
llassert( Reply::min() <= mMaximumReplyDelay && mMaximumReplyDelay <= Reply::max());
llassert(mLowSpeedTime == 0 ||
(Speed::min() <= mLowSpeedTime && mLowSpeedTime <= Speed::max(this)));
llassert(mLowSpeedLimit == 0 ||
(Speed::lmin() <= mLowSpeedLimit && mLowSpeedLimit <= Speed::lmax()));
llassert(Transaction::min(this) <= mMaximumCurlTransaction && mMaximumCurlTransaction <= Transaction::max());
llassert( Total::min(this) <= mMaximumTotalDelay && mMaximumTotalDelay <= Total::max());
}
//=======================================================================================================
// Start of policy definitions.
// Policy with hardcoded default values.
AIHTTPTimeoutPolicyBase HTTPTimeoutPolicy_default(
AITP_default_DNS_lookup_grace,
AITP_default_maximum_connect_time,
AITP_default_maximum_reply_delay,
AITP_default_low_speed_time,
AITP_default_low_speed_limit,
AITP_default_maximum_curl_transaction,
AITP_default_maximum_total_delay);
//static. Initialized here, but shortly overwritten by Debug Settings.
AIHTTPTimeoutPolicyBase AIHTTPTimeoutPolicy::sDebugSettingsCurlTimeout(
AITP_default_DNS_lookup_grace,
AITP_default_maximum_connect_time,
AITP_default_maximum_reply_delay,
AITP_default_low_speed_time,
AITP_default_low_speed_limit,
AITP_default_maximum_curl_transaction,
AITP_default_maximum_total_delay);
// This used to be '5 seconds'.
AIHTTPTimeoutPolicyBase transfer_5s(AIHTTPTimeoutPolicyBase::getDebugSettingsCurlTimeout(),
Transaction(5)
);
// This used to be '18 seconds'.
AIHTTPTimeoutPolicyBase transfer_18s(AIHTTPTimeoutPolicyBase::getDebugSettingsCurlTimeout(),
Transaction(18)
);
// This used to be '300 seconds'. We derive this from the hardcoded result so users can't mess with it.
AIHTTPTimeoutPolicyBase transfer_300s(HTTPTimeoutPolicy_default,
Transaction(300)
);
// This used to be a call to setopt(CURLOPT_CONNECTTIMEOUT, 40L) with the remark 'Be a little impatient about establishing connections.'
AIHTTPTimeoutPolicyBase connect_40s(AIHTTPTimeoutPolicyBase::getDebugSettingsCurlTimeout(),
Connect(40));
// End of policy definitions.
//=======================================================================================================
bool validateCurlTimeoutDNSLookup(LLSD const& newvalue)
{
U32 new_value = newvalue.asInteger();
AIHTTPTimeoutPolicyBase timeout = AIHTTPTimeoutPolicyBase::getDebugSettingsCurlTimeout();
AIHTTPTimeoutPolicyOperators::DNS op(new_value);
op.perform(&timeout);
return timeout.getDNSLookup() == new_value;
}
bool handleCurlTimeoutDNSLookup(LLSD const& newvalue)
{
AIHTTPTimeoutPolicyBase timeout = AIHTTPTimeoutPolicyBase::getDebugSettingsCurlTimeout();
AIHTTPTimeoutPolicyOperators::DNS op(newvalue.asInteger());
op.perform(&timeout);
AIHTTPTimeoutPolicy::setDefaultCurlTimeout(timeout);
return true;
}
bool validateCurlTimeoutConnect(LLSD const& newvalue)
{
U32 new_value = newvalue.asInteger();
AIHTTPTimeoutPolicyBase timeout = AIHTTPTimeoutPolicyBase::getDebugSettingsCurlTimeout();
AIHTTPTimeoutPolicyOperators::Connect op(new_value);
op.perform(&timeout);
return timeout.getConnect() == new_value;
}
bool handleCurlTimeoutConnect(LLSD const& newvalue)
{
AIHTTPTimeoutPolicyBase timeout = AIHTTPTimeoutPolicyBase::getDebugSettingsCurlTimeout();
AIHTTPTimeoutPolicyOperators::Connect op(newvalue.asInteger());
op.perform(&timeout);
AIHTTPTimeoutPolicy::setDefaultCurlTimeout(timeout);
return true;
}
bool validateCurlTimeoutReplyDelay(LLSD const& newvalue)
{
U32 new_value = newvalue.asInteger();
AIHTTPTimeoutPolicyBase timeout = AIHTTPTimeoutPolicyBase::getDebugSettingsCurlTimeout();
AIHTTPTimeoutPolicyOperators::Reply op(new_value);
op.perform(&timeout);
return timeout.getReplyDelay() == new_value;
}
bool handleCurlTimeoutReplyDelay(LLSD const& newvalue)
{
AIHTTPTimeoutPolicyBase timeout = AIHTTPTimeoutPolicyBase::getDebugSettingsCurlTimeout();
AIHTTPTimeoutPolicyOperators::Reply op(newvalue.asInteger());
op.perform(&timeout);
AIHTTPTimeoutPolicy::setDefaultCurlTimeout(timeout);
return true;
}
bool validateCurlTimeoutLowSpeedLimit(LLSD const& newvalue)
{
U32 new_value = newvalue.asInteger();
AIHTTPTimeoutPolicyBase timeout = AIHTTPTimeoutPolicyBase::getDebugSettingsCurlTimeout();
AIHTTPTimeoutPolicyOperators::Speed op(timeout.getLowSpeedTime(), new_value);
op.perform(&timeout);
return timeout.getLowSpeedLimit() == new_value;
}
bool handleCurlTimeoutLowSpeedLimit(LLSD const& newvalue)
{
AIHTTPTimeoutPolicyBase timeout = AIHTTPTimeoutPolicyBase::getDebugSettingsCurlTimeout();
AIHTTPTimeoutPolicyOperators::Speed op(timeout.getLowSpeedTime(), newvalue.asInteger());
op.perform(&timeout);
AIHTTPTimeoutPolicy::setDefaultCurlTimeout(timeout);
return true;
}
bool validateCurlTimeoutLowSpeedTime(LLSD const& newvalue)
{
U32 new_value = newvalue.asInteger();
AIHTTPTimeoutPolicyBase timeout = AIHTTPTimeoutPolicyBase::getDebugSettingsCurlTimeout();
AIHTTPTimeoutPolicyOperators::Speed op(new_value, timeout.getLowSpeedLimit());
op.perform(&timeout);
return timeout.getLowSpeedTime() == new_value;
}
bool handleCurlTimeoutLowSpeedTime(LLSD const& newvalue)
{
AIHTTPTimeoutPolicyBase timeout = AIHTTPTimeoutPolicyBase::getDebugSettingsCurlTimeout();
AIHTTPTimeoutPolicyOperators::Speed op(newvalue.asInteger(), timeout.getLowSpeedLimit());
op.perform(&timeout);
AIHTTPTimeoutPolicy::setDefaultCurlTimeout(timeout);
return true;
}
bool validateCurlTimeoutMaxTransaction(LLSD const& newvalue)
{
U32 new_value = newvalue.asInteger();
AIHTTPTimeoutPolicyBase timeout = AIHTTPTimeoutPolicyBase::getDebugSettingsCurlTimeout();
AIHTTPTimeoutPolicyOperators::Transaction op(new_value);
op.perform(&timeout);
return timeout.getCurlTransaction() == new_value;
}
bool handleCurlTimeoutMaxTransaction(LLSD const& newvalue)
{
AIHTTPTimeoutPolicyBase timeout = AIHTTPTimeoutPolicyBase::getDebugSettingsCurlTimeout();
AIHTTPTimeoutPolicyOperators::Transaction op(newvalue.asInteger());
op.perform(&timeout);
AIHTTPTimeoutPolicy::setDefaultCurlTimeout(timeout);
return true;
}
bool validateCurlTimeoutMaxTotalDelay(LLSD const& newvalue)
{
U32 new_value = newvalue.asInteger();
AIHTTPTimeoutPolicyBase timeout = AIHTTPTimeoutPolicyBase::getDebugSettingsCurlTimeout();
AIHTTPTimeoutPolicyOperators::Total op(new_value);
op.perform(&timeout);
return timeout.getTotalDelay() == new_value;
}
bool handleCurlTimeoutMaxTotalDelay(LLSD const& newvalue)
{
AIHTTPTimeoutPolicyBase timeout = AIHTTPTimeoutPolicyBase::getDebugSettingsCurlTimeout();
AIHTTPTimeoutPolicyOperators::Total op(newvalue.asInteger());
op.perform(&timeout);
AIHTTPTimeoutPolicy::setDefaultCurlTimeout(timeout);
return true;
}
//static
AIHTTPTimeoutPolicy::namemap_t AIHTTPTimeoutPolicy::sNameMap;
//static
AIHTTPTimeoutPolicy const* AIHTTPTimeoutPolicy::getTimeoutPolicyByName(std::string const& name)
{
namemap_t::iterator iter = sNameMap.find(name);
if (iter == sNameMap.end())
{
if (!name.empty())
{
llwarns << "Cannot find AIHTTPTimeoutPolicy with name \"" << name << "\"." << llendl;
}
return &sDebugSettingsCurlTimeout;
}
return iter->second;
}
//=======================================================================================================
// Start of Responder timeout policy list.
// Note: to find the actual responder class back, search for the name listed here but with upper case first character.
// For example, if the actual responder class is LLAccountingCostResponder then the name used here is accountingCostResponder.
#undef P
#define P(n) AIHTTPTimeoutPolicy n##_timeout(#n)
#define P2(n, b) AIHTTPTimeoutPolicy n##_timeout(#n, b)
// Policy name Policy
P(accountingCostResponder);
P(agentStateResponder);
P(assetUploadResponder);
P(asyncConsoleResponder);
P(authHandler);
P(avatarNameResponder);
P2(baseCapabilitiesComplete, transfer_18s);
P(blockingLLSDPost);
P(blockingLLSDGet);
P(blockingRawGet);
P(charactersResponder);
P(classifiedStatsResponder);
P(consoleResponder);
P2(crashLoggerResponder, transfer_5s);
P(createInventoryCategoryResponder);
P(emeraldDicDownloader);
P(environmentApplyResponder);
P(environmentRequestResponder);
P(estateChangeInfoResponder);
P(eventPollResponder);
P(fetchInventoryResponder);
P(fnPtrResponder);
P2(groupProposalBallotResponder, transfer_300s);
P(homeLocationResponder);
P(HTTPGetResponder);
P(iamHereLogin);
P(iamHere);
P(iamHereVoice);
P2(inventoryModelFetchDescendentsResponder, transfer_300s);
P(inventoryModelFetchItemResponder);
P(lcl_responder);
P(mapLayerResponder);
P(mediaTypeResponder);
P(meshDecompositionResponder);
P(meshHeaderResponder);
P(meshLODResponder);
P(meshPhysicsShapeResponder);
P(meshSkinInfoResponder);
P(mimeDiscoveryResponder);
P(moderationModeResponder);
P(muteTextResponder);
P(muteVoiceResponder);
P(navMeshRebakeResponder);
P(navMeshResponder);
P(navMeshStatusResponder);
P(newAgentInventoryVariablePriceResponder);
P(objectCostResponder);
P(objectLinksetsResponder);
P(physicsFlagsResponder);
P(placeAvatarTeleportResponder);
P(productInfoRequestResponder);
P(regionResponder);
P(remoteParcelRequestResponder);
P(responderIgnore);
P(sessionInviteResponder);
P(setDisplayNameResponder);
P2(simulatorFeaturesReceived, transfer_18s);
P(startConferenceChatResponder);
P2(startGroupVoteResponder, transfer_300s);
P(terrainLinksetsResponder);
P(translationReceiver);
P(uploadModelPremissionsResponder);
P(userReportResponder);
P(verifiedDestinationResponder);
P(viewerChatterBoxInvitationAcceptResponder);
P(viewerMediaOpenIDResponder);
P(viewerMediaWebProfileResponder);
P(viewerStatsResponder);
P(viewerVoiceAccountProvisionResponder);
P(voiceCallCapResponder);
P(voiceClientCapResponder);
P(wholeModelFeeResponder);
P(wholeModelUploadResponder);
P2(XMLRPCResponder, connect_40s);

View File

@@ -0,0 +1,138 @@
/**
* @file aihttptimeoutpolicy.h
* @brief Store the policy on timing out a HTTP curl transaction.
*
* Copyright (c) 2012, Aleric Inglewood.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution.
*
* CHANGELOG
* and additional copyright holders.
*
* 24/09/2012
* Initial version, written by Aleric Inglewood @ SL
*/
#ifndef AIHTTPTIMEOUTPOLICY_H
#define AIHTTPTIMEOUTPOLICY_H
#include "stdtypes.h"
#include <string>
#include <map>
class AIHTTPTimeoutPolicyBase;
namespace AIHTTPTimeoutPolicyOperators {
struct DNS;
struct Connect;
struct Reply;
struct Speed;
struct Transaction;
struct Total;
} // namespace AIHTTPTimeoutPolicyOperators
class AIHTTPTimeoutPolicy {
protected:
char const* const mName; // The name of this policy, for debugging purposes.
AIHTTPTimeoutPolicyBase* const mBase; // Policy this policy was based on.
static AIHTTPTimeoutPolicyBase sDebugSettingsCurlTimeout; // CurlTimeout* debug settings.
typedef std::map<std::string, AIHTTPTimeoutPolicy*> namemap_t; // Type of sNameMap.
static namemap_t sNameMap; // Map of name of timeout policies (as returned by name()) to AIHTTPTimeoutPolicy* (this).
private:
U16 mDNSLookupGrace; // Extra connect timeout the first time we connect to a host (this is to allow for DNS lookups).
U16 mMaximumConnectTime; // Connect timeouts any subsequent connects to the same host, assuming the DNS will be cached now.
U16 mMaximumReplyDelay; // Timeout for the period between sending data to the server and the HTTP header of the reply.
U16 mLowSpeedTime; // The time in seconds that a transfer should be below mLowSpeedLimit before to consider it too slow and abort.
U32 mLowSpeedLimit; // Transfer speed in bytes per second that a transfer should be below during mLowSpeedTime seconds to consider it too slow and abort.
U16 mMaximumCurlTransaction; // Timeout for the whole curl transaction (including connect and DNS lookup).
U16 mMaximumTotalDelay; // Timeout from moment of request (including the time a request is/was queued).
friend struct AIHTTPTimeoutPolicyOperators::DNS;
friend struct AIHTTPTimeoutPolicyOperators::Connect;
friend struct AIHTTPTimeoutPolicyOperators::Reply;
friend struct AIHTTPTimeoutPolicyOperators::Speed;
friend struct AIHTTPTimeoutPolicyOperators::Transaction;
friend struct AIHTTPTimeoutPolicyOperators::Total;
public:
// Construct a HTTP timeout policy object that mimics base, or Debug Settings if none given.
AIHTTPTimeoutPolicy(
char const* name,
AIHTTPTimeoutPolicyBase& base = sDebugSettingsCurlTimeout);
// Construct a HTTP timeout policy with exact specifications.
AIHTTPTimeoutPolicy(
char const* name,
U16 dns_lookup_grace,
U16 subsequent_connects,
U16 reply_delay,
U16 low_speed_time,
U32 low_speed_limit,
U16 curl_transaction,
U16 total_delay);
void sanity_checks(void) const;
// Accessors.
char const* name(void) const { return mName; }
U16 getConnectTimeout(std::string const& hostname) const;
U16 getDNSLookup(void) const { return mDNSLookupGrace; }
U16 getConnect(void) const { return mMaximumConnectTime; }
U16 getReplyDelay(void) const { return mMaximumReplyDelay; }
U16 getLowSpeedTime(void) const { return mLowSpeedTime; }
U32 getLowSpeedLimit(void) const { return mLowSpeedLimit; }
U16 getCurlTransaction(void) const { return mMaximumCurlTransaction; }
U16 getTotalDelay(void) const { return mMaximumTotalDelay; }
static AIHTTPTimeoutPolicy const& getDebugSettingsCurlTimeout(void);
static AIHTTPTimeoutPolicy const* getTimeoutPolicyByName(std::string const& name);
// Called once at start up of viewer to set a different default timeout policy than HTTPTimeoutPolicy_default.
static void setDefaultCurlTimeout(AIHTTPTimeoutPolicy const& defaultCurlTimeout);
// Called when a connect to a hostname timed out.
static bool connect_timed_out(std::string const& hostname);
protected:
// Used by AIHTTPTimeoutPolicyBase::AIHTTPTimeoutPolicyBase(AIHTTPTimeoutPolicyBase&).
AIHTTPTimeoutPolicy(AIHTTPTimeoutPolicy&);
// Abused assigned operator (called by AIHTTPTimeoutPolicyBase::operator=).
AIHTTPTimeoutPolicy& operator=(AIHTTPTimeoutPolicy const&);
};
class LLSD;
// Handlers for Debug Setting changes.
bool validateCurlTimeoutDNSLookup(LLSD const& newvalue);
bool handleCurlTimeoutDNSLookup(LLSD const& newvalue);
bool validateCurlTimeoutConnect(LLSD const& newvalue);
bool handleCurlTimeoutConnect(LLSD const& newvalue);
bool validateCurlTimeoutReplyDelay(LLSD const& newvalue);
bool handleCurlTimeoutReplyDelay(LLSD const& newvalue);
bool validateCurlTimeoutLowSpeedLimit(LLSD const& newvalue);
bool handleCurlTimeoutLowSpeedLimit(LLSD const& newvalue);
bool validateCurlTimeoutLowSpeedTime(LLSD const& newvalue);
bool handleCurlTimeoutLowSpeedTime(LLSD const& newvalue);
bool validateCurlTimeoutMaxTransaction(LLSD const& newvalue);
bool handleCurlTimeoutMaxTransaction(LLSD const& newvalue);
bool validateCurlTimeoutMaxTotalDelay(LLSD const& newvalue);
bool handleCurlTimeoutMaxTotalDelay(LLSD const& newvalue);
#endif // AIHTTPTIMEOUTPOLICY_H

View File

@@ -249,7 +249,7 @@ std::ostream& operator<<(std::ostream& os, CURLoption option)
CASEPRINT(CURLOPT_LOW_SPEED_TIME);
CASEPRINT(CURLOPT_RESUME_FROM);
CASEPRINT(CURLOPT_COOKIE);
CASEPRINT(CURLOPT_RTSPHEADER);
CASEPRINT(CURLOPT_HTTPHEADER);
CASEPRINT(CURLOPT_HTTPPOST);
CASEPRINT(CURLOPT_SSLCERT);
CASEPRINT(CURLOPT_KEYPASSWD);
@@ -702,7 +702,11 @@ CURLcode debug_curl_easy_setopt(CURL* handle, CURLoption option, ...)
{
LibcwDoutStream << "NULL";
}
LibcwDoutStream << "](" << (is_postfield ? postfieldsize : size) << " bytes))";
LibcwDoutStream << "]";
if (str)
{
LibcwDoutStream << "(" << (is_postfield ? postfieldsize : size) << " bytes))";
}
}
else
{

View File

@@ -129,7 +129,7 @@ namespace LLAvatarNameCache
// Erase expired names from cache
void eraseUnrefreshed();
bool expirationFromCacheControl(LLSD headers, F64 *expires);
bool expirationFromCacheControl(AIHTTPReceivedHeaders const& headers, F64* expires);
}
/* Sample response:
@@ -171,7 +171,10 @@ namespace LLAvatarNameCache
</llsd>
*/
class LLAvatarNameResponder : public LLHTTPClient::Responder
class AIHTTPTimeoutPolicy;
extern AIHTTPTimeoutPolicy avatarNameResponder_timeout;
class LLAvatarNameResponder : public LLHTTPClient::ResponderWithResult
{
private:
// need to store agent ids that are part of this request in case of
@@ -179,16 +182,16 @@ private:
std::vector<LLUUID> mAgentIDs;
// Need the headers to look up Expires: and Retry-After:
LLSD mHeaders;
AIHTTPReceivedHeaders mHeaders;
public:
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return avatarNameResponder_timeout; }
LLAvatarNameResponder(const std::vector<LLUUID>& agent_ids)
: mAgentIDs(agent_ids),
mHeaders()
: mAgentIDs(agent_ids)
{ }
/*virtual*/ void completedHeader(U32 status, const std::string& reason,
const LLSD& headers)
/*virtual*/ void completedHeaders(U32 status, std::string const& reason, AIHTTPReceivedHeaders const& headers)
{
mHeaders = headers;
}
@@ -788,43 +791,33 @@ void LLAvatarNameCache::insert(const LLUUID& agent_id, const LLAvatarName& av_na
sCache[agent_id] = av_name;
}
F64 LLAvatarNameCache::nameExpirationFromHeaders(LLSD headers)
F64 LLAvatarNameCache::nameExpirationFromHeaders(AIHTTPReceivedHeaders const& headers)
{
F64 expires = 0.0;
if (expirationFromCacheControl(headers, &expires))
{
return expires;
}
else
{
// With no expiration info, default to an hour
const F64 DEFAULT_EXPIRES = 60.0 * 60.0;
F64 now = LLFrameTimer::getTotalSeconds();
return now + DEFAULT_EXPIRES;
}
F64 expires;
expirationFromCacheControl(headers, &expires);
return expires;
}
bool LLAvatarNameCache::expirationFromCacheControl(LLSD headers, F64 *expires)
bool LLAvatarNameCache::expirationFromCacheControl(AIHTTPReceivedHeaders const& headers, F64* expires)
{
bool fromCacheControl = false;
S32 max_age = 3600; // With no expiration info, default to an hour.
F64 now = LLFrameTimer::getTotalSeconds();
// Allow the header to override the default
LLSD cache_control_header = headers["cache-control"];
if (cache_control_header.isDefined())
std::string cache_control;
if (headers.getFirstValue("cache-control", cache_control))
{
S32 max_age = 0;
std::string cache_control = cache_control_header.asString();
if (max_age_from_cache_control(cache_control, &max_age))
{
*expires = now + (F64)max_age;
fromCacheControl = true;
}
}
*expires = now + (F64)max_age;
LL_DEBUGS("AvNameCache")
<< ( fromCacheControl ? "expires based on cache control " : "default expiration " )
<< "in " << *expires - now << " seconds"
<< LL_ENDL;
return fromCacheControl;
}

View File

@@ -32,8 +32,8 @@
#include <boost/signals2.hpp>
class LLSD;
class LLUUID;
class AIHTTPReceivedHeaders;
namespace LLAvatarNameCache
{
@@ -98,7 +98,7 @@ namespace LLAvatarNameCache
// Compute name expiration time from HTTP Cache-Control header,
// or return default value, in seconds from epoch.
F64 nameExpirationFromHeaders(LLSD headers);
F64 nameExpirationFromHeaders(AIHTTPReceivedHeaders const& headers);
void addUseDisplayNamesCallback(const use_display_name_signal_t::slot_type& cb);
}

View File

@@ -377,7 +377,7 @@ LLBufferArray::segment_iterator_t LLBufferArray::splitAfter(U8* address)
// We have the location and the segment.
U8* base = (*it).data();
S32 size = (*it).size();
if(address == (base + size))
if(address == (base + size - 1))
{
// No need to split, since this is the last byte of the
// segment. We do not want to have zero length segments, since
@@ -393,7 +393,14 @@ LLBufferArray::segment_iterator_t LLBufferArray::splitAfter(U8* address)
mSegments.insert(it, segment2);
return rv;
}
//mMutexp should be locked before calling this.
LLBufferArray::const_segment_iterator_t LLBufferArray::beginSegment() const
{
ASSERT_LLBUFFERARRAY_MUTEX_LOCKED
return mSegments.begin();
}
//mMutexp should be locked before calling this.
LLBufferArray::segment_iterator_t LLBufferArray::beginSegment()
{
@@ -401,6 +408,13 @@ LLBufferArray::segment_iterator_t LLBufferArray::beginSegment()
return mSegments.begin();
}
//mMutexp should be locked before calling this.
LLBufferArray::const_segment_iterator_t LLBufferArray::endSegment() const
{
ASSERT_LLBUFFERARRAY_MUTEX_LOCKED
return mSegments.end();
}
//mMutexp should be locked before calling this.
LLBufferArray::segment_iterator_t LLBufferArray::endSegment()
{
@@ -636,6 +650,20 @@ U8* LLBufferArray::readAfter(
return rv;
}
void LLBufferArray::writeChannelTo(std::ostream& ostr, S32 channel) const
{
LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
LLMutexLock lock(mMutexp) ;
const_segment_iterator_t const end = mSegments.end();
for (const_segment_iterator_t it = mSegments.begin(); it != end; ++it)
{
if (it->isOnChannel(channel))
{
ostr.write((char*)it->data(), it->size());
}
}
}
U8* LLBufferArray::seek(
S32 channel,
U8* start,

View File

@@ -306,7 +306,7 @@ public:
typedef std::list<LLSegment> segment_list_t;
typedef segment_list_t::const_iterator const_segment_iterator_t;
typedef segment_list_t::iterator segment_iterator_t;
enum { npos = 0xffffffff };
static size_t const npos = (size_t)-1; // (U8*)npos is used as a magic address.
LLBufferArray();
~LLBufferArray();
@@ -495,6 +495,7 @@ public:
* @return Returns the segment if there is one.
*/
segment_iterator_t beginSegment();
const_segment_iterator_t beginSegment() const;
/**
* @brief Get the one-past-the-end segment in the buffer array
@@ -502,6 +503,7 @@ public:
* @return Returns the iterator for an invalid segment location.
*/
segment_iterator_t endSegment();
const_segment_iterator_t endSegment() const;
/**
* @brief Get the segment which holds the given address.
@@ -590,6 +592,12 @@ public:
void setThreaded(bool threaded);
//@}
/**
* @brief Read channel channel of LLBufferArray and write it to ostr.
* This is a Singularity extension.
*/
void writeChannelTo(std::ostream& ostr, S32 channel) const;
protected:
/**
* @brief Optimally put data in buffers, and reutrn segments.

View File

@@ -118,6 +118,17 @@ protected:
//virtual streamsize xsputn(char* src, streamsize length);
//@}
public:
/*
* @brief Return number of bytes in input channel.
*/
S32 count_in(void) const { return mBuffer->count(mChannels.in()); }
/*
* @brief Return number of bytes in output channel.
*/
S32 count_out(void) const { return mBuffer->count(mChannels.out()); }
protected:
// This channels we are working on.
LLChannelDescriptors mChannels;
@@ -144,6 +155,9 @@ public:
LLBufferArray* buffer);
~LLBufferStream();
S32 count_in(void) const { return mStreamBuf.count_in(); }
S32 count_out(void) const { return mStreamBuf.count_out(); }
protected:
LLBufferStreamBuf mStreamBuf;
};

View File

@@ -1,154 +0,0 @@
/**
* @file llcurl.cpp
* @author Zero / Donovan
* @date 2006-10-15
* @brief Implementation of wrapper around libcurl.
*
* $LicenseInfo:firstyear=2006&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
//////////////////////////////////////////////////////////////////////////////
/*
The trick to getting curl to do keep-alives is to reuse the
same easy handle for the requests. It appears that curl
keeps a pool of connections alive for each easy handle, but
doesn't share them between easy handles. Therefore it is
important to keep a pool of easy handles and reuse them,
rather than create and destroy them with each request. This
code does this.
Furthermore, it would behoove us to keep track of which
hosts an easy handle was used for and pick an easy handle
that matches the next request. This code does not current
do this.
*/
//////////////////////////////////////////////////////////////////////////////
static const U32 EASY_HANDLE_POOL_SIZE = 5;
static const S32 MULTI_PERFORM_CALL_REPEAT = 5;
static const S32 CURL_REQUEST_TIMEOUT = 30; // seconds per operation
static const S32 MAX_ACTIVE_REQUEST_COUNT = 100;
//static
F32 LLCurl::sCurlRequestTimeOut = 120.f; //seconds
S32 LLCurl::sMaxHandles = 256; //max number of handles, (multi handles and easy handles combined).
//////////////////////////////////////////////////////////////////////////////
AIThreadSafeSimpleDC<LLCurl::Easy::Handles> LLCurl::Easy::sHandles;
//static
CURL* LLCurl::Easy::allocEasyHandle()
{
llassert(*AIAccess<LLCurlThread*>(LLCurl::getCurlThread())) ;
CURL* ret = NULL;
//*** Multi-threaded.
AIAccess<Handles> handles_w(sHandles);
if (handles_w->free.empty())
{
ret = LLCurl::newEasyHandle();
}
else
{
ret = *(handles_w->free.begin());
handles_w->free.erase(ret);
}
if (ret)
{
handles_w->active.insert(ret);
}
return ret;
}
//static
void LLCurl::Easy::releaseEasyHandle(CURL* handle)
{
DoutEntering(dc::curl, "LLCurl::Easy::releaseEasyHandle(" << (void*)handle << ")");
BACKTRACE;
static const S32 MAX_NUM_FREE_HANDLES = 32 ;
if (!handle)
{
return ; //handle allocation failed.
//llerrs << "handle cannot be NULL!" << llendl;
}
//*** Multi-Threaded (logout only?)
AIAccess<Handles> handles_w(sHandles);
if (handles_w->active.find(handle) != handles_w->active.end())
{
handles_w->active.erase(handle);
if (handles_w->free.size() < MAX_NUM_FREE_HANDLES)
{
curl_easy_reset(handle);
handles_w->free.insert(handle);
}
else
{
LLCurl::deleteEasyHandle(handle) ;
}
}
else
{
llerrs << "Invalid handle." << llendl;
}
}
LLCurl::Easy::~Easy()
{
AISTAccess<LLCurl::ResponderPtr> responder_w(mResponder);
if (*responder_w && LLCurl::getNotQuitting()) //aborted
{
std::string reason("Request timeout, aborted.") ;
(*responder_w)->completedRaw(408, //HTTP_REQUEST_TIME_OUT, timeout, abort
reason, mChannels, mOutput);
}
*responder_w = NULL;
}
LLCurl::Easy* LLCurlRequest::allocEasy()
{
if (!mActiveMulti ||
mActiveRequestCount >= MAX_ACTIVE_REQUEST_COUNT ||
mActiveMulti->mErrorCount > 0)
{
addMulti();
}
if(!mActiveMulti)
{
return NULL ;
}
//llassert_always(mActiveMulti);
++mActiveRequestCount;
LLCurl::Easy* easy = mActiveMulti->allocEasy();
return easy;
}

View File

@@ -332,7 +332,7 @@ LLIOPipe::EStatus LLFilterSD2XMLRPCResponse::process_impl(
// we have everyting in the buffer, so turn the structure data rpc
// response into an xml rpc response.
LLBufferStream stream(channels, buffer.get());
stream << XML_HEADER << XMLRPC_METHOD_RESPONSE_HEADER;
stream << XML_HEADER << XMLRPC_METHOD_RESPONSE_HEADER << std::flush; // Flush, or buffer->count() returns too much!
LLSD sd;
LLSDSerialize::fromNotation(sd, stream, buffer->count(channels.in()));
@@ -484,7 +484,7 @@ LLIOPipe::EStatus LLFilterSD2XMLRPCRequest::process_impl(
break;
}
stream << XMLRPC_REQUEST_FOOTER;
stream << XMLRPC_REQUEST_FOOTER << std::flush;
return STATUS_DONE;
}
@@ -647,7 +647,7 @@ LLIOPipe::EStatus LLFilterXMLRPCResponse2LLSD::process_impl(
fault_string.assign(fault_str);
}
stream << "'" << LLSDNotationFormatter::escapeString(fault_string)
<< "'" <<LLSDRPC_FAULT_FOOTER;
<< "'" <<LLSDRPC_FAULT_FOOTER << std::flush;
}
else
{
@@ -658,7 +658,7 @@ LLIOPipe::EStatus LLFilterXMLRPCResponse2LLSD::process_impl(
{
stream_out(stream, param);
}
stream << LLSDRPC_RESPONSE_FOOTER;
stream << LLSDRPC_RESPONSE_FOOTER << std::flush;
}
PUMP_DEBUG;
XMLRPC_RequestFree(response, 1);
@@ -768,7 +768,7 @@ LLIOPipe::EStatus LLFilterXMLRPCRequest2LLSD::process_impl(
stream << "]";
}
}
stream << LLSDRPC_REQUEST_FOOTER;
stream << LLSDRPC_REQUEST_FOOTER << std::flush;
XMLRPC_RequestFree(request, 1);
delete[] buf;
PUMP_DEBUG;

File diff suppressed because it is too large Load Diff

View File

@@ -32,85 +32,350 @@
*/
#include <string>
#include <curl/curl.h> // CURLcode
#include <boost/intrusive_ptr.hpp>
#include "llassettype.h"
#include "llcurl.h"
#include "lliopipe.h"
#include "llurlrequest.h"
extern const F32 HTTP_REQUEST_EXPIRY_SECS;
#include "llassettype.h"
#include "llhttpstatuscodes.h"
#include "aihttpheaders.h"
class LLUUID;
class LLPumpIO;
class LLSD;
class AIHTTPTimeoutPolicy;
class LLBufferArray;
class LLChannelDescriptors;
extern AIHTTPTimeoutPolicy responderIgnore_timeout;
typedef struct _xmlrpc_request* XMLRPC_REQUEST;
typedef struct _xmlrpc_value* XMLRPC_VALUE;
class LLHTTPClient
{
// Output parameter of AICurlPrivate::CurlEasyRequest::getResult.
// Used in XMLRPCResponder.
struct AITransferInfo {
AITransferInfo() : mSizeDownload(0.0), mTotalTime(0.0), mSpeedDownload(0.0) { }
F64 mSizeDownload;
F64 mTotalTime;
F64 mSpeedDownload;
};
// Events generated by AICurlPrivate::CurlResponderBuffer.
struct AICurlResponderBufferEvents {
virtual void received_HTTP_header(void) = 0; // For example "HTTP/1.0 200 OK", the first header of a reply.
virtual void received_header(std::string const& key, std::string const& value) = 0; // Subsequent headers.
virtual void completed_headers(U32 status, std::string const& reason, AITransferInfo* info) = 0; // Transaction completed.
};
class LLHTTPClient {
public:
// class Responder moved to LLCurl
// For convenience
typedef LLCurl::Responder Responder;
typedef LLCurl::ResponderPtr ResponderPtr;
/** @name Responder base classes */
//@{
// The default actually already ignores responses.
class ResponderIgnore : public Responder { };
/**
* @class ResponderBase
* @brief Base class for all Responders.
*
* The life cycle of classes derived from this class is as follows:
* They are allocated with new on the line where get(), getByteRange() or post() is called,
* and the pointer to the allocated object is then put in a reference counting ResponderPtr.
* This ResponderPtr is passed to CurlResponderBuffer::prepRequest which stores it in its
* member mResponder. Hence, the life time of a Responder is never longer than its
* associated CurlResponderBuffer, however, if everything works correctly, then normally a
* responder is deleted in CurlResponderBuffer::removed_from_multi_handle by setting
* mReponder to NULL.
*/
class ResponderBase : public AICurlResponderBufferEvents {
public:
typedef boost::shared_ptr<LLBufferArray> buffer_ptr_t;
protected:
ResponderBase(void);
virtual ~ResponderBase();
// Read body from buffer and put it into content. If status indicates success, interpret it as LLSD, otherwise copy it as-is.
void decode_llsd_body(U32 status, std::string const& reason, LLChannelDescriptors const& channels, buffer_ptr_t const& buffer, LLSD& content);
// Read body from buffer and put it into content. Always copy it as-is.
void decode_raw_body(U32 status, std::string const& reason, LLChannelDescriptors const& channels, buffer_ptr_t const& buffer, std::string& content);
protected:
// Associated URL, used for debug output.
std::string mURL;
// Headers received from the server.
AIHTTPReceivedHeaders mReceivedHeaders;
// The curl result code.
CURLcode mCode;
// Set when the transaction finished (with or without errors).
bool mFinished;
public:
// Called to set the URL of the current request for this Responder,
// used only when printing debug output regarding activity of the Responder.
void setURL(std::string const& url);
// Accessors.
std::string const& getURL(void) const { return mURL; }
CURLcode result_code(void) const { return mCode; }
// Called by CurlResponderBuffer::timed_out or CurlResponderBuffer::processOutput.
virtual void finished(CURLcode code, U32 http_status, std::string const& reason, LLChannelDescriptors const& channels, buffer_ptr_t const& buffer) = 0;
// Return true if the curl thread is done with this transaction.
// If this returns true then it is guaranteed that none of the
// virtual functions will be called anymore: the curl thread
// will not access this object anymore.
// Note that normally you don't need to call this function.
bool is_finished(void) const { return mFinished; }
protected:
// AICurlResponderBufferEvents
// Called when the "HTTP/1.x <status> <reason>" header is received.
/*virtual*/ void received_HTTP_header(void)
{
// It's possible that this page was moved (302), so we already saw headers
// from the 302 page and are starting over on the new page now.
mReceivedHeaders.clear();
}
// Called for all remaining headers.
/*virtual*/ void received_header(std::string const& key, std::string const& value)
{
mReceivedHeaders.addHeader(key, value);
}
// Called when the whole transaction is completed (also the body was received), but before the body is processed.
/*virtual*/ void completed_headers(U32 status, std::string const& reason, AITransferInfo* info)
{
completedHeaders(status, reason, mReceivedHeaders);
}
public:
// Derived classes that implement completed_headers()/completedHeaders() should return true here.
virtual bool needsHeaders(void) const { return false; }
// A derived class should return true if curl should follow redirections.
// The default is not to follow redirections.
virtual bool followRedir(void) { return false; }
// Timeout policy to use.
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const = 0;
protected:
// Derived classes can override this to get the HTML headers that were received, when the message is completed.
virtual void completedHeaders(U32 status, std::string const& reason, AIHTTPReceivedHeaders const& headers)
{
// The default does nothing.
}
private:
// Used by ResponderPtr. Object is deleted when reference count reaches zero.
LLAtomicU32 mReferenceCount;
friend void intrusive_ptr_add_ref(ResponderBase* p); // Called by boost::intrusive_ptr when a new copy of a boost::intrusive_ptr<ResponderBase> is made.
friend void intrusive_ptr_release(ResponderBase* p); // Called by boost::intrusive_ptr when a boost::intrusive_ptr<ResponderBase> is destroyed.
// This function must delete the ResponderBase object when the reference count reaches zero.
};
/**
* @class ResponderWithCompleted
* @brief Base class for Responders that implement completed, or completedRaw if the response is not LLSD.
*/
class ResponderWithCompleted : public ResponderBase {
protected:
// ResponderBase event
// The responder finished. Do not override this function in derived classes; override completedRaw instead.
/*virtual*/ void finished(CURLcode code, U32 http_status, std::string const& reason, LLChannelDescriptors const& channels, buffer_ptr_t const& buffer)
{
mCode = code;
// Allow classes derived from ResponderBase to override completedRaw
// (if not they should override completed or be derived from Responder instead).
completedRaw(http_status, reason, channels, buffer);
mFinished = true;
}
protected:
// Events generated by this class.
// Derived classes can override this to get the raw data of the body of the HTML message that was received.
// The default is to interpret the content as LLSD and call completed().
virtual void completedRaw(U32 status, std::string const& reason, LLChannelDescriptors const& channels, buffer_ptr_t const& buffer);
// ... or, derived classes can override this to get LLSD content when the message is completed.
// The default aborts, as it should never be called (can't make it pure virtual though, so
// classes that override completedRaw don't need to implement this function, too).
virtual void completed(U32 status, std::string const& reason, LLSD const& content);
#ifdef SHOW_ASSERT
// Responders derived from this class must override either completedRaw or completed.
// They may not attempt to override any of the virual functions defined by ResponderBase.
// Define those functions here with different parameters in order to cause a compile
// warning when a class accidently tries to override them.
enum YOU_ARE_DERIVING_FROM_THE_WRONG_CLASS { };
virtual void result(YOU_ARE_DERIVING_FROM_THE_WRONG_CLASS) { }
virtual void errorWithContent(YOU_ARE_DERIVING_FROM_THE_WRONG_CLASS) { }
virtual void error(YOU_ARE_DERIVING_FROM_THE_WRONG_CLASS) { }
#endif
};
/**
* @class ResponderWithResult
* @brief Base class for reponders that expect LLSD in the body of the reply.
*
* Classes derived from ResponderWithResult must implement result, and either errorWithContent or error.
*/
class ResponderWithResult : public ResponderBase {
protected:
// The responder finished. Do not override this function in derived classes; use ResponderWithCompleted instead.
/*virtual*/ void finished(CURLcode code, U32 http_status, std::string const& reason, LLChannelDescriptors const& channels, buffer_ptr_t const& buffer);
protected:
// Events generated by this class.
// Derived classes must override this to receive the content of a body upon success.
virtual void result(LLSD const& content) = 0;
// Derived classes can override this to get informed when a bad HTML status code is received.
// The default calls error().
virtual void errorWithContent(U32 status, std::string const& reason, LLSD const& content);
// ... or, derived classes can override this to get informed when a bad HTML status code is received.
// The default prints the error to llinfos.
virtual void error(U32 status, std::string const& reason);
public:
// Called from LLSDMessage::ResponderAdapter::listener.
// LLSDMessage::ResponderAdapter is a hack, showing among others by fact that it needs these functions.
void pubErrorWithContent(CURLcode code, U32 status, std::string const& reason, LLSD const& content) { mCode = code; errorWithContent(status, reason, content); mFinished = true; }
void pubResult(LLSD const& content) { mCode = CURLE_OK; result(content); mFinished = true; }
#ifdef SHOW_ASSERT
// Responders derived from this class must override result, and either errorWithContent or error.
// They may not attempt to override any of the virual functions defined by ResponderWithCompleted.
// Define those functions here with different parameter in order to cause a compile
// warning when a class accidently tries to override them.
enum YOU_ARE_DERIVING_FROM_THE_WRONG_CLASS { };
virtual void completedRaw(YOU_ARE_DERIVING_FROM_THE_WRONG_CLASS) { }
virtual void completed(YOU_ARE_DERIVING_FROM_THE_WRONG_CLASS) { }
#endif
};
/**
* @class LegacyPolledResponder
* @brief As ResponderWithCompleted but caches the result for polling.
*
* This class allows old polling code to poll if the transaction finished
* by calling is_finished() (from the main the thread) and then access the
* results-- as opposed to immediately digesting the results when any of
* the virtual functions are called.
*/
class LegacyPolledResponder : public ResponderWithCompleted {
protected:
U32 mStatus;
std::string mReason;
protected:
// The responder finished. Do not override this function in derived classes.
/*virtual*/ void finished(CURLcode code, U32 http_status, std::string const& reason, LLChannelDescriptors const& channels, buffer_ptr_t const& buffer)
{
mStatus = http_status;
mReason = reason;
// Call base class implementation.
ResponderWithCompleted::finished(code, http_status, reason, channels, buffer);
}
public:
LegacyPolledResponder(void) : mStatus(HTTP_INTERNAL_ERROR) { }
// Accessors.
U32 http_status(void) const { return mStatus; }
std::string const& reason(void) const { return mReason; }
};
/**
* @class ResponderIgnoreBody
* @brief Base class for responders that ignore the result body.
*/
class ResponderIgnoreBody : public ResponderWithResult {
void result(LLSD const&) { }
};
/**
* @class ResponderIgnore
* @brief Responder that ignores the reply, if any, from the server.
*/
class ResponderIgnore : public ResponderIgnoreBody {
/*virtual*/ AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return responderIgnore_timeout;}
};
// A Responder is passed around as ResponderPtr, which causes it to automatically
// destruct when there are no pointers left pointing to it.
typedef boost::intrusive_ptr<ResponderBase> ResponderPtr;
//@}
/** @name non-blocking API */
//@{
static void head(
const std::string& url,
ResponderPtr,
const LLSD& headers = LLSD(),
const F32 timeout=HTTP_REQUEST_EXPIRY_SECS);
static void getByteRange(const std::string& url, S32 offset, S32 bytes, ResponderPtr, const LLSD& headers=LLSD(), const F32 timeout=HTTP_REQUEST_EXPIRY_SECS);
static void get(const std::string& url, ResponderPtr, const LLSD& headers = LLSD(), const F32 timeout=HTTP_REQUEST_EXPIRY_SECS);
static void get(const std::string& url, const LLSD& query, ResponderPtr, const LLSD& headers = LLSD(), const F32 timeout=HTTP_REQUEST_EXPIRY_SECS);
static void head(std::string const& url, ResponderPtr responder, AIHTTPHeaders& headers);
static void head(std::string const& url, ResponderPtr responder)
{ AIHTTPHeaders headers; head(url, responder, headers); }
static void put(
const std::string& url,
const LLSD& body,
ResponderPtr,
const LLSD& headers = LLSD(),
const F32 timeout=HTTP_REQUEST_EXPIRY_SECS);
static void getHeaderOnly(const std::string& url, ResponderPtr, const F32 timeout=HTTP_REQUEST_EXPIRY_SECS);
static void getHeaderOnly(const std::string& url, ResponderPtr, const LLSD& headers, const F32 timeout=HTTP_REQUEST_EXPIRY_SECS);
static void getByteRange(std::string const& url, S32 offset, S32 bytes, ResponderPtr responder, AIHTTPHeaders& headers);
static void getByteRange(std::string const& url, S32 offset, S32 bytes, ResponderPtr responder)
{ AIHTTPHeaders headers; getByteRange(url, offset, bytes, responder, headers); }
static void get(std::string const& url, ResponderPtr responder, AIHTTPHeaders& headers);
static void get(std::string const& url, ResponderPtr responder)
{ AIHTTPHeaders headers; get(url, responder, headers); }
static void get(std::string const& url, LLSD const& query, ResponderPtr responder, AIHTTPHeaders& headers);
static void get(std::string const& url, LLSD const& query, ResponderPtr responder)
{ AIHTTPHeaders headers; get(url, query, responder, headers); }
static void put(std::string const& url, LLSD const& body, ResponderPtr responder, AIHTTPHeaders& headers);
static void put(std::string const& url, LLSD const& body, ResponderPtr responder)
{ AIHTTPHeaders headers; put(url, body, responder, headers); }
static void getHeaderOnly(std::string const& url, ResponderPtr responder, AIHTTPHeaders& headers);
static void getHeaderOnly(std::string const& url, ResponderPtr responder)
{ AIHTTPHeaders headers; getHeaderOnly(url, responder, headers); }
static void post(std::string const& url, LLSD const& body, ResponderPtr responder, AIHTTPHeaders& headers);
static void post(std::string const& url, LLSD const& body, ResponderPtr responder)
{ AIHTTPHeaders headers; post(url, body, responder, headers); }
/** Takes ownership of request and deletes it when sent */
static void postXMLRPC(std::string const& url, XMLRPC_REQUEST request, ResponderPtr responder, AIHTTPHeaders& headers);
static void postXMLRPC(std::string const& url, XMLRPC_REQUEST request, ResponderPtr responder)
{ AIHTTPHeaders headers; postXMLRPC(url, request, responder, headers); }
static void postXMLRPC(std::string const& url, char const* method, XMLRPC_VALUE value, ResponderPtr responder, AIHTTPHeaders& headers);
static void postXMLRPC(std::string const& url, char const* method, XMLRPC_VALUE value, ResponderPtr responder)
{ AIHTTPHeaders headers; postXMLRPC(url, method, value, responder, headers); }
static void post(
const std::string& url,
const LLSD& body,
ResponderPtr,
const LLSD& headers = LLSD(),
const F32 timeout=HTTP_REQUEST_EXPIRY_SECS);
/** Takes ownership of data and deletes it when sent */
static void postRaw(
const std::string& url,
const U8* data,
S32 size,
ResponderPtr responder,
const LLSD& headers = LLSD(),
const F32 timeout=HTTP_REQUEST_EXPIRY_SECS);
static void postFile(
const std::string& url,
const std::string& filename,
ResponderPtr,
const LLSD& headers = LLSD(),
const F32 timeout=HTTP_REQUEST_EXPIRY_SECS);
static void postFile(
const std::string& url,
const LLUUID& uuid,
LLAssetType::EType asset_type,
ResponderPtr responder,
const LLSD& headers = LLSD(),
const F32 timeout=HTTP_REQUEST_EXPIRY_SECS);
static void postRaw(std::string const& url, const char* data, S32 size, ResponderPtr responder, AIHTTPHeaders& headers);
static void postRaw(std::string const& url, const char* data, S32 size, ResponderPtr responder)
{ AIHTTPHeaders headers; postRaw(url, data, size, responder, headers); }
static void postFile(std::string const& url, std::string const& filename, ResponderPtr responder, AIHTTPHeaders& headers);
static void postFile(std::string const& url, std::string const& filename, ResponderPtr responder)
{ AIHTTPHeaders headers; postFile(url, filename, responder, headers); }
static void postFile(std::string const& url, const LLUUID& uuid, LLAssetType::EType asset_type, ResponderPtr responder, AIHTTPHeaders& headers);
static void postFile(std::string const& url, const LLUUID& uuid, LLAssetType::EType asset_type, ResponderPtr responder)
{ AIHTTPHeaders headers; postFile(url, uuid, asset_type, responder, headers); }
static void del(std::string const& url, ResponderPtr responder, AIHTTPHeaders& headers);
static void del(std::string const& url, ResponderPtr responder)
{ AIHTTPHeaders headers; del(url, responder, headers); }
static void del(
const std::string& url,
ResponderPtr responder,
const LLSD& headers = LLSD(),
const F32 timeout=HTTP_REQUEST_EXPIRY_SECS);
///< sends a DELETE method, but we can't call it delete in c++
/**
@@ -122,22 +387,28 @@ public:
* @param headers A map of key:value headers to pass to the request
* @param timeout The number of seconds to give the server to respond.
*/
static void move(
const std::string& url,
const std::string& destination,
ResponderPtr responder,
const LLSD& headers = LLSD(),
const F32 timeout=HTTP_REQUEST_EXPIRY_SECS);
static void move(std::string const& url, std::string const& destination, ResponderPtr responder, AIHTTPHeaders& headers);
static void move(std::string const& url, std::string const& destination, ResponderPtr responder)
{ AIHTTPHeaders headers; move(url, destination, responder, headers); }
//@}
/**
* @brief Blocking HTTP get that returns an LLSD map of status and body.
* @brief Blocking HTTP GET that returns an LLSD map of status and body.
*
* @param url the complete serialized (and escaped) url to get
* @return An LLSD of { 'status':status, 'body':payload }
*/
static LLSD blockingGet(const std::string& url);
static LLSD blockingGet(std::string const& url);
/**
* @brief Blocking HTTP GET that returns the raw body.
*
* @param url the complete serialized (and escaped) url to get
* @param result the target string to write the body to
* @return HTTP status
*/
static U32 blockingGetRaw(const std::string& url, std::string& result);
/**
* @brief Blocking HTTP POST that returns an LLSD map of status and body.
@@ -146,15 +417,7 @@ public:
* @param body the LLSD post body
* @return An LLSD of { 'status':status (an int), 'body':payload (an LLSD) }
*/
static LLSD blockingPost(const std::string& url, const LLSD& body);
static void setPump(LLPumpIO& pump);
///< must be called before any of the above calls are made
static bool hasPump();
///< for testing
static LLPumpIO &getPump();
///< Hippo special
static LLSD blockingPost(std::string const& url, LLSD const& body);
};
#endif // LL_LLHTTPCLIENT_H

View File

@@ -1,55 +0,0 @@
/**
* @file llhttpclientadapter.cpp
* @brief
*
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "llhttpclientadapter.h"
#include "llhttpclient.h"
LLHTTPClientAdapter::~LLHTTPClientAdapter()
{
}
void LLHTTPClientAdapter::get(const std::string& url, LLCurl::ResponderPtr responder)
{
LLSD empty_pragma_header;
// Pragma is required to stop curl adding "no-cache"
// Space is required to stop llurlrequest from turnning off proxying
empty_pragma_header["Pragma"] = " ";
LLHTTPClient::get(url, responder, empty_pragma_header);
}
void LLHTTPClientAdapter::get(const std::string& url, LLCurl::ResponderPtr responder, const LLSD& headers)
{
LLSD empty_pragma_header = headers;
// as above
empty_pragma_header["Pragma"] = " ";
LLHTTPClient::get(url, responder, empty_pragma_header);
}
void LLHTTPClientAdapter::put(const std::string& url, const LLSD& body, LLCurl::ResponderPtr responder)
{
LLHTTPClient::put(url, body, responder);
}

View File

@@ -1,43 +0,0 @@
/**
* @file llhttpclientadepter.h
* @brief
*
* $LicenseInfo:firstyear=2008&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LL_HTTPCLIENTADAPTER_H
#define LL_HTTPCLIENTADAPTER_H
#include "llhttpclientinterface.h"
#include "llsingleton.h" // LLSingleton<>
class LLHTTPClientAdapter : public LLHTTPClientInterface, public LLSingleton<LLHTTPClientAdapter>
{
public:
virtual ~LLHTTPClientAdapter();
virtual void get(const std::string& url, LLCurl::ResponderPtr responder);
virtual void get(const std::string& url, LLCurl::ResponderPtr responder, const LLSD& headers);
virtual void put(const std::string& url, const LLSD& body, LLCurl::ResponderPtr responder);
};
#endif

View File

@@ -1,45 +0,0 @@
/**
* @file llhttpclientinterface.h
* @brief
*
* $LicenseInfo:firstyear=2008&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LL_LLHTTPCLIENTINTERFACE_H
#define LL_LLHTTPCLIENTINTERFACE_H
#include "linden_common.h"
#include "llcurl.h"
#include <string>
class LLHTTPClientInterface
{
public:
virtual ~LLHTTPClientInterface() {}
virtual void get(const std::string& url, LLCurl::ResponderPtr responder) = 0;
virtual void get(const std::string& url, LLCurl::ResponderPtr responder, const LLSD& headers) = 0;
virtual void put(const std::string& url, const LLSD& body, LLCurl::ResponderPtr responder) = 0;
};
#endif // LL_LLHTTPCLIENTINTERFACE_H

View File

@@ -272,6 +272,7 @@ LLIOPipe::EStatus LLHTTPPipe::process_impl(
context[CONTEXT_RESPONSE][CONTEXT_HEADERS] = headers;
LLBufferStream ostr(channels, buffer.get());
LLSDSerialize::toXML(mGoodResult, ostr);
ostr << std::flush;
return STATUS_DONE;
}
@@ -284,7 +285,7 @@ LLIOPipe::EStatus LLHTTPPipe::process_impl(
context[CONTEXT_RESPONSE]["statusCode"] = mStatusCode;
context[CONTEXT_RESPONSE]["statusMessage"] = mStatusMessage;
LLBufferStream ostr(channels, buffer.get());
ostr << mStatusMessage;
ostr << mStatusMessage << std::flush;
return STATUS_DONE;
}
@@ -293,7 +294,7 @@ LLIOPipe::EStatus LLHTTPPipe::process_impl(
context[CONTEXT_RESPONSE][CONTEXT_HEADERS] = mHeaders;
context[CONTEXT_RESPONSE]["statusCode"] = mStatusCode;
LLBufferStream ostr(channels, buffer.get());
ostr << mStatusMessage;
ostr << mStatusMessage << std::flush;
return STATUS_DONE;
}
@@ -633,7 +634,7 @@ void LLHTTPResponder::markBad(
LLBufferStream out(channels, buffer.get());
out << HTTP_VERSION_STR << " 400 Bad Request\r\n\r\n<html>\n"
<< "<title>Bad Request</title>\n<body>\nBad Request.\n"
<< "</body>\n</html>\n";
<< "</body>\n</html>\n" << std::flush;
}
static LLFastTimer::DeclareTimer FTM_PROCESS_HTTP_RESPONDER("HTTP Responder");
@@ -926,7 +927,7 @@ LLIOPipe::EStatus LLHTTPResponder::process_impl(
mState = STATE_SHORT_CIRCUIT;
str << HTTP_VERSION_STR << " 404 Not Found\r\n\r\n<html>\n"
<< "<title>Not Found</title>\n<body>\nNode '" << mAbsPathAndQuery
<< "' not found.\n</body>\n</html>\n";
<< "' not found.\n</body>\n</html>\n" << std::flush;
}
}

View File

@@ -27,7 +27,6 @@
#include "linden_common.h"
#include "llregionpresenceverifier.h"
#include "llhttpclientinterface.h"
#include <sstream>
#include "net.h"
#include "message.h"
@@ -78,7 +77,7 @@ void LLRegionPresenceVerifier::RegionResponder::result(const LLSD& content)
std::stringstream uri;
uri << "http://" << destination.getString() << "/state/basic/";
mSharedData->getHttpClient().get(
LLHTTPClient::get(
uri.str(),
new VerifiedDestinationResponder(mUri, mSharedData, content, mRetryCount));
}
@@ -131,12 +130,11 @@ void LLRegionPresenceVerifier::VerifiedDestinationResponder::result(const LLSD&
void LLRegionPresenceVerifier::VerifiedDestinationResponder::retry()
{
LLSD headers;
headers["Cache-Control"] = "no-cache, max-age=0";
AIHTTPHeaders headers("Cache-Control", "no-cache, max-age=0");
llinfos << "Requesting region information, get uncached for region "
<< mUri << llendl;
--mRetryCount;
mSharedData->getHttpClient().get(mUri, new RegionResponder(mUri, mSharedData, mRetryCount), headers);
LLHTTPClient::get(mUri, new RegionResponder(mUri, mSharedData, mRetryCount), headers);
}
void LLRegionPresenceVerifier::VerifiedDestinationResponder::error(U32 status, const std::string& reason)

View File

@@ -33,7 +33,9 @@
#include "llsd.h"
#include <boost/intrusive_ptr.hpp>
class LLHTTPClientInterface;
class AIHTTPTimeoutPolicy;
extern AIHTTPTimeoutPolicy regionResponder_timeout;
extern AIHTTPTimeoutPolicy verifiedDestinationResponder_timeout;
class LLRegionPresenceVerifier
{
@@ -47,17 +49,17 @@ public:
virtual void onRegionVerified(const LLSD& region_details) = 0;
virtual void onRegionVerificationFailed() = 0;
virtual LLHTTPClientInterface& getHttpClient() = 0;
public: /* but not really -- don't touch this */
U32 mReferenceCount;
};
typedef boost::intrusive_ptr<Response> ResponsePtr;
class RegionResponder : public LLHTTPClient::Responder
class RegionResponder : public LLHTTPClient::ResponderWithResult
{
public:
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return regionResponder_timeout; }
RegionResponder(const std::string& uri, ResponsePtr data,
S32 retry_count);
virtual ~RegionResponder();
@@ -70,9 +72,11 @@ public:
S32 mRetryCount;
};
class VerifiedDestinationResponder : public LLHTTPClient::Responder
class VerifiedDestinationResponder : public LLHTTPClient::ResponderWithResult
{
public:
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return verifiedDestinationResponder_timeout; }
VerifiedDestinationResponder(const std::string& uri, ResponsePtr data,
const LLSD& content, S32 retry_count);
virtual ~VerifiedDestinationResponder();

View File

@@ -45,6 +45,7 @@
#include "llhost.h"
#include "message.h"
#include "llsdutil.h"
#include "aihttptimeoutpolicy.h"
// Declare a static LLSDMessage instance to ensure that we have a listener as
// soon as someone tries to post on our canonical LLEventPump name.
@@ -62,13 +63,15 @@ LLSDMessage::LLSDMessage():
bool LLSDMessage::httpListener(const LLSD& request)
{
llassert(false); // This function is never called. --Aleric
// Extract what we want from the request object. We do it all up front
// partly to document what we expect.
LLSD::String url(request["url"]);
LLSD payload(request["payload"]);
LLSD::String reply(request["reply"]);
LLSD::String error(request["error"]);
LLSD::Real timeout(request["timeout"]);
LLSD::String timeoutpolicy(request["timeoutpolicy"]);
// If the LLSD doesn't even have a "url" key, we doubt it was intended for
// this listener.
if (url.empty())
@@ -77,21 +80,25 @@ bool LLSDMessage::httpListener(const LLSD& request)
out << "request event without 'url' key to '" << mEventPump.getName() << "'";
throw ArgError(out.str());
}
// Establish default timeout. This test relies on LLSD::asReal() returning
// exactly 0.0 for an undef value.
if (! timeout)
{
timeout = HTTP_REQUEST_EXPIRY_SECS;
}
LLHTTPClient::post(url, payload,
new LLSDMessage::EventResponder(LLEventPumps::instance(),
request,
url, "POST", reply, error),
LLSD(), // headers
(F32)timeout);
LLSDMessage::EventResponder* responder =
new LLSDMessage::EventResponder(LLEventPumps::instance(), request, url, "POST", reply, error);
responder->setTimeoutPolicy(timeoutpolicy);
LLHTTPClient::post(url, payload, responder);
return false;
}
LLSDMessage::EventResponder::EventResponder(LLEventPumps& pumps, LLSD const& request, std::string const& target,
std::string const& message, std::string const& replyPump, std::string const& errorPump) :
mPumps(pumps), mReqID(request), mTarget(target), mMessage(message), mReplyPump(replyPump), mErrorPump(errorPump),
mHTTPTimeoutPolicy(AIHTTPTimeoutPolicy::getTimeoutPolicyByName(std::string()))
{
}
void LLSDMessage::EventResponder::setTimeoutPolicy(std::string const& name)
{
mHTTPTimeoutPolicy = AIHTTPTimeoutPolicy::getTimeoutPolicyByName(name);
}
void LLSDMessage::EventResponder::result(const LLSD& data)
{
// If our caller passed an empty replyPump name, they're not
@@ -121,6 +128,7 @@ void LLSDMessage::EventResponder::errorWithContent(U32 status, const std::string
LLSD info(mReqID.makeResponse());
info["target"] = mTarget;
info["message"] = mMessage;
info["code"] = mCode;
info["status"] = LLSD::Integer(status);
info["reason"] = reason;
info["content"] = content;
@@ -137,7 +145,7 @@ void LLSDMessage::EventResponder::errorWithContent(U32 status, const std::string
}
}
LLSDMessage::ResponderAdapter::ResponderAdapter(LLHTTPClient::ResponderPtr responder,
LLSDMessage::ResponderAdapter::ResponderAdapter(LLHTTPClient::ResponderWithResult* responder,
const std::string& name):
mResponder(responder),
mReplyPump(name + ".reply", true), // tweak name for uniqueness
@@ -147,15 +155,25 @@ LLSDMessage::ResponderAdapter::ResponderAdapter(LLHTTPClient::ResponderPtr respo
mErrorPump.listen("self", boost::bind(&ResponderAdapter::listener, this, _1, false));
}
std::string LLSDMessage::ResponderAdapter::getTimeoutPolicyName(void) const
{
return mResponder->getHTTPTimeoutPolicy().name();
}
bool LLSDMessage::ResponderAdapter::listener(const LLSD& payload, bool success)
{
LLHTTPClient::ResponderWithResult* responder = dynamic_cast<LLHTTPClient::ResponderWithResult*>(mResponder.get());
// If this assertion fails then ResponderAdapter has been used for a ResponderWithCompleted derived class,
// which is not allowed because ResponderAdapter can only work for classes derived from Responder that
// implement result() and errorWithContent (or just error).
llassert_always(responder);
if (success)
{
mResponder->pubResult(payload);
responder->pubResult(payload);
}
else
{
mResponder->pubErrorWithContent(payload["status"].asInteger(), payload["reason"], payload["content"]);
responder->pubErrorWithContent((CURLcode)payload["code"].asInteger(), payload["status"].asInteger(), payload["reason"], payload["content"]);
}
/*---------------- MUST BE LAST STATEMENT BEFORE RETURN ----------------*/

View File

@@ -37,6 +37,8 @@
#include <stdexcept>
class LLSD;
class AIHTTPTimeoutPolicy;
extern AIHTTPTimeoutPolicy eventResponder_timeout;
/**
* Class managing the messaging API described in
@@ -64,37 +66,47 @@ public:
* must be visible to the reply/error methods can conveniently be stored
* on that class itself, if it's not already.
*
* The LLHTTPClient::Responder idiom requires a separate instance of a
* The LLHTTPClient::ResponderBase idiom requires a separate instance of a
* separate class so that it can dispatch to the code of interest by
* calling canonical virtual methods. Interesting state must be copied
* into that new object.
*
* With some trepidation, because existing response code is packaged in
* LLHTTPClient::Responder subclasses, we provide this adapter class
* LLHTTPClient::ResponderWithResult subclasses, we provide this adapter class
* <i>for transitional purposes only.</i> Instantiate a new heap
* ResponderAdapter with your new LLHTTPClient::ResponderPtr. Pass
* ResponderAdapter::getReplyName() and/or getErrorName() in your
* LLSDMessage (or LLViewerRegion::getCapAPI()) request event. The
* ResponderAdapter will call the appropriate Responder method, then
* @c delete itself.
*
* Singularity note: I think this class/API is a bad idea that makes things
* more complex, a lot slower and less OO. The idea to get methods called
* on the same class that does the request is a nice idea, but should
* be implemented through boost::bind and NOT use LLSD. Avoid.
* Also note that this only works for ResponderWithResult derived classes,
* not for responders derived from ResponderWithCompleted.
* --Aleric
*/
class ResponderAdapter
{
public:
/**
* Bind the new LLHTTPClient::Responder subclass instance.
* Bind the new LLHTTPClient::ResponderWithResult subclass instance.
*
* Passing the constructor a name other than the default is only
* interesting if you suspect some usage will lead to an exception or
* log message.
*/
ResponderAdapter(LLHTTPClient::ResponderPtr responder,
ResponderAdapter(LLHTTPClient::ResponderWithResult* responder,
const std::string& name="ResponderAdapter");
/// EventPump name on which LLSDMessage should post reply event
std::string getReplyName() const { return mReplyPump.getName(); }
/// EventPump name on which LLSDMessage should post error event
std::string getErrorName() const { return mErrorPump.getName(); }
/// Name of timeout policy to use.
std::string getTimeoutPolicyName() const;
private:
// We have two different LLEventStreams, though we route them both to
@@ -121,11 +133,13 @@ private:
friend class LLCapabilityListener;
/// Responder used for internal purposes by LLSDMessage and
/// LLCapabilityListener. Others should use higher-level APIs.
class EventResponder: public LLHTTPClient::Responder
class EventResponder: public LLHTTPClient::ResponderWithResult
{
public:
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return *mHTTPTimeoutPolicy; }
/**
* LLHTTPClient::Responder that dispatches via named LLEventPump instances.
* LLHTTPClient::ResponderWithResult that dispatches via named LLEventPump instances.
* We bind LLEventPumps, even though it's an LLSingleton, for testability.
* We bind the string names of the desired LLEventPump instances rather
* than actually obtain()ing them so we only obtain() the one we're going
@@ -140,14 +154,9 @@ private:
EventResponder(LLEventPumps& pumps,
const LLSD& request,
const std::string& target, const std::string& message,
const std::string& replyPump, const std::string& errorPump):
mPumps(pumps),
mReqID(request),
mTarget(target),
mMessage(message),
mReplyPump(replyPump),
mErrorPump(errorPump)
{}
const std::string& replyPump, const std::string& errorPump);
void setTimeoutPolicy(std::string const& name);
virtual void result(const LLSD& data);
virtual void errorWithContent(U32 status, const std::string& reason, const LLSD& content);
@@ -156,6 +165,7 @@ private:
LLEventPumps& mPumps;
LLReqID mReqID;
const std::string mTarget, mMessage, mReplyPump, mErrorPump;
AIHTTPTimeoutPolicy const* mHTTPTimeoutPolicy;
};
private:

View File

@@ -36,8 +36,7 @@
#include <algorithm>
#include <openssl/x509_vfy.h>
#include <openssl/ssl.h>
#include "llcurl.h"
#include "aicurleasyrequeststatemachine.h"
#include "llioutil.h"
#include "llmemtype.h"
#include "llpumpio.h"
@@ -47,6 +46,7 @@
#include "llapr.h"
#include "llscopedvolatileaprpool.h"
#include "llfasttimer.h"
#include "message.h"
static const U32 HTTP_STATUS_PIPE_ERROR = 499;
/**
@@ -54,41 +54,6 @@ static const U32 HTTP_STATUS_PIPE_ERROR = 499;
*/
const std::string CONTEXT_TRANSFERED_BYTES("transfered_bytes");
static size_t headerCallback(char* data, size_t size, size_t nmemb, void* user);
/**
* class LLURLRequestDetail
*/
class LLURLRequestDetail
{
public:
LLURLRequestDetail();
~LLURLRequestDetail();
std::string mURL;
AICurlEasyRequest mCurlEasyRequest;
LLIOPipe::buffer_ptr_t mResponseBuffer;
LLChannelDescriptors mChannels;
U8* mLastRead;
U32 mBodyLimit;
S32 mByteAccumulator;
bool mIsBodyLimitSet;
};
LLURLRequestDetail::LLURLRequestDetail() :
mCurlEasyRequest(false),
mLastRead(NULL),
mBodyLimit(0),
mByteAccumulator(0),
mIsBodyLimitSet(false)
{
}
LLURLRequestDetail::~LLURLRequestDetail()
{
mLastRead = NULL;
}
/**
* class LLURLRequest
*/
@@ -96,7 +61,8 @@ LLURLRequestDetail::~LLURLRequestDetail()
// static
std::string LLURLRequest::actionAsVerb(LLURLRequest::ERequestAction action)
{
static const std::string VERBS[] =
static int const array_size = HTTP_MOVE + 1; // INVALID == 0
static char const* const VERBS[array_size] =
{
"(invalid)",
"HEAD",
@@ -106,81 +72,88 @@ std::string LLURLRequest::actionAsVerb(LLURLRequest::ERequestAction action)
"DELETE",
"MOVE"
};
if(((S32)action <=0) || ((S32)action >= REQUEST_ACTION_COUNT))
return VERBS[action >= array_size ? INVALID : action];
}
// This might throw AICurlNoEasyHandle.
LLURLRequest::LLURLRequest(LLURLRequest::ERequestAction action, std::string const& url, Injector* body,
LLHTTPClient::ResponderPtr responder, AIHTTPHeaders& headers, bool is_auth, bool no_compression) :
mAction(action), mURL(url), mIsAuth(is_auth), mNoCompression(no_compression),
mBody(body), mResponder(responder), mHeaders(headers)
{
}
void LLURLRequest::initialize_impl(void)
{
if (mHeaders.hasHeader("Cookie"))
{
return VERBS[0];
allowCookies();
}
return VERBS[action];
}
LLURLRequest::LLURLRequest(LLURLRequest::ERequestAction action) :
mAction(action)
{
LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
// This might throw AICurlNoEasyHandle.
initialize();
}
LLURLRequest::LLURLRequest(
LLURLRequest::ERequestAction action,
const std::string& url) :
mAction(action)
{
LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
// This might throw AICurlNoEasyHandle.
initialize();
setURL(url);
}
LLURLRequest::~LLURLRequest()
{
LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
// If the header is "Pragma" with no value, the caller intends to
// force libcurl to drop the Pragma header it so gratuitously inserts.
// Before inserting the header, force libcurl to not use the proxy.
std::string pragma_value;
if (mHeaders.getValue("Pragma", pragma_value) && pragma_value.empty())
{
AICurlEasyRequest_wat curl_easy_request_w(*mDetail->mCurlEasyRequest);
curl_easy_request_w->revokeCallbacks();
curl_easy_request_w->send_events_to(NULL);
useProxy(false);
}
delete mDetail;
}
void LLURLRequest::setURL(const std::string& url)
{
mDetail->mURL = url;
}
if (mAction == HTTP_PUT || mAction == HTTP_POST)
{
// If the Content-Type header was passed in we defer to the caller's wisdom,
// but if they did not specify a Content-Type, then ask the injector.
mHeaders.addHeader("Content-Type", mBody->contentType(), AIHTTPHeaders::keep_existing_header);
}
else
{
// Check to see if we have already set Accept or not. If no one
// set it, set it to application/llsd+xml since that's what we
// almost always want.
mHeaders.addHeader("Accept", "application/llsd+xml", AIHTTPHeaders::keep_existing_header);
}
std::string LLURLRequest::getURL() const
{
return mDetail->mURL;
if (mAction == HTTP_POST && gMessageSystem)
{
mHeaders.addHeader("X-SecondLife-UDP-Listen-Port", llformat("%d", gMessageSystem->mPort));
}
bool success = false;
try
{
AICurlEasyRequest_wat easy_request_w(*mCurlEasyRequest);
easy_request_w->prepRequest(easy_request_w, mHeaders, mResponder);
if (mBody)
{
// This might throw AICurlNoBody.
mBodySize = mBody->get_body(easy_request_w->sChannels, easy_request_w->getInput());
}
success = configure(easy_request_w);
}
catch (AICurlNoBody const& error)
{
llwarns << "Injector::get_body() failed: " << error.what() << llendl;
}
if (success)
{
// Continue to initialize base class.
AICurlEasyRequestStateMachine::initialize_impl();
}
else
{
abort();
}
}
void LLURLRequest::addHeader(const char* header)
{
LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
AICurlEasyRequest_wat curlEasyRequest_w(*mDetail->mCurlEasyRequest);
AICurlEasyRequest_wat curlEasyRequest_w(*mCurlEasyRequest);
curlEasyRequest_w->addHeader(header);
}
void LLURLRequest::checkRootCertificate(bool check)
{
AICurlEasyRequest_wat curlEasyRequest_w(*mDetail->mCurlEasyRequest);
curlEasyRequest_w->setopt(CURLOPT_SSL_VERIFYPEER, check ? 1L : 0L);
curlEasyRequest_w->setoptString(CURLOPT_ENCODING, "");
}
void LLURLRequest::setBodyLimit(U32 size)
{
mDetail->mBodyLimit = size;
mDetail->mIsBodyLimitSet = true;
}
void LLURLRequest::setCallback(LLURLRequestComplete* callback)
{
LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
mCompletionCallback = callback;
AICurlEasyRequest_wat curlEasyRequest_w(*mDetail->mCurlEasyRequest);
curlEasyRequest_w->setHeaderCallback(&headerCallback, (void*)callback);
}
// Added to mitigate the effect of libcurl looking
// for the ALL_PROXY and http_proxy env variables
// and deciding to insert a Pragma: no-cache
@@ -214,284 +187,28 @@ void LLURLRequest::useProxy(bool use_proxy)
LL_DEBUGS("Proxy") << "use_proxy = " << (use_proxy?'Y':'N') << ", env_proxy = " << (!env_proxy.empty() ? env_proxy : "(null)") << LL_ENDL;
AICurlEasyRequest_wat curlEasyRequest_w(*mDetail->mCurlEasyRequest);
AICurlEasyRequest_wat curlEasyRequest_w(*mCurlEasyRequest);
curlEasyRequest_w->setoptString(CURLOPT_PROXY, (use_proxy && !env_proxy.empty()) ? env_proxy : std::string(""));
}
#ifdef AI_UNUSED
void LLURLRequest::useProxy(const std::string &proxy)
{
AICurlEasyRequest_wat curlEasyRequest_w(*mDetail->mCurlEasyRequest);
AICurlEasyRequest_wat curlEasyRequest_w(*mCurlEasyRequest);
curlEasyRequest_w->setoptString(CURLOPT_PROXY, proxy);
}
#endif
void LLURLRequest::allowCookies()
{
AICurlEasyRequest_wat curlEasyRequest_w(*mDetail->mCurlEasyRequest);
AICurlEasyRequest_wat curlEasyRequest_w(*mCurlEasyRequest);
curlEasyRequest_w->setoptString(CURLOPT_COOKIEFILE, "");
}
//virtual
bool LLURLRequest::hasExpiration(void) const
bool LLURLRequest::configure(AICurlEasyRequest_wat const& curlEasyRequest_w)
{
// Currently, this ALWAYS returns false -- because only AICurlEasyRequestStateMachine uses buffered
// AICurlEasyRequest objects, and LLURLRequest uses (unbuffered) AICurlEasyRequest directly, which
// have no expiration facility.
return mDetail->mCurlEasyRequest.isBuffered();
}
//virtual
bool LLURLRequest::hasNotExpired(void) const
{
if (!mDetail->mCurlEasyRequest.isBuffered())
return true;
AICurlEasyRequest_wat buffered_easy_request_w(*mDetail->mCurlEasyRequest);
AICurlResponderBuffer_wat buffer_w(*mDetail->mCurlEasyRequest);
return buffer_w->isValid();
}
// virtual
LLIOPipe::EStatus LLURLRequest::handleError(
LLIOPipe::EStatus status,
LLPumpIO* pump)
{
DoutEntering(dc::curl, "LLURLRequest::handleError(" << LLIOPipe::lookupStatusString(status) << ", " << (void*)pump << ")");
LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
if (LL_LIKELY(!mDetail->mCurlEasyRequest.isBuffered())) // Currently always true.
{
// The last reference will be deleted when the pump that this chain belongs to
// is removed from the running chains vector, upon returning from this function.
// This keeps the CurlEasyRequest object alive until the curl thread cleanly removed it.
Dout(dc::curl, "Calling mDetail->mCurlEasyRequest.removeRequest()");
mDetail->mCurlEasyRequest.removeRequest();
}
else if (!hasNotExpired())
{
// The buffered version has it's own time out handling, and that already expired,
// so we can ignore the expiration of this timer (currently never happens).
// I left it here because it's what LL did (in the form if (!isValid() ...),
// and it would be relevant if this characteristic of mDetail->mCurlEasyRequest
// would change. --Aleric
return STATUS_EXPIRED ;
}
if(mCompletionCallback && pump)
{
LLURLRequestComplete* complete = NULL;
complete = (LLURLRequestComplete*)mCompletionCallback.get();
complete->httpStatus(
HTTP_STATUS_PIPE_ERROR,
LLIOPipe::lookupStatusString(status));
complete->responseStatus(status);
pump->respond(complete);
mCompletionCallback = NULL;
}
return status;
}
void LLURLRequest::added_to_multi_handle(AICurlEasyRequest_wat&)
{
}
void LLURLRequest::finished(AICurlEasyRequest_wat&)
{
}
void LLURLRequest::removed_from_multi_handle(AICurlEasyRequest_wat&)
{
mRemoved = true;
}
static LLFastTimer::DeclareTimer FTM_PROCESS_URL_REQUEST("URL Request");
// virtual
LLIOPipe::EStatus LLURLRequest::process_impl(
const LLChannelDescriptors& channels,
buffer_ptr_t& buffer,
bool& eos,
LLSD& context,
LLPumpIO* pump)
{
LLFastTimer t(FTM_PROCESS_URL_REQUEST);
PUMP_DEBUG;
LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
//llinfos << "LLURLRequest::process_impl()" << llendl;
if (!buffer) return STATUS_ERROR;
if (!mDetail) return STATUS_ERROR; //Seems to happen on occasion. Need to hunt down why.
// we're still waiting or processing, check how many
// bytes we have accumulated.
const S32 MIN_ACCUMULATION = 100000;
if(pump && (mDetail->mByteAccumulator > MIN_ACCUMULATION))
{
static LLFastTimer::DeclareTimer FTM_URL_ADJUST_TIMEOUT("Adjust Timeout");
LLFastTimer t(FTM_URL_ADJUST_TIMEOUT);
// This is a pretty sloppy calculation, but this
// tries to make the gross assumption that if data
// is coming in at 56kb/s, then this transfer will
// probably succeed. So, if we're accumlated
// 100,000 bytes (MIN_ACCUMULATION) then let's
// give this client another 2s to complete.
const F32 TIMEOUT_ADJUSTMENT = 2.0f;
mDetail->mByteAccumulator = 0;
pump->adjustTimeoutSeconds(TIMEOUT_ADJUSTMENT);
lldebugs << "LLURLRequest adjustTimeoutSeconds for request: " << mDetail->mURL << llendl;
if (mState == STATE_INITIALIZED)
{
llinfos << "LLURLRequest adjustTimeoutSeconds called during upload" << llendl;
}
}
switch(mState)
{
case STATE_INITIALIZED:
{
PUMP_DEBUG;
// We only need to wait for input if we are uploading
// something.
if(((HTTP_PUT == mAction) || (HTTP_POST == mAction)) && !eos)
{
// we're waiting to get all of the information
return STATUS_BREAK;
}
// *FIX: bit of a hack, but it should work. The configure and
// callback method expect this information to be ready.
mDetail->mResponseBuffer = buffer;
mDetail->mChannels = channels;
if(!configure())
{
return STATUS_ERROR;
}
mRemoved = false;
mState = STATE_WAITING_FOR_RESPONSE;
mDetail->mCurlEasyRequest.addRequest(); // Add easy handle to multi handle.
return STATUS_BREAK;
}
case STATE_WAITING_FOR_RESPONSE:
case STATE_PROCESSING_RESPONSE:
{
if (!mRemoved) // Not removed from multi handle yet?
{
// Easy handle is still being processed.
return STATUS_BREAK;
}
// Curl thread finished with this easy handle.
mState = STATE_CURL_FINISHED;
}
case STATE_CURL_FINISHED:
{
PUMP_DEBUG;
LLIOPipe::EStatus status = STATUS_NO_CONNECTION; // Catch-all failure code.
// Left braces in order not to change indentation.
{
CURLcode result;
static LLFastTimer::DeclareTimer FTM_PROCESS_URL_REQUEST_GET_RESULT("Get Result");
AICurlEasyRequest_wat(*mDetail->mCurlEasyRequest)->getResult(&result);
mState = STATE_HAVE_RESPONSE;
context[CONTEXT_REQUEST][CONTEXT_TRANSFERED_BYTES] = mRequestTransferedBytes;
context[CONTEXT_RESPONSE][CONTEXT_TRANSFERED_BYTES] = mResponseTransferedBytes;
lldebugs << this << "Setting context to " << context << llendl;
switch(result)
{
case CURLE_OK:
case CURLE_WRITE_ERROR:
// NB: The error indication means that we stopped the
// writing due the body limit being reached
if(mCompletionCallback && pump)
{
LLURLRequestComplete* complete = NULL;
complete = (LLURLRequestComplete*)
mCompletionCallback.get();
complete->responseStatus(
result == CURLE_OK
? STATUS_OK : STATUS_STOP);
LLPumpIO::links_t chain;
LLPumpIO::LLLinkInfo link;
link.mPipe = mCompletionCallback;
link.mChannels = LLBufferArray::makeChannelConsumer(
channels);
chain.push_back(link);
static LLFastTimer::DeclareTimer FTM_PROCESS_URL_PUMP_RESPOND("Pump Respond");
{
LLFastTimer t(FTM_PROCESS_URL_PUMP_RESPOND);
pump->respond(chain, buffer, context);
}
mCompletionCallback = NULL;
}
status = STATUS_BREAK; // This is what the old code returned. Does it make sense?
break;
case CURLE_FAILED_INIT:
case CURLE_COULDNT_CONNECT:
status = STATUS_NO_CONNECTION;
break;
default:
llwarns << "URLRequest Error: " << result
<< ", "
<< LLCurl::strerror(result)
<< ", "
<< (mDetail->mURL.empty() ? "<EMPTY URL>" : mDetail->mURL)
<< llendl;
status = STATUS_ERROR;
break;
}
}
return status;
}
case STATE_HAVE_RESPONSE:
PUMP_DEBUG;
// we already stuffed everything into channel in in the curl
// callback, so we are done.
eos = true;
context[CONTEXT_REQUEST][CONTEXT_TRANSFERED_BYTES] = mRequestTransferedBytes;
context[CONTEXT_RESPONSE][CONTEXT_TRANSFERED_BYTES] = mResponseTransferedBytes;
lldebugs << this << "Setting context to " << context << llendl;
return STATUS_DONE;
default:
PUMP_DEBUG;
context[CONTEXT_REQUEST][CONTEXT_TRANSFERED_BYTES] = mRequestTransferedBytes;
context[CONTEXT_RESPONSE][CONTEXT_TRANSFERED_BYTES] = mResponseTransferedBytes;
lldebugs << this << "Setting context to " << context << llendl;
return STATUS_ERROR;
}
}
void LLURLRequest::initialize()
{
LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
mState = STATE_INITIALIZED;
// This might throw AICurlNoEasyHandle.
mDetail = new LLURLRequestDetail;
{
AICurlEasyRequest_wat curlEasyRequest_w(*mDetail->mCurlEasyRequest);
curlEasyRequest_w->setWriteCallback(&downCallback, (void*)this);
curlEasyRequest_w->setReadCallback(&upCallback, (void*)this);
}
mRequestTransferedBytes = 0;
mResponseTransferedBytes = 0;
}
static LLFastTimer::DeclareTimer FTM_URL_REQUEST_CONFIGURE("URL Configure");
bool LLURLRequest::configure()
{
LLFastTimer t(FTM_URL_REQUEST_CONFIGURE);
LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
bool rv = false;
S32 bytes = mDetail->mResponseBuffer->countAfter(
mDetail->mChannels.in(),
NULL);
{
AICurlEasyRequest_wat curlEasyRequest_w(*mDetail->mCurlEasyRequest);
switch(mAction)
{
case HTTP_HEAD:
@@ -506,28 +223,30 @@ bool LLURLRequest::configure()
curlEasyRequest_w->setopt(CURLOPT_FOLLOWLOCATION, 1);
// Set Accept-Encoding to allow response compression
curlEasyRequest_w->setoptString(CURLOPT_ENCODING, "");
curlEasyRequest_w->setoptString(CURLOPT_ENCODING, mNoCompression ? "identity" : "");
rv = true;
break;
case HTTP_PUT:
{
// Disable the expect http 1.1 extension. POST and PUT default
// to turning this on, and I am not too sure what it means.
// to using this, causing the broken server to get confused.
curlEasyRequest_w->addHeader("Expect:");
curlEasyRequest_w->setopt(CURLOPT_UPLOAD, 1);
curlEasyRequest_w->setopt(CURLOPT_INFILESIZE, bytes);
curlEasyRequest_w->setopt(CURLOPT_INFILESIZE, mBodySize);
rv = true;
break;
}
case HTTP_POST:
{
// Set the handle for an http post
curlEasyRequest_w->setPost(bytes);
curlEasyRequest_w->setPost(mBodySize);
// Set Accept-Encoding to allow response compression
curlEasyRequest_w->setoptString(CURLOPT_ENCODING, "");
curlEasyRequest_w->setoptString(CURLOPT_ENCODING, mNoCompression ? "identity" : "");
rv = true;
break;
}
case HTTP_DELETE:
// Set the handle for an http post
curlEasyRequest_w->setoptString(CURLOPT_CUSTOMREQUEST, "DELETE");
@@ -537,7 +256,6 @@ bool LLURLRequest::configure()
case HTTP_MOVE:
// Set the handle for an http post
curlEasyRequest_w->setoptString(CURLOPT_CUSTOMREQUEST, "MOVE");
// *NOTE: should we check for the Destination header?
rv = true;
break;
@@ -547,205 +265,12 @@ bool LLURLRequest::configure()
}
if(rv)
{
curlEasyRequest_w->finalizeRequest(mDetail->mURL);
curlEasyRequest_w->send_events_to(this);
curlEasyRequest_w->setopt(CURLOPT_SSL_VERIFYPEER, gNoVerifySSLCert ? 0L : 1L);
// Don't verify host name if this is not an authentication request,
// so urls with scrubbed host names will work (improves DNS performance).
curlEasyRequest_w->setopt(CURLOPT_SSL_VERIFYHOST, (gNoVerifySSLCert || !mIsAuth) ? 0L : 2L);
curlEasyRequest_w->finalizeRequest(mURL, mResponder->getHTTPTimeoutPolicy(), this);
}
}
return rv;
}
// static
size_t LLURLRequest::downCallback(
char* data,
size_t size,
size_t nmemb,
void* user)
{
LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
LLURLRequest* req = (LLURLRequest*)user;
if(STATE_WAITING_FOR_RESPONSE == req->mState)
{
req->mState = STATE_PROCESSING_RESPONSE;
}
U32 bytes = size * nmemb;
if (req->mDetail->mIsBodyLimitSet)
{
if (bytes > req->mDetail->mBodyLimit)
{
bytes = req->mDetail->mBodyLimit;
req->mDetail->mBodyLimit = 0;
}
else
{
req->mDetail->mBodyLimit -= bytes;
}
}
req->mDetail->mResponseBuffer->append(
req->mDetail->mChannels.out(),
(U8*)data,
bytes);
req->mResponseTransferedBytes += bytes;
req->mDetail->mByteAccumulator += bytes;
return bytes;
}
// static
size_t LLURLRequest::upCallback(
char* data,
size_t size,
size_t nmemb,
void* user)
{
LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
LLURLRequest* req = (LLURLRequest*)user;
S32 bytes = llmin(
(S32)(size * nmemb),
req->mDetail->mResponseBuffer->countAfter(
req->mDetail->mChannels.in(),
req->mDetail->mLastRead));
req->mDetail->mLastRead = req->mDetail->mResponseBuffer->readAfter(
req->mDetail->mChannels.in(),
req->mDetail->mLastRead,
(U8*)data,
bytes);
req->mRequestTransferedBytes += bytes;
return bytes;
}
static size_t headerCallback(char* header_line, size_t size, size_t nmemb, void* user)
{
size_t header_len = size * nmemb;
LLURLRequestComplete* complete = (LLURLRequestComplete*)user;
if (!complete || !header_line)
{
return header_len;
}
// *TODO: This should be a utility in llstring.h: isascii()
for (size_t i = 0; i < header_len; ++i)
{
if (header_line[i] < 0)
{
return header_len;
}
}
std::string header(header_line, header_len);
// Per HTTP spec the first header line must be the status line.
if (header.substr(0,5) == "HTTP/")
{
std::string::iterator end = header.end();
std::string::iterator pos1 = std::find(header.begin(), end, ' ');
if (pos1 != end) ++pos1;
std::string::iterator pos2 = std::find(pos1, end, ' ');
if (pos2 != end) ++pos2;
std::string::iterator pos3 = std::find(pos2, end, '\r');
std::string version(header.begin(), pos1);
std::string status(pos1, pos2);
std::string reason(pos2, pos3);
S32 status_code = atoi(status.c_str());
if (status_code > 0)
{
complete->httpStatus((U32)status_code, reason);
return header_len;
}
}
std::string::iterator sep = std::find(header.begin(),header.end(),':');
if (sep != header.end())
{
std::string key(header.begin(), sep);
std::string value(sep + 1, header.end());
key = utf8str_tolower(utf8str_trim(key));
value = utf8str_trim(value);
complete->header(key, value);
}
else
{
LLStringUtil::trim(header);
if (!header.empty())
{
llwarns << "Unable to parse header: " << header << llendl;
}
}
return header_len;
}
/**
* LLURLRequestComplete
*/
LLURLRequestComplete::LLURLRequestComplete() :
mRequestStatus(LLIOPipe::STATUS_ERROR)
{
LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
}
// virtual
LLURLRequestComplete::~LLURLRequestComplete()
{
LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
}
//virtual
void LLURLRequestComplete::header(const std::string& header, const std::string& value)
{
}
//virtual
void LLURLRequestComplete::complete(const LLChannelDescriptors& channels,
const buffer_ptr_t& buffer)
{
if(STATUS_OK == mRequestStatus)
{
response(channels, buffer);
}
else
{
noResponse();
}
}
//virtual
void LLURLRequestComplete::response(const LLChannelDescriptors& channels,
const buffer_ptr_t& buffer)
{
llwarns << "LLURLRequestComplete::response default implementation called"
<< llendl;
}
//virtual
void LLURLRequestComplete::noResponse()
{
llwarns << "LLURLRequestComplete::noResponse default implementation called"
<< llendl;
}
void LLURLRequestComplete::responseStatus(LLIOPipe::EStatus status)
{
LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
mRequestStatus = status;
}
static LLFastTimer::DeclareTimer FTM_PROCESS_URL_COMPLETE("URL Complete");
// virtual
LLIOPipe::EStatus LLURLRequestComplete::process_impl(
const LLChannelDescriptors& channels,
buffer_ptr_t& buffer,
bool& eos,
LLSD& context,
LLPumpIO* pump)
{
LLFastTimer t(FTM_PROCESS_URL_COMPLETE);
PUMP_DEBUG;
complete(channels, buffer);
return STATUS_OK;
}

View File

@@ -7,6 +7,7 @@
* $LicenseInfo:firstyear=2005&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
* Copyright (C) 2012, Aleric Inglewood.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -35,41 +36,21 @@
*/
#include <string>
#include "lliopipe.h"
#include "llchainio.h"
#include "llerror.h"
#include "llcurl.h"
#include "aicurleasyrequeststatemachine.h"
#include "aihttpheaders.h"
extern const std::string CONTEXT_REQUEST;
extern const std::string CONTEXT_RESPONSE;
extern const std::string CONTEXT_TRANSFERED_BYTES;
class LLURLRequestDetail;
class LLURLRequestComplete;
struct x509_store_ctx_st;
typedef struct x509_store_ctx_st X509_STORE_CTX;
/**
* @class LLURLRequest
* @brief Class to handle url based requests.
* @see LLIOPipe
*
* Currently, this class is implemented on top of curl. From the
* vantage of a programmer using this class, you do not care so much,
* but it's useful to know since in order to accomplish 'non-blocking'
* behavior, we have to use a more expensive curl interface which can
* still block if the server enters a half-accepted state. It would be
* worth the time and effort to eventually port this to a raw client
* socket.
*/
class LLURLRequest : public LLIOPipe, protected AICurlEasyHandleEvents
class Injector
{
LOG_CLASS(LLURLRequest);
public:
public:
typedef LLHTTPClient::ResponderBase::buffer_ptr_t buffer_ptr_t;
virtual char const* contentType(void) const = 0;
virtual U32 get_body(LLChannelDescriptors const& channels, buffer_ptr_t& buffer) = 0;
// To avoid compiler warning.
virtual ~Injector() { }
};
typedef int (* SSLCertVerifyCallback)(X509_STORE_CTX *ctx, void *param);
class LLURLRequest : public AICurlEasyRequestStateMachine {
public:
/**
* @brief This enumeration is for specifying the type of request.
*/
@@ -86,44 +67,33 @@ public:
};
/**
* @brief Turn the requst action into an http verb.
* @brief Turn the request action into an http verb.
*/
static std::string actionAsVerb(ERequestAction action);
/**
* @brief Constructor.
*
* @param action One of the ERequestAction enumerations.
*/
LLURLRequest(ERequestAction action);
/**
* @brief Constructor.
*
* @param action One of the ERequestAction enumerations.
* @param url The url of the request. It should already be encoded.
*/
LLURLRequest(ERequestAction action, const std::string& url);
LLURLRequest(ERequestAction action, std::string const& url, Injector* body, LLHTTPClient::ResponderPtr responder, AIHTTPHeaders& headers, bool is_auth, bool no_compression);
/**
* @brief Destructor.
*/
virtual ~LLURLRequest();
protected:
// Call abort(), not delete.
/*virtual*/ ~LLURLRequest() { }
/* @name Instance methods
public:
/**
* @brief Turn on cookie handling for this request with CURLOPT_COOKIEFILE.
*/
//@{
/**
* @brief Set the url for the request
*
* This method assumes the url is encoded appropriately for the
* request.
* The url must be set somehow before the first call to process(),
* or the url will not be set correctly.
*
*/
void setURL(const std::string& url);
std::string getURL() const;
void allowCookies(void);
/**
* @ brief Turn off (or on) the CURLOPT_PROXY header.
*/
void useProxy(bool use_proxy);
/**
* @brief Add a header to the http post.
*
@@ -133,213 +103,29 @@ public:
* required headers will be automatically constructed, so this is
* usually useful for encoding parameters.
*/
void addHeader(const char* header);
/**
* @brief Check remote server certificate signed by a known root CA.
*
* Set whether request will check that remote server
* certificates are signed by a known root CA when using HTTPS.
*/
void checkRootCertificate(bool check);
/**
* @brief Return at most size bytes of body.
*
* If the body had more bytes than this limit, they will not be
* returned and the connection closed. In this case, STATUS_STOP
* will be passed to responseStatus();
*/
void setBodyLimit(U32 size);
/**
* @brief Set a completion callback for this URLRequest.
*
* The callback is added to this URLRequet's pump when either the
* entire buffer is known or an error like timeout or connection
* refused has happened. In the case of a complete transfer, this
* object builds a response chain such that the callback and the
* next process consumer get to read the output.
*
* This setup is a little fragile since the url request consumer
* might not just read the data - it may do a channel change,
* which invalidates the input to the callback, but it works well
* in practice.
*/
void setCallback(LLURLRequestComplete* callback);
//@}
/* @name LLIOPipe virtual implementations
*/
/**
* @ brief Turn off (or on) the CURLOPT_PROXY header.
*/
void useProxy(bool use_proxy);
/**
* @ brief Set the CURLOPT_PROXY header to the given value.
*/
void useProxy(const std::string& proxy);
/**
* @brief Turn on cookie handling for this request with CURLOPT_COOKIEFILE.
*/
void allowCookies();
/*virtual*/ bool hasExpiration(void) const;
/*virtual*/ bool hasNotExpired(void) const;
public:
/**
* @brief Give this pipe a chance to handle a generated error
*/
virtual EStatus handleError(EStatus status, LLPumpIO* pump);
protected:
/**
* @brief Process the data in buffer
*/
virtual EStatus process_impl(
const LLChannelDescriptors& channels,
buffer_ptr_t& buffer,
bool& eos,
LLSD& context,
LLPumpIO* pump);
//@}
protected:
enum EState
{
STATE_INITIALIZED,
STATE_WAITING_FOR_RESPONSE,
STATE_PROCESSING_RESPONSE,
STATE_CURL_FINISHED,
STATE_HAVE_RESPONSE,
};
EState mState;
ERequestAction mAction;
LLURLRequestDetail* mDetail;
LLIOPipe::ptr_t mCompletionCallback;
S32 mRequestTransferedBytes;
S32 mResponseTransferedBytes;
// mRemoved is used instead of changing mState directly, because I'm not convinced the latter is atomic.
// Set to false before adding curl request and then only tested.
// Reset in removed_from_multi_handle (by another thread), this is thread-safe.
bool mRemoved;
/*virtual*/ void added_to_multi_handle(AICurlEasyRequest_wat&);
/*virtual*/ void finished(AICurlEasyRequest_wat&);
/*virtual*/ void removed_from_multi_handle(AICurlEasyRequest_wat&);
private:
/**
* @brief Initialize the object. Called during construction.
*/
void initialize();
void addHeader(char const* header);
private:
/**
* @brief Handle action specific url request configuration.
*
* @return Returns true if this is configured.
*/
bool configure();
bool configure(AICurlEasyRequest_wat const& curlEasyRequest_w);
/**
* @brief Download callback method.
*/
static size_t downCallback(
char* data,
size_t size,
size_t nmemb,
void* user);
private:
ERequestAction mAction;
std::string mURL;
bool mIsAuth; // Set for authentication messages (login, buy land, buy currency).
bool mNoCompression; // Set to disable using gzip.
Injector* mBody; // Non-zero iff the action is HTTP_POST and HTTP_PUT.
U32 mBodySize;
LLHTTPClient::ResponderPtr mResponder;
AIHTTPHeaders mHeaders;
/**
* @brief Upload callback method.
*/
static size_t upCallback(
char* data,
size_t size,
size_t nmemb,
void* user);
/**
* @brief Declaration of unimplemented method to prevent copy
* construction.
*/
LLURLRequest(const LLURLRequest&);
};
/**
* @class LLURLRequestComplete
* @brief Class which can optionally be used with an LLURLRequest to
* get notification when the url request is complete.
*/
class LLURLRequestComplete : public LLIOPipe
{
public:
// Called once for each header received, except status lines
virtual void header(const std::string& header, const std::string& value);
// May be called more than once, particularly for redirects and proxy madness.
// Ex. a 200 for a connection to https through a proxy, followed by the "real" status
// a 3xx for a redirect followed by a "real" status, or more redirects.
virtual void httpStatus(U32 status, const std::string& reason) { }
virtual void complete(
const LLChannelDescriptors& channels,
const buffer_ptr_t& buffer);
/**
* @brief This method is called when we got a valid response.
*
* It is up to class implementers to do something useful here.
*/
virtual void response(
const LLChannelDescriptors& channels,
const buffer_ptr_t& buffer);
/**
* @brief This method is called if there was no response.
*
* It is up to class implementers to do something useful here.
*/
virtual void noResponse();
/**
* @brief This method will be called by the LLURLRequest object.
*
* If this is set to STATUS_OK or STATUS_STOP, then the transfer
* is asssumed to have worked. This will lead to calling response()
* on the next call to process(). Otherwise, this object will call
* noResponse() on the next call to process.
* @param status The status of the URLRequest.
*/
void responseStatus(EStatus status);
// constructor & destructor.
LLURLRequestComplete();
virtual ~LLURLRequestComplete();
protected:
/* @name LLIOPipe virtual implementations
*/
//@{
/**
* @brief Process the data in buffer
*/
virtual EStatus process_impl(
const LLChannelDescriptors& channels,
buffer_ptr_t& buffer,
bool& eos,
LLSD& context,
LLPumpIO* pump);
//@}
// value to note if we actually got the response. This value
// depends on correct useage from the LLURLRequest instance.
EStatus mRequestStatus;
protected:
// Handle initializing the object.
/*virtual*/ void initialize_impl(void);
};
#endif // LL_LLURLREQUEST_H

View File

@@ -83,6 +83,9 @@
#include "llmemtype.h"
#include "llpacketring.h"
class AIHTTPTimeoutPolicy;
extern AIHTTPTimeoutPolicy fnPtrResponder_timeout;
// Constants
//const char* MESSAGE_LOG_FILENAME = "message.log";
static const F32 CIRCUIT_DUMP_TIMEOUT = 30.f;
@@ -104,7 +107,7 @@ public:
namespace
{
class LLFnPtrResponder : public LLHTTPClient::Responder
class LLFnPtrResponder : public LLHTTPClient::ResponderWithResult
{
LOG_CLASS(LLFnPtrResponder);
public:
@@ -133,6 +136,8 @@ namespace
if(NULL != mCallback) mCallback(mCallbackData, LL_ERR_NOERR);
}
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return fnPtrResponder_timeout; }
private:
void (*mCallback)(void **,S32);

View File

@@ -3,23 +3,16 @@
project(llplugin)
include(00-Common)
include(CURL)
include(LLCommon)
include(LLImage)
include(LLMath)
include(LLMessage)
include(LLRender)
include(LLXML)
include(LLWindow)
include_directories(
${LLCOMMON_INCLUDE_DIRS}
${LLIMAGE_INCLUDE_DIRS}
${LLMATH_INCLUDE_DIRS}
${LLMESSAGE_INCLUDE_DIRS}
${LLRENDER_INCLUDE_DIRS}
${LLXML_INCLUDE_DIRS}
${LLWINDOW_INCLUDE_DIRS}
${LLQTWEBKIT_INCLUDE_DIR}
)

View File

@@ -5,7 +5,6 @@ project(llui)
include(00-Common)
include(LLCommon)
include(LLImage)
include(LLInventory)
include(LLMath)
include(LLMessage)
include(LLRender)
@@ -16,7 +15,6 @@ include(LLXML)
include_directories(
${LLCOMMON_INCLUDE_DIRS}
${LLIMAGE_INCLUDE_DIRS}
${LLINVENTORY_INCLUDE_DIRS}
${LLMATH_INCLUDE_DIRS}
${LLMESSAGE_INCLUDE_DIRS}
${LLRENDER_INCLUDE_DIRS}

View File

@@ -18,7 +18,6 @@ include(LLMath)
include(LLRender)
include(LLVFS)
include(LLWindow)
include(LLXML)
include(UI)
include_directories(
@@ -28,7 +27,6 @@ include_directories(
${LLRENDER_INCLUDE_DIRS}
${LLVFS_INCLUDE_DIRS}
${LLWINDOW_INCLUDE_DIRS}
${LLXML_INCLUDE_DIRS}
)
set(llwindow_SOURCE_FILES

View File

@@ -27,7 +27,7 @@ include(LLInventory)
include(LLMath)
include(LLMessage)
include(LLPlugin)
include(AIStateMachine)
include(StateMachine)
include(LLPrimitive)
include(LLRender)
include(LLUI)
@@ -50,6 +50,7 @@ endif (WINDOWS)
include_directories(
${CMAKE_SOURCE_DIR}/newview
${STATEMACHINE_INCLUDE_DIRS}
${DBUSGLIB_INCLUDE_DIRS}
${HUNSPELL_INCLUDE_DIR}
${ELFIO_INCLUDE_DIR}
@@ -90,7 +91,6 @@ set(viewer_SOURCE_FILES
hippogridmanager.cpp
hippolimits.cpp
hippopanelgrids.cpp
hipporestrequest.cpp
importtracker.cpp
jcfloaterareasearch.cpp
lggdicdownload.cpp
@@ -132,7 +132,6 @@ set(viewer_SOURCE_FILES
llconfirmationmanager.cpp
llconsole.cpp
llcontainerview.cpp
llcurlrequest.cpp
llcurrencyuimanager.cpp
llcylinder.cpp
lldaycyclemanager.cpp
@@ -544,7 +543,7 @@ set(viewer_SOURCE_FILES
llworldmap.cpp
llworldmipmap.cpp
llworldmapview.cpp
llxmlrpctransaction.cpp
llxmlrpcresponder.cpp
m7wlinterface.cpp
NACLantispam.cpp
noise.cpp
@@ -591,7 +590,6 @@ set(viewer_HEADER_FILES
hippogridmanager.h
hippolimits.h
hippopanelgrids.h
hipporestrequest.h
importtracker.h
jcfloaterareasearch.h
lggdicdownload.h
@@ -634,7 +632,6 @@ set(viewer_HEADER_FILES
llconfirmationmanager.h
llconsole.h
llcontainerview.h
llcurlrequest.h
llcurrencyuimanager.h
llcylinder.h
lldaycyclemanager.h
@@ -1052,7 +1049,7 @@ set(viewer_HEADER_FILES
llworldmap.h
llworldmipmap.h
llworldmapview.h
llxmlrpctransaction.h
llxmlrpcresponder.h
m7wlinterface.h
macmain.h
NACLantispam.h
@@ -1549,7 +1546,7 @@ target_link_libraries(${VIEWER_BINARY_NAME}
${LLINVENTORY_LIBRARIES}
${LLMESSAGE_LIBRARIES}
${LLPLUGIN_LIBRARIES}
${AISTATEMACHINE_LIBRARIES}
${STATEMACHINE_LIBRARIES}
${LLPRIMITIVE_LIBRARIES}
${LLRENDER_LIBRARIES}
${FREETYPE_LIBRARIES}

View File

@@ -4208,7 +4208,7 @@
<key>CurlConcurrentConnections</key>
<map>
<key>Comment</key>
<string>Maximum number of simultaneous curl connections (requires restart)</string>
<string>Maximum number of simultaneous curl connections</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
@@ -4227,16 +4227,82 @@
<key>Value</key>
<integer>256</integer>
</map>
<key>CurlRequestTimeOut</key>
<key>CurlTimeoutDNSLookup</key>
<map>
<key>Comment</key>
<string>Max idle time of a curl request before killed (requires restart)</string>
<string>Extra time in seconds added to CurlTimeoutConnect for the initial connect to a host</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>F32</string>
<string>U32</string>
<key>Value</key>
<real>120.0</real>
<real>60</real>
</map>
<key>CurlTimeoutConnect</key>
<map>
<key>Comment</key>
<string>Maximum time allowed until a connection is established (after adding the easy handle) until the server is considered unreachable and the connection is terminated</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>U32</string>
<key>Value</key>
<real>10</real>
</map>
<key>CurlTimeoutReplyDelay</key>
<map>
<key>Comment</key>
<string>Maximum time the viewer will wait between sending data to the server and receiving (a partial) reply</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>U32</string>
<key>Value</key>
<real>60</real>
</map>
<key>CurlTimeoutLowSpeedLimit</key>
<map>
<key>Comment</key>
<string>If a transfer speed drops below this value (in bytes/s) during CurlTimeoutLowSpeedTime seconds, the transfer is considered too slow and is terminated</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>U32</string>
<key>Value</key>
<real>56000</real>
</map>
<key>CurlTimeoutLowSpeedTime</key>
<map>
<key>Comment</key>
<string>If a transfer speed drops below CurlTimeoutLowSpeedLimit (in bytes/s) during this amount of seconds, the transfer is considered too slow and is terminated</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>U32</string>
<key>Value</key>
<real>30</real>
</map>
<key>CurlTimeoutMaxTransaction</key>
<map>
<key>Comment</key>
<string>Maximum total time of a curl transaction, from when the easy handle is added till the transaction has completed. This INCLUDES DNS lookups (CurlTimeoutDNSLookup), connect time (CurlTimeoutConnect) and waiting for the first server reply (CurlTimeoutReply) as well as the actually time needed for data transfer</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>U32</string>
<key>Value</key>
<real>300</real>
</map>
<key>CurlTimeoutMaxTotalDelay</key>
<map>
<key>Comment</key>
<string>Maximum total time of a curl request, from when it is requested till the transaction has completed. This includes queuing due to connection throttling on top of the events covered by CurlTimeoutMaxTransaction</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>U32</string>
<key>Value</key>
<real>600</real>
</map>
<key>Cursor3D</key>
<map>
@@ -8993,7 +9059,7 @@
<key>NoVerifySSLCert</key>
<map>
<key>Comment</key>
<string>Do not verify SSL peers.</string>
<string>Do not verify SSL peers</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>

View File

@@ -52,6 +52,8 @@
#include "llvfile.h"
#include "message.h"
class AIHTTPTimeoutPolicy;
extern AIHTTPTimeoutPolicy iamHereVoice_timeout;
FloaterVoiceLicense::FloaterVoiceLicense(const LLSD& key)
: LLModalDialog( std::string(" "), 100, 100 ),
@@ -63,7 +65,7 @@ FloaterVoiceLicense::FloaterVoiceLicense(const LLSD& key)
// helper class that trys to download a URL from a web site and calls a method
// on parent class indicating if the web server is working or not
class LLIamHereVoice : public LLHTTPClient::Responder
class LLIamHereVoice : public LLHTTPClient::ResponderWithResult
{
private:
LLIamHereVoice( FloaterVoiceLicense* parent ) :
@@ -73,7 +75,6 @@ class LLIamHereVoice : public LLHTTPClient::Responder
FloaterVoiceLicense* mParent;
public:
static boost::intrusive_ptr< LLIamHereVoice > build( FloaterVoiceLicense* parent )
{
return boost::intrusive_ptr< LLIamHereVoice >( new LLIamHereVoice( parent ) );
@@ -101,6 +102,8 @@ class LLIamHereVoice : public LLHTTPClient::Responder
mParent->setSiteIsAlive( alive );
}
};
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return iamHereVoice_timeout; }
};
// this is global and not a class member to keep crud out of the header file

View File

@@ -17,9 +17,6 @@
#include "llviewercontrol.h"
#include "llweb.h"
#include "hipporestrequest.h"
// ********************************************************************
// Global Variables
@@ -492,7 +489,7 @@ bool HippoGridInfo::retrieveGridInfo()
uri += '/';
}
std::string reply;
int result = HippoRestRequest::getBlocking(uri + "get_grid_info", &reply);
int result = LLHTTPClient::blockingGetRaw(uri + "get_grid_info", reply);
if (result != 200) return false;
llinfos << "Received: " << reply << llendl;
@@ -860,7 +857,6 @@ void HippoGridManager::loadFromFile()
setCurrentGrid(last_grid);
}
void HippoGridManager::parseUrl(const std::string url, bool mergeIfNewer)
{
llinfos << "Loading grid info from '" << url << "'." << llendl;

View File

@@ -1,365 +0,0 @@
#include "llviewerprecompiledheaders.h"
#include "hipporestrequest.h"
#ifndef CURL_STATICLIB
#define CURL_STATICLIB 1
#endif
#include <stdtypes.h>
#include "llbufferstream.h"
#include "llerror.h"
#include "llhttpclient.h"
#include "llurlrequest.h"
#include "llxmltree.h"
#include <curl/curl.h>
#ifdef DEBUG_CURLIO
#include "debug_libcurl.h"
#endif
// ********************************************************************
class HippoRestComplete : public LLURLRequestComplete
{
public:
HippoRestComplete(HippoRestHandler *handler) :
mHandler(handler),
mStatus(499),
mReason("Request completed w/o status")
{
}
~HippoRestComplete()
{
delete mHandler;
}
// Called once for each header received, prior to httpStatus
void header(const std::string& header, const std::string& value)
{
mHandler->addHeader(header, value);
}
// Always called on request completion, prior to complete
void httpStatus(U32 status, const std::string& reason)
{
LLURLRequestComplete::httpStatus(status, reason);
mStatus = status;
mReason = reason;
}
void complete(const LLChannelDescriptors &channels, const buffer_ptr_t &buffer)
{
mHandler->handle(mStatus, mReason, channels, buffer);
}
private:
HippoRestHandler *mHandler;
int mStatus;
std::string mReason;
};
// ********************************************************************
static std::string gEmptyString;
void HippoRestHandler::addHeader(const std::string &header, const std::string &content)
{
mHeaders[header] = content;
}
bool HippoRestHandler::hasHeader(const std::string &header) const
{
return (mHeaders.find(header) != mHeaders.end());
}
const std::string &HippoRestHandler::getHeader(const std::string &header) const
{
std::map<std::string, std::string>::const_iterator it;
it = mHeaders.find(header);
if (it != mHeaders.end()) {
return it->second;
} else {
return gEmptyString;
}
}
// ********************************************************************
void HippoRestHandlerRaw::handle(int status, const std::string &reason,
const LLChannelDescriptors &channels,
const boost::shared_ptr<LLBufferArray> &body)
{
if (status == 200) {
std::string data;
LLBufferArray *buffer = body.get();
LLBufferArray::segment_iterator_t it, end = buffer->endSegment();
for (it=buffer->beginSegment(); it!=end; ++it)
if (it->isOnChannel(channels.in()))
data.append((char*)it->data(), it->size());
result(data);
} else {
llwarns << "Rest request error " << status << ": " << reason << llendl;
}
}
void HippoRestHandlerXml::handle(int status, const std::string &reason,
const LLChannelDescriptors &channels,
const boost::shared_ptr<LLBufferArray> &body)
{
if (status == 200) {
LLXmlTree *tree = new LLXmlTree();
bool success = tree->parseBufferStart();
LLBufferArray *buffer = body.get();
LLBufferArray::segment_iterator_t it, end = buffer->endSegment();
for (it=buffer->beginSegment(); success && (it!=end); ++it)
if (it->isOnChannel(channels.in()))
success = success && tree->parseBuffer((char*)it->data(), it->size());
success = success && tree->parseBufferFinalize();
if (success) result(tree);
delete tree;
} else {
llwarns << "Rest request error " << status << ": " << reason << llendl;
}
}
// ********************************************************************
class BodyData : public LLIOPipe
{
public:
virtual ~BodyData() { }
virtual const char *getContentMimeType() const = 0;
};
class BodyDataRaw : public BodyData
{
public:
explicit BodyDataRaw(const std::string &data) :
mData(data)
{
}
virtual ~BodyDataRaw() { }
const char *getContentMimeType() const { return "application/octet-stream"; }
EStatus process_impl(const LLChannelDescriptors &channels,
buffer_ptr_t &buffer, bool &eos,
LLSD &context, LLPumpIO *pump)
{
LLBufferStream ostream(channels, buffer.get());
ostream.write(mData.data(), mData.size());
eos = true;
return STATUS_DONE;
}
private:
std::string mData;
};
class BodyDataXml : public BodyData
{
public:
explicit BodyDataXml(const LLXmlTree *tree) :
mTree(tree)
{
}
virtual ~BodyDataXml()
{
if (mTree) delete mTree;
}
const char *getContentMimeType() const { return "application/xml"; }
EStatus process_impl(const LLChannelDescriptors &channels,
buffer_ptr_t &buffer, bool &eos,
LLSD &context, LLPumpIO *pump)
{
std::string data;
mTree->write(data);
LLBufferStream ostream(channels, buffer.get());
ostream.write(data.data(), data.size());
eos = true;
return STATUS_DONE;
}
private:
const LLXmlTree *mTree;
};
// ********************************************************************
static void request(const std::string &url,
LLURLRequest::ERequestAction method,
BodyData *body,
HippoRestHandler *handler, float timeout);
// static
void HippoRestRequest::get(const std::string &url,
HippoRestHandler *handler, float timeout)
{
request(url, LLURLRequest::HTTP_GET, 0, handler, timeout);
}
// static
void HippoRestRequest::put(const std::string &url, const std::string &body,
HippoRestHandler *handler, float timeout)
{
request(url, LLURLRequest::HTTP_PUT, new BodyDataRaw(body), handler, timeout);
}
// static
void HippoRestRequest::put(const std::string &url, const LLXmlTree *body,
HippoRestHandler *handler, float timeout)
{
request(url, LLURLRequest::HTTP_PUT, new BodyDataXml(body), handler, timeout);
}
// static
void HippoRestRequest::post(const std::string &url, const std::string &body,
HippoRestHandler *handler, float timeout)
{
request(url, LLURLRequest::HTTP_POST, new BodyDataRaw(body), handler, timeout);
}
// static
void HippoRestRequest::post(const std::string &url, const LLXmlTree *body,
HippoRestHandler *handler, float timeout)
{
request(url, LLURLRequest::HTTP_POST, new BodyDataXml(body), handler, timeout);
}
// ********************************************************************
static void request(const std::string &url,
LLURLRequest::ERequestAction method,
BodyData *body,
HippoRestHandler *handler, float timeout)
{
if (!LLHTTPClient::hasPump())
{
// !!! responder->completed(U32_MAX, "No pump", LLSD());
return;
}
LLPumpIO::chain_t chain;
LLURLRequest *req;
try
{
req = new LLURLRequest(method, url);
}
catch(AICurlNoEasyHandle const& error)
{
llwarns << "Failed to create LLURLRequest: " << error.what() << llendl;
return;
}
req->checkRootCertificate(true);
/*
// Insert custom headers if the caller sent any
if (headers.isMap())
{
LLSD::map_const_iterator iter = headers.beginMap();
LLSD::map_const_iterator end = headers.endMap();
for (; iter != end; ++iter)
{
std::ostringstream header;
//if the header is "Pragma" with no value
//the caller intends to force libcurl to drop
//the Pragma header it so gratuitously inserts
//Before inserting the header, force libcurl
//to not use the proxy (read: llurlrequest.cpp)
static const std::string PRAGMA("Pragma");
if ((iter->first == PRAGMA) && (iter->second.asString().empty()))
{
req->useProxy(false);
}
header << iter->first << ": " << iter->second.asString() ;
lldebugs << "header = " << header.str() << llendl;
req->addHeader(header.str().c_str());
}
}
*/
if ((method != LLURLRequest::HTTP_PUT) && (method != LLURLRequest::HTTP_POST)) {
std::string accept = "Accept: ";
accept += handler->getAcceptMimeType();
req->addHeader(accept.c_str());
}
req->setCallback(new HippoRestComplete(handler));
if ((method == LLURLRequest::HTTP_PUT) || (method == LLURLRequest::HTTP_POST)) {
std::string content = "Content-Type: ";
content += body->getContentMimeType();
req->addHeader(content.c_str());
chain.push_back(LLIOPipe::ptr_t(body));
}
chain.push_back(LLIOPipe::ptr_t(req));
LLHTTPClient::getPump().addChain(chain, timeout);
}
// ********************************************************************
static size_t curlWrite(void *ptr, size_t size, size_t nmemb, void *userData)
{
std::string *result = (std::string*)userData;
size_t bytes = (size * nmemb);
result->append((char*)ptr, bytes);
return nmemb;
}
// static
int HippoRestRequest::getBlocking(const std::string &url, std::string *result)
{
llinfos << "Requesting: " << url << llendl;
char curlErrorBuffer[CURL_ERROR_SIZE];
CURL* curlp = curl_easy_init();
llassert_always(curlp);
curl_easy_setopt(curlp, CURLOPT_NOSIGNAL, 1); // don't use SIGALRM for timeouts
curl_easy_setopt(curlp, CURLOPT_TIMEOUT, 5); // seconds
curl_easy_setopt(curlp, CURLOPT_CAINFO, gDirUtilp->getCAFile().c_str());
curl_easy_setopt(curlp, CURLOPT_WRITEFUNCTION, curlWrite);
curl_easy_setopt(curlp, CURLOPT_WRITEDATA, result);
curl_easy_setopt(curlp, CURLOPT_URL, url.c_str());
curl_easy_setopt(curlp, CURLOPT_ERRORBUFFER, curlErrorBuffer);
curl_easy_setopt(curlp, CURLOPT_FAILONERROR, 1);
*result = "";
S32 curlSuccess = curl_easy_perform(curlp);
long httpStatus = 499L; // curl_easy_getinfo demands pointer to long.
curl_easy_getinfo(curlp, CURLINFO_RESPONSE_CODE, &httpStatus);
if (curlSuccess != 0) {
llwarns << "CURL ERROR (HTTP Status " << httpStatus << "): " << curlErrorBuffer << llendl;
} else if (httpStatus != 200) {
llwarns << "HTTP Error " << httpStatus << ", but no Curl error." << llendl;
}
curl_easy_cleanup(curlp);
return httpStatus;
}

View File

@@ -1,104 +0,0 @@
#ifndef __HIPPO_REST_REQUEST_H__
#define __HIPPO_REST_REQUEST_H__
#include <map>
#include <string>
#include <boost/shared_ptr.hpp>
class LLBufferArray;
class LLChannelDescriptors;
class LLXmlTree;
#define HIPPO_REST_TIMEOUT 60.f
// ********************************************************************
class HippoRestHandler
{
public:
virtual ~HippoRestHandler() { }
virtual const char *getAcceptMimeType() const = 0;
bool hasHeader(const std::string &header) const;
const std::string &getHeader(const std::string &header) const;
private:
// These functions are called by the request engine
void addHeader(const std::string &header, const std::string &content);
virtual void handle(int status, const std::string &reason,
const LLChannelDescriptors &channels,
const boost::shared_ptr<LLBufferArray> &body) = 0;
std::map<std::string, std::string> mHeaders;
friend class HippoRestComplete;
};
class HippoRestHandlerRaw : public HippoRestHandler
{
public:
virtual ~HippoRestHandlerRaw() { }
const char *getAcceptMimeType() const { return "application/octet-stream"; }
private:
// This function must be implemented to receive the content
// it is executed on (status == 200) only
virtual void result(const std::string &content) = 0;
// This function is called by the request engine
void handle(int status, const std::string &reason,
const LLChannelDescriptors &channels,
const boost::shared_ptr<LLBufferArray> &body);
};
class HippoRestHandlerXml : public HippoRestHandler
{
public:
virtual ~HippoRestHandlerXml() { }
const char *getAcceptMimeType() const { return "application/xml"; }
private:
// This function must be implemented to receive the content
virtual void result(LLXmlTree *content) = 0;
// This function is called by the request engine
void handle(int status, const std::string &reason,
const LLChannelDescriptors &channels,
const boost::shared_ptr<LLBufferArray> &body);
};
// ********************************************************************
class HippoRestRequest
{
public:
// asynchronous interface
static void get(const std::string &url,
HippoRestHandler *handler, float timeout=HIPPO_REST_TIMEOUT);
static void put(const std::string &url, const std::string &body,
HippoRestHandler *handler, float timeout=HIPPO_REST_TIMEOUT);
static void put(const std::string &url, const LLXmlTree *body,
HippoRestHandler *handler, float timeout=HIPPO_REST_TIMEOUT);
static void post(const std::string &url, const std::string &body,
HippoRestHandler *handler, float timeout=HIPPO_REST_TIMEOUT);
static void post(const std::string &url, const LLXmlTree *body,
HippoRestHandler *handler, float timeout=HIPPO_REST_TIMEOUT);
// synchronous interface
static int getBlocking(const std::string &url, std::string *result);
};
#endif

View File

@@ -49,8 +49,10 @@
#include "llbufferstream.h"
class lggDicDownloadFloater;
class AIHTTPTimeoutPolicy;
extern AIHTTPTimeoutPolicy emeraldDicDownloader_timeout;
class EmeraldDicDownloader : public LLHTTPClient::Responder
class EmeraldDicDownloader : public LLHTTPClient::ResponderWithCompleted
{
public:
EmeraldDicDownloader(lggDicDownloadFloater* spanel, std::string sname);
@@ -59,7 +61,8 @@ public:
U32 status,
const std::string& reason,
const LLChannelDescriptors& channels,
const LLIOPipe::buffer_ptr_t& buffer);
const buffer_ptr_t& buffer);
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return emeraldDicDownloader_timeout; }
private:
lggDicDownloadFloater* panel;
std::string name;
@@ -163,7 +166,7 @@ EmeraldDicDownloader::EmeraldDicDownloader(lggDicDownloadFloater* spanel, std::s
}
void EmeraldDicDownloader::completedRaw(U32 status, const std::string& reason, const LLChannelDescriptors& channels, const LLIOPipe::buffer_ptr_t& buffer)
void EmeraldDicDownloader::completedRaw(U32 status, const std::string& reason, const LLChannelDescriptors& channels, const buffer_ptr_t& buffer)
{
if (status < 200 || status >= 300)
{

View File

@@ -30,12 +30,15 @@
#include "llcurl.h"
#include "llhttpclient.h"
class AIHTTPTimeoutPolicy;
extern AIHTTPTimeoutPolicy accountingCostResponder_timeout;
//===============================================================================
LLAccountingCostManager::LLAccountingCostManager()
{
}
//===============================================================================
class LLAccountingCostResponder : public LLCurl::Responder
class LLAccountingCostResponder : public LLHTTPClient::ResponderWithResult
{
public:
LLAccountingCostResponder( const LLSD& objectIDs )
@@ -85,6 +88,8 @@ public:
clearPendingRequests();
}
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return accountingCostResponder_timeout; }
private:
//List of posted objects
LLSD mObjectIDs;

View File

@@ -2171,7 +2171,7 @@ void LLAgent::setStartPosition( U32 location_id )
// This awkward idiom warrants explanation.
// For starters, LLSDMessage::ResponderAdapter is ONLY for testing the new
// LLSDMessage functionality with a pre-existing LLHTTPClient::Responder.
// LLSDMessage functionality with a pre-existing LLHTTPClient::ResponderWithResult.
// In new code, define your reply/error methods on the same class as the
// sending method, bind them to local LLEventPump objects and pass those
// LLEventPump names in the request LLSD object.
@@ -2188,6 +2188,7 @@ void LLAgent::setStartPosition( U32 location_id )
request["payload"] = body;
request["reply"] = adapter->getReplyName();
request["error"] = adapter->getErrorName();
request["timeoutpolicy"] = adapter->getTimeoutPolicyName();
gAgent.getRegion()->getCapAPI().post(request);

View File

@@ -95,6 +95,7 @@
#include "llprimitive.h"
#include "llnotifications.h"
#include "llnotificationsutil.h"
#include "llcurl.h"
#include <boost/bind.hpp>
#if LL_WINDOWS
@@ -118,6 +119,8 @@
// <edit>
#include "lldelayeduidelete.h"
#include "llbuildnewviewsscheduler.h"
#include "aicurleasyrequeststatemachine.h"
#include "aihttptimeoutpolicy.h"
// </edit>
// The files below handle dependencies from cleanup.
#include "llcalc.h"
@@ -592,8 +595,10 @@ bool LLAppViewer::init()
//
LLFastTimer::reset();
// <edit>
// We can call this early.
LLFrameTimer::global_initialization();
// </edit>
// initialize SSE options
LLVector4a::initClass();
@@ -610,13 +615,13 @@ bool LLAppViewer::init()
initLogging();
// <edit>
// Curl must be initialized before any thread is running.
AICurlInterface::initCurl(&AIStateMachine::flush);
// Logging is initialized. Now it's safe to start the error thread.
startErrorThread();
// <edit>
gDeleteScheduler = new LLDeleteScheduler();
gBuildNewViewsScheduler = new LLBuildNewViewsScheduler();
// </edit>
@@ -638,6 +643,20 @@ bool LLAppViewer::init()
mAlloc.setProfilingEnabled(gSavedSettings.getBOOL("MemProfiling"));
AIStateMachine::setMaxCount(gSavedSettings.getU32("StateMachineMaxTime"));
AIHTTPTimeoutPolicy::setDefaultCurlTimeout(
AIHTTPTimeoutPolicy(
"CurlTimeout* Debug Settings",
gSavedSettings.getU32("CurlTimeoutDNSLookup"),
gSavedSettings.getU32("CurlTimeoutConnect"),
gSavedSettings.getU32("CurlTimeoutReplyDelay"),
gSavedSettings.getU32("CurlTimeoutLowSpeedTime"),
gSavedSettings.getU32("CurlTimeoutLowSpeedLimit"),
gSavedSettings.getU32("CurlTimeoutMaxTransaction"),
gSavedSettings.getU32("CurlTimeoutMaxTotalDelay")
));
initThreads();
LL_INFOS("InitInfo") << "Threads initialized." << LL_ENDL ;
@@ -1026,6 +1045,7 @@ static LLFastTimer::DeclareTimer FTM_PUMP_SERVICE("Service");
static LLFastTimer::DeclareTimer FTM_SERVICE_CALLBACK("Callback");
static LLFastTimer::DeclareTimer FTM_AGENT_AUTOPILOT("Autopilot");
static LLFastTimer::DeclareTimer FTM_AGENT_UPDATE("Update");
static LLFastTimer::DeclareTimer FTM_STATEMACHINE("State Machine");
bool LLAppViewer::mainLoop()
{
@@ -1039,7 +1059,6 @@ bool LLAppViewer::mainLoop()
// Create IO Pump to use for HTTP Requests.
gServicePump = new LLPumpIO;
LLHTTPClient::setPump(*gServicePump);
LLCurl::setCAFile(gDirUtilp->getCAFile());
// Note: this is where gLocalSpeakerMgr and gActiveSpeakerMgr used to be instantiated.
@@ -1817,7 +1836,7 @@ bool LLAppViewer::initThreads()
LLWatchdog::getInstance()->init(watchdog_killer_callback);
}
AICurlInterface::startCurlThread(gSavedSettings.getU32("CurlConcurrentConnections"));
AICurlInterface::startCurlThread(gSavedSettings.getU32("CurlConcurrentConnections"), gSavedSettings.getBOOL("NoVerifySSLCert"));
LLImage::initClass();
@@ -3754,6 +3773,16 @@ void LLAppViewer::idle()
}
}
//////////////////////////////////////
//
// Run state machines
//
{
LLFastTimer t(FTM_STATEMACHINE);
AIStateMachine::mainloop();
}
// Must wait until both have avatar object and mute list, so poll
// here.
request_initial_instant_messages();

View File

@@ -47,7 +47,7 @@ public:
LLAssetUploadChainResponder(const LLSD& post_data,
const std::string& file_name,
const LLUUID& queue_id,
U8* data,
char* data,
U32 data_size,
std::string script_name,
LLAssetUploadQueueSupplier *supplier) :
@@ -137,7 +137,7 @@ public:
}
LLAssetUploadQueueSupplier *mSupplier;
U8* mData;
char* mData;
U32 mDataSize;
std::string mScriptName;
};
@@ -189,7 +189,7 @@ void LLAssetUploadQueue::queue(const std::string& filename,
BOOL is_running,
BOOL is_target_mono,
const LLUUID& queue_id,
U8* script_data,
char* script_data,
U32 data_size,
std::string script_name)
{

View File

@@ -54,7 +54,7 @@ public:
BOOL is_running,
BOOL is_target_mono,
const LLUUID& queue_id,
U8* data,
char* data,
U32 data_size,
std::string script_name);
@@ -72,7 +72,7 @@ private:
BOOL mIsRunning;
BOOL mIsTargetMono;
LLUUID mQueueId;
U8* mData;
char* mData;
U32 mDataSize;
std::string mScriptName;
};

View File

@@ -192,8 +192,7 @@ void on_new_single_inventory_upload_complete(
LLAssetUploadResponder::LLAssetUploadResponder(const LLSD &post_data,
const LLUUID& vfile_id,
LLAssetType::EType asset_type)
: LLHTTPClient::Responder(),
mPostData(post_data),
: mPostData(post_data),
mVFileID(vfile_id),
mAssetType(asset_type)
{
@@ -210,8 +209,7 @@ LLAssetUploadResponder::LLAssetUploadResponder(
const LLSD &post_data,
const std::string& file_name,
LLAssetType::EType asset_type)
: LLHTTPClient::Responder(),
mPostData(post_data),
: mPostData(post_data),
mFileName(file_name),
mAssetType(asset_type)
{

View File

@@ -36,6 +36,10 @@
#include "llhttpclient.h"
#include "llinventory.h"
class AIHTTPTimeoutPolicy;
extern AIHTTPTimeoutPolicy assetUploadResponder_timeout;
extern AIHTTPTimeoutPolicy newAgentInventoryVariablePriceResponder_timeout;
void on_new_single_inventory_upload_complete(LLAssetType::EType asset_type,
LLInventoryType::EType inventory_type,
const std::string inventory_type_string,
@@ -47,7 +51,7 @@ void on_new_single_inventory_upload_complete(LLAssetType::EType asset_type,
// Abstract class for supporting asset upload
// via capabilities
class LLAssetUploadResponder : public LLHTTPClient::Responder
class LLAssetUploadResponder : public LLHTTPClient::ResponderWithResult
{
public:
LLAssetUploadResponder(const LLSD& post_data,
@@ -59,6 +63,7 @@ public:
~LLAssetUploadResponder();
virtual void error(U32 statusNum, const std::string& reason);
virtual void result(const LLSD& content);
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return assetUploadResponder_timeout; }
virtual void uploadUpload(const LLSD& content);
virtual void uploadComplete(const LLSD& content);
virtual void uploadFailure(const LLSD& content);
@@ -91,7 +96,7 @@ public:
// are needed (such as different confirmation messages, etc.)
// the functions onApplicationLevelError and showConfirmationDialog.
class LLNewAgentInventoryVariablePriceResponder :
public LLHTTPClient::Responder
public LLHTTPClient::ResponderWithResult
{
public:
LLNewAgentInventoryVariablePriceResponder(
@@ -110,6 +115,7 @@ public:
const std::string& reason,
const LLSD& content);
void result(const LLSD& content);
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return newAgentInventoryVariablePriceResponder_timeout; }
virtual void onApplicationLevelError(
const LLSD& error);

View File

@@ -84,7 +84,7 @@ bool LLCapabilityListener::capListener(const LLSD& request)
LLSD payload(request["payload"]);
LLSD::String reply(request["reply"]);
LLSD::String error(request["error"]);
LLSD::Real timeout(request["timeout"]);
LLSD::String timeoutpolicy(request["timeoutpolicy"]);
// If the LLSD doesn't even have a "message" key, we doubt it was intended
// for this listener.
if (cap.empty())
@@ -95,24 +95,15 @@ bool LLCapabilityListener::capListener(const LLSD& request)
<< LL_ENDL;
return false; // in case fatal-error function isn't
}
// Establish default timeout. This test relies on LLSD::asReal() returning
// exactly 0.0 for an undef value.
if (! timeout)
{
timeout = HTTP_REQUEST_EXPIRY_SECS;
}
// Look up the url for the requested capability name.
std::string url = mProvider.getCapability(cap);
if (! url.empty())
{
LLSDMessage::EventResponder* responder =
new LLSDMessage::EventResponder(LLEventPumps::instance(), request, mProvider.getDescription(), cap, reply, error);
responder->setTimeoutPolicy(timeoutpolicy);
// This capability is supported by the region to which we're talking.
LLHTTPClient::post(url, payload,
new LLSDMessage::EventResponder(LLEventPumps::instance(),
request,
mProvider.getDescription(),
cap, reply, error),
LLSD(), // headers
timeout);
LLHTTPClient::post(url, payload, responder);
}
else
{

View File

@@ -37,7 +37,10 @@
#include "llview.h"
#include "lluuid.h"
class LLClassifiedStatsResponder : public LLHTTPClient::Responder
class AIHTTPTimeoutPolicy;
extern AIHTTPTimeoutPolicy classifiedStatsResponder_timeout;
class LLClassifiedStatsResponder : public LLHTTPClient::ResponderWithResult
{
public:
LLClassifiedStatsResponder(LLHandle<LLView> classified_panel_handle, LLUUID classified_id);
@@ -45,6 +48,7 @@ public:
virtual void result(const LLSD& content);
//If we get back an error (not found, etc...), handle it here
virtual void error(U32 status, const std::string& reason);
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return classifiedStatsResponder_timeout; }
protected:
LLHandle<LLView> mClassifiedPanelHandle;

View File

@@ -437,8 +437,8 @@ void LLFloaterCompileQueue::scriptArrived(LLVFS *vfs, const LLUUID& asset_id,
{
// Read script source in to buffer.
U32 script_size = file.getSize();
U8* script_data = new U8[script_size];
file.read(script_data, script_size);
char* script_data = new char[script_size];
file.read(reinterpret_cast<U8*>(script_data), script_size);
queue->mUploadQueue->queue(filename, data->mTaskId,
data->mItemId, is_running, queue->mMono, queue->getID(),

View File

@@ -1,146 +0,0 @@
/**
* @file llcurlrequest.cpp
* @brief Implementation of Request.
*
* Copyright (c) 2012, Aleric Inglewood.
* Copyright (C) 2010, Linden Research, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution.
*
* CHANGELOG
* and additional copyright holders.
*
* 17/03/2012
* Initial version, written by Aleric Inglewood @ SL
*
* 20/03/2012
* Added copyright notice for Linden Lab for those parts that were
* copied or derived from llcurl.cpp. The code of those parts are
* already in their own llcurl.cpp, so they do not ever need to
* even look at this file; the reason I added the copyright notice
* is to make clear that I am not the author of 100% of this code
* and hence I cannot change the license of it.
*/
#include "llviewerprecompiledheaders.h"
#include "llsdserialize.h"
#include "llcurlrequest.h"
#include "llbuffer.h"
#include "llbufferstream.h"
#include "statemachine/aicurleasyrequeststatemachine.h"
//-----------------------------------------------------------------------------
// class Request
//
namespace AICurlInterface {
bool Request::get(std::string const& url, ResponderPtr responder)
{
return getByteRange(url, headers_t(), 0, -1, responder);
}
bool Request::getByteRange(std::string const& url, headers_t const& headers, S32 offset, S32 length, ResponderPtr responder)
{
DoutEntering(dc::curl, "Request::getByteRange(" << url << ", ...)");
// This might throw AICurlNoEasyHandle.
AICurlEasyRequestStateMachine* buffered_easy_request = new AICurlEasyRequestStateMachine(true);
{
AICurlEasyRequest_wat buffered_easy_request_w(*buffered_easy_request->mCurlEasyRequest);
AICurlResponderBuffer_wat(*buffered_easy_request->mCurlEasyRequest)->prepRequest(buffered_easy_request_w, headers, responder);
buffered_easy_request_w->setopt(CURLOPT_HTTPGET, 1);
if (length > 0)
{
std::string range = llformat("Range: bytes=%d-%d", offset, offset + length - 1);
buffered_easy_request_w->addHeader(range.c_str());
}
buffered_easy_request_w->finalizeRequest(url);
}
buffered_easy_request->run();
return true; // We throw in case of problems.
}
bool Request::post(std::string const& url, headers_t const& headers, std::string const& data, ResponderPtr responder, S32 time_out)
{
DoutEntering(dc::curl, "Request::post(" << url << ", ...)");
// This might throw AICurlNoEasyHandle.
AICurlEasyRequestStateMachine* buffered_easy_request = new AICurlEasyRequestStateMachine(true);
{
AICurlEasyRequest_wat buffered_easy_request_w(*buffered_easy_request->mCurlEasyRequest);
AICurlResponderBuffer_wat buffer_w(*buffered_easy_request->mCurlEasyRequest);
buffer_w->prepRequest(buffered_easy_request_w, headers, responder);
U32 bytes = data.size();
bool success = buffer_w->getInput()->append(buffer_w->sChannels.out(), (U8 const*)data.data(), bytes);
llassert_always(success); // AIFIXME: Maybe throw an error.
if (!success)
return false;
buffered_easy_request_w->setPost(bytes);
buffered_easy_request_w->addHeader("Content-Type: application/octet-stream");
buffered_easy_request_w->finalizeRequest(url);
lldebugs << "POSTING: " << bytes << " bytes." << llendl;
}
buffered_easy_request->run();
return true; // We throw in case of problems.
}
bool Request::post(std::string const& url, headers_t const& headers, LLSD const& data, ResponderPtr responder, S32 time_out)
{
DoutEntering(dc::curl, "Request::post(" << url << ", ...)");
// This might throw AICurlNoEasyHandle.
AICurlEasyRequestStateMachine* buffered_easy_request = new AICurlEasyRequestStateMachine(true);
{
AICurlEasyRequest_wat buffered_easy_request_w(*buffered_easy_request->mCurlEasyRequest);
AICurlResponderBuffer_wat buffer_w(*buffered_easy_request->mCurlEasyRequest);
buffer_w->prepRequest(buffered_easy_request_w, headers, responder);
LLBufferStream buffer_stream(buffer_w->sChannels, buffer_w->getInput().get());
LLSDSerialize::toXML(data, buffer_stream);
S32 bytes = buffer_w->getInput()->countAfter(buffer_w->sChannels.out(), NULL);
buffered_easy_request_w->setPost(bytes);
buffered_easy_request_w->addHeader("Content-Type: application/llsd+xml");
buffered_easy_request_w->finalizeRequest(url);
lldebugs << "POSTING: " << bytes << " bytes." << llendl;
}
buffered_easy_request->run();
return true; // We throw in case of problems.
}
} // namespace AICurlInterface
//==================================================================================

View File

@@ -1,57 +0,0 @@
/**
* @file llcurlrequest.h
* @brief Declaration of class Request
*
* Copyright (c) 2012, Aleric Inglewood.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution.
*
* CHANGELOG
* and additional copyright holders.
*
* 17/03/2012
* Initial version, written by Aleric Inglewood @ SL
*/
#ifndef AICURLREQUEST_H
#define AICURLREQUEST_H
#include <string>
#include <vector>
#include <boost/intrusive_ptr.hpp>
// Things defined in this namespace are called from elsewhere in the viewer code.
namespace AICurlInterface {
// Forward declaration.
class Responder;
typedef boost::intrusive_ptr<Responder> ResponderPtr;
class Request {
public:
typedef std::vector<std::string> headers_t;
bool get(std::string const& url, ResponderPtr responder);
bool getByteRange(std::string const& url, headers_t const& headers, S32 offset, S32 length, ResponderPtr responder);
bool post(std::string const& url, headers_t const& headers, std::string const& data, ResponderPtr responder, S32 time_out = 0);
bool post(std::string const& url, headers_t const& headers, LLSD const& data, ResponderPtr responder, S32 time_out = 0);
};
} // namespace AICurlInterface
#endif

View File

@@ -44,7 +44,8 @@
#include "llframetimer.h"
#include "lllineeditor.h"
#include "llviewchildren.h"
#include "llxmlrpctransaction.h"
#include "llxmlrpcresponder.h"
#include "llhttpclient.h"
#include "llviewernetwork.h"
#include "hippogridmanager.h"
@@ -89,8 +90,8 @@ public:
};
TransactionType mTransactionType;
LLXMLRPCTransaction* mTransaction;
boost::intrusive_ptr<XMLRPCResponder> mResponder;
bool mCurrencyChanged;
LLFrameTimer mCurrencyKeyTimer;
@@ -128,14 +129,13 @@ LLCurrencyUIManager::Impl::Impl(LLPanel& dialog)
mSiteCurrencyEstimated(false),
mSiteCurrencyEstimatedCost(0),
mBought(false),
mTransactionType(TransactionNone), mTransaction(0),
mTransactionType(TransactionNone),
mCurrencyChanged(false)
{
}
LLCurrencyUIManager::Impl::~Impl()
{
delete mTransaction;
}
void LLCurrencyUIManager::Impl::updateCurrencyInfo()
@@ -166,7 +166,7 @@ void LLCurrencyUIManager::Impl::updateCurrencyInfo()
void LLCurrencyUIManager::Impl::finishCurrencyInfo()
{
LLXMLRPCValue result = mTransaction->responseValue();
LLXMLRPCValue result = mResponder->responseValue();
bool success = result["success"].asBool();
if (!success)
@@ -219,7 +219,7 @@ void LLCurrencyUIManager::Impl::startCurrencyBuy(const std::string& password)
void LLCurrencyUIManager::Impl::finishCurrencyBuy()
{
LLXMLRPCValue result = mTransaction->responseValue();
LLXMLRPCValue result = mResponder->responseValue();
bool success = result["success"].asBool();
if (!success)
@@ -246,34 +246,28 @@ void LLCurrencyUIManager::Impl::startTransaction(TransactionType type,
transactionURI = LLViewerLogin::getInstance()->getHelperURI() + "currency.php";
}
delete mTransaction;
mTransactionType = type;
mTransaction = new LLXMLRPCTransaction(
transactionURI,
method,
params,
false /* don't use gzip */
);
mResponder = new XMLRPCResponder;
LLHTTPClient::postXMLRPC(transactionURI, method, params.getValue(), mResponder);
clearError();
}
bool LLCurrencyUIManager::Impl::checkTransaction()
{
if (!mTransaction)
if (!mResponder)
{
return false;
}
if (!mTransaction->is_finished())
if (!mResponder->is_finished())
{
return false;
}
if (mTransaction->status(NULL) != LLXMLRPCTransaction::StatusComplete)
if (mResponder->result_code() != CURLE_OK || mResponder->http_status() < 200 || mResponder->http_status() >= 400)
{
setError(mTransaction->statusMessage(), mTransaction->statusURI());
setError(mResponder->reason(), mResponder->getURL());
}
else {
switch (mTransactionType)
@@ -283,11 +277,11 @@ bool LLCurrencyUIManager::Impl::checkTransaction()
default: ;
}
}
delete mTransaction;
mTransaction = NULL;
// We're done with this data.
mResponder = NULL;
mTransactionType = TransactionNone;
return true;
}
@@ -309,7 +303,7 @@ void LLCurrencyUIManager::Impl::clearError()
bool LLCurrencyUIManager::Impl::considerUpdateCurrency()
{
if (mCurrencyChanged
&& !mTransaction
&& !mResponder
&& mCurrencyKeyTimer.getElapsedTimeF32() >= CURRENCY_ESTIMATE_FREQUENCY)
{
updateCurrencyInfo();

View File

@@ -46,6 +46,9 @@
#include "message.h"
#include "lltrans.h"
class AIHTTPTimeoutPolicy;
extern AIHTTPTimeoutPolicy eventPollResponder_timeout;
namespace
{
// We will wait RETRY_SECONDS + (errorCount * RETRY_SECONDS_INC) before retrying after an error.
@@ -55,7 +58,7 @@ namespace
const F32 EVENT_POLL_ERROR_RETRY_SECONDS_INC = 5.f; // ~ half of a normal timeout.
const S32 MAX_EVENT_POLL_HTTP_ERRORS = 10; // ~5 minutes, by the above rules.
class LLEventPollResponder : public LLHTTPClient::Responder
class LLEventPollResponder : public LLHTTPClient::ResponderWithResult
{
public:
@@ -70,13 +73,10 @@ namespace
void handleMessage(const LLSD& content);
virtual void error(U32 status, const std::string& reason);
virtual void error(U32 status, const std::string& reason);
virtual void result(const LLSD& content);
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return eventPollResponder_timeout; }
virtual void completedRaw(U32 status,
const std::string& reason,
const LLChannelDescriptors& channels,
const LLIOPipe::buffer_ptr_t& buffer);
private:
bool mDone;
@@ -156,24 +156,6 @@ namespace
<< mPollURL << llendl;
}
// virtual
void LLEventPollResponder::completedRaw(U32 status,
const std::string& reason,
const LLChannelDescriptors& channels,
const LLIOPipe::buffer_ptr_t& buffer)
{
if (status == HTTP_BAD_GATEWAY)
{
// These errors are not parsable as LLSD,
// which LLHTTPClient::Responder::completedRaw will try to do.
completed(status, reason, LLSD());
}
else
{
LLHTTPClient::Responder::completedRaw(status,reason,channels,buffer);
}
}
void LLEventPollResponder::makeRequest()
{
LLSD request;
@@ -291,7 +273,7 @@ LLEventPoll::LLEventPoll(const std::string& poll_url, const LLHost& sender)
LLEventPoll::~LLEventPoll()
{
LLHTTPClient::Responder* responderp = mImpl.get();
LLHTTPClient::ResponderBase* responderp = mImpl.get();
LLEventPollResponder* event_poll_responder = dynamic_cast<LLEventPollResponder*>(responderp);
if (event_poll_responder) event_poll_responder->stop();
}

View File

@@ -1146,7 +1146,7 @@ void LLFastTimerView::exportCharts(const std::string& base, const std::string& t
{ //read base log into memory
S32 i = 0;
std::ifstream is(base.c_str());
while (!is.eof() && LLSDSerialize::fromXML(cur, is))
while (!is.eof() && LLSDSerialize::fromXML(cur, is) > 0)
{
base_data[i++] = cur;
}
@@ -1159,7 +1159,7 @@ void LLFastTimerView::exportCharts(const std::string& base, const std::string& t
{ //read current log into memory
S32 i = 0;
std::ifstream is(target.c_str());
while (!is.eof() && LLSDSerialize::fromXML(cur, is))
while (!is.eof() && LLSDSerialize::fromXML(cur, is) > 0)
{
cur_data[i++] = cur;
@@ -1450,7 +1450,7 @@ LLSD LLFastTimerView::analyzePerformanceLogDefault(std::istream& is)
stats_map_t time_stats;
stats_map_t sample_stats;
while (!is.eof() && LLSDSerialize::fromXML(cur, is))
while (!is.eof() && LLSDSerialize::fromXML(cur, is) > 0)
{
for (LLSD::map_iterator iter = cur.beginMap(); iter != cur.endMap(); ++iter)
{

View File

@@ -57,6 +57,11 @@
#include "llavatarname.h"
class AIHTTPTimeoutPolicy;
extern AIHTTPTimeoutPolicy muteVoiceResponder_timeout;
extern AIHTTPTimeoutPolicy muteTextResponder_timeout;
extern AIHTTPTimeoutPolicy moderationModeResponder_timeout;
using namespace LLOldEvents;
const F32 SPEAKER_TIMEOUT = 10.f; // seconds of not being on voice channel before removed from list of active speakers
@@ -844,7 +849,7 @@ void LLPanelActiveSpeakers::onModeratorMuteVoice(LLUICtrl* ctrl, void* user_data
// ctrl value represents ability to type, so invert
data["params"]["mute_info"]["voice"] = !ctrl->getValue();
class MuteVoiceResponder : public LLHTTPClient::Responder
class MuteVoiceResponder : public LLHTTPClient::ResponderIgnoreBody
{
public:
MuteVoiceResponder(const LLUUID& session_id)
@@ -882,6 +887,8 @@ void LLPanelActiveSpeakers::onModeratorMuteVoice(LLUICtrl* ctrl, void* user_data
}
}
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return muteVoiceResponder_timeout; }
private:
LLUUID mSessionID;
};
@@ -909,7 +916,7 @@ void LLPanelActiveSpeakers::onModeratorMuteText(LLUICtrl* ctrl, void* user_data)
// ctrl value represents ability to type, so invert
data["params"]["mute_info"]["text"] = !ctrl->getValue();
class MuteTextResponder : public LLHTTPClient::Responder
class MuteTextResponder : public LLHTTPClient::ResponderIgnoreBody
{
public:
MuteTextResponder(const LLUUID& session_id)
@@ -947,6 +954,8 @@ void LLPanelActiveSpeakers::onModeratorMuteText(LLUICtrl* ctrl, void* user_data)
}
}
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return muteTextResponder_timeout; }
private:
LLUUID mSessionID;
};
@@ -981,12 +990,13 @@ void LLPanelActiveSpeakers::onChangeModerationMode(LLUICtrl* ctrl, void* user_da
data["params"]["update_info"]["moderated_mode"]["voice"] = true;
}
struct ModerationModeResponder : public LLHTTPClient::Responder
struct ModerationModeResponder : public LLHTTPClient::ResponderIgnoreBody
{
virtual void error(U32 status, const std::string& reason)
{
llwarns << status << ": " << reason << llendl;
}
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return moderationModeResponder_timeout; }
};
LLHTTPClient::post(url, data, new ModerationModeResponder());

View File

@@ -62,8 +62,9 @@
#include "llweb.h"
#include "llwindow.h"
#include "llworld.h"
#include "llxmlrpctransaction.h"
#include "llxmlrpcresponder.h"
#include "llviewernetwork.h"
#include "llhttpclient.h"
#include "roles_constants.h"
#include "hippogridmanager.h"
@@ -158,7 +159,7 @@ private:
TransactionCurrency,
TransactionBuy
};
LLXMLRPCTransaction* mTransaction;
boost::intrusive_ptr<XMLRPCResponder> mResponder;
TransactionType mTransactionType;
LLViewerParcelMgr::ParcelBuyInfo* mParcelBuyInfo;
@@ -283,7 +284,7 @@ LLFloaterBuyLandUI::LLFloaterBuyLandUI()
mParcel(0),
mBought(false),
mParcelValid(false), mSiteValid(false),
mChildren(*this), mCurrency(*this), mTransaction(0),
mChildren(*this), mCurrency(*this),
mParcelBuyInfo(0)
{
LLUICtrlFactory::getInstance()->buildFloater(this, "floater_buy_land.xml");
@@ -294,8 +295,6 @@ LLFloaterBuyLandUI::~LLFloaterBuyLandUI()
{
LLViewerParcelMgr::getInstance()->removeObserver(&mParcelSelectionObserver);
LLViewerParcelMgr::getInstance()->deleteParcelBuy(&mParcelBuyInfo);
delete mTransaction;
}
// virtual
@@ -619,7 +618,7 @@ void LLFloaterBuyLandUI::updateWebSiteInfo()
S32 askBillableArea = mIsForGroup ? 0 : mParcelBillableArea;
S32 askCurrencyBuy = mCurrency.getAmount();
if (mTransaction && mTransactionType == TransactionPreflight
if (mResponder && mTransactionType == TransactionPreflight
&& mPreflightAskBillableArea == askBillableArea
&& mPreflightAskCurrencyBuy == askCurrencyBuy)
{
@@ -660,7 +659,7 @@ void LLFloaterBuyLandUI::updateWebSiteInfo()
void LLFloaterBuyLandUI::finishWebSiteInfo()
{
LLXMLRPCValue result = mTransaction->responseValue();
LLXMLRPCValue result = mResponder->responseValue();
mSiteValid = result["success"].asBool();
if (!mSiteValid)
@@ -755,7 +754,7 @@ void LLFloaterBuyLandUI::runWebSitePrep(const std::string& password)
void LLFloaterBuyLandUI::finishWebSitePrep()
{
LLXMLRPCValue result = mTransaction->responseValue();
LLXMLRPCValue result = mResponder->responseValue();
bool success = result["success"].asBool();
if (!success)
@@ -825,9 +824,6 @@ void LLFloaterBuyLandUI::updateName(const LLUUID& id,
void LLFloaterBuyLandUI::startTransaction(TransactionType type, const LLXMLRPCValue& params)
{
delete mTransaction;
mTransaction = NULL;
mTransactionType = type;
// Select a URI and method appropriate for the transaction type.
@@ -851,29 +847,25 @@ void LLFloaterBuyLandUI::startTransaction(TransactionType type, const LLXMLRPCVa
return;
}
mTransaction = new LLXMLRPCTransaction(
transaction_uri,
method,
params,
false /* don't use gzip */
);
mResponder = new XMLRPCResponder;
LLHTTPClient::postXMLRPC(transaction_uri, method, params.getValue(), mResponder);
}
bool LLFloaterBuyLandUI::checkTransaction()
{
if (!mTransaction)
if (!mResponder)
{
return false;
}
if (!mTransaction->is_finished())
if (!mResponder->is_finished())
{
return false;
}
if (mTransaction->status(NULL) != LLXMLRPCTransaction::StatusComplete)
if (mResponder->result_code() != CURLE_OK || mResponder->http_status() < 200 || mResponder->http_status() >= 400)
{
tellUserError(mTransaction->statusMessage(), mTransaction->statusURI());
tellUserError(mResponder->reason(), mResponder->getURL());
}
else {
switch (mTransactionType)
@@ -884,8 +876,8 @@ bool LLFloaterBuyLandUI::checkTransaction()
}
}
delete mTransaction;
mTransaction = NULL;
// We're done with this data.
mResponder = NULL;
return true;
}
@@ -918,7 +910,7 @@ BOOL LLFloaterBuyLandUI::postBuild()
void LLFloaterBuyLandUI::setParcel(LLViewerRegion* region, LLParcelSelectionHandle parcel)
{
if (mTransaction && mTransactionType == TransactionBuy)
if (mResponder && mTransactionType == TransactionBuy)
{
// the user is buying, don't change the selection
return;
@@ -968,7 +960,7 @@ void LLFloaterBuyLandUI::draw()
// virtual
BOOL LLFloaterBuyLandUI::canClose()
{
bool can_close = (mTransaction ? FALSE : TRUE) && mCurrency.canCancel();
bool can_close = !mResponder && mCurrency.canCancel();
if (!can_close)
{
// explain to user why they can't do this, see DEV-9605
@@ -1285,7 +1277,7 @@ void LLFloaterBuyLandUI::refreshUI()
}
childSetEnabled("buy_btn",
mCanBuy && mSiteValid && willHaveEnough && !mTransaction && agrees_to_covenant);
mCanBuy && mSiteValid && willHaveEnough && !mResponder && agrees_to_covenant);
}
void LLFloaterBuyLandUI::startBuyPreConfirm()

View File

@@ -37,6 +37,10 @@
#include "llviewerregion.h"
#include "lluictrlfactory.h"
class AIHTTPTimeoutPolicy;
extern AIHTTPTimeoutPolicy asyncConsoleResponder_timeout;
extern AIHTTPTimeoutPolicy consoleResponder_timeout;
// Two versions of the sim console API are supported.
//
// SimConsole capability (deprecated):
@@ -72,7 +76,8 @@ namespace
// This responder handles the initial response. Unless error() is called
// we assume that the simulator has received our request. Error will be
// called if this request times out.
class AsyncConsoleResponder : public LLHTTPClient::Responder
//
class AsyncConsoleResponder : public LLHTTPClient::ResponderIgnoreBody
{
public:
/* virtual */
@@ -80,9 +85,10 @@ namespace
{
sConsoleReplySignal(UNABLE_TO_SEND_COMMAND);
}
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return asyncConsoleResponder_timeout; }
};
class ConsoleResponder : public LLHTTPClient::Responder
class ConsoleResponder : public LLHTTPClient::ResponderWithResult
{
public:
ConsoleResponder(LLTextEditor *output) : mOutput(output)
@@ -110,11 +116,14 @@ namespace
}
}
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return consoleResponder_timeout; }
LLTextEditor * mOutput;
};
// This handles responses for console commands sent via the asynchronous
// API.
class ConsoleResponseNode : public LLHTTPNode
{
public:

View File

@@ -34,11 +34,13 @@
#include "llhttpclient.h"
class LLTextEditor;
class AIHTTPTimeoutPolicy;
extern AIHTTPTimeoutPolicy floaterRegionDebugConsole_timeout;
typedef boost::signals2::signal<
void (const std::string& output)> console_reply_signal_t;
class LLFloaterRegionDebugConsole : public LLFloater, public LLHTTPClient::Responder, public LLSingleton<LLFloaterRegionDebugConsole>
class LLFloaterRegionDebugConsole : public LLFloater, public LLSingleton<LLFloaterRegionDebugConsole>
{
public:
LLFloaterRegionDebugConsole();

View File

@@ -91,6 +91,9 @@
const S32 TERRAIN_TEXTURE_COUNT = 4;
const S32 CORNER_COUNT = 4;
class AIHTTPTimeoutPolicy;
extern AIHTTPTimeoutPolicy estateChangeInfoResponder_timeout;
///----------------------------------------------------------------------------
/// Local class declaration
///----------------------------------------------------------------------------
@@ -2307,7 +2310,7 @@ void LLPanelEstateInfo::getEstateOwner()
}
*/
class LLEstateChangeInfoResponder : public LLHTTPClient::Responder
class LLEstateChangeInfoResponder : public LLHTTPClient::ResponderWithResult
{
public:
LLEstateChangeInfoResponder(void* userdata) : mpPanel((LLPanelEstateInfo*)userdata) {};
@@ -2325,6 +2328,9 @@ public:
llinfos << "LLEstateChangeInfoResponder::error "
<< status << ": " << reason << llendl;
}
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return estateChangeInfoResponder_timeout; }
private:
LLPanelEstateInfo* mpPanel;
};

View File

@@ -96,6 +96,9 @@
const U32 INCLUDE_SCREENSHOT = 0x01 << 0;
class AIHTTPTimeoutPolicy;
extern AIHTTPTimeoutPolicy userReportResponder_timeout;
//-----------------------------------------------------------------------------
// Globals
//-----------------------------------------------------------------------------
@@ -855,10 +858,10 @@ public:
}
};
class LLUserReportResponder : public LLHTTPClient::Responder
class LLUserReportResponder : public LLHTTPClient::ResponderWithResult
{
public:
LLUserReportResponder(): LLHTTPClient::Responder() {}
LLUserReportResponder() { }
void error(U32 status, const std::string& reason)
{
@@ -870,6 +873,7 @@ public:
// we don't care about what the server returns
LLUploadDialog::modalUploadFinished();
}
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return userReportResponder_timeout; }
};
void LLFloaterReporter::sendReportViaCaps(std::string url, std::string sshot_url, const LLSD& report)
@@ -884,7 +888,7 @@ void LLFloaterReporter::sendReportViaCaps(std::string url, std::string sshot_url
else
{
// screenshot not wanted or we don't have screenshot cap
LLHTTPClient::post(url, report, new LLUserReportResponder());
LLHTTPClient::post(url, report, new LLUserReportResponder);
}
}

View File

@@ -55,6 +55,8 @@
#include "llworld.h"
#include "pipeline.h" // for gPipeline
class AIHTTPTimeoutPolicy;
extern AIHTTPTimeoutPolicy placeAvatarTeleportResponder_timeout;
// OGPX HTTP responder for PlaceAvatar cap used for Teleport
// very similar to the responder in Login, but not as many fields are returned in the TP version
@@ -62,7 +64,7 @@
// OGPX TODO: mResult should not get replaced in result(), instead
// should replace individual LLSD fields in mResult.
class LLPlaceAvatarTeleportResponder :
public LLHTTPClient::Responder
public LLHTTPClient::ResponderWithResult
{
public:
LLPlaceAvatarTeleportResponder()
@@ -218,6 +220,8 @@ public:
// gViewerWindow->setShowProgress(FALSE);
}
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return placeAvatarTeleportResponder_timeout; }
};
// Statics

View File

@@ -54,6 +54,8 @@
#include "llvfile.h"
#include "message.h"
class AIHTTPTimeoutPolicy;
extern AIHTTPTimeoutPolicy iamHere_timeout;
// static
LLFloaterTOS* LLFloaterTOS::sInstance = NULL;
@@ -90,7 +92,7 @@ LLFloaterTOS::LLFloaterTOS(ETOSType type, const std::string & message)
// helper class that trys to download a URL from a web site and calls a method
// on parent class indicating if the web server is working or not
class LLIamHere : public LLHTTPClient::Responder
class LLIamHere : public LLHTTPClient::ResponderWithResult
{
private:
LLIamHere( LLFloaterTOS* parent ) :
@@ -128,6 +130,8 @@ class LLIamHere : public LLHTTPClient::Responder
mParent->setSiteIsAlive( alive );
}
};
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return iamHere_timeout; }
};
// this is global and not a class member to keep crud out of the header file

View File

@@ -45,12 +45,15 @@
#include "llviewerwindow.h"
#include "llhttpclient.h"
class AIHTTPTimeoutPolicy;
extern AIHTTPTimeoutPolicy mediaTypeResponder_timeout;
static LLFloaterURLEntry* sInstance = NULL;
// Move this to its own file.
// helper class that tries to download a URL from a web site and calls a method
// on the Panel Land Media and to discover the MIME type
class LLMediaTypeResponder : public LLHTTPClient::Responder
class LLMediaTypeResponder : public LLHTTPClient::ResponderIgnoreBody
{
public:
LLMediaTypeResponder( const LLHandle<LLFloater> parent ) :
@@ -59,10 +62,13 @@ public:
LLHandle<LLFloater> mParent;
virtual bool needsHeaders(void) const { return true; }
virtual void completedHeader(U32 status, const std::string& reason, const LLSD& content)
virtual void completedHeaders(U32 status, std::string const& reason, AIHTTPReceivedHeaders const& headers)
{
std::string media_type = content["content-type"].asString();
std::string media_type;
bool content_type_found = headers.getFirstValue("content-type", media_type);
llassert_always(content_type_found);
std::string::size_type idx1 = media_type.find_first_of(";");
std::string mime_type = media_type.substr(0, idx1);
completeAny(status, mime_type);
@@ -82,6 +88,8 @@ public:
if ( floater_url_entry )
floater_url_entry->headerFetchComplete( status, resolved_mime_type );
}
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return mediaTypeResponder_timeout; }
};
//-----------------------------------------------------------------------------

View File

@@ -38,11 +38,15 @@
/* File Inclusions */
#include "llhttpclient.h"
class AIHTTPTimeoutPolicy;
extern AIHTTPTimeoutPolicy homeLocationResponder_timeout;
/* Typedef, Enum, Class, Struct, etc. */
class LLHomeLocationResponder : public LLHTTPClient::Responder
class LLHomeLocationResponder : public LLHTTPClient::ResponderWithResult
{
virtual void result( const LLSD& content );
virtual void error( U32 status, const std::string& reason );
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return homeLocationResponder_timeout; }
};
#endif

View File

@@ -84,6 +84,11 @@
#include "rlvhandler.h"
// [/RLVa:KB]
class AIHTTPTimeoutPolicy;
extern AIHTTPTimeoutPolicy startConferenceChatResponder_timeout;
extern AIHTTPTimeoutPolicy voiceCallCapResponder_timeout;
extern AIHTTPTimeoutPolicy sessionInviteResponder_timeout;
//
// Constants
//
@@ -179,7 +184,7 @@ void start_deprecated_conference_chat(
delete[] bucket;
}
class LLStartConferenceChatResponder : public LLHTTPClient::Responder
class LLStartConferenceChatResponder : public LLHTTPClient::ResponderIgnoreBody
{
public:
LLStartConferenceChatResponder(
@@ -214,6 +219,8 @@ public:
//the possible different language translations
}
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return startConferenceChatResponder_timeout; }
private:
LLUUID mTempSessionID;
LLUUID mCreatorID;
@@ -294,13 +301,14 @@ bool send_start_session_messages(
return false;
}
class LLVoiceCallCapResponder : public LLHTTPClient::Responder
class LLVoiceCallCapResponder : public LLHTTPClient::ResponderWithResult
{
public:
LLVoiceCallCapResponder(const LLUUID& session_id) : mSessionID(session_id) {};
virtual void error(U32 status, const std::string& reason); // called with bad status codes
virtual void result(const LLSD& content);
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return voiceCallCapResponder_timeout; }
private:
LLUUID mSessionID;
@@ -1550,7 +1558,7 @@ void LLFloaterIMPanel::draw()
LLFloater::draw();
}
class LLSessionInviteResponder : public LLHTTPClient::Responder
class LLSessionInviteResponder : public LLHTTPClient::ResponderIgnoreBody
{
public:
LLSessionInviteResponder(const LLUUID& session_id)
@@ -1564,6 +1572,8 @@ public:
//throw something back to the viewer here?
}
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return sessionInviteResponder_timeout; }
private:
LLUUID mSessionID;
};

View File

@@ -77,6 +77,9 @@
#include "rlvhandler.h"
// [/RLVa:KB]
class AIHTTPTimeoutPolicy;
extern AIHTTPTimeoutPolicy viewerChatterBoxInvitationAcceptResponder_timeout;
//
// Globals
//
@@ -91,9 +94,8 @@ LLIMMgr* gIMMgr = NULL;
//{
// return (LLStringUtil::compareDict( a->mName, b->mName ) < 0);
//}
class LLViewerChatterBoxInvitationAcceptResponder :
public LLHTTPClient::Responder
public LLHTTPClient::ResponderWithResult
{
public:
LLViewerChatterBoxInvitationAcceptResponder(
@@ -175,6 +177,8 @@ public:
}
}
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return viewerChatterBoxInvitationAcceptResponder_timeout; }
private:
LLUUID mSessionID;
LLIMMgr::EInvitationType mInvitiationType;

View File

@@ -60,6 +60,9 @@
#include "process.h"
#endif
class AIHTTPTimeoutPolicy;
extern AIHTTPTimeoutPolicy createInventoryCategoryResponder_timeout;
// Increment this if the inventory contents change in a non-backwards-compatible way.
// For viewers with link items support, former caches are incorrect.
const S32 LLInventoryModel::sCurrentInvCacheVersion = 2;
@@ -467,7 +470,7 @@ LLUUID LLInventoryModel::findCategoryByName(std::string name)
return LLUUID::null;
}
class LLCreateInventoryCategoryResponder : public LLHTTPClient::Responder
class LLCreateInventoryCategoryResponder : public LLHTTPClient::ResponderWithResult
{
public:
LLCreateInventoryCategoryResponder(LLInventoryModel* model,
@@ -511,6 +514,8 @@ public:
}
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return createInventoryCategoryResponder_timeout; }
private:
void (*mCallback)(const LLSD&, void*);
void* mData;
@@ -562,14 +567,15 @@ LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id,
{
//Let's use the new capability.
LLSD request, body;
// LLSD request;
LLSD body;
body["folder_id"] = id;
body["parent_id"] = parent_id;
body["type"] = (LLSD::Integer) preferred_type;
body["name"] = name;
request["message"] = "CreateInventoryCategory";
request["payload"] = body;
// request["message"] = "CreateInventoryCategory";
// request["payload"] = body;
// viewer_region->getCapAPI().post(request);
LLHTTPClient::post(

View File

@@ -43,8 +43,10 @@
#include <string>
#include <vector>
class LLInventoryObserver;
class AIHTTPTimeoutPolicy;
extern AIHTTPTimeoutPolicy fetchInventoryResponder_timeout;
class LLInventoryObserver;
class LLInventoryObject;
class LLInventoryItem;
class LLInventoryCategory;
@@ -55,7 +57,6 @@ class LLViewerInventoryCategory;
class LLMessageSystem;
class LLInventoryCollectFunctor;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// LLInventoryModel
//
@@ -82,12 +83,13 @@ public:
typedef LLDynamicArray<LLPointer<LLViewerInventoryItem> > item_array_t;
typedef std::set<LLUUID> changed_items_t;
class fetchInventoryResponder : public LLHTTPClient::Responder
class fetchInventoryResponder : public LLHTTPClient::ResponderWithResult
{
public:
fetchInventoryResponder(const LLSD& request_sd) : mRequestSD(request_sd) {};
void result(const LLSD& content);
void error(U32 status, const std::string& reason);
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return fetchInventoryResponder_timeout; }
protected:
LLSD mRequestSD;
};

View File

@@ -38,6 +38,10 @@
#include "llviewerregion.h"
#include "llviewerwindow.h"
class AIHTTPTimeoutPolicy;
extern AIHTTPTimeoutPolicy inventoryModelFetchDescendentsResponder_timeout;
extern AIHTTPTimeoutPolicy inventoryModelFetchItemResponder_timeout;
const F32 MAX_TIME_FOR_SINGLE_FETCH = 10.f;
const S32 MAX_FETCH_RETRIES = 10;
@@ -374,6 +378,7 @@ public:
LLInventoryModelFetchItemResponder(const LLSD& request_sd) : LLInventoryModel::fetchInventoryResponder(request_sd) {};
void result(const LLSD& content);
void error(U32 status, const std::string& reason);
AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return inventoryModelFetchItemResponder_timeout; }
};
void LLInventoryModelFetchItemResponder::result( const LLSD& content )
@@ -388,8 +393,7 @@ void LLInventoryModelFetchItemResponder::error( U32 status, const std::string& r
LLInventoryModelBackgroundFetch::instance().incrFetchCount(-1);
}
class LLInventoryModelFetchDescendentsResponder: public LLHTTPClient::Responder
class LLInventoryModelFetchDescendentsResponder: public LLHTTPClient::ResponderWithResult
{
public:
LLInventoryModelFetchDescendentsResponder(const LLSD& request_sd, uuid_vec_t recursive_cats) :
@@ -399,6 +403,8 @@ class LLInventoryModelFetchDescendentsResponder: public LLHTTPClient::Responder
//LLInventoryModelFetchDescendentsResponder() {};
void result(const LLSD& content);
void error(U32 status, const std::string& reason);
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return inventoryModelFetchDescendentsResponder_timeout; }
protected:
BOOL getIsRecursive(const LLUUID& cat_id) const;
private:
@@ -699,14 +705,14 @@ void LLInventoryModelBackgroundFetch::bulkFetch()
if (folder_request_body["folders"].size())
{
LLInventoryModelFetchDescendentsResponder *fetcher = new LLInventoryModelFetchDescendentsResponder(folder_request_body, recursive_cats);
LLHTTPClient::post(url, folder_request_body, fetcher, 300.0);
LLHTTPClient::post(url, folder_request_body, fetcher);
}
if (folder_request_body_lib["folders"].size())
{
std::string url_lib = gAgent.getRegion()->getCapability("FetchLibDescendents2");
LLInventoryModelFetchDescendentsResponder *fetcher = new LLInventoryModelFetchDescendentsResponder(folder_request_body_lib, recursive_cats);
LLHTTPClient::post(url_lib, folder_request_body_lib, fetcher, 300.0);
LLHTTPClient::post(url_lib, folder_request_body_lib, fetcher);
}
}
if (item_count)

View File

@@ -35,9 +35,13 @@
#include "llhttpclient.h"
class LLMapLayerResponder : public LLHTTPClient::Responder
class AIHTTPTimeoutPolicy;
extern AIHTTPTimeoutPolicy mapLayerResponder_timeout;
class LLMapLayerResponder : public LLHTTPClient::ResponderWithResult
{
virtual void result(const LLSD& content);
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return mapLayerResponder_timeout; }
};
#endif // LL_LLMAPLAYERRESPONDER_H

View File

@@ -35,7 +35,6 @@
#include "llappviewer.h"
#include "llbufferstream.h"
#include "llcallbacklist.h"
#include "llcurlrequest.h"
#include "lldatapacker.h"
#include "llfasttimer.h"
#if MESH_IMPORT
@@ -66,6 +65,7 @@
#include "llinventorymodel.h"
#include "llfoldertype.h"
#include "llviewerparcelmgr.h"
#include "aicurl.h"
#include "boost/lexical_cast.hpp"
#ifndef LL_WINDOWS
@@ -74,6 +74,15 @@
#include <queue>
class AIHTTPTimeoutPolicy;
extern AIHTTPTimeoutPolicy meshHeaderResponder_timeout;
extern AIHTTPTimeoutPolicy meshLODResponder_timeout;
extern AIHTTPTimeoutPolicy meshSkinInfoResponder_timeout;
extern AIHTTPTimeoutPolicy meshDecompositionResponder_timeout;
extern AIHTTPTimeoutPolicy meshPhysicsShapeResponder_timeout;
extern AIHTTPTimeoutPolicy wholeModelFeeResponder_timeout;
extern AIHTTPTimeoutPolicy wholeModelUploadResponder_timeout;
LLMeshRepository gMeshRepo;
const U32 MAX_MESH_REQUESTS_PER_SECOND = 100;
@@ -201,7 +210,7 @@ S32 LLMeshRepoThread::sActiveHeaderRequests = 0;
S32 LLMeshRepoThread::sActiveLODRequests = 0;
U32 LLMeshRepoThread::sMaxConcurrentRequests = 1;
class LLMeshHeaderResponder : public LLCurl::Responder
class LLMeshHeaderResponder : public LLHTTPClient::ResponderWithCompleted
{
public:
LLVolumeParams mMeshParams;
@@ -221,9 +230,10 @@ public:
const LLChannelDescriptors& channels,
const LLIOPipe::buffer_ptr_t& buffer);
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return meshHeaderResponder_timeout; }
};
class LLMeshLODResponder : public LLCurl::Responder
class LLMeshLODResponder : public LLHTTPClient::ResponderWithCompleted
{
public:
LLVolumeParams mMeshParams;
@@ -246,9 +256,10 @@ public:
const LLChannelDescriptors& channels,
const LLIOPipe::buffer_ptr_t& buffer);
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return meshLODResponder_timeout; }
};
class LLMeshSkinInfoResponder : public LLCurl::Responder
class LLMeshSkinInfoResponder : public LLHTTPClient::ResponderWithCompleted
{
public:
LLUUID mMeshID;
@@ -264,9 +275,10 @@ public:
const LLChannelDescriptors& channels,
const LLIOPipe::buffer_ptr_t& buffer);
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return meshSkinInfoResponder_timeout; }
};
class LLMeshDecompositionResponder : public LLCurl::Responder
class LLMeshDecompositionResponder : public LLHTTPClient::ResponderWithCompleted
{
public:
LLUUID mMeshID;
@@ -282,9 +294,10 @@ public:
const LLChannelDescriptors& channels,
const LLIOPipe::buffer_ptr_t& buffer);
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return meshDecompositionResponder_timeout; }
};
class LLMeshPhysicsShapeResponder : public LLCurl::Responder
class LLMeshPhysicsShapeResponder : public LLHTTPClient::ResponderWithCompleted
{
public:
LLUUID mMeshID;
@@ -300,6 +313,7 @@ public:
const LLChannelDescriptors& channels,
const LLIOPipe::buffer_ptr_t& buffer);
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return meshPhysicsShapeResponder_timeout; }
};
#if MESH_IMPORT
@@ -352,7 +366,7 @@ void log_upload_error(S32 status, const LLSD& content, std::string stage, std::s
}
}
class LLWholeModelFeeResponder: public LLCurl::Responder
class LLWholeModelFeeResponder: public LLHTTPClient::ResponderWithCompleted
{
LLMeshUploadThread* mThread;
LLSD mModelData;
@@ -403,9 +417,10 @@ public:
}
}
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return wholeModelFeeResponder_timeout; }
};
class LLWholeModelUploadResponder: public LLCurl::Responder
class LLWholeModelUploadResponder: public LLHTTPClient::ResponderWithCompleted
{
LLMeshUploadThread* mThread;
LLSD mModelData;
@@ -459,6 +474,8 @@ public:
}
}
}
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return wholeModelUploadResponder_timeout; }
};
#endif //MESH_IMPORT
@@ -482,7 +499,6 @@ LLMeshRepoThread::~LLMeshRepoThread()
void LLMeshRepoThread::run()
{
mCurlRequest = new AICurlInterface::Request;
#if MESH_IMPORT
LLCDResult res = LLConvexDecomposition::initThread();
if (res != LLCD_OK)
@@ -637,9 +653,6 @@ void LLMeshRepoThread::run()
llwarns << "convex decomposition unable to be quit" << llendl;
}
#endif //MESH_IMPORT
delete mCurlRequest;
mCurlRequest = NULL;
}
void LLMeshRepoThread::loadMeshSkinInfo(const LLUUID& mesh_id)
@@ -766,15 +779,13 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id)
}
//reading from VFS failed for whatever reason, fetch from sim
std::vector<std::string> headers;
headers.push_back("Accept: application/octet-stream");
AIHTTPHeaders headers("Accept", "application/octet-stream");
std::string http_url = constructUrl(mesh_id);
if (!http_url.empty())
{
// This might throw AICurlNoEasyHandle.
mCurlRequest->getByteRange(http_url, headers, offset, size,
new LLMeshSkinInfoResponder(mesh_id, offset, size));
LLHTTPClient::getByteRange(http_url, offset, size,
new LLMeshSkinInfoResponder(mesh_id, offset, size), headers);
LLMeshRepository::sHTTPRequestCount++;
}
}
@@ -841,15 +852,14 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id)
}
//reading from VFS failed for whatever reason, fetch from sim
std::vector<std::string> headers;
headers.push_back("Accept: application/octet-stream");
AIHTTPHeaders headers("Accept", "application/octet-stream");
std::string http_url = constructUrl(mesh_id);
if (!http_url.empty())
{
// This might throw AICurlNoEasyHandle.
mCurlRequest->getByteRange(http_url, headers, offset, size,
new LLMeshDecompositionResponder(mesh_id, offset, size));
LLHTTPClient::getByteRange(http_url, offset, size,
new LLMeshDecompositionResponder(mesh_id, offset, size), headers);
LLMeshRepository::sHTTPRequestCount++;
}
}
@@ -916,15 +926,14 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id)
}
//reading from VFS failed for whatever reason, fetch from sim
std::vector<std::string> headers;
headers.push_back("Accept: application/octet-stream");
AIHTTPHeaders headers("Accept", "application/octet-stream");
std::string http_url = constructUrl(mesh_id);
if (!http_url.empty())
{
// This might throw AICurlNoEasyHandle.
mCurlRequest->getByteRange(http_url, headers, offset, size,
new LLMeshPhysicsShapeResponder(mesh_id, offset, size));
LLHTTPClient::getByteRange(http_url, offset, size,
new LLMeshPhysicsShapeResponder(mesh_id, offset, size), headers);
LLMeshRepository::sHTTPRequestCount++;
}
}
@@ -966,8 +975,7 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, U32& c
}
//either cache entry doesn't exist or is corrupt, request header from simulator
std::vector<std::string> headers;
headers.push_back("Accept: application/octet-stream");
AIHTTPHeaders headers("Accept", "application/octet-stream");
std::string http_url = constructUrl(mesh_params.getSculptID());
if (!http_url.empty())
@@ -976,7 +984,7 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, U32& c
//within the first 4KB
//NOTE -- this will break of headers ever exceed 4KB
// This might throw AICurlNoEasyHandle.
mCurlRequest->getByteRange(http_url, headers, 0, 4096, new LLMeshHeaderResponder(mesh_params));
LLHTTPClient::getByteRange(http_url, 0, 4096, new LLMeshHeaderResponder(mesh_params), headers);
LLMeshRepository::sHTTPRequestCount++;
count++;
}
@@ -1031,15 +1039,14 @@ void LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod,
}
//reading from VFS failed for whatever reason, fetch from sim
std::vector<std::string> headers;
headers.push_back("Accept: application/octet-stream");
AIHTTPHeaders headers("Accept", "application/octet-stream");
std::string http_url = constructUrl(mesh_id);
if (!http_url.empty())
{
// This might throw AICurlNoEasyHandle.
mCurlRequest->getByteRange(constructUrl(mesh_id), headers, offset, size,
new LLMeshLODResponder(mesh_params, lod, offset, size));
LLHTTPClient::getByteRange(constructUrl(mesh_id), offset, size,
new LLMeshLODResponder(mesh_params, lod, offset, size), headers);
LLMeshRepository::sHTTPRequestCount++;
count++;
}
@@ -1275,9 +1282,7 @@ LLMeshUploadThread::LLMeshUploadThread(LLMeshUploadThread::instance_list& data,
mUploadSkin = upload_skin;
mUploadJoints = upload_joints;
mMutex = new LLMutex();
mCurlRequest = NULL;
mPendingUploads = 0;
mFinished = false;
mOrigin = gAgent.getPositionAgent();
mHost = gAgent.getRegionHost();
@@ -1603,8 +1608,6 @@ void LLMeshUploadThread::generateHulls()
void LLMeshUploadThread::doWholeModelUpload()
{
mCurlRequest = new AICurlInterface::Request();
if (mWholeModelUploadURL.empty())
{
llinfos << "unable to upload, fee request failed" << llendl;
@@ -1617,33 +1620,15 @@ void LLMeshUploadThread::doWholeModelUpload()
wholeModelToLLSD(full_model_data, true);
LLSD body = full_model_data["asset_resources"];
dump_llsd_to_file(body,make_dump_name("whole_model_body_",dump_num));
LLCurlRequest::headers_t headers;
// This might throw AICurlNoEasyHandle.
mCurlRequest->post(mWholeModelUploadURL, headers, body,
new LLWholeModelUploadResponder(this, full_model_data, mUploadObserverHandle), mMeshUploadTimeOut);
do
{
mCurlRequest->process(); // FIXME: This function does not exist anymore. The post() gets CPU time from AICurlEasyRequestStateMachine.
// Therefore, if we do not want to continue here unless this upload is done... no wait, that would
// be blocking and we don't want blocking...
//sleep for 10ms to prevent eating a whole core
apr_sleep(10000);
} while (mCurlRequest->getQueued() > 0);
LLHTTPClient::post(mWholeModelUploadURL, body,
new LLWholeModelUploadResponder(this, full_model_data, mUploadObserverHandle));
}
delete mCurlRequest;
mCurlRequest = NULL;
// Currently a no-op.
mFinished = true;
}
void LLMeshUploadThread::requestWholeModelFee()
{
dump_num++;
mCurlRequest = new AICurlInterface::Request;
generateHulls();
LLSD model_data;
@@ -1651,23 +1636,9 @@ void LLMeshUploadThread::requestWholeModelFee()
dump_llsd_to_file(model_data,make_dump_name("whole_model_fee_request_",dump_num));
mPendingUploads++;
LLCurlRequest::headers_t headers;
// This might throw AICurlNoEasyHandle.
mCurlRequest->post(mWholeModelFeeCapability, headers, model_data,
new LLWholeModelFeeResponder(this,model_data, mFeeObserverHandle), mMeshUploadTimeOut);
do
{
mCurlRequest->process();
//sleep for 10ms to prevent eating a whole core
apr_sleep(10000);
} while (mCurlRequest->getQueued() > 0);
delete mCurlRequest;
mCurlRequest = NULL;
// Currently a no-op.
mFinished = true;
LLHTTPClient::post(mWholeModelFeeCapability, model_data,
new LLWholeModelFeeResponder(this, model_data, mFeeObserverHandle));
}
#endif //MESH_IMPORT
@@ -1799,7 +1770,7 @@ void LLMeshLODResponder::completedRaw(U32 status, const std::string& reason,
S32 data_size = buffer->countAfter(channels.in(), NULL);
if (status < 200 || status > 400)
if (status < 200 || status >= 400)
{
llwarns << status << ": " << reason << llendl;
}
@@ -1853,7 +1824,7 @@ void LLMeshSkinInfoResponder::completedRaw(U32 status, const std::string& reason
{
S32 data_size = buffer->countAfter(channels.in(), NULL);
if (status < 200 || status > 400)
if (status < 200 || status >= 400)
{
llwarns << status << ": " << reason << llendl;
}
@@ -1907,7 +1878,7 @@ void LLMeshDecompositionResponder::completedRaw(U32 status, const std::string& r
{
S32 data_size = buffer->countAfter(channels.in(), NULL);
if (status < 200 || status > 400)
if (status < 200 || status >= 400)
{
llwarns << status << ": " << reason << llendl;
}
@@ -1961,7 +1932,7 @@ void LLMeshPhysicsShapeResponder::completedRaw(U32 status, const std::string& re
{
S32 data_size = buffer->countAfter(channels.in(), NULL);
if (status < 200 || status > 400)
if (status < 200 || status >= 400)
{
llwarns << status << ": " << reason << llendl;
}
@@ -2013,7 +1984,7 @@ void LLMeshHeaderResponder::completedRaw(U32 status, const std::string& reason,
const LLChannelDescriptors& channels,
const LLIOPipe::buffer_ptr_t& buffer)
{
if (status < 200 || status > 400)
if (status < 200 || status >= 400)
{
//llwarns
// << "Header responder failed with status: "

View File

@@ -32,7 +32,6 @@
#include "lluuid.h"
#include "llviewertexture.h"
#include "llvolume.h"
#include "llcurlrequest.h"
#if MESH_IMPORT
#define LLCONVEXDECOMPINTER_STATIC 1
@@ -246,7 +245,6 @@ public:
static S32 sActiveLODRequests;
static U32 sMaxConcurrentRequests;
AICurlInterface::Request* mCurlRequest;
LLMutex* mMutex;
LLMutex* mHeaderMutex;
LLCondition* mSignal;
@@ -413,10 +411,8 @@ public:
instance_map mInstance;
LLMutex* mMutex;
LLCurlRequest* mCurlRequest;
S32 mPendingUploads;
LLVector3 mOrigin;
bool mFinished;
bool mUploadTextures;
bool mUploadSkin;
bool mUploadJoints;
@@ -431,7 +427,6 @@ public:
LLHandle<LLWholeModelFeeObserver> fee_observer= (LLHandle<LLWholeModelFeeObserver>()), LLHandle<LLWholeModelUploadObserver> upload_observer = (LLHandle<LLWholeModelUploadObserver>()));
~LLMeshUploadThread();
bool finished() { return mFinished; }
virtual void run();
void preStart();
void discard() ;

View File

@@ -52,6 +52,10 @@
#include "llviewerwindow.h"
#include "llviewerregion.h"
class AIHTTPTimeoutPolicy;
extern AIHTTPTimeoutPolicy startGroupVoteResponder_timeout;
extern AIHTTPTimeoutPolicy groupProposalBallotResponder_timeout;
class LLPanelGroupVoting::impl
{
public:
@@ -680,7 +684,7 @@ void LLPanelGroupVoting::handleFailure(
}
}
class LLStartGroupVoteResponder : public LLHTTPClient::Responder
class LLStartGroupVoteResponder : public LLHTTPClient::ResponderWithResult
{
public:
LLStartGroupVoteResponder(const LLUUID& group_id)
@@ -705,11 +709,15 @@ public:
LLPanelGroupVoting::handleFailure(mGroupID);
}
//Return our timeout policy.
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return startGroupVoteResponder_timeout; }
private:
LLUUID mGroupID;
};
class LLGroupProposalBallotResponder : public LLHTTPClient::Responder
class LLGroupProposalBallotResponder : public LLHTTPClient::ResponderWithResult
{
public:
LLGroupProposalBallotResponder(const LLUUID& group_id)
@@ -735,6 +743,10 @@ public:
LLPanelGroupVoting::handleFailure(mGroupID);
}
//Return out timeout policy.
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return groupProposalBallotResponder_timeout; }
private:
LLUUID mGroupID;
};
@@ -781,8 +793,7 @@ void LLPanelGroupVoting::impl::sendStartGroupProposal()
LLHTTPClient::post(
url,
body,
new LLStartGroupVoteResponder(mGroupID),
300);
new LLStartGroupVoteResponder(mGroupID));
}
else
{ //DEPRECATED!!!!!!! This is a fallback just in case our backend cap is not there. Delete this block ASAP!
@@ -828,8 +839,7 @@ void LLPanelGroupVoting::impl::sendGroupProposalBallot(const std::string& vote)
LLHTTPClient::post(
url,
body,
new LLGroupProposalBallotResponder(mGroupID),
300);
new LLGroupProposalBallotResponder(mGroupID));
}
else
{ //DEPRECATED!!!!!!! This is a fallback just in case our backend cap is not there. Delete this block ASAP!

View File

@@ -95,6 +95,9 @@
#define USE_VIEWER_AUTH 0
class AIHTTPTimeoutPolicy;
extern AIHTTPTimeoutPolicy iamHereLogin_timeout;
const S32 BLACK_BORDER_HEIGHT = 160;
const S32 MAX_PASSWORD = 16;
@@ -111,7 +114,7 @@ static bool nameSplit(const std::string& full, std::string& first, std::string&
if (fragments.size() == 1)
{
if (gHippoGridManager->getConnectedGrid()->isSecondLife())
last = "resident";
last = "Resident";
else
last = "";
}
@@ -120,8 +123,8 @@ static bool nameSplit(const std::string& full, std::string& first, std::string&
return (fragments.size() <= 2);
}
static std::string nameJoin(const std::string& first,const std::string& last) {
if (last.empty() || boost::algorithm::iequals(last, "resident"))
static std::string nameJoin(const std::string& first,const std::string& last, bool strip_resident) {
if (last.empty() || (strip_resident && boost::algorithm::iequals(last, "Resident")))
return first;
else {
if(std::islower(last[0]))
@@ -131,15 +134,15 @@ static std::string nameJoin(const std::string& first,const std::string& last) {
}
}
static std::string getDisplayString(const std::string& first, const std::string& last, const std::string& grid) {
static std::string getDisplayString(const std::string& first, const std::string& last, const std::string& grid, bool is_secondlife) {
if(grid == gHippoGridManager->getDefaultGridNick())
return nameJoin(first, last);
return nameJoin(first, last, is_secondlife);
else
return nameJoin(first, last) + " (" + grid + ")";
return nameJoin(first, last, is_secondlife) + " (" + grid + ")";
}
static std::string getDisplayString(const LLSavedLoginEntry& entry) {
return getDisplayString(entry.getFirstName(), entry.getLastName(), entry.getGrid());
return getDisplayString(entry.getFirstName(), entry.getLastName(), entry.getGrid(), entry.isSecondLife());
}
class LLLoginRefreshHandler : public LLCommandHandler
@@ -165,7 +168,7 @@ std::string gFullName;
// helper class that trys to download a URL from a web site and calls a method
// on parent class indicating if the web server is working or not
class LLIamHereLogin : public LLHTTPClient::Responder
class LLIamHereLogin : public LLHTTPClient::ResponderWithCompleted
{
private:
LLIamHereLogin( LLPanelLogin* parent ) :
@@ -190,20 +193,13 @@ class LLIamHereLogin : public LLHTTPClient::Responder
const LLChannelDescriptors& channels,
const LLIOPipe::buffer_ptr_t& buffer)
{
completed(status, reason, LLSD()); // will call result() or error()
if (mParent)
{
mParent->setSiteIsAlive(200 <= status && status < 300);
}
}
virtual void result( const LLSD& content )
{
if ( mParent )
mParent->setSiteIsAlive( true );
};
virtual void error( U32 status, const std::string& reason )
{
if ( mParent )
mParent->setSiteIsAlive( false );
};
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return iamHereLogin_timeout; }
};
// this is global and not a class member to keep crud out of the header file
@@ -665,8 +661,7 @@ void LLPanelLogin::show(const LLRect &rect,
// static
void LLPanelLogin::setFields(const std::string& firstname,
const std::string& lastname,
const std::string& password,
const LLSavedLogins& login_history)
const std::string& password)
{
if (!sInstance)
{
@@ -677,7 +672,7 @@ void LLPanelLogin::setFields(const std::string& firstname,
LLComboBox* login_combo = sInstance->getChild<LLComboBox>("name_combo");
llassert_always(firstname.find(' ') == std::string::npos);
login_combo->setLabel(nameJoin(firstname, lastname));
login_combo->setLabel(nameJoin(firstname, lastname, false));
// Max "actual" password length is 16 characters.
// Hex digests are always 32 characters.
@@ -714,7 +709,7 @@ void LLPanelLogin::setFields(const LLSavedLoginEntry& entry, bool takeFocus)
}
LLCheckBoxCtrl* remember_pass_check = sInstance->getChild<LLCheckBoxCtrl>("remember_check");
std::string fullname = nameJoin(entry.getFirstName(), entry.getLastName());
std::string fullname = nameJoin(entry.getFirstName(), entry.getLastName(), entry.isSecondLife());
LLComboBox* login_combo = sInstance->getChild<LLComboBox>("name_combo");
login_combo->setTextEntry(fullname);
login_combo->resetTextDirty();

View File

@@ -71,10 +71,10 @@ public:
* @param firstname First name value.
* @param lastname Last name value.
* @param password Password, as plaintext or munged.
* @param login_history Login history object. An empty one can be provided if no history is available.
* @param is_secondlife True if First/Last refer to a SecondLife(tm) account.
*/
static void setFields(const std::string& firstname, const std::string& lastname,
const std::string& password, const LLSavedLogins& login_history = LLSavedLogins());
const std::string& password);
/**
* @brief Set the values of the displayed fields from a populated history entry.

View File

@@ -56,6 +56,15 @@
#include "llviewerregion.h"
#include "llweb.h"
class AIHTTPTimeoutPolicy;
extern AIHTTPTimeoutPolicy navMeshStatusResponder_timeout;
extern AIHTTPTimeoutPolicy navMeshResponder_timeout;
extern AIHTTPTimeoutPolicy agentStateResponder_timeout;
extern AIHTTPTimeoutPolicy navMeshRebakeResponder_timeout;
extern AIHTTPTimeoutPolicy objectLinksetsResponder_timeout;
extern AIHTTPTimeoutPolicy terrainLinksetsResponder_timeout;
extern AIHTTPTimeoutPolicy charactersResponder_timeout;
#define CAP_SERVICE_RETRIEVE_NAVMESH "RetrieveNavMeshSrc"
#define CAP_SERVICE_NAVMESH_STATUS "NavMeshGenerationStatus"
@@ -101,7 +110,7 @@ LLHTTPRegistration<LLAgentStateChangeNode> gHTTPRegistrationAgentStateChangeNode
// NavMeshStatusResponder
//---------------------------------------------------------------------------
class NavMeshStatusResponder : public LLHTTPClient::Responder
class NavMeshStatusResponder : public LLHTTPClient::ResponderWithResult
{
public:
NavMeshStatusResponder(const std::string &pCapabilityURL, LLViewerRegion *pRegion, bool pIsGetStatusOnly);
@@ -109,6 +118,7 @@ public:
virtual void result(const LLSD &pContent);
virtual void error(U32 pStatus, const std::string& pReason);
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return navMeshStatusResponder_timeout; }
protected:
@@ -123,7 +133,7 @@ private:
// NavMeshResponder
//---------------------------------------------------------------------------
class NavMeshResponder : public LLHTTPClient::Responder
class NavMeshResponder : public LLHTTPClient::ResponderWithResult
{
public:
NavMeshResponder(const std::string &pCapabilityURL, U32 pNavMeshVersion, LLPathfindingNavMeshPtr pNavMeshPtr);
@@ -131,6 +141,7 @@ public:
virtual void result(const LLSD &pContent);
virtual void error(U32 pStatus, const std::string& pReason);
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return navMeshResponder_timeout; }
protected:
@@ -144,7 +155,7 @@ private:
// AgentStateResponder
//---------------------------------------------------------------------------
class AgentStateResponder : public LLHTTPClient::Responder
class AgentStateResponder : public LLHTTPClient::ResponderWithResult
{
public:
AgentStateResponder(const std::string &pCapabilityURL);
@@ -152,6 +163,7 @@ public:
virtual void result(const LLSD &pContent);
virtual void error(U32 pStatus, const std::string& pReason);
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return agentStateResponder_timeout; }
protected:
@@ -163,7 +175,7 @@ private:
//---------------------------------------------------------------------------
// NavMeshRebakeResponder
//---------------------------------------------------------------------------
class NavMeshRebakeResponder : public LLHTTPClient::Responder
class NavMeshRebakeResponder : public LLHTTPClient::ResponderWithResult
{
public:
NavMeshRebakeResponder(const std::string &pCapabilityURL, LLPathfindingManager::rebake_navmesh_callback_t pRebakeNavMeshCallback);
@@ -171,6 +183,7 @@ public:
virtual void result(const LLSD &pContent);
virtual void error(U32 pStatus, const std::string& pReason);
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return navMeshRebakeResponder_timeout; }
protected:
@@ -222,8 +235,7 @@ typedef boost::shared_ptr<LinksetsResponder> LinksetsResponderPtr;
//---------------------------------------------------------------------------
// ObjectLinksetsResponder
//---------------------------------------------------------------------------
class ObjectLinksetsResponder : public LLHTTPClient::Responder
class ObjectLinksetsResponder : public LLHTTPClient::ResponderWithResult
{
public:
ObjectLinksetsResponder(const std::string &pCapabilityURL, LinksetsResponderPtr pLinksetsResponsderPtr);
@@ -231,6 +243,7 @@ public:
virtual void result(const LLSD &pContent);
virtual void error(U32 pStatus, const std::string &pReason);
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return objectLinksetsResponder_timeout; }
protected:
@@ -242,8 +255,7 @@ private:
//---------------------------------------------------------------------------
// TerrainLinksetsResponder
//---------------------------------------------------------------------------
class TerrainLinksetsResponder : public LLHTTPClient::Responder
class TerrainLinksetsResponder : public LLHTTPClient::ResponderWithResult
{
public:
TerrainLinksetsResponder(const std::string &pCapabilityURL, LinksetsResponderPtr pLinksetsResponsderPtr);
@@ -251,6 +263,7 @@ public:
virtual void result(const LLSD &pContent);
virtual void error(U32 pStatus, const std::string &pReason);
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return terrainLinksetsResponder_timeout; }
protected:
@@ -262,8 +275,7 @@ private:
//---------------------------------------------------------------------------
// CharactersResponder
//---------------------------------------------------------------------------
class CharactersResponder : public LLHTTPClient::Responder
class CharactersResponder : public LLHTTPClient::ResponderWithResult
{
public:
CharactersResponder(const std::string &pCapabilityURL, LLPathfindingManager::request_id_t pRequestId, LLPathfindingManager::object_request_callback_t pCharactersCallback);
@@ -271,6 +283,7 @@ public:
virtual void result(const LLSD &pContent);
virtual void error(U32 pStatus, const std::string &pReason);
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return charactersResponder_timeout; }
protected:
@@ -778,8 +791,7 @@ void LLAgentStateChangeNode::post(ResponsePtr pResponse, const LLSD &pContext, c
// NavMeshStatusResponder
//---------------------------------------------------------------------------
NavMeshStatusResponder::NavMeshStatusResponder(const std::string &pCapabilityURL, LLViewerRegion *pRegion, bool pIsGetStatusOnly)
: LLHTTPClient::Responder(),
NavMeshStatusResponder::NavMeshStatusResponder(const std::string &pCapabilityURL, LLViewerRegion *pRegion, bool pIsGetStatusOnly) :
mCapabilityURL(pCapabilityURL),
mRegion(pRegion),
mRegionUUID(),
@@ -812,8 +824,7 @@ void NavMeshStatusResponder::error(U32 pStatus, const std::string& pReason)
// NavMeshResponder
//---------------------------------------------------------------------------
NavMeshResponder::NavMeshResponder(const std::string &pCapabilityURL, U32 pNavMeshVersion, LLPathfindingNavMeshPtr pNavMeshPtr)
: LLHTTPClient::Responder(),
NavMeshResponder::NavMeshResponder(const std::string &pCapabilityURL, U32 pNavMeshVersion, LLPathfindingNavMeshPtr pNavMeshPtr) :
mCapabilityURL(pCapabilityURL),
mNavMeshVersion(pNavMeshVersion),
mNavMeshPtr(pNavMeshPtr)
@@ -838,9 +849,7 @@ void NavMeshResponder::error(U32 pStatus, const std::string& pReason)
// AgentStateResponder
//---------------------------------------------------------------------------
AgentStateResponder::AgentStateResponder(const std::string &pCapabilityURL)
: LLHTTPClient::Responder()
, mCapabilityURL(pCapabilityURL)
AgentStateResponder::AgentStateResponder(const std::string &pCapabilityURL) : mCapabilityURL(pCapabilityURL)
{
}
@@ -866,8 +875,7 @@ void AgentStateResponder::error(U32 pStatus, const std::string &pReason)
//---------------------------------------------------------------------------
// navmesh rebake responder
//---------------------------------------------------------------------------
NavMeshRebakeResponder::NavMeshRebakeResponder(const std::string &pCapabilityURL, LLPathfindingManager::rebake_navmesh_callback_t pRebakeNavMeshCallback)
: LLHTTPClient::Responder(),
NavMeshRebakeResponder::NavMeshRebakeResponder(const std::string &pCapabilityURL, LLPathfindingManager::rebake_navmesh_callback_t pRebakeNavMeshCallback) :
mCapabilityURL(pCapabilityURL),
mRebakeNavMeshCallback(pRebakeNavMeshCallback)
{
@@ -973,8 +981,7 @@ void LinksetsResponder::sendCallback()
// ObjectLinksetsResponder
//---------------------------------------------------------------------------
ObjectLinksetsResponder::ObjectLinksetsResponder(const std::string &pCapabilityURL, LinksetsResponderPtr pLinksetsResponsderPtr)
: LLHTTPClient::Responder(),
ObjectLinksetsResponder::ObjectLinksetsResponder(const std::string &pCapabilityURL, LinksetsResponderPtr pLinksetsResponsderPtr) :
mCapabilityURL(pCapabilityURL),
mLinksetsResponsderPtr(pLinksetsResponsderPtr)
{
@@ -998,8 +1005,7 @@ void ObjectLinksetsResponder::error(U32 pStatus, const std::string &pReason)
// TerrainLinksetsResponder
//---------------------------------------------------------------------------
TerrainLinksetsResponder::TerrainLinksetsResponder(const std::string &pCapabilityURL, LinksetsResponderPtr pLinksetsResponsderPtr)
: LLHTTPClient::Responder(),
TerrainLinksetsResponder::TerrainLinksetsResponder(const std::string &pCapabilityURL, LinksetsResponderPtr pLinksetsResponsderPtr) :
mCapabilityURL(pCapabilityURL),
mLinksetsResponsderPtr(pLinksetsResponsderPtr)
{
@@ -1023,8 +1029,7 @@ void TerrainLinksetsResponder::error(U32 pStatus, const std::string &pReason)
// CharactersResponder
//---------------------------------------------------------------------------
CharactersResponder::CharactersResponder(const std::string &pCapabilityURL, LLPathfindingManager::request_id_t pRequestId, LLPathfindingManager::object_request_callback_t pCharactersCallback)
: LLHTTPClient::Responder(),
CharactersResponder::CharactersResponder(const std::string &pCapabilityURL, LLPathfindingManager::request_id_t pRequestId, LLPathfindingManager::object_request_callback_t pCharactersCallback) :
mCapabilityURL(pCapabilityURL),
mRequestId(pRequestId),
mCharactersCallback(pCharactersCallback)

View File

@@ -39,7 +39,10 @@
#include "lltrans.h"
#include "llviewerregion.h"
class LLProductInfoRequestResponder : public LLHTTPClient::Responder
class AIHTTPTimeoutPolicy;
extern AIHTTPTimeoutPolicy productInfoRequestResponder_timeout;
class LLProductInfoRequestResponder : public LLHTTPClient::ResponderWithResult
{
public:
//If we get back a normal response, handle it here
@@ -54,6 +57,8 @@ public:
llwarns << "LLProductInfoRequest::error("
<< status << ": " << reason << ")" << llendl;
}
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return productInfoRequestResponder_timeout; }
};
LLProductInfoRequestManager::LLProductInfoRequestManager() : mSkuDescriptions()

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