Merge remote-tracking branch 'singu/master'

Conflicts:
	indra/cmake/WebKitLibPlugin.cmake
	indra/llmessage/llurlrequest.cpp
	indra/plugins/webkit/media_plugin_webkit.cpp
This commit is contained in:
Drake Arconis
2012-10-04 16:17:41 -04:00
115 changed files with 7973 additions and 6004 deletions

View File

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

View File

@@ -1,7 +1,7 @@
# -*- cmake -*-
include(Prebuilt)
set(CURL_FIND_QUIETLY ON)
set(CURL_FIND_QUIETLY OFF)
set(CURL_FIND_REQUIRED ON)
if (STANDALONE)

View File

@@ -1 +1,18 @@
set(CWDEBUG_LIBRARIES cwdebug)
include_directories (${CMAKE_SOURCE_DIR}/cwdebug)
set(cwdebug_SOURCE_FILES
${CMAKE_SOURCE_DIR}/cwdebug/debug.cc
)
set(cwdebug_HEADER_FILES
${CMAKE_SOURCE_DIR}/cwdebug/cwdebug.h
${CMAKE_SOURCE_DIR}/cwdebug/sys.h
${CMAKE_SOURCE_DIR}/cwdebug/debug.h
${CMAKE_SOURCE_DIR}/cwdebug/debug_ostream_operators.h
)
set_source_files_properties(${cwdebug_HEADER_FILES}
PROPERTIES HEADER_FILE_ONLY TRUE)
list(APPEND cwdebug_SOURCE_FILES ${cwdebug_HEADER_FILES})

View File

@@ -1,39 +0,0 @@
# -*- cmake -*-
project(cwdebug)
include(00-Common)
include(LLCommon)
include(LLMath)
include(LLMessage)
include(LLVFS)
include_directories (${CMAKE_CURRENT_SOURCE_DIR})
set(cwdebug_SOURCE_FILES
debug.cc
)
set(cwdebug_HEADER_FILES
CMakeLists.txt
cwdebug.h
sys.h
debug.h
debug_ostream_operators.h
)
set_source_files_properties(${cwdebug_HEADER_FILES}
PROPERTIES HEADER_FILE_ONLY TRUE)
if(NOT WORD_SIZE EQUAL 32)
if(WINDOWS)
add_definitions(/FIXED:NO)
else(WINDOWS) # not windows therefore gcc LINUX and DARWIN
add_definitions(-fPIC)
endif(WINDOWS)
endif (NOT WORD_SIZE EQUAL 32)
list(APPEND cwdebug_SOURCE_FILES ${cwdebug_HEADER_FILES})
add_library (cwdebug ${cwdebug_SOURCE_FILES})

View File

@@ -173,6 +173,8 @@ void stop_recording_backtraces(void)
channel_ct backtrace DDCN("BACKTRACE"); //!< This debug channel is used for backtraces.
channel_ct statemachine DDCN("STATEMACHINE"); //!< This debug channel is used for output related to class AIStateMachine.
channel_ct caps DDCN("CAPS"); //!< This debug channel is used for output related to Capabilities.
channel_ct curl DDCN("CURL"); //!< This debug channel is used for output related to Curl.
channel_ct curlio DDCN("CURLIO"); //!< This debug channel is used to print debug output of libcurl.
} // namespace dc
} // namespace DEBUGCHANNELS
@@ -411,4 +413,94 @@ void cwdebug_backtrace(int n)
}
#endif
#endif // CWDEBUG
#elif defined(DEBUG_CURLIO)
#include "debug.h"
#include "aithreadid.h"
namespace debug
{
namespace libcwd { libcwd_do_type const libcw_do; }
ll_thread_local int Indent::S_indentation;
Indent::Indent(int indent) : M_indent(indent)
{
S_indentation += M_indent;
}
Indent::~Indent()
{
S_indentation -= M_indent;
}
std::ostream& operator<<(std::ostream& os, Indent::print_nt)
{
if (Indent::S_indentation)
os << std::string(Indent::S_indentation, ' ');
return os;
}
#ifdef DEBUG_CURLIO
std::ostream& operator<<(std::ostream& os, print_thread_id_t)
{
if (!AIThreadID::in_main_thread_inline())
{
os << std::hex << (size_t)AIThreadID::getCurrentThread_inline() << std::dec << ' ';
}
return os;
}
#endif
std::ostream& operator<<(std::ostream& os, libcwd::buf2str const& b2s)
{
static char const c2s_tab[7] = { 'a', 'b', 't', 'n', 'v', 'f', 'r' };
size_t size = b2s.mSize;
for (char const* p1 = b2s.mBuf; size > 0; --size, ++p1)
{
char c =*p1;
if ((c > 31 && c != 92 && c != 127) || (unsigned char)c > 159)
os.put(c);
else
{
os.put('\\');
if (c > 6 && c < 14)
{
os.put(c2s_tab[c - 7]);
return os;
}
else if (c == 27)
{
os.put('e');
return os;
}
else if (c == '\\')
{
os.put('\\');
return os;
}
std::ostream::char_type old_fill = os.fill('0');
std::ios_base::fmtflags old_flgs = os.flags();
os.width(3);
os << std::oct << (int)((unsigned char)c);
os.setf(old_flgs);
os.fill(old_fill);
}
}
return os;
}
namespace dc
{
fake_channel const warning(1, "WARNING ");
fake_channel const curl(1, "CURL ");
fake_channel const curlio(1, "CURLIO ");
fake_channel const statemachine(1, "STATEMACHINE");
fake_channel const notice(1, "NOTICE ");
} // namespace dc
} // namespace debug
#endif

View File

@@ -27,6 +27,94 @@
#ifndef CWDEBUG
#ifdef DEBUG_CURLIO
// If CWDEBUG is not defined, but DEBUG_CURLIO is, then replace
// some of the cwd macro's with something that generates viewer
// specific debug output. Note that this generates a LOT of
// output and should not normally be defined.
#include <string>
#include "llpreprocessor.h"
namespace debug {
namespace libcwd {
struct buf2str {
buf2str(char const* buf, int size) : mBuf(buf), mSize(size) { }
char const* mBuf;
int mSize;
};
struct libcwd_do_type {
void on() const { }
};
extern LL_COMMON_API libcwd_do_type const libcw_do;
} // namespace libcwd
enum print_thread_id_t { print_thread_id };
inline void init() { }
struct Indent {
int M_indent;
static ll_thread_local int S_indentation;
enum LL_COMMON_API print_nt { print };
LL_COMMON_API Indent(int indent);
LL_COMMON_API ~Indent();
};
extern LL_COMMON_API std::ostream& operator<<(std::ostream& os, libcwd::buf2str const& b2s);
extern LL_COMMON_API std::ostream& operator<<(std::ostream& os, Indent::print_nt);
extern LL_COMMON_API std::ostream& operator<<(std::ostream& os, print_thread_id_t);
namespace dc {
struct fake_channel {
int mOn;
char const* mLabel;
fake_channel(int on, char const* label) : mOn(on), mLabel(label) { }
fake_channel(void) : mOn(0) { }
bool is_on() const { return !!mOn; }
bool is_off() const { return !mOn; }
void on() const { }
void off() const { }
};
extern LL_COMMON_API fake_channel const warning;
extern LL_COMMON_API fake_channel const curl;
extern LL_COMMON_API fake_channel const curlio;
extern LL_COMMON_API fake_channel const statemachine;
extern LL_COMMON_API fake_channel const notice;
} // namespace dc
} // namespace debug
#define LIBCWD_DEBUG_CHANNELS debug
#define LibcwDoutScopeBegin(a, b, c) do { using namespace debug; using namespace debug::libcwd; llinfos_nf << print_thread_id << (c).mLabel << ": " << Indent::print;
#define LibcwDoutStream llcont
#define LibcwDoutScopeEnd llcont << llendl; } while(0)
#define Debug(x) do { using namespace debug; using namespace debug::libcwd; x; } while(0)
#define Dout(a, b) do { using namespace debug; using namespace debug::libcwd; if ((a).mOn) { llinfos_nf << print_thread_id << (a).mLabel << ": " << Indent::print << b << llendl; } } while(0)
#define DoutEntering(a, b) \
int __slviewer_debug_indentation = 2; \
{ \
using namespace debug; \
using namespace debug::libcwd; \
if ((a).mOn) \
llinfos_nf << print_thread_id << (a).mLabel << ": " << Indent::print << "Entering " << b << llendl; \
else \
__slviewer_debug_indentation = 0; \
} \
debug::Indent __slviewer_debug_indent(__slviewer_debug_indentation);
#else // !DEBUG_CURLIO
#define Debug(x)
#define Dout(a, b)
#define DoutEntering(a, b)
#endif // !DEBUG_CURLIO
#ifndef DOXYGEN // No need to document this. See http://libcwd.sourceforge.net/ for more info.
#include <iostream>
@@ -36,9 +124,6 @@
#define AllocTag2(p, desc)
#define AllocTag_dynamic_description(p, x)
#define AllocTag(p, x)
#define Debug(x)
#define Dout(a, b)
#define DoutEntering(a, b)
#define DoutFatal(a, b) LibcwDoutFatal(::std, , a, b)
#define ForAllDebugChannels(STATEMENT)
#define ForAllDebugObjects(STATEMENT)
@@ -118,6 +203,8 @@ extern CWD_API channel_ct sdl;
extern CWD_API channel_ct backtrace;
extern CWD_API channel_ct statemachine;
extern CWD_API channel_ct caps;
extern CWD_API channel_ct curl;
extern CWD_API channel_ct curlio;
#endif

View File

@@ -41,6 +41,7 @@
#include "lldir.h"
#include "llendianswizzle.h"
#include "llassetstorage.h"
#include "llrefcount.h"
#include "vorbis/codec.h"
#include "vorbis/vorbisfile.h"

View File

@@ -16,6 +16,7 @@ include_directories(
set(llcommon_SOURCE_FILES
aiframetimer.cpp
aithreadid.cpp
imageids.cpp
indra_constants.cpp
llallocator.cpp
@@ -104,6 +105,7 @@ set(llcommon_HEADER_FILES
CMakeLists.txt
aiframetimer.h
aithreadid.h
aithreadsafe.h
bitpack.h
ctype_workaround.h
@@ -254,6 +256,7 @@ set(llcommon_HEADER_FILES
set_source_files_properties(${llcommon_HEADER_FILES}
PROPERTIES HEADER_FILE_ONLY TRUE)
list(APPEND llcommon_SOURCE_FILES ${cwdebug_SOURCE_FILES})
list(APPEND llcommon_SOURCE_FILES ${llcommon_HEADER_FILES})
add_library (llcommon SHARED ${llcommon_SOURCE_FILES})
@@ -266,15 +269,9 @@ target_link_libraries(
${ZLIB_LIBRARIES}
${WINDOWS_LIBRARIES}
${Boost_REGEX_LIBRARY}
${CWDEBUG_LIBRARIES}
${CORESERVICES_LIBRARY}
)
if (LINUX)
# When linking with llcommon later, we do not want to link with cwdebug.a again.
set_property(TARGET llcommon PROPERTY LINK_INTERFACE_LIBRARIES "-lapr-1 -laprutil-1 -lz")
endif (LINUX)
if (DARWIN)
# Don't embed a full path in the library's install name
set_target_properties(

View File

@@ -0,0 +1,78 @@
/**
* @file aithreadid.cpp
*
* 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.
*
* 08/08/2012
* - Initial version, written by Aleric Inglewood @ SL
*/
#include <iostream>
#include <iomanip>
#include "aithreadid.h"
AIThreadID const AIThreadID::sNone(AIThreadID::none);
apr_os_thread_t AIThreadID::sMainThreadID;
apr_os_thread_t const AIThreadID::undefinedID = (apr_os_thread_t)-1;
#ifndef LL_DARWIN
apr_os_thread_t ll_thread_local AIThreadID::lCurrentThread;
#endif
void AIThreadID::set_main_thread_id(void)
{
sMainThreadID = apr_os_thread_current();
}
void AIThreadID::set_current_thread_id(void)
{
#ifndef LL_DARWIN
lCurrentThread = apr_os_thread_current();
#endif
}
#ifndef LL_DARWIN
void AIThreadID::reset(void)
{
mID = lCurrentThread;
}
bool AIThreadID::equals_current_thread(void) const
{
return apr_os_thread_equal(mID, lCurrentThread);
}
bool AIThreadID::in_main_thread(void)
{
return apr_os_thread_equal(lCurrentThread, sMainThreadID);
}
apr_os_thread_t AIThreadID::getCurrentThread(void)
{
return lCurrentThread;
}
#endif
std::ostream& operator<<(std::ostream& os, AIThreadID const& id)
{
return os << id.mID;
}

View File

@@ -0,0 +1,94 @@
/**
* @file aithreadid.h
* @brief Declaration of AIThreadID.
*
* 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.
*
* 08/08/2012
* Initial version, written by Aleric Inglewood @ SL
*/
#ifndef AI_THREAD_ID
#define AI_THREAD_ID
#include <apr_portable.h> // apr_os_thread_t, apr_os_thread_current(), apr_os_thread_equal().
#include <iosfwd> // std::ostream.
#include "llpreprocessor.h" // LL_COMMON_API, LL_COMMON_API_TLS
// Lightweight wrapper around apr_os_thread_t.
// This class introduces no extra assembly code after optimization; it's only intend is to provide type-safety.
class AIThreadID
{
private:
apr_os_thread_t mID;
static LL_COMMON_API apr_os_thread_t sMainThreadID;
static LL_COMMON_API apr_os_thread_t const undefinedID;
#ifndef LL_DARWIN
static ll_thread_local apr_os_thread_t lCurrentThread;
#endif
public:
static LL_COMMON_API AIThreadID const sNone;
enum undefined_thread_t { none };
public:
AIThreadID(void) : mID(apr_os_thread_current()) { }
explicit AIThreadID(undefined_thread_t) : mID(undefinedID) { } // Used for sNone.
AIThreadID(AIThreadID const& id) : mID(id.mID) { }
AIThreadID& operator=(AIThreadID const& id) { mID = id.mID; return *this; }
bool is_main_thread(void) const { return apr_os_thread_equal(mID, sMainThreadID); }
bool is_no_thread(void) const { return apr_os_thread_equal(mID, sNone.mID); }
friend LL_COMMON_API bool operator==(AIThreadID const& id1, AIThreadID const& id2) { return apr_os_thread_equal(id1.mID, id2.mID); }
friend LL_COMMON_API bool operator!=(AIThreadID const& id1, AIThreadID const& id2) { return !apr_os_thread_equal(id1.mID, id2.mID); }
friend LL_COMMON_API std::ostream& operator<<(std::ostream& os, AIThreadID const& id);
static void set_main_thread_id(void); // Called once to set sMainThreadID.
static void set_current_thread_id(void); // Called once for every thread to set lCurrentThread.
#ifndef LL_DARWIN
LL_COMMON_API void reset(void);
LL_COMMON_API bool equals_current_thread(void) const;
LL_COMMON_API static bool in_main_thread(void);
LL_COMMON_API static apr_os_thread_t getCurrentThread(void);
// The *_inline variants cannot be exported because they access a thread-local member.
void reset_inline(void) { mID = lCurrentThread; }
bool equals_current_thread_inline(void) const { return apr_os_thread_equal(mID, lCurrentThread); }
static bool in_main_thread_inline(void) { return apr_os_thread_equal(lCurrentThread, sMainThreadID); }
static apr_os_thread_t getCurrentThread_inline(void) { return lCurrentThread; }
#else
// Both variants are inline on OS X.
void reset(void) { mID = apr_os_thread_current(); }
void reset_inline(void) { mID = apr_os_thread_current(); }
bool equals_current_thread(void) const { return apr_os_thread_equal(mID, apr_os_thread_current()); }
bool equals_current_thread_inline(void) const { return apr_os_thread_equal(mID, apr_os_thread_current()); }
static bool in_main_thread(void) { return apr_os_thread_equal(apr_os_thread_current(), sMainThreadID); }
static bool in_main_thread_inline(void) { return apr_os_thread_equal(apr_os_thread_current(), sMainThreadID); }
static apr_os_thread_t getCurrentThread(void) { return apr_os_thread_current(); }
static apr_os_thread_t getCurrentThread_inline(void) { return apr_os_thread_current(); }
#endif
};
// Legacy function.
inline bool is_main_thread(void)
{
return AIThreadID::in_main_thread();
}
#endif // AI_THREAD_ID

View File

@@ -673,18 +673,18 @@ protected:
#ifdef LL_DEBUG
mutable bool mAccessed;
mutable apr_os_thread_t mTheadID;
mutable AIThreadID mTheadID;
void accessed(void) const
{
if (!mAccessed)
{
mAccessed = true;
mTheadID = apr_os_thread_current();
mTheadID.reset();
}
else
{
llassert_always(apr_os_thread_equal(mTheadID, apr_os_thread_current()));
llassert_always(mTheadID.equals_current_thread());
}
}
#endif

View File

@@ -297,6 +297,20 @@ void LLApp::startErrorThread()
}
}
void LLApp::stopErrorThread()
{
LLApp::setStopped(); // Signal error thread that we stopped.
int count = 0;
while (mThreadErrorp && !mThreadErrorp->isStopped() && ++count < 100)
{
ms_sleep(10);
}
if (mThreadErrorp && !mThreadErrorp->isStopped())
{
llwarns << "Failed to stop Error Thread." << llendl;
}
}
void LLApp::setErrorHandler(LLAppErrorHandler handler)
{
LLApp::sErrorHandler = handler;

View File

@@ -264,6 +264,10 @@ protected:
* @ brief This method is called once as soon as logging is initialized.
*/
void startErrorThread();
/**
* @brief This method is called at the end, just prior to deinitializing curl.
*/
void stopErrorThread();
private:
void setupErrorHandling(); // Do platform-specific error-handling setup (signals, structured exceptions)

View File

@@ -60,10 +60,10 @@ void LLAPRPool::create(LLAPRPool& parent)
//
// In other words, it's safe for any thread to create a (sub)pool, independent of who
// owns the parent pool.
mOwner = apr_os_thread_current();
mOwner.reset_inline();
#else
mOwner = mParent->mOwner;
llassert(apr_os_thread_equal(mOwner, apr_os_thread_current()));
llassert(mOwner.equals_current_thread_inline());
#endif
apr_status_t const apr_pool_create_status = apr_pool_create(&mPool, mParent->mPool);
llassert_always(apr_pool_create_status == APR_SUCCESS);
@@ -83,7 +83,7 @@ void LLAPRPool::destroy(void)
// of course. Otherwise, if we are a subpool, only the thread that owns
// the parent may destruct us, since that is the pool that is still alive,
// possibly being used by others and being altered here.
llassert(!mParent || apr_os_thread_equal(mParent->mOwner, apr_os_thread_current()));
llassert(!mParent || mParent->mOwner.equals_current_thread_inline());
#endif
apr_pool_t* pool = mPool;
mPool = NULL; // Mark that we are BEING destructed.

View File

@@ -48,6 +48,7 @@
#include "apr_portable.h"
#include "apr_pools.h"
#include "llerror.h"
#include "aithreadid.h"
extern void ll_init_apr();
@@ -62,22 +63,22 @@ class LL_COMMON_API LLAPRPool
protected:
apr_pool_t* mPool; //!< Pointer to the underlaying pool. NULL if not initialized.
LLAPRPool* mParent; //!< Pointer to the parent pool, if any. Only valid when mPool is non-zero.
apr_os_thread_t mOwner; //!< The thread that owns this memory pool. Only valid when mPool is non-zero.
AIThreadID mOwner; //!< The thread that owns this memory pool. Only valid when mPool is non-zero.
public:
//! Construct an uninitialized (destructed) pool.
LLAPRPool(void) : mPool(NULL) { }
LLAPRPool(void) : mPool(NULL), mOwner(AIThreadID::none) { }
//! Construct a subpool from an existing pool.
// This is not a copy-constructor, this class doesn't have one!
LLAPRPool(LLAPRPool& parent) : mPool(NULL) { create(parent); }
LLAPRPool(LLAPRPool& parent) : mPool(NULL), mOwner(AIThreadID::none) { create(parent); }
//! Destruct the memory pool (free all of it's subpools and allocated memory).
~LLAPRPool() { destroy(); }
protected:
// Create a pool that is allocated from the Operating System. Only used by LLAPRRootPool.
LLAPRPool(int) : mPool(NULL), mParent(NULL), mOwner(apr_os_thread_current())
LLAPRPool(int) : mPool(NULL), mParent(NULL)
{
apr_status_t const apr_pool_create_status = apr_pool_create(&mPool, NULL);
llassert_always(apr_pool_create_status == APR_SUCCESS);
@@ -104,7 +105,7 @@ public:
apr_pool_t* operator()(void) const
{
llassert(mPool);
llassert(apr_os_thread_equal(mOwner, apr_os_thread_current()));
llassert(mOwner.equals_current_thread());
return mPool;
}
@@ -112,7 +113,7 @@ public:
void clear(void)
{
llassert(mPool);
llassert(apr_os_thread_equal(mOwner, apr_os_thread_current()));
llassert(mOwner.equals_current_thread());
apr_pool_clear(mPool);
}
@@ -124,13 +125,13 @@ public:
void* palloc(size_t size)
{
llassert(mPool);
llassert(apr_os_thread_equal(mOwner, apr_os_thread_current()));
llassert(mOwner.equals_current_thread());
return apr_palloc(mPool, size);
}
void* pcalloc(size_t size)
{
llassert(mPool);
llassert(apr_os_thread_equal(mOwner, apr_os_thread_current()));
llassert(mOwner.equals_current_thread());
return apr_pcalloc(mPool, size);
}
#endif

View File

@@ -963,10 +963,14 @@ namespace LLError
settings_w->shouldLogCallCounter += 1;
std::string class_name = className(site.mClassInfo);
std::string function_name = functionName(site.mFunction);
if (site.mClassInfo != typeid(NoClassInfo))
std::string function_name;
if (site.mFunction)
{
function_name = class_name + "::" + function_name;
function_name = functionName(site.mFunction);
if (site.mClassInfo != typeid(NoClassInfo))
{
function_name = class_name + "::" + function_name;
}
}
ELevel compareLevel = settings_w->defaultLevel;
@@ -976,7 +980,7 @@ namespace LLError
// So, in increasing order of importance:
// Default < Broad Tag < File < Class < Function < Narrow Tag
((site.mNarrowTag != NULL) ? checkLevelMap(settings_w->tagLevelMap, site.mNarrowTag, compareLevel) : false)
|| checkLevelMap(settings_w->functionLevelMap, function_name, compareLevel)
|| (site.mFunction && checkLevelMap(settings_w->functionLevelMap, function_name, compareLevel))
|| checkLevelMap(settings_w->classLevelMap, class_name, compareLevel)
|| checkLevelMap(settings_w->fileLevelMap, abbreviateFile(site.mFile), compareLevel)
|| ((site.mBroadTag != NULL) ? checkLevelMap(settings_w->tagLevelMap, site.mBroadTag, compareLevel) : false);
@@ -1083,8 +1087,8 @@ namespace LLError
default: prefix << "XXX"; break;
};
bool need_function = true;
if (site.mBroadTag && *site.mBroadTag != '\0')
bool need_function = site.mFunction;
if (need_function && site.mBroadTag && *site.mBroadTag != '\0')
{
prefix << "(\"" << site.mBroadTag << "\")";
#if LL_DEBUG
@@ -1112,7 +1116,7 @@ namespace LLError
#if LL_WINDOWS
// DevStudio: __FUNCTION__ already includes the full class name
#else
if (need_function && site.mClassInfo != typeid(NoClassInfo))
if (site.mClassInfo != typeid(NoClassInfo))
{
prefix << className(site.mClassInfo) << "::";
}

View File

@@ -238,10 +238,10 @@ typedef LLError::NoClassInfo _LL_CLASS_TO_LOG;
See top of file for common usage.
*/
#define lllog(level, broadTag, narrowTag, once) \
#define lllog(level, broadTag, narrowTag, once, nofunction) \
do { \
static LLError::CallSite _site( \
level, __FILE__, __LINE__, typeid(_LL_CLASS_TO_LOG), __FUNCTION__, broadTag, narrowTag, once);\
level, __FILE__, __LINE__, typeid(_LL_CLASS_TO_LOG), nofunction ? NULL : __FUNCTION__, broadTag, narrowTag, once);\
if (LL_UNLIKELY(_site.shouldLog())) \
{ \
std::ostringstream* _out = LLError::Log::out(); \
@@ -255,33 +255,39 @@ typedef LLError::NoClassInfo _LL_CLASS_TO_LOG;
} while(0)
// DEPRECATED: Use the new macros that allow tags and *look* like macros.
#define lldebugs lllog(LLError::LEVEL_DEBUG, NULL, NULL, false)
#define llinfos lllog(LLError::LEVEL_INFO, NULL, NULL, false)
#define llwarns lllog(LLError::LEVEL_WARN, NULL, NULL, false)
#define llerrs lllog(LLError::LEVEL_ERROR, NULL, NULL, false)
#define lldebugs lllog(LLError::LEVEL_DEBUG, NULL, NULL, false, false)
#define llinfos lllog(LLError::LEVEL_INFO, NULL, NULL, false, false)
#define llwarns lllog(LLError::LEVEL_WARN, NULL, NULL, false, false)
#define llerrs lllog(LLError::LEVEL_ERROR, NULL, NULL, false, false)
#define llcont (*_out)
// No function name
#define lldebugs_nf lllog(LLError::LEVEL_DEBUG, NULL, NULL, false, true)
#define llinfos_nf lllog(LLError::LEVEL_INFO, NULL, NULL, false, true)
#define llwarns_nf lllog(LLError::LEVEL_WARN, NULL, NULL, false, true)
#define llerrs_nf lllog(LLError::LEVEL_ERROR, NULL, NULL, false, true)
// NEW Macros for debugging, allow the passing of a string tag
// One Tag
#define LL_DEBUGS(broadTag) lllog(LLError::LEVEL_DEBUG, broadTag, NULL, false)
#define LL_INFOS(broadTag) lllog(LLError::LEVEL_INFO, broadTag, NULL, false)
#define LL_WARNS(broadTag) lllog(LLError::LEVEL_WARN, broadTag, NULL, false)
#define LL_ERRS(broadTag) lllog(LLError::LEVEL_ERROR, broadTag, NULL, false)
#define LL_DEBUGS(broadTag) lllog(LLError::LEVEL_DEBUG, broadTag, NULL, false, false)
#define LL_INFOS(broadTag) lllog(LLError::LEVEL_INFO, broadTag, NULL, false, false)
#define LL_WARNS(broadTag) lllog(LLError::LEVEL_WARN, broadTag, NULL, false, false)
#define LL_ERRS(broadTag) lllog(LLError::LEVEL_ERROR, broadTag, NULL, false, false)
// Two Tags
#define LL_DEBUGS2(broadTag, narrowTag) lllog(LLError::LEVEL_DEBUG, broadTag, narrowTag, false)
#define LL_INFOS2(broadTag, narrowTag) lllog(LLError::LEVEL_INFO, broadTag, narrowTag, false)
#define LL_WARNS2(broadTag, narrowTag) lllog(LLError::LEVEL_WARN, broadTag, narrowTag, false)
#define LL_ERRS2(broadTag, narrowTag) lllog(LLError::LEVEL_ERROR, broadTag, narrowTag, false)
#define LL_DEBUGS2(broadTag, narrowTag) lllog(LLError::LEVEL_DEBUG, broadTag, narrowTag, false, false)
#define LL_INFOS2(broadTag, narrowTag) lllog(LLError::LEVEL_INFO, broadTag, narrowTag, false, false)
#define LL_WARNS2(broadTag, narrowTag) lllog(LLError::LEVEL_WARN, broadTag, narrowTag, false, false)
#define LL_ERRS2(broadTag, narrowTag) lllog(LLError::LEVEL_ERROR, broadTag, narrowTag, false, false)
// Only print the log message once (good for warnings or infos that would otherwise
// spam the log file over and over, such as tighter loops).
#define LL_DEBUGS_ONCE(broadTag) lllog(LLError::LEVEL_DEBUG, broadTag, NULL, true)
#define LL_INFOS_ONCE(broadTag) lllog(LLError::LEVEL_INFO, broadTag, NULL, true)
#define LL_WARNS_ONCE(broadTag) lllog(LLError::LEVEL_WARN, broadTag, NULL, true)
#define LL_DEBUGS2_ONCE(broadTag, narrowTag) lllog(LLError::LEVEL_DEBUG, broadTag, narrowTag, true)
#define LL_INFOS2_ONCE(broadTag, narrowTag) lllog(LLError::LEVEL_INFO, broadTag, narrowTag, true)
#define LL_WARNS2_ONCE(broadTag, narrowTag) lllog(LLError::LEVEL_WARN, broadTag, narrowTag, true)
#define LL_DEBUGS_ONCE(broadTag) lllog(LLError::LEVEL_DEBUG, broadTag, NULL, true, false)
#define LL_INFOS_ONCE(broadTag) lllog(LLError::LEVEL_INFO, broadTag, NULL, true, false)
#define LL_WARNS_ONCE(broadTag) lllog(LLError::LEVEL_WARN, broadTag, NULL, true, false)
#define LL_DEBUGS2_ONCE(broadTag, narrowTag) lllog(LLError::LEVEL_DEBUG, broadTag, narrowTag, true, false)
#define LL_INFOS2_ONCE(broadTag, narrowTag) lllog(LLError::LEVEL_INFO, broadTag, narrowTag, true, false)
#define LL_WARNS2_ONCE(broadTag, narrowTag) lllog(LLError::LEVEL_WARN, broadTag, narrowTag, true, false)
#define LL_ENDL llendl
#define LL_CONT (*_out)
@@ -302,8 +308,4 @@ typedef LLError::NoClassInfo _LL_CLASS_TO_LOG;
Such computation is done iff the message will be logged.
*/
#ifdef SHOW_ASSERT
extern LL_COMMON_API bool is_main_thread();
#endif
#endif // LL_LLERROR_H

View File

@@ -114,7 +114,7 @@ const int LL_ERR_PRICE_MISMATCH = -23018;
: liru_slashpos2 == std::string::npos ? std::string(__FILE__)/*Apparently, we're in / or perhaps the top of the drive, print as is*/\
: std::string(__FILE__).substr(1+liru_slashpos2))/*print foo/bar.cpp or perhaps foo\bar.cpp*/
#define llassert_always(func) if (LL_UNLIKELY(!(func))) llerrs <<"\nASSERT(" #func ")\nfile:"<<liru_assert_strip<<" line:"<<__LINE__ << llendl;
#define llassert_always(func) do { if (LL_UNLIKELY(!(func))) llerrs << "\nASSERT(" #func ")\nfile:" << liru_assert_strip << " line:" << std::dec << __LINE__ << llendl; } while(0)
#ifdef SHOW_ASSERT
#define llassert(func) llassert_always(func)

View File

@@ -194,17 +194,17 @@ void LLErrorThread::run()
if (LLApp::isError())
{
// The app is in an error state, run the application's error handler.
//llinfos << "thread_error - An error has occurred, running error callback!" << llendl;
Dout(dc::notice, "thread_error - An error has occurred, running error callback!");
// Run the error handling callback
LLApp::runErrorHandler();
}
else
{
// Everything is okay, a clean exit.
//llinfos << "thread_error - Application exited cleanly" << llendl;
Dout(dc::notice, "thread_error - Application exited cleanly");
}
//llinfos << "thread_error - Exiting" << llendl;
Dout(dc::notice, "thread_error - Exiting");
LLApp::sErrorThreadRunning = FALSE;
}

View File

@@ -44,6 +44,9 @@
#include "lltimer.h"
#include "timing.h"
#include <apr_thread_mutex.h>
#ifdef SHOW_ASSERT
#include "aithreadid.h" // is_main_thread()
#endif
class LL_COMMON_API LLFrameTimer
{

View File

@@ -523,7 +523,6 @@ void LLPrivateMemoryPoolTester::operator delete[](void* addr)
//EVENTUALLY REMOVE THESE:
#include "llpointer.h"
#include "llrefcount.h"
#include "llsingleton.h"
#include "llsafehandle.h"

View File

@@ -195,4 +195,13 @@
# define LL_COMMON_API
#endif // LL_COMMON_LINK_SHARED
// Darwin does not support thread-local data.
#ifndef LL_DARWIN
#if LL_WINDOWS
#define ll_thread_local __declspec(thread)
#else // Linux
#define ll_thread_local __thread
#endif
#endif
#endif // not LL_LINDEN_PREPROCESSOR_H

View File

@@ -251,7 +251,7 @@ bool LLQueuedThread::addRequest(QueuedRequest* req)
// MAIN thread
bool LLQueuedThread::waitForResult(LLQueuedThread::handle_t handle, bool auto_complete)
{
llassert (handle != nullHandle())
llassert (handle != nullHandle());
bool res = false;
bool waspaused = isPaused();
bool done = false;

View File

@@ -111,6 +111,8 @@ public:
return mPriority > second.mPriority;
}
virtual void deleteRequest(); // Only method to delete a request
protected:
status_t setStatus(status_t newstatus)
{
@@ -130,7 +132,6 @@ public:
virtual bool processRequest() = 0; // Return true when request has completed
virtual void finishRequest(bool completed); // Always called from thread after request has completed or aborted
virtual void deleteRequest(); // Only method to delete a request
void setPriority(U32 pri)
{

View File

@@ -38,14 +38,6 @@ LLRefCount::LLRefCount(const LLRefCount& other)
: mRef(0)
{
#if LL_REF_COUNT_DEBUG
if(gAPRPoolp)
{
mMutexp = new LLMutex(gAPRPoolp) ;
}
else
{
mMutexp = NULL ;
}
mCrashAtUnlock = FALSE ;
#endif
}
@@ -60,14 +52,6 @@ LLRefCount::LLRefCount() :
mRef(0)
{
#if LL_REF_COUNT_DEBUG
if(gAPRPoolp)
{
mMutexp = new LLMutex(gAPRPoolp) ;
}
else
{
mMutexp = NULL ;
}
mCrashAtUnlock = FALSE ;
#endif
}
@@ -78,29 +62,20 @@ LLRefCount::~LLRefCount()
{
llerrs << "deleting non-zero reference" << llendl;
}
#if LL_REF_COUNT_DEBUG
if(gAPRPoolp)
{
delete mMutexp ;
}
#endif
}
#if LL_REF_COUNT_DEBUG
void LLRefCount::ref() const
{
if(mMutexp)
{
if(mMutexp->isLocked())
if(mMutex.isLocked())
{
mCrashAtUnlock = TRUE ;
llerrs << "the mutex is locked by the thread: " << mLockedThreadID
<< " Current thread: " << LLThread::currentID() << llendl ;
<< " Current thread: " << AIThreadID() << llendl ;
}
mMutexp->lock() ;
mLockedThreadID = LLThread::currentID() ;
mMutex.lock() ;
mLockedThreadID.reset_inline();
mRef++;
@@ -108,27 +83,20 @@ void LLRefCount::ref() const
{
while(1); //crash here.
}
mMutexp->unlock() ;
}
else
{
mRef++;
}
mMutex.unlock() ;
}
S32 LLRefCount::unref() const
{
if(mMutexp)
{
if(mMutexp->isLocked())
if(mMutex.isLocked())
{
mCrashAtUnlock = TRUE ;
llerrs << "the mutex is locked by the thread: " << mLockedThreadID
<< " Current thread: " << LLThread::currentID() << llendl ;
<< " Current thread: " << AIThreadID() << llendl ;
}
mMutexp->lock() ;
mLockedThreadID = LLThread::currentID() ;
mMutex.lock() ;
mLockedThreadID.reset_inline();
llassert(mRef >= 1);
if (0 == --mRef)
@@ -137,7 +105,7 @@ S32 LLRefCount::unref() const
{
while(1); //crash here.
}
mMutexp->unlock() ;
mMutex.unlock() ;
delete this;
return 0;
@@ -147,18 +115,7 @@ S32 LLRefCount::unref() const
{
while(1); //crash here.
}
mMutexp->unlock() ;
mMutex.unlock() ;
return mRef;
}
else
{
llassert(mRef >= 1);
if (0 == --mRef)
{
delete this;
return 0;
}
return mRef;
}
}
#endif

View File

@@ -30,7 +30,7 @@
#define LL_REF_COUNT_DEBUG 0
#if LL_REF_COUNT_DEBUG
class LLMutex ;
#include "llthread.h" // LLMutexRootPool
#endif
//----------------------------------------------------------------------------
@@ -80,8 +80,8 @@ private:
mutable S32 mRef;
#if LL_REF_COUNT_DEBUG
LLMutex* mMutexp ;
mutable U32 mLockedThreadID ;
mutable LLMutexRootPool mMutex ;
mutable AIThreadID mLockedThreadID ;
mutable BOOL mCrashAtUnlock ;
#endif
};

View File

@@ -29,6 +29,11 @@
* $/LicenseInfo$
*/
#ifdef __GNUC__
// Generate code for inlines from llthread.h (needed for is_main_thread()).
#pragma implementation "llthread.h"
#endif
#include "linden_common.h"
#include "llapr.h"
@@ -62,18 +67,12 @@
//
//----------------------------------------------------------------------------
#if !LL_DARWIN
U32 ll_thread_local local_thread_ID = 0;
#endif
U32 LLThread::sIDIter = 0;
LLAtomicS32 LLThread::sCount = 0;
LLAtomicS32 LLThread::sRunning = 0;
LL_COMMON_API void assert_main_thread()
{
static U32 s_thread_id = LLThread::currentID();
if (LLThread::currentID() != s_thread_id)
if (!AIThreadID::in_main_thread_inline())
{
llerrs << "Illegal execution outside main thread." << llendl;
}
@@ -90,9 +89,8 @@ void *APR_THREAD_FUNC LLThread::staticRun(apr_thread_t *apr_threadp, void *datap
LLThread *threadp = (LLThread *)datap;
#if !LL_DARWIN
local_thread_ID = threadp->mID;
#endif
// Initialize thread-local cache of current thread ID (if supported).
AIThreadID::set_current_thread_id();
// Create a thread local data.
LLThreadLocalData::create(threadp);
@@ -132,7 +130,6 @@ LLThread::LLThread(std::string const& name) :
mStatus(STOPPED),
mThreadLocalData(NULL)
{
mID = ++sIDIter;
sCount++;
llassert(sCount <= 50);
mRunCondition = new LLCondition;
@@ -276,12 +273,6 @@ void LLThread::setQuitting()
wake();
}
// static
U32 LLThread::currentID()
{
return (U32)apr_os_thread_current();
}
// static
void LLThread::yield()
{
@@ -310,15 +301,19 @@ void LLThread::wakeLocked()
}
}
#ifdef SHOW_ASSERT
// This allows the use of llassert(is_main_thread()) to assure the current thread is the main thread.
static apr_os_thread_t main_thread_id;
LL_COMMON_API bool is_main_thread(void) { return apr_os_thread_equal(main_thread_id, apr_os_thread_current()); }
#endif
// The thread private handle to access the LLThreadLocalData instance.
apr_threadkey_t* LLThreadLocalData::sThreadLocalDataKey;
LLThreadLocalData::LLThreadLocalData(char const* name) : mCurlMultiHandle(NULL), mCurlErrorBuffer(NULL), mName(name)
{
}
LLThreadLocalData::~LLThreadLocalData()
{
delete mCurlMultiHandle;
delete [] mCurlErrorBuffer;
}
//static
void LLThreadLocalData::init(void)
{
@@ -328,6 +323,10 @@ void LLThreadLocalData::init(void)
return;
}
// This function is called by the main thread (these values are also needed in the next line).
AIThreadID::set_main_thread_id();
AIThreadID::set_current_thread_id();
apr_status_t status = apr_threadkey_private_create(&sThreadLocalDataKey, &LLThreadLocalData::destroy, LLAPRRootPool::get()());
ll_apr_assert_status(status); // Or out of memory, or system-imposed limit on the
// total number of keys per process {PTHREAD_KEYS_MAX}
@@ -335,11 +334,6 @@ void LLThreadLocalData::init(void)
// Create the thread-local data for the main thread (this function is called by the main thread).
LLThreadLocalData::create(NULL);
#ifdef SHOW_ASSERT
// This function is called by the main thread.
main_thread_id = apr_os_thread_current();
#endif
}
// This is called once for every thread when the thread is destructed.
@@ -352,7 +346,7 @@ void LLThreadLocalData::destroy(void* thread_local_data)
//static
void LLThreadLocalData::create(LLThread* threadp)
{
LLThreadLocalData* new_tld = new LLThreadLocalData;
LLThreadLocalData* new_tld = new LLThreadLocalData(threadp ? threadp->mName.c_str() : "main thread");
if (threadp)
{
threadp->mThreadLocalData = new_tld;
@@ -406,27 +400,19 @@ void LLCondition::broadcast()
//============================================================================
LLMutexBase::LLMutexBase() :
mLockingThread(NO_THREAD),
mLockingThread(AIThreadID::sNone),
mCount(0)
{
}
bool LLMutexBase::isSelfLocked() const
{
#if LL_DARWIN
return mLockingThread == LLThread::currentID();
#else
return mLockingThread == local_thread_ID;
#endif
return mLockingThread.equals_current_thread_inline();
}
void LLMutexBase::lock()
{
#if LL_DARWIN
if (mLockingThread == LLThread::currentID())
#else
if (mLockingThread == local_thread_ID)
#endif
if (mLockingThread.equals_current_thread_inline())
{ //redundant lock
mCount++;
return;
@@ -434,11 +420,33 @@ void LLMutexBase::lock()
apr_thread_mutex_lock(mAPRMutexp);
#if LL_DARWIN
mLockingThread = LLThread::currentID();
#else
mLockingThread = local_thread_ID;
#endif
mLockingThread.reset_inline();
}
bool LLMutexBase::tryLock()
{
if (mLockingThread.equals_current_thread_inline())
{ //redundant lock
mCount++;
return true;
}
bool success = !APR_STATUS_IS_EBUSY(apr_thread_mutex_trylock(mAPRMutexp));
if (success)
{
mLockingThread.reset_inline();
}
return success;
}
// non-blocking, but does do a lock/unlock so not free
bool LLMutexBase::isLocked() const
{
if (mLockingThread.equals_current_thread_inline())
return false; // A call to lock() won't block.
if (APR_STATUS_IS_EBUSY(apr_thread_mutex_trylock(mAPRMutexp)))
return true;
apr_thread_mutex_unlock(mAPRMutexp);
return false;
}
void LLMutexBase::unlock()
@@ -448,7 +456,7 @@ void LLMutexBase::unlock()
mCount--;
return;
}
mLockingThread = NO_THREAD;
mLockingThread = AIThreadID::sNone;
apr_thread_mutex_unlock(mAPRMutexp);
}

View File

@@ -33,29 +33,29 @@
#ifndef LL_LLTHREAD_H
#define LL_LLTHREAD_H
#ifdef __GNUC__
// Needed for is_main_thread() when compiling with optimization (relwithdebinfo).
// It doesn't hurt to just always specify it though.
#pragma interface
#endif
#include "llapp.h"
#include "llapr.h"
#include "llmemory.h"
#include "apr_thread_cond.h"
#include "llaprpool.h"
#include "llatomic.h"
#ifdef SHOW_ASSERT
extern LL_COMMON_API bool is_main_thread(void);
#define ASSERT_SINGLE_THREAD do { static apr_os_thread_t first_thread_id = apr_os_thread_current(); llassert(apr_os_thread_equal(first_thread_id, apr_os_thread_current())); } while(0)
#else
#define ASSERT_SINGLE_THREAD do { } while(0)
#endif
#include "aithreadid.h"
class LLThread;
class LLMutex;
class LLCondition;
#if LL_WINDOWS
#define ll_thread_local __declspec(thread)
#else
#define ll_thread_local __thread
#endif
class LL_COMMON_API LLThreadLocalDataMember
{
public:
virtual ~LLThreadLocalDataMember() { };
};
class LL_COMMON_API LLThreadLocalData
{
@@ -66,13 +66,23 @@ public:
// Thread-local memory pool.
LLAPRRootPool mRootPool;
LLVolatileAPRPool mVolatileAPRPool;
LLThreadLocalDataMember* mCurlMultiHandle; // Initialized by AICurlMultiHandle::getInstance
char* mCurlErrorBuffer; // NULL, or pointing to a buffer used by libcurl.
std::string mName; // "main thread", or a copy of LLThread::mName.
static void init(void);
static void destroy(void* thread_local_data);
static void create(LLThread* pthread);
static LLThreadLocalData& tldata(void);
private:
LLThreadLocalData(char const* name);
~LLThreadLocalData();
};
// Print to llerrs if the current thread is not the main thread.
LL_COMMON_API void assert_main_thread();
class LL_COMMON_API LLThread
{
private:
@@ -95,7 +105,6 @@ public:
bool isQuitting() const { return (QUITTING == mStatus); }
bool isStopped() const { return (STOPPED == mStatus); }
static U32 currentID(); // Return ID of current thread
static S32 getCount() { return sCount; }
static S32 getRunning() { return sRunning; }
static void yield(); // Static because it can be called by the main thread, which doesn't have an LLThread data structure.
@@ -122,8 +131,6 @@ public:
// Return thread-local data for the current thread.
static LLThreadLocalData& tldata(void) { return LLThreadLocalData::tldata(); }
U32 getID() const { return mID; }
private:
bool mPaused;
@@ -136,7 +143,6 @@ protected:
apr_thread_t *mAPRThreadp;
volatile EThreadStatus mStatus;
U32 mID;
friend void LLThreadLocalData::create(LLThread* threadp);
LLThreadLocalData* mThreadLocalData;
@@ -165,6 +171,12 @@ protected:
// mRunCondition->unlock();
};
#ifdef SHOW_ASSERT
#define ASSERT_SINGLE_THREAD do { static AIThreadID first_thread_id; llassert(first_thread_id.equals_current_thread()); } while(0)
#else
#define ASSERT_SINGLE_THREAD do { } while(0)
#endif
//============================================================================
#define MUTEX_DEBUG (LL_DEBUG || LL_RELEASE_WITH_DEBUG_INFO)
@@ -180,32 +192,29 @@ protected:
class LL_COMMON_API LLMutexBase
{
public:
typedef enum
{
NO_THREAD = 0xFFFFFFFF
} e_locking_thread;
LLMutexBase() ;
void lock(); // blocks
void unlock();
// Returns true if lock was obtained successfully.
bool tryLock() { return !APR_STATUS_IS_EBUSY(apr_thread_mutex_trylock(mAPRMutexp)); }
bool tryLock();
// non-blocking, but does do a lock/unlock so not free
bool isLocked() { bool is_not_locked = tryLock(); if (is_not_locked) unlock(); return !is_not_locked; }
// Returns true if a call to lock() would block (returns false if self-locked()).
bool isLocked() const;
// Returns true if locked by this thread.
bool isSelfLocked() const;
// get ID of locking thread
U32 lockingThread() const { return mLockingThread; }
protected:
// mAPRMutexp is initialized and uninitialized in the derived class.
apr_thread_mutex_t* mAPRMutexp;
mutable U32 mCount;
mutable U32 mLockingThread;
mutable AIThreadID mLockingThread;
private:
// Disallow copy construction and assignment.
LLMutexBase(LLMutexBase const&);
LLMutexBase& operator=(LLMutexBase const&);
};
class LL_COMMON_API LLMutex : public LLMutexBase
@@ -225,10 +234,6 @@ public:
protected:
LLAPRPool mPool;
private:
// Disable copy construction, as si teh bomb!!! -SG
LLMutex(const LLMutex&);
LLMutex& operator=(const LLMutex&);
};
#if APR_HAS_THREADS
@@ -289,7 +294,7 @@ private:
LLMutexBase* mMutex;
};
class AIRWLock
class LL_COMMON_API AIRWLock
{
public:
AIRWLock(LLAPRPool& parent = LLThread::tldata().mRootPool) :

View File

@@ -371,6 +371,9 @@ void LLCrashLogger::updateApplication(const std::string& message)
bool LLCrashLogger::init()
{
// Initialize curl
AICurlInterface::initCurl();
// We assume that all the logs we're looking for reside on the current drive
gDirUtilp->initAppDirs("SecondLife");

View File

@@ -32,7 +32,12 @@
#ifndef LL_LLPNGWRAPPER_H
#define LL_LLPNGWRAPPER_H
#ifdef LL_STANDALONE
#include <png.h>
#else
// Workaround for wrongly packaged prebuilt.
#include "libpng15/png.h"
#endif
#include "llimage.h"
class LLPngWrapper

View File

@@ -29,7 +29,9 @@ set(llmessage_SOURCE_FILES
llchainio.cpp
llcircuit.cpp
llclassifiedflags.cpp
llcurl.cpp
aicurl.cpp
debug_libcurl.cpp
aicurlthread.cpp
lldatapacker.cpp
lldispatcher.cpp
llfiltersd2xmlrpc.cpp
@@ -66,8 +68,6 @@ set(llmessage_SOURCE_FILES
llsdmessage.cpp
llsdmessagebuilder.cpp
llsdmessagereader.cpp
llsdrpcclient.cpp
llsdrpcserver.cpp
llservicebuilder.cpp
llservice.cpp
llstoredmessage.cpp
@@ -117,6 +117,10 @@ 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
@@ -164,8 +168,6 @@ set(llmessage_HEADER_FILES
llsdmessage.h
llsdmessagebuilder.h
llsdmessagereader.h
llsdrpcclient.h
llsdrpcserver.h
llservice.h
llservicebuilder.h
llstoredmessage.h

1502
indra/llmessage/aicurl.cpp Normal file

File diff suppressed because it is too large Load Diff

343
indra/llmessage/aicurl.h Normal file
View File

@@ -0,0 +1,343 @@
/**
* @file aicurl.h
* @brief Thread safe wrapper for libcurl.
*
* 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 AICURL_H
#define AICURL_H
#include <string>
#include <vector>
#include <set>
#include <stdexcept>
#include <boost/intrusive_ptr.hpp>
#include <boost/utility.hpp>
#include "llpreprocessor.h"
#include <curl/curl.h> // Needed for files that include this header (also for aicurlprivate.h).
#ifdef DEBUG_CURLIO
#include "debug_libcurl.h"
#endif
// Make sure we don't use this option: it is not thread-safe.
#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 "llatomic.h" // LLAtomicU32
#include "aithreadsafe.h"
class LLSD;
//-----------------------------------------------------------------------------
// Exceptions.
//
// A general curl exception.
//
class AICurlError : public std::runtime_error {
public:
AICurlError(std::string const& message) : std::runtime_error(message) { }
};
class AICurlNoEasyHandle : public AICurlError {
public:
AICurlNoEasyHandle(std::string const& message) : AICurlError(message) { }
};
class AICurlNoMultiHandle : public AICurlError {
public:
AICurlNoMultiHandle(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 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(void);
// 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().
std::string getVersionString(void);
// Called from newview/llappviewer.cpp (and llcrashlogger/llcrashlogger.cpp) to set
// the Certificate Authority file used to verify HTTPS certs.
void setCAFile(std::string const& file);
// Not called from anywhere.
// 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;
} // namespace AICurlPrivate
// Define access types (_crat = Const Read Access Type, _rat = Read Access Type, _wat = Write Access Type).
// Typical usage is:
// 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.
// 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;
// Events generated by AICurlPrivate::CurlEasyHandle.
struct AICurlEasyHandleEvents {
// Events.
virtual void added_to_multi_handle(AICurlEasyRequest_wat& curl_easy_request_w) = 0;
virtual void finished(AICurlEasyRequest_wat& curl_easy_request_w) = 0;
virtual void removed_from_multi_handle(AICurlEasyRequest_wat& curl_easy_request_w) = 0;
// Avoid compiler warning.
virtual ~AICurlEasyHandleEvents() { }
};
// Pointer to data we're going to POST.
class AIPostField : public LLThreadSafeRefCount {
protected:
char const* mData;
public:
AIPostField(char const* data) : mData(data) { }
char const* data(void) const { return mData; }
};
// The pointer to the data that we have to POST is passed around as AIPostFieldPtr,
// which causes it to automatically clean up when there are no pointers left
// pointing to it.
typedef LLPointer<AIPostField> AIPostFieldPtr;
#include "aicurlprivate.h"
// AICurlPrivate::CurlEasyRequestPtr, 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
// read accesses on it.
// AICurlEasyRequest: a thread safe, reference counting, auto-cleaning curl easy handle.
class AICurlEasyRequest {
public:
// 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.
// This might throw AICurlNoEasyHandle.
AICurlEasyRequest(bool buffered) :
mCurlEasyRequest(buffered ? new AICurlPrivate::ThreadSafeBufferedCurlEasyRequest : new AICurlPrivate::ThreadSafeCurlEasyRequest) { }
AICurlEasyRequest(AICurlEasyRequest const& orig) : mCurlEasyRequest(orig.mCurlEasyRequest) { }
// 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(); }
// 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 a different CurlEasyRequest object.
bool operator!=(AICurlEasyRequest const& cer) const { return mCurlEasyRequest != cer.mCurlEasyRequest; }
// Queue this request for insertion in the multi session.
void addRequest(void);
// 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;
private:
// Assignment would not be thread-safe; we may create this object and read from it.
// Note: Destruction is implicitly assumed thread-safe, as it would be a logic error to
// destruct it while another thread still needs it, concurrent or not.
AICurlEasyRequest& operator=(AICurlEasyRequest const&) { return *this; }
public:
// The more exotic member functions of this class, to deal with passing this class
// as CURLOPT_PRIVATE pointer to a curl handle and afterwards restore it.
// 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; }
// If we have a correct (with regard to reference counting) AICurlPrivate::CurlEasyRequestPtr,
// then it's OK to construct a AICurlEasyRequest from it.
// Note that the external AICurlPrivate::CurlEasyRequestPtr needs its own locking, because
// it's not thread-safe in itself.
AICurlEasyRequest(AICurlPrivate::CurlEasyRequestPtr const& ptr) : mCurlEasyRequest(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)) { }
};
#define AICurlPrivate DONTUSE_AICurlPrivate
#endif

View File

@@ -0,0 +1,478 @@
/**
* @file aicurlprivate.h
* @brief Thread safe wrapper for libcurl.
*
* 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.
*
* 28/04/2012
* Initial version, written by Aleric Inglewood @ SL
*/
#ifndef AICURLPRIVATE_H
#define AICURLPRIVATE_H
#include <sstream>
#include "llatomic.h"
namespace AICurlPrivate {
namespace curlthread { class MultiHandle; }
struct Stats {
static LLAtomicU32 easy_calls;
static LLAtomicU32 easy_errors;
static LLAtomicU32 easy_init_calls;
static LLAtomicU32 easy_init_errors;
static LLAtomicU32 easy_cleanup_calls;
static LLAtomicU32 multi_calls;
static LLAtomicU32 multi_errors;
static void print(void);
};
void handle_multi_error(CURLMcode code);
inline CURLMcode check_multi_code(CURLMcode code) { Stats::multi_calls++; if (code != CURLM_OK) handle_multi_error(code); return code; }
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)
// This class wraps CURL*'s.
// It guarantees that a pointer is cleaned up when no longer needed, as required by libcurl.
class CurlEasyHandle : public boost::noncopyable, protected AICurlEasyHandleEvents {
public:
CurlEasyHandle(void);
~CurlEasyHandle();
private:
// Disallow assignment.
CurlEasyHandle& operator=(CurlEasyHandle const*);
public:
// Reset all options of a libcurl session handle.
void reset(void) { llassert(!mActiveMultiHandle); curl_easy_reset(mEasyHandle); }
// Set options for a curl easy handle.
DECLARE_SETOPT(long);
DECLARE_SETOPT(long long);
DECLARE_SETOPT(void const*);
DECLARE_SETOPT(curl_debug_callback);
DECLARE_SETOPT(curl_write_callback);
//DECLARE_SETOPT(curl_read_callback); Same type as curl_write_callback
DECLARE_SETOPT(curl_ssl_ctx_callback);
DECLARE_SETOPT(curl_conv_callback);
#if 0 // Not used by the viewer.
DECLARE_SETOPT(curl_progress_callback);
DECLARE_SETOPT(curl_seek_callback);
DECLARE_SETOPT(curl_ioctl_callback);
DECLARE_SETOPT(curl_sockopt_callback);
DECLARE_SETOPT(curl_opensocket_callback);
DECLARE_SETOPT(curl_closesocket_callback);
DECLARE_SETOPT(curl_sshkeycallback);
DECLARE_SETOPT(curl_chunk_bgn_callback);
DECLARE_SETOPT(curl_chunk_end_callback);
DECLARE_SETOPT(curl_fnmatch_callback);
#endif
// Automatically cast int types to a long. Note that U32/S32 are int and
// that you can overload int and long even if they have the same size.
CURLcode setopt(CURLoption option, U32 parameter) { return setopt(option, (long)parameter); }
CURLcode setopt(CURLoption option, S32 parameter) { return setopt(option, (long)parameter); }
// Clone a libcurl session handle using all the options previously set.
//CurlEasyHandle(CurlEasyHandle const& orig);
// URL encode/decode the given string.
char* escape(char* url, int length);
char* unescape(char* url, int inlength , int* outlength);
// Extract information from a curl handle.
private:
CURLcode getinfo_priv(CURLINFO info, void* data);
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); }
#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; }
#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)); }
#endif
// Perform a file transfer (blocking).
CURLcode perform(void);
// Pause and unpause a connection.
CURLcode pause(int bitmask);
// Called when a request is queued for removal. In that case a race between the actual removal
// and revoking of the callbacks is harmless (and happens for the raw non-statemachine version).
void remove_queued(void) { mQueuedForRemoval = true; }
// In case it's added after being removed.
void add_queued(void) { mQueuedForRemoval = false; }
private:
CURL* mEasyHandle;
CURLM* mActiveMultiHandle;
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
public:
bool mRemovedPerCommand; // Set if mActiveMultiHandle was reset as per command from the main thread.
#endif
private:
// This should only be called from MultiHandle; add/remove an easy handle to/from a multi handle.
friend class curlthread::MultiHandle;
CURLMcode add_handle_to_multi(AICurlEasyRequest_wat& curl_easy_request_w, CURLM* multi_handle);
CURLMcode remove_handle_from_multi(AICurlEasyRequest_wat& curl_easy_request_w, CURLM* multi_handle);
public:
// Returns true if this easy handle was added to a curl multi handle.
bool active(void) const { return mActiveMultiHandle; }
// If there was an error code as result, then this returns a human readable error string.
// Only valid when setErrorBuffer was called and the curl_easy function returned an error.
std::string getErrorString(void) const { return mErrorBuffer ? mErrorBuffer : "(null)"; }
// Returns true when it is expected that the parent will revoke callbacks before the curl
// easy handle is removed from the multi handle; that usually happens when an external
// error demands termination of the request (ie, an expiration).
bool no_warning(void) const { return mQueuedForRemoval || LLApp::isExiting(); }
// Used for debugging purposes.
bool operator==(CURL* easy_handle) const { return mEasyHandle == easy_handle; }
private:
// Call this prior to every curl_easy function whose return value is passed to check_easy_code.
void setErrorBuffer(void);
static void handle_easy_error(CURLcode code);
// Always first call setErrorBuffer()!
static inline CURLcode check_easy_code(CURLcode code)
{
Stats::easy_calls++;
if (code != CURLE_OK)
handle_easy_error(code);
return code;
}
protected:
// Return the underlying curl easy handle.
CURL* getEasyHandle(void) const { return mEasyHandle; }
// Keep POSTFIELD data alive.
void setPostField(AIPostFieldPtr const& post_field_ptr) { mPostField = post_field_ptr; }
private:
// Return, and possibly create, the curl (easy) error buffer used by the current thread.
static char* getTLErrorBuffer(void);
};
// CurlEasyRequest adds a slightly more powerful interface that can be used
// to set the options on a curl easy handle.
//
// Calling sendRequest() will then connect to the given URL and perform
// the data exchange. If an error occurs related to this handle, it can
// be read by calling getErrorString().
//
// Note that the life cycle of a CurlEasyRequest is controlled by AICurlEasyRequest:
// a CurlEasyRequest is only ever created as base class of a ThreadSafeCurlEasyRequest,
// which is only created by creating a AICurlEasyRequest. When the last copy of such
// AICurlEasyRequest is deleted, then also the ThreadSafeCurlEasyRequest is deleted
// and the CurlEasyRequest destructed.
class CurlEasyRequest : public CurlEasyHandle {
private:
void setPost_raw(S32 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 setoptString(CURLoption option, std::string const& value);
void addHeader(char const* str);
private:
// Callback stubs.
static size_t headerCallback(char* ptr, size_t size, size_t nmemb, void* userdata);
static size_t writeCallback(char* ptr, size_t size, size_t nmemb, void* userdata);
static size_t readCallback(char* ptr, size_t size, size_t nmemb, void* userdata);
static CURLcode SSLCtxCallback(CURL* curl, void* sslctx, void* userdata);
curl_write_callback mHeaderCallback;
void* mHeaderCallbackUserData;
curl_write_callback mWriteCallback;
void* mWriteCallbackUserData;
curl_read_callback mReadCallback;
void* mReadCallbackUserData;
curl_ssl_ctx_callback mSSLCtxCallback;
void* mSSLCtxCallbackUserData;
public:
void setHeaderCallback(curl_write_callback callback, void* userdata);
void setWriteCallback(curl_write_callback callback, void* userdata);
void setReadCallback(curl_read_callback callback, void* userdata);
void setSSLCtxCallback(curl_ssl_ctx_callback callback, void* userdata);
// Call this if the set callbacks are about to be invalidated.
void revokeCallbacks(void);
// Reset everything to the state it was in when this object was just created.
void resetState(void);
private:
// Called from applyDefaultOptions.
void applyProxySettings(void);
// Used in applyProxySettings.
static CURLcode curlCtxCallback(CURL* curl, void* sslctx, void* parm);
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);
// Store result code that is returned by getResult.
void store_result(CURLcode result) { mResult = result; }
// Called when the curl easy handle is done.
void done(AICurlEasyRequest_wat& curl_easy_request_w) { finished(curl_easy_request_w); }
// Fill info with the transfer info.
void getTransferInfo(AICurlInterface::TransferInfo* info);
// If result != CURLE_FAILED_INIT then also info was filled.
void getResult(CURLcode* result, AICurlInterface::TransferInfo* info = NULL);
private:
curl_slist* mHeaders;
bool mRequestFinalized;
AICurlEasyHandleEvents* mEventsTarget;
CURLcode mResult;
private:
// This class may only be created by constructing a ThreadSafeCurlEasyRequest.
friend class ThreadSafeCurlEasyRequest;
// Throws AICurlNoEasyHandle.
CurlEasyRequest(void) :
mHeaders(NULL), mRequestFinalized(false), mEventsTarget(NULL), mResult(CURLE_FAILED_INIT)
{ applyDefaultOptions(); }
public:
~CurlEasyRequest();
public:
// Post-initialization, set the parent to pass the events to.
void send_events_to(AICurlEasyHandleEvents* target) { mEventsTarget = target; }
// For debugging purposes
bool is_finalized(void) const { return mRequestFinalized; }
// Return pointer to the ThreadSafe (wrapped) version of this object.
ThreadSafeCurlEasyRequest* get_lockobj(void);
protected:
// Pass events to parent.
/*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);
};
// 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 {
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);
LLIOPipe::buffer_ptr_t& getInput(void) { return mInput; }
std::stringstream& getHeaderOutput(void) { return mHeaderOutput; }
LLIOPipe::buffer_ptr_t& getOutput(void) { return mOutput; }
// Called if libcurl doesn't deliver within CurlRequestTimeOut seconds.
void timed_out(void);
// Called after removed_from_multi_handle was called.
void processOutput(AICurlEasyRequest_wat& curl_easy_request_w);
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);
private:
LLIOPipe::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;
public:
static LLChannelDescriptors const sChannels; // Channel object for mInput (channel out()) and mOutput (channel in()).
private:
// This class may only be created by constructing a ThreadSafeBufferedCurlEasyRequest.
friend class ThreadSafeBufferedCurlEasyRequest;
CurlResponderBuffer(void);
public:
~CurlResponderBuffer();
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);
public:
// Return pointer to the ThreadSafe (wrapped) version of this object.
ThreadSafeBufferedCurlEasyRequest* get_lockobj(void);
// 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
// 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> {
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; }
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; }
};
// The curl easy request type wrapped in a reference counting pointer.
typedef boost::intrusive_ptr<AICurlPrivate::ThreadSafeCurlEasyRequest> CurlEasyRequestPtr;
// This class wraps CURLM*'s.
// It guarantees that a pointer is cleaned up when no longer needed, as required by libcurl.
class CurlMultiHandle : public boost::noncopyable {
public:
CurlMultiHandle(void);
~CurlMultiHandle();
private:
// Disallow assignment.
CurlMultiHandle& operator=(CurlMultiHandle const*);
private:
static LLAtomicU32 sTotalMultiHandles;
protected:
CURLM* mMultiHandle;
public:
// Set options for a curl multi handle.
CURLMcode setopt(CURLMoption option, long parameter);
CURLMcode setopt(CURLMoption option, curl_socket_callback parameter);
CURLMcode setopt(CURLMoption option, curl_multi_timer_callback parameter);
CURLMcode setopt(CURLMoption option, void* parameter);
// Returns total number of existing CURLM* handles (excluding ones created outside this class).
static U32 getTotalMultiHandles(void) { return sTotalMultiHandles; }
};
// Overload the setopt methods in order to enforce the correct types (ie, convert an int to a long).
// curl_multi_setopt may only be passed a long,
inline CURLMcode CurlMultiHandle::setopt(CURLMoption option, long parameter)
{
llassert(option == CURLMOPT_MAXCONNECTS || option == CURLMOPT_PIPELINING);
return check_multi_code(curl_multi_setopt(mMultiHandle, option, parameter));
}
// ... or a function pointer,
inline CURLMcode CurlMultiHandle::setopt(CURLMoption option, curl_socket_callback parameter)
{
llassert(option == CURLMOPT_SOCKETFUNCTION);
return check_multi_code(curl_multi_setopt(mMultiHandle, option, parameter));
}
inline CURLMcode CurlMultiHandle::setopt(CURLMoption option, curl_multi_timer_callback parameter)
{
llassert(option == CURLMOPT_TIMERFUNCTION);
return check_multi_code(curl_multi_setopt(mMultiHandle, option, parameter));
}
// ... or an object pointer.
inline CURLMcode CurlMultiHandle::setopt(CURLMoption option, void* parameter)
{
llassert(option == CURLMOPT_SOCKETDATA || option == CURLMOPT_TIMERDATA);
return check_multi_code(curl_multi_setopt(mMultiHandle, option, parameter));
}
} // namespace AICurlPrivate
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,128 @@
/**
* @file aicurlthread.h
* @brief Thread safe wrapper for libcurl.
*
* 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.
*
* 28/04/2012
* Initial version, written by Aleric Inglewood @ SL
*/
#ifndef AICURLTHREAD_H
#define AICURLTHREAD_H
#include "aicurl.h"
#include <vector>
#undef AICurlPrivate
namespace AICurlPrivate {
namespace curlthread {
class PollSet;
// For ordering a std::set with AICurlEasyRequest objects.
struct AICurlEasyRequestCompare {
bool operator()(AICurlEasyRequest const& h1, AICurlEasyRequest const& h2) { return h1.get() < h2.get(); }
};
//-----------------------------------------------------------------------------
// MultiHandle
// This class adds member functions that will only be called from the AICurlThread thread.
// This class guarantees that all added easy handles will be removed from the multi handle
// before the multi handle is cleaned up, as is required by libcurl.
class MultiHandle : public CurlMultiHandle
{
public:
MultiHandle(void);
~MultiHandle();
// Add/remove an easy handle to/from a multi session.
CURLMcode add_easy_request(AICurlEasyRequest const& easy_request);
CURLMcode remove_easy_request(AICurlEasyRequest const& easy_request, bool as_per_command = false);
// Reads/writes available data from a particular socket (non-blocking).
CURLMcode socket_action(curl_socket_t sockfd, int ev_bitmask);
// Set data to association with an internal socket.
CURLMcode assign(curl_socket_t sockfd, void* sockptr);
// Read multi stack informationals.
CURLMsg const* info_read(int* msgs_in_queue) const;
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.
private:
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);
public:
// Returns the number of active easy handles as reported by the last call to curl_multi_socket_action.
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; }
// This is called before sleeping, after calling (one or more times) socket_action.
void check_run_count(void);
public:
//-----------------------------------------------------------------------------
// Curl socket administration:
PollSet* mReadPollSet;
PollSet* mWritePollSet;
};
} // namespace curlthread
} // namespace AICurlPrivate
// Thread safe, noncopyable curl multi handle.
// This class wraps MultiHandle for thread-safety.
// AIThreadSafeSingleThreadDC cannot be copied, but that is OK as we don't need that (or want that);
// this class provides a thread-local singleton (exactly one instance per thread), and because it
// can't be copied, that guarantees that the CURLM* handle is never used concurrently, which is
// not allowed by libcurl.
class AICurlMultiHandle : public AIThreadSafeSingleThreadDC<AICurlPrivate::curlthread::MultiHandle>, public LLThreadLocalDataMember {
public:
static AICurlMultiHandle& getInstance(void);
static void destroyInstance(void);
private:
// Use getInstance().
AICurlMultiHandle(void) { }
};
typedef AISTAccessConst<AICurlPrivate::curlthread::MultiHandle> AICurlMultiHandle_rat;
typedef AISTAccess<AICurlPrivate::curlthread::MultiHandle> AICurlMultiHandle_wat;
#define AICurlPrivate DONTUSE_AICurlPrivate
#endif

View File

@@ -0,0 +1,942 @@
#ifdef DEBUG_CURLIO
#include "sys.h"
#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <stdarg.h>
#include <cstring>
#include "llpreprocessor.h"
#include <curl/curl.h>
#define COMPILING_DEBUG_LIBCURL_CC
#include "debug_libcurl.h"
#include "debug.h"
#include "llerror.h"
#ifdef CWDEBUG
#include <libcwd/buf2str.h>
#endif
#define CURL_VERSION(major, minor, patch) \
(LIBCURL_VERSION_MAJOR > major || \
(LIBCURL_VERSION_MAJOR == major && \
LIBCURL_VERSION_MINOR > minor || \
(LIBCURL_VERSION_MINOR == minor && \
LIBCURL_VERSION_PATCH >= patch)))
static struct curl_slist unchanged_slist;
std::ostream& operator<<(std::ostream& os, struct curl_slist const& slist)
{
struct curl_slist const* ptr = &slist;
if (ptr == &unchanged_slist)
os << " <unchanged> ";
else
{
os << "(curl_slist)@0x" << std::hex << (size_t)ptr << std::dec << "{";
do
{
os << '"' << ptr->data << '"';
ptr = ptr->next;
if (ptr)
os << ", ";
}
while(ptr);
os << "}";
}
return os;
}
#define CASEPRINT(x) case x: os << #x; break
std::ostream& operator<<(std::ostream& os, CURLINFO info)
{
switch (info)
{
CASEPRINT(CURLINFO_EFFECTIVE_URL);
CASEPRINT(CURLINFO_RESPONSE_CODE);
CASEPRINT(CURLINFO_TOTAL_TIME);
CASEPRINT(CURLINFO_NAMELOOKUP_TIME);
CASEPRINT(CURLINFO_CONNECT_TIME);
CASEPRINT(CURLINFO_PRETRANSFER_TIME);
CASEPRINT(CURLINFO_SIZE_UPLOAD);
CASEPRINT(CURLINFO_SIZE_DOWNLOAD);
CASEPRINT(CURLINFO_SPEED_DOWNLOAD);
CASEPRINT(CURLINFO_SPEED_UPLOAD);
CASEPRINT(CURLINFO_HEADER_SIZE);
CASEPRINT(CURLINFO_REQUEST_SIZE);
CASEPRINT(CURLINFO_SSL_VERIFYRESULT);
CASEPRINT(CURLINFO_FILETIME);
CASEPRINT(CURLINFO_CONTENT_LENGTH_DOWNLOAD);
CASEPRINT(CURLINFO_CONTENT_LENGTH_UPLOAD);
CASEPRINT(CURLINFO_STARTTRANSFER_TIME);
CASEPRINT(CURLINFO_CONTENT_TYPE);
CASEPRINT(CURLINFO_REDIRECT_TIME);
CASEPRINT(CURLINFO_REDIRECT_COUNT);
CASEPRINT(CURLINFO_PRIVATE);
CASEPRINT(CURLINFO_HTTP_CONNECTCODE);
CASEPRINT(CURLINFO_HTTPAUTH_AVAIL);
CASEPRINT(CURLINFO_PROXYAUTH_AVAIL);
CASEPRINT(CURLINFO_OS_ERRNO);
CASEPRINT(CURLINFO_NUM_CONNECTS);
CASEPRINT(CURLINFO_SSL_ENGINES);
CASEPRINT(CURLINFO_COOKIELIST);
CASEPRINT(CURLINFO_LASTSOCKET);
CASEPRINT(CURLINFO_FTP_ENTRY_PATH);
CASEPRINT(CURLINFO_REDIRECT_URL);
CASEPRINT(CURLINFO_PRIMARY_IP);
CASEPRINT(CURLINFO_APPCONNECT_TIME);
CASEPRINT(CURLINFO_CERTINFO);
CASEPRINT(CURLINFO_CONDITION_UNMET);
CASEPRINT(CURLINFO_RTSP_SESSION_ID);
CASEPRINT(CURLINFO_RTSP_CLIENT_CSEQ);
CASEPRINT(CURLINFO_RTSP_SERVER_CSEQ);
CASEPRINT(CURLINFO_RTSP_CSEQ_RECV);
CASEPRINT(CURLINFO_PRIMARY_PORT);
CASEPRINT(CURLINFO_LOCAL_IP);
CASEPRINT(CURLINFO_LOCAL_PORT);
default:
os << "<unknown" << " CURLINFO value (" << (int)info << ")>";
}
return os;
}
std::ostream& operator<<(std::ostream& os, CURLcode code)
{
switch(code)
{
CASEPRINT(CURLE_OK);
CASEPRINT(CURLE_UNSUPPORTED_PROTOCOL);
CASEPRINT(CURLE_FAILED_INIT);
CASEPRINT(CURLE_URL_MALFORMAT);
#if CURL_VERSION(7, 21, 5)
CASEPRINT(CURLE_NOT_BUILT_IN);
#endif
CASEPRINT(CURLE_COULDNT_RESOLVE_PROXY);
CASEPRINT(CURLE_COULDNT_RESOLVE_HOST);
CASEPRINT(CURLE_COULDNT_CONNECT);
CASEPRINT(CURLE_FTP_WEIRD_SERVER_REPLY);
CASEPRINT(CURLE_REMOTE_ACCESS_DENIED);
#if 0
CASEPRINT(CURLE_FTP_ACCEPT_FAILED);
#endif
CASEPRINT(CURLE_FTP_WEIRD_PASS_REPLY);
#if 0
CASEPRINT(CURLE_FTP_ACCEPT_TIMEOUT);
#endif
CASEPRINT(CURLE_FTP_WEIRD_PASV_REPLY);
CASEPRINT(CURLE_FTP_WEIRD_227_FORMAT);
CASEPRINT(CURLE_FTP_CANT_GET_HOST);
CASEPRINT(CURLE_OBSOLETE16);
CASEPRINT(CURLE_FTP_COULDNT_SET_TYPE);
CASEPRINT(CURLE_PARTIAL_FILE);
CASEPRINT(CURLE_FTP_COULDNT_RETR_FILE);
CASEPRINT(CURLE_OBSOLETE20);
CASEPRINT(CURLE_QUOTE_ERROR);
CASEPRINT(CURLE_HTTP_RETURNED_ERROR);
CASEPRINT(CURLE_WRITE_ERROR);
CASEPRINT(CURLE_OBSOLETE24);
CASEPRINT(CURLE_UPLOAD_FAILED);
CASEPRINT(CURLE_READ_ERROR);
CASEPRINT(CURLE_OUT_OF_MEMORY);
CASEPRINT(CURLE_OPERATION_TIMEDOUT);
CASEPRINT(CURLE_OBSOLETE29);
CASEPRINT(CURLE_FTP_PORT_FAILED);
CASEPRINT(CURLE_FTP_COULDNT_USE_REST);
CASEPRINT(CURLE_OBSOLETE32);
CASEPRINT(CURLE_RANGE_ERROR);
CASEPRINT(CURLE_HTTP_POST_ERROR);
CASEPRINT(CURLE_SSL_CONNECT_ERROR);
CASEPRINT(CURLE_BAD_DOWNLOAD_RESUME);
CASEPRINT(CURLE_FILE_COULDNT_READ_FILE);
CASEPRINT(CURLE_LDAP_CANNOT_BIND);
CASEPRINT(CURLE_LDAP_SEARCH_FAILED);
CASEPRINT(CURLE_OBSOLETE40);
CASEPRINT(CURLE_FUNCTION_NOT_FOUND);
CASEPRINT(CURLE_ABORTED_BY_CALLBACK);
CASEPRINT(CURLE_BAD_FUNCTION_ARGUMENT);
CASEPRINT(CURLE_OBSOLETE44);
CASEPRINT(CURLE_INTERFACE_FAILED);
CASEPRINT(CURLE_OBSOLETE46);
CASEPRINT(CURLE_TOO_MANY_REDIRECTS );
#if CURL_VERSION(7, 21, 5)
CASEPRINT(CURLE_UNKNOWN_OPTION);
#else
CASEPRINT(CURLE_UNKNOWN_TELNET_OPTION);
#endif
CASEPRINT(CURLE_TELNET_OPTION_SYNTAX );
CASEPRINT(CURLE_OBSOLETE50);
CASEPRINT(CURLE_PEER_FAILED_VERIFICATION);
CASEPRINT(CURLE_GOT_NOTHING);
CASEPRINT(CURLE_SSL_ENGINE_NOTFOUND);
CASEPRINT(CURLE_SSL_ENGINE_SETFAILED);
CASEPRINT(CURLE_SEND_ERROR);
CASEPRINT(CURLE_RECV_ERROR);
CASEPRINT(CURLE_OBSOLETE57);
CASEPRINT(CURLE_SSL_CERTPROBLEM);
CASEPRINT(CURLE_SSL_CIPHER);
CASEPRINT(CURLE_SSL_CACERT);
CASEPRINT(CURLE_BAD_CONTENT_ENCODING);
CASEPRINT(CURLE_LDAP_INVALID_URL);
CASEPRINT(CURLE_FILESIZE_EXCEEDED);
CASEPRINT(CURLE_USE_SSL_FAILED);
CASEPRINT(CURLE_SEND_FAIL_REWIND);
CASEPRINT(CURLE_SSL_ENGINE_INITFAILED);
CASEPRINT(CURLE_LOGIN_DENIED);
CASEPRINT(CURLE_TFTP_NOTFOUND);
CASEPRINT(CURLE_TFTP_PERM);
CASEPRINT(CURLE_REMOTE_DISK_FULL);
CASEPRINT(CURLE_TFTP_ILLEGAL);
CASEPRINT(CURLE_TFTP_UNKNOWNID);
CASEPRINT(CURLE_REMOTE_FILE_EXISTS);
CASEPRINT(CURLE_TFTP_NOSUCHUSER);
CASEPRINT(CURLE_CONV_FAILED);
CASEPRINT(CURLE_CONV_REQD);
CASEPRINT(CURLE_SSL_CACERT_BADFILE);
CASEPRINT(CURLE_REMOTE_FILE_NOT_FOUND);
CASEPRINT(CURLE_SSH);
CASEPRINT(CURLE_SSL_SHUTDOWN_FAILED);
CASEPRINT(CURLE_AGAIN);
CASEPRINT(CURLE_SSL_CRL_BADFILE);
CASEPRINT(CURLE_SSL_ISSUER_ERROR);
CASEPRINT(CURLE_FTP_PRET_FAILED);
CASEPRINT(CURLE_RTSP_CSEQ_ERROR);
CASEPRINT(CURLE_RTSP_SESSION_ERROR);
CASEPRINT(CURLE_FTP_BAD_FILE_LIST);
CASEPRINT(CURLE_CHUNK_FAILED);
default:
os << (code == CURL_LAST ? "<illegal" : "<unknown") << " CURLcode value (" << (int)code << ")>";
}
return os;
}
struct AICURL;
struct AICURLM;
std::ostream& operator<<(std::ostream& os, AICURL* curl)
{
os << "(CURL*)0x" << std::hex << (size_t)curl << std::dec;
return os;
}
std::ostream& operator<<(std::ostream& os, AICURLM* curl)
{
os << "(CURLM*)0x" << std::hex << (size_t)curl << std::dec;
return os;
}
std::ostream& operator<<(std::ostream& os, CURLoption option)
{
switch(option)
{
CASEPRINT(CURLOPT_WRITEDATA);
CASEPRINT(CURLOPT_URL);
CASEPRINT(CURLOPT_PORT);
CASEPRINT(CURLOPT_PROXY);
CASEPRINT(CURLOPT_USERPWD);
CASEPRINT(CURLOPT_PROXYUSERPWD);
CASEPRINT(CURLOPT_RANGE);
CASEPRINT(CURLOPT_READDATA);
CASEPRINT(CURLOPT_ERRORBUFFER);
CASEPRINT(CURLOPT_WRITEFUNCTION);
CASEPRINT(CURLOPT_READFUNCTION);
CASEPRINT(CURLOPT_TIMEOUT);
CASEPRINT(CURLOPT_INFILESIZE);
CASEPRINT(CURLOPT_POSTFIELDS);
CASEPRINT(CURLOPT_REFERER);
CASEPRINT(CURLOPT_FTPPORT);
CASEPRINT(CURLOPT_USERAGENT);
CASEPRINT(CURLOPT_LOW_SPEED_LIMIT);
CASEPRINT(CURLOPT_LOW_SPEED_TIME);
CASEPRINT(CURLOPT_RESUME_FROM);
CASEPRINT(CURLOPT_COOKIE);
CASEPRINT(CURLOPT_RTSPHEADER);
CASEPRINT(CURLOPT_HTTPPOST);
CASEPRINT(CURLOPT_SSLCERT);
CASEPRINT(CURLOPT_KEYPASSWD);
CASEPRINT(CURLOPT_CRLF);
CASEPRINT(CURLOPT_QUOTE);
CASEPRINT(CURLOPT_HEADERDATA);
CASEPRINT(CURLOPT_COOKIEFILE);
CASEPRINT(CURLOPT_SSLVERSION);
CASEPRINT(CURLOPT_TIMECONDITION);
CASEPRINT(CURLOPT_TIMEVALUE);
CASEPRINT(CURLOPT_CUSTOMREQUEST);
CASEPRINT(CURLOPT_STDERR);
CASEPRINT(CURLOPT_POSTQUOTE);
CASEPRINT(CURLOPT_WRITEINFO);
CASEPRINT(CURLOPT_VERBOSE);
CASEPRINT(CURLOPT_HEADER);
CASEPRINT(CURLOPT_NOPROGRESS);
CASEPRINT(CURLOPT_NOBODY);
CASEPRINT(CURLOPT_FAILONERROR);
CASEPRINT(CURLOPT_UPLOAD);
CASEPRINT(CURLOPT_POST);
CASEPRINT(CURLOPT_DIRLISTONLY);
CASEPRINT(CURLOPT_APPEND);
CASEPRINT(CURLOPT_NETRC);
CASEPRINT(CURLOPT_FOLLOWLOCATION);
CASEPRINT(CURLOPT_TRANSFERTEXT);
CASEPRINT(CURLOPT_PUT);
CASEPRINT(CURLOPT_PROGRESSFUNCTION);
CASEPRINT(CURLOPT_PROGRESSDATA);
CASEPRINT(CURLOPT_AUTOREFERER);
CASEPRINT(CURLOPT_PROXYPORT);
CASEPRINT(CURLOPT_POSTFIELDSIZE);
CASEPRINT(CURLOPT_HTTPPROXYTUNNEL);
CASEPRINT(CURLOPT_INTERFACE);
CASEPRINT(CURLOPT_KRBLEVEL);
CASEPRINT(CURLOPT_SSL_VERIFYPEER);
CASEPRINT(CURLOPT_CAINFO);
CASEPRINT(CURLOPT_MAXREDIRS);
CASEPRINT(CURLOPT_FILETIME);
CASEPRINT(CURLOPT_TELNETOPTIONS);
CASEPRINT(CURLOPT_MAXCONNECTS);
CASEPRINT(CURLOPT_CLOSEPOLICY);
CASEPRINT(CURLOPT_FRESH_CONNECT);
CASEPRINT(CURLOPT_FORBID_REUSE);
CASEPRINT(CURLOPT_RANDOM_FILE);
CASEPRINT(CURLOPT_EGDSOCKET);
CASEPRINT(CURLOPT_CONNECTTIMEOUT);
CASEPRINT(CURLOPT_HEADERFUNCTION);
CASEPRINT(CURLOPT_HTTPGET);
CASEPRINT(CURLOPT_SSL_VERIFYHOST);
CASEPRINT(CURLOPT_COOKIEJAR);
CASEPRINT(CURLOPT_SSL_CIPHER_LIST);
CASEPRINT(CURLOPT_HTTP_VERSION);
CASEPRINT(CURLOPT_FTP_USE_EPSV);
CASEPRINT(CURLOPT_SSLCERTTYPE);
CASEPRINT(CURLOPT_SSLKEY);
CASEPRINT(CURLOPT_SSLKEYTYPE);
CASEPRINT(CURLOPT_SSLENGINE);
CASEPRINT(CURLOPT_SSLENGINE_DEFAULT);
CASEPRINT(CURLOPT_DNS_USE_GLOBAL_CACHE);
CASEPRINT(CURLOPT_DNS_CACHE_TIMEOUT);
CASEPRINT(CURLOPT_PREQUOTE);
CASEPRINT(CURLOPT_DEBUGFUNCTION);
CASEPRINT(CURLOPT_DEBUGDATA);
CASEPRINT(CURLOPT_COOKIESESSION);
CASEPRINT(CURLOPT_CAPATH);
CASEPRINT(CURLOPT_BUFFERSIZE);
CASEPRINT(CURLOPT_NOSIGNAL);
CASEPRINT(CURLOPT_SHARE);
CASEPRINT(CURLOPT_PROXYTYPE);
#if CURL_VERSION(7, 21, 6)
CASEPRINT(CURLOPT_ACCEPT_ENCODING);
#else
CASEPRINT(CURLOPT_ENCODING);
#endif
CASEPRINT(CURLOPT_PRIVATE);
CASEPRINT(CURLOPT_HTTP200ALIASES);
CASEPRINT(CURLOPT_UNRESTRICTED_AUTH);
CASEPRINT(CURLOPT_FTP_USE_EPRT);
CASEPRINT(CURLOPT_HTTPAUTH);
CASEPRINT(CURLOPT_SSL_CTX_FUNCTION);
CASEPRINT(CURLOPT_SSL_CTX_DATA);
CASEPRINT(CURLOPT_FTP_CREATE_MISSING_DIRS);
CASEPRINT(CURLOPT_PROXYAUTH);
CASEPRINT(CURLOPT_FTP_RESPONSE_TIMEOUT);
CASEPRINT(CURLOPT_IPRESOLVE);
CASEPRINT(CURLOPT_MAXFILESIZE);
CASEPRINT(CURLOPT_INFILESIZE_LARGE);
CASEPRINT(CURLOPT_RESUME_FROM_LARGE);
CASEPRINT(CURLOPT_MAXFILESIZE_LARGE);
CASEPRINT(CURLOPT_NETRC_FILE);
CASEPRINT(CURLOPT_USE_SSL);
CASEPRINT(CURLOPT_POSTFIELDSIZE_LARGE);
CASEPRINT(CURLOPT_TCP_NODELAY);
CASEPRINT(CURLOPT_FTPSSLAUTH);
CASEPRINT(CURLOPT_IOCTLFUNCTION);
CASEPRINT(CURLOPT_IOCTLDATA);
CASEPRINT(CURLOPT_FTP_ACCOUNT);
CASEPRINT(CURLOPT_COOKIELIST);
CASEPRINT(CURLOPT_IGNORE_CONTENT_LENGTH);
CASEPRINT(CURLOPT_FTP_SKIP_PASV_IP);
CASEPRINT(CURLOPT_FTP_FILEMETHOD);
CASEPRINT(CURLOPT_LOCALPORT);
CASEPRINT(CURLOPT_LOCALPORTRANGE);
CASEPRINT(CURLOPT_CONNECT_ONLY);
CASEPRINT(CURLOPT_CONV_FROM_NETWORK_FUNCTION);
CASEPRINT(CURLOPT_CONV_TO_NETWORK_FUNCTION);
CASEPRINT(CURLOPT_CONV_FROM_UTF8_FUNCTION);
CASEPRINT(CURLOPT_MAX_SEND_SPEED_LARGE);
CASEPRINT(CURLOPT_MAX_RECV_SPEED_LARGE);
CASEPRINT(CURLOPT_FTP_ALTERNATIVE_TO_USER);
CASEPRINT(CURLOPT_SOCKOPTFUNCTION);
CASEPRINT(CURLOPT_SOCKOPTDATA);
CASEPRINT(CURLOPT_SSL_SESSIONID_CACHE);
CASEPRINT(CURLOPT_SSH_AUTH_TYPES);
CASEPRINT(CURLOPT_SSH_PUBLIC_KEYFILE);
CASEPRINT(CURLOPT_SSH_PRIVATE_KEYFILE);
CASEPRINT(CURLOPT_FTP_SSL_CCC);
CASEPRINT(CURLOPT_TIMEOUT_MS);
CASEPRINT(CURLOPT_CONNECTTIMEOUT_MS);
CASEPRINT(CURLOPT_HTTP_TRANSFER_DECODING);
CASEPRINT(CURLOPT_HTTP_CONTENT_DECODING);
CASEPRINT(CURLOPT_NEW_FILE_PERMS);
CASEPRINT(CURLOPT_NEW_DIRECTORY_PERMS);
CASEPRINT(CURLOPT_POSTREDIR);
CASEPRINT(CURLOPT_SSH_HOST_PUBLIC_KEY_MD5);
CASEPRINT(CURLOPT_OPENSOCKETFUNCTION);
CASEPRINT(CURLOPT_OPENSOCKETDATA);
CASEPRINT(CURLOPT_COPYPOSTFIELDS);
CASEPRINT(CURLOPT_PROXY_TRANSFER_MODE);
CASEPRINT(CURLOPT_SEEKFUNCTION);
CASEPRINT(CURLOPT_SEEKDATA);
CASEPRINT(CURLOPT_CRLFILE);
CASEPRINT(CURLOPT_ISSUERCERT);
CASEPRINT(CURLOPT_ADDRESS_SCOPE);
CASEPRINT(CURLOPT_CERTINFO);
CASEPRINT(CURLOPT_USERNAME);
CASEPRINT(CURLOPT_PASSWORD);
CASEPRINT(CURLOPT_PROXYUSERNAME);
CASEPRINT(CURLOPT_PROXYPASSWORD);
CASEPRINT(CURLOPT_NOPROXY);
CASEPRINT(CURLOPT_TFTP_BLKSIZE);
CASEPRINT(CURLOPT_SOCKS5_GSSAPI_SERVICE);
CASEPRINT(CURLOPT_SOCKS5_GSSAPI_NEC);
CASEPRINT(CURLOPT_PROTOCOLS);
CASEPRINT(CURLOPT_REDIR_PROTOCOLS);
CASEPRINT(CURLOPT_SSH_KNOWNHOSTS);
CASEPRINT(CURLOPT_SSH_KEYFUNCTION);
CASEPRINT(CURLOPT_SSH_KEYDATA);
CASEPRINT(CURLOPT_MAIL_FROM);
CASEPRINT(CURLOPT_MAIL_RCPT);
CASEPRINT(CURLOPT_FTP_USE_PRET);
CASEPRINT(CURLOPT_RTSP_REQUEST);
CASEPRINT(CURLOPT_RTSP_SESSION_ID);
CASEPRINT(CURLOPT_RTSP_STREAM_URI);
CASEPRINT(CURLOPT_RTSP_TRANSPORT);
CASEPRINT(CURLOPT_RTSP_CLIENT_CSEQ);
CASEPRINT(CURLOPT_RTSP_SERVER_CSEQ);
CASEPRINT(CURLOPT_INTERLEAVEDATA);
CASEPRINT(CURLOPT_INTERLEAVEFUNCTION);
CASEPRINT(CURLOPT_WILDCARDMATCH);
CASEPRINT(CURLOPT_CHUNK_BGN_FUNCTION);
CASEPRINT(CURLOPT_CHUNK_END_FUNCTION);
CASEPRINT(CURLOPT_FNMATCH_FUNCTION);
CASEPRINT(CURLOPT_CHUNK_DATA);
CASEPRINT(CURLOPT_FNMATCH_DATA);
#if CURL_VERSION(7, 21, 3)
CASEPRINT(CURLOPT_RESOLVE);
#endif
#if CURL_VERSION(7, 21, 4)
CASEPRINT(CURLOPT_TLSAUTH_USERNAME);
CASEPRINT(CURLOPT_TLSAUTH_PASSWORD);
CASEPRINT(CURLOPT_TLSAUTH_TYPE);
#endif
#if CURL_VERSION(7, 21, 6)
CASEPRINT(CURLOPT_TRANSFER_ENCODING);
#endif
#if CURL_VERSION(7, 21, 7)
CASEPRINT(CURLOPT_CLOSESOCKETFUNCTION);
CASEPRINT(CURLOPT_CLOSESOCKETDATA);
#endif
#if CURL_VERSION(7, 22, 0)
CASEPRINT(CURLOPT_GSSAPI_DELEGATION);
#endif
#if CURL_VERSION(7, 24, 0)
CASEPRINT(CURLOPT_DNS_SERVERS);
CASEPRINT(CURLOPT_ACCEPTTIMEOUT_MS);
#endif
#if CURL_VERSION(7, 25, 0)
CASEPRINT(CURLOPT_TCP_KEEPALIVE);
CASEPRINT(CURLOPT_TCP_KEEPIDLE);
CASEPRINT(CURLOPT_TCP_KEEPINTVL);
#endif
#if CURL_VERSION(7, 25, 0)
CASEPRINT(CURLOPT_SSL_OPTIONS);
CASEPRINT(CURLOPT_MAIL_AUTH);
#endif
default:
os << "<unknown CURLoption value (" << (int)option << ")>";
}
return os;
}
std::ostream& operator<<(std::ostream& os, CURLMoption option)
{
switch(option)
{
CASEPRINT(CURLMOPT_SOCKETFUNCTION);
CASEPRINT(CURLMOPT_SOCKETDATA);
CASEPRINT(CURLMOPT_PIPELINING);
CASEPRINT(CURLMOPT_TIMERFUNCTION);
CASEPRINT(CURLMOPT_TIMERDATA);
CASEPRINT(CURLMOPT_MAXCONNECTS);
default:
os << "<unknown CURLMoption value (" << (int)option << ")>";
}
return os;
}
std::ostream& operator<<(std::ostream& os, CURLMsg* msg)
{
if (msg)
{
os << "(CURLMsg*){";
if (msg->msg == CURLMSG_DONE)
os << "CURLMSG_DONE";
else
os << msg->msg;
os << ", " << (AICURL*)msg->easy_handle << ", 0x" << std::hex << (size_t)msg->data.whatever << std::dec << '}';
}
else
os << "(CURLMsg*)NULL";
return os;
}
struct Socket {
curl_socket_t mSocket;
Socket(curl_socket_t sockfd) : mSocket(sockfd) { }
};
std::ostream& operator<<(std::ostream& os, Socket const& sock)
{
if (sock.mSocket == CURL_SOCKET_TIMEOUT)
os << "CURL_SOCKET_TIMEOUT";
else
os << sock.mSocket;
return os;
}
struct EvBitmask {
int mBitmask;
EvBitmask(int mask) : mBitmask(mask) { }
};
std::ostream& operator<<(std::ostream& os, EvBitmask const& bitmask)
{
int m = bitmask.mBitmask;
if (m == 0)
os << '0';
if ((m & CURL_CSELECT_IN))
{
os << "CURL_CSELECT_IN";
if ((m & (CURL_CSELECT_OUT|CURL_CSELECT_ERR)))
os << '|';
}
if ((m & CURL_CSELECT_OUT))
{
os << "CURL_CSELECT_OUT";
if ((m & CURL_CSELECT_ERR))
os << '|';
}
if ((m & CURL_CSELECT_ERR))
{
os << "CURL_CSELECT_ERR";
}
return os;
}
extern "C" {
void debug_curl_easy_cleanup(CURL* handle)
{
curl_easy_cleanup(handle);
Dout(dc::curl, "curl_easy_cleanup(" << (AICURL*)handle << ")");
}
CURL* debug_curl_easy_duphandle(CURL* handle)
{
CURL* ret;
ret = curl_easy_duphandle(handle);
Dout(dc::curl, "curl_easy_duphandle(" << (AICURL*)handle << ") = " << (AICURL*)ret);
return ret;
}
char* debug_curl_easy_escape(CURL* curl, char* url, int length)
{
char* ret;
ret = curl_easy_escape(curl, url, length);
Dout(dc::curl, "curl_easy_escape(" << curl << ", \"" << url << "\", " << length << ") = \"" << ret << '"');
return ret;
}
CURLcode debug_curl_easy_getinfo(CURL* curl, CURLINFO info, ...)
{
CURLcode ret;
va_list ap;
union param_type {
void* some_ptr;
long* long_ptr;
char** char_ptr;
curl_slist** curl_slist_ptr;
double* double_ptr;
} param;
va_start(ap, info);
param.some_ptr = va_arg(ap, void*);
va_end(ap);
ret = curl_easy_getinfo(curl, info, param.some_ptr);
if (info == CURLINFO_PRIVATE)
{
Dout(dc::curl, "curl_easy_getinfo(" << curl << ", " << info << ", 0x" << std::hex << (size_t)param.some_ptr << std::dec << ") = " << ret);
}
else
{
switch((info & CURLINFO_TYPEMASK))
{
case CURLINFO_STRING:
Dout(dc::curl, "curl_easy_getinfo(" << curl << ", " << info << ", (char**){ \"" << (ret == CURLE_OK ? *param.char_ptr : " <unchanged> ") << "\" }) = " << ret);
break;
case CURLINFO_LONG:
Dout(dc::curl, "curl_easy_getinfo(" << curl << ", " << info << ", (long*){ " << (ret == CURLE_OK ? *param.long_ptr : 0L) << "L }) = " << ret);
break;
case CURLINFO_DOUBLE:
Dout(dc::curl, "curl_easy_getinfo(" << curl << ", " << info << ", (double*){" << (ret == CURLE_OK ? *param.double_ptr : 0.) << "}) = " << ret);
break;
case CURLINFO_SLIST:
Dout(dc::curl, "curl_easy_getinfo(" << curl << ", " << info << ", (curl_slist**){ " << (ret == CURLE_OK ? **param.curl_slist_ptr : unchanged_slist) << " }) = " << ret);
break;
}
}
return ret;
}
CURL* debug_curl_easy_init(void)
{
CURL* ret;
ret = curl_easy_init();
Dout(dc::curl, "curl_easy_init() = " << (AICURL*)ret);
return ret;
}
CURLcode debug_curl_easy_pause(CURL* handle, int bitmask)
{
CURLcode ret;
ret = curl_easy_pause(handle, bitmask);
Dout(dc::curl, "curl_easy_pause(" << (AICURL*)handle << ", 0x" << std::hex << bitmask << std::dec << ") = " << ret);
return ret;
}
CURLcode debug_curl_easy_perform(CURL* handle)
{
CURLcode ret;
ret = curl_easy_perform(handle);
Dout(dc::curl, "curl_easy_perform(" << (AICURL*)handle << ") = " << ret);
return ret;
}
void debug_curl_easy_reset(CURL* handle)
{
curl_easy_reset(handle);
Dout(dc::curl, "curl_easy_reset(" << (AICURL*)handle << ")");
}
CURLcode debug_curl_easy_setopt(CURL* handle, CURLoption option, ...)
{
CURLcode ret;
va_list ap;
union param_type {
long along;
void* ptr;
curl_off_t offset;
} param;
unsigned int param_type = (option / 10000) * 10000;
va_start(ap, option);
switch (param_type)
{
case CURLOPTTYPE_LONG:
param.along = va_arg(ap, long);
break;
case CURLOPTTYPE_OBJECTPOINT:
case CURLOPTTYPE_FUNCTIONPOINT:
param.ptr = va_arg(ap, void*);
break;
case CURLOPTTYPE_OFF_T:
param.offset = va_arg(ap, curl_off_t);
break;
default:
std::cerr << "Extracting param_type failed; option = " << option << "; param_type = " << param_type << std::endl;
std::exit(EXIT_FAILURE);
}
va_end(ap);
static long postfieldsize; // Cache. Assumes only one thread sets CURLOPT_POSTFIELDSIZE.
switch (param_type)
{
case CURLOPTTYPE_LONG:
{
ret = curl_easy_setopt(handle, option, param.along);
Dout(dc::curl, "curl_easy_setopt(" << (AICURL*)handle << ", " << option << ", " << param.along << "L) = " << ret);
if (option == CURLOPT_POSTFIELDSIZE)
{
postfieldsize = param.along;
}
break;
}
case CURLOPTTYPE_OBJECTPOINT:
{
ret = curl_easy_setopt(handle, option, param.ptr);
LibcwDoutScopeBegin(LIBCWD_DEBUGCHANNELS, libcwd::libcw_do, dc::curl)
LibcwDoutStream << "curl_easy_setopt(" << (AICURL*)handle << ", " << option << ", ";
// For a subset of all options that take a char*, print the string passed.
if (option == CURLOPT_PROXY || // Set HTTP proxy to use. The parameter should be a char* to a zero terminated string holding the host name or dotted IP address.
option == CURLOPT_PROXYUSERPWD || // Pass a char* as parameter, which should be [user name]:[password] to use for the connection to the HTTP proxy.
option == CURLOPT_CAINFO || // Pass a char * to a zero terminated string naming a file holding one or more certificates to verify the peer with.
option == CURLOPT_URL || // Pass in a pointer to the actual URL to deal with. The parameter should be a char * to a zero terminated string [...]
option == CURLOPT_COOKIEFILE || // Pass a pointer to a zero terminated string as parameter. It should contain the name of your file holding cookie data to read.
option == CURLOPT_CUSTOMREQUEST || // Pass a pointer to a zero terminated string as parameter. It can be used to specify the request instead of GET or HEAD when performing HTTP based requests
option == CURLOPT_ENCODING || // Sets the contents of the Accept-Encoding: header sent in a HTTP request, and enables decoding of a response when a Content-Encoding: header is received.
option == CURLOPT_POSTFIELDS ||
option == CURLOPT_COPYPOSTFIELDS) // Full data to post in a HTTP POST operation.
{
bool const is_postfield = option == CURLOPT_POSTFIELDS || option == CURLOPT_COPYPOSTFIELDS;
char* str = (char*)param.ptr;
long size;
LibcwDoutStream << "(char*)0x" << std::hex << (size_t)param.ptr << std::dec << ")";
if (is_postfield)
{
size = postfieldsize < 32 ? postfieldsize : 32; // Only print first 32 characters (this was already written to debug output before).
}
else
{
size = strlen(str);
}
LibcwDoutStream << "[";
if (str)
{
LibcwDoutStream << libcwd::buf2str(str, size);
if (is_postfield && postfieldsize > 32)
LibcwDoutStream << "...";
}
else
{
LibcwDoutStream << "NULL";
}
LibcwDoutStream << "](" << (is_postfield ? postfieldsize : size) << " bytes))";
}
else
{
LibcwDoutStream << "(object*)0x" << std::hex << (size_t)param.ptr << std::dec << ")";
}
LibcwDoutStream << " = " << ret;
LibcwDoutScopeEnd;
if (option == CURLOPT_HTTPHEADER && param.ptr)
{
debug::Indent indent(2);
Dout(dc::curl, "HTTP Headers:");
struct curl_slist* list = (struct curl_slist*)param.ptr;
while (list)
{
Dout(dc::curl, '"' << list->data << '"');
list = list->next;
}
}
break;
}
case CURLOPTTYPE_FUNCTIONPOINT:
ret = curl_easy_setopt(handle, option, param.ptr);
Dout(dc::curl, "curl_easy_setopt(" << (AICURL*)handle << ", " << option << ", (function*)0x" << std::hex << (size_t)param.ptr << std::dec << ") = " << ret);
break;
case CURLOPTTYPE_OFF_T:
{
ret = curl_easy_setopt(handle, option, param.offset);
Dout(dc::curl, "curl_easy_setopt(" << (AICURL*)handle << ", " << option << ", (curl_off_t)" << param.offset << ") = " << ret);
if (option == CURLOPT_POSTFIELDSIZE_LARGE)
{
postfieldsize = (long)param.offset;
}
break;
}
default:
break;
}
return ret;
}
char const* debug_curl_easy_strerror(CURLcode errornum)
{
char const* ret;
ret = curl_easy_strerror(errornum);
Dout(dc::curl, "curl_easy_strerror(" << errornum << ") = \"" << ret << '"');
return ret;
}
char* debug_curl_easy_unescape(CURL* curl, char* url, int inlength, int* outlength)
{
char* ret;
ret = curl_easy_unescape(curl, url, inlength, outlength);
Dout(dc::curl, "curl_easy_unescape(" << curl << ", \"" << url << "\", " << inlength << ", " << ((ret && outlength) ? *outlength : 1) << ") = \"" << ret << '"');
return ret;
}
void debug_curl_free(char* ptr)
{
curl_free(ptr);
Dout(dc::curl, "curl_free(0x" << std::hex << (size_t)ptr << std::dec << ")");
}
time_t debug_curl_getdate(char const* datestring, time_t* now)
{
time_t ret;
ret = curl_getdate(datestring, now);
Dout(dc::curl, "curl_getdate(\"" << datestring << "\", " << (now == NULL ? "NULL" : "<erroneous non-NULL value for 'now'>") << ") = " << ret);
return ret;
}
void debug_curl_global_cleanup(void)
{
curl_global_cleanup();
Dout(dc::curl, "curl_global_cleanup()");
}
CURLcode debug_curl_global_init(long flags)
{
CURLcode ret;
ret = curl_global_init(flags);
Dout(dc::curl, "curl_global_init(0x" << std::hex << flags << std::dec << ") = " << ret);
return ret;
}
CURLMcode debug_curl_multi_add_handle(CURLM* multi_handle, CURL* easy_handle)
{
CURLMcode ret;
ret = curl_multi_add_handle(multi_handle, easy_handle);
Dout(dc::curl, "curl_multi_add_handle(" << (AICURLM*)multi_handle << ", " << (AICURL*)easy_handle << ") = " << ret);
return ret;
}
CURLMcode debug_curl_multi_assign(CURLM* multi_handle, curl_socket_t sockfd, void* sockptr)
{
CURLMcode ret;
ret = curl_multi_assign(multi_handle, sockfd, sockptr);
Dout(dc::curl, "curl_multi_assign(" << (AICURLM*)multi_handle << ", " << Socket(sockfd) << ", " << sockptr << ") = " << ret);
return ret;
}
CURLMcode debug_curl_multi_cleanup(CURLM* multi_handle)
{
CURLMcode ret;
ret = curl_multi_cleanup(multi_handle);
Dout(dc::curl, "curl_multi_cleanup(" << (AICURLM*)multi_handle << ") = " << ret);
return ret;
}
CURLMsg* debug_curl_multi_info_read(CURLM* multi_handle, int* msgs_in_queue)
{
CURLMsg* ret;
ret = curl_multi_info_read(multi_handle, msgs_in_queue);
Dout(dc::curl, "curl_multi_info_read(" << (AICURLM*)multi_handle << ", {" << *msgs_in_queue << "}) = " << ret);
return ret;
}
CURLM* debug_curl_multi_init(void)
{
CURLM* ret;
ret = curl_multi_init();
Dout(dc::curl, "curl_multi_init() = " << (AICURLM*)ret);
return ret;
}
CURLMcode debug_curl_multi_remove_handle(CURLM* multi_handle, CURL* easy_handle)
{
CURLMcode ret;
ret = curl_multi_remove_handle(multi_handle, easy_handle);
Dout(dc::curl, "curl_multi_remove_handle(" << (AICURLM*)multi_handle << ", " << (AICURL*)easy_handle << ") = " << ret);
return ret;
}
CURLMcode debug_curl_multi_setopt(CURLM* multi_handle, CURLMoption option, ...)
{
CURLMcode ret;
va_list ap;
union param_type {
long along;
void* ptr;
curl_off_t offset;
} param;
unsigned int param_type = (option / 10000) * 10000;
va_start(ap, option);
switch (param_type)
{
case CURLOPTTYPE_LONG:
param.along = va_arg(ap, long);
break;
case CURLOPTTYPE_OBJECTPOINT:
case CURLOPTTYPE_FUNCTIONPOINT:
param.ptr = va_arg(ap, void*);
break;
case CURLOPTTYPE_OFF_T:
param.offset = va_arg(ap, curl_off_t);
break;
default:
std::cerr << "Extracting param_type failed; option = " << option << "; param_type = " << param_type << std::endl;
std::exit(EXIT_FAILURE);
}
va_end(ap);
switch (param_type)
{
case CURLOPTTYPE_LONG:
ret = curl_multi_setopt(multi_handle, option, param.along);
Dout(dc::curl, "curl_easy_setopt(" << (AICURLM*)multi_handle << ", " << option << ", " << param.along << "L) = " << ret);
break;
case CURLOPTTYPE_OBJECTPOINT:
ret = curl_multi_setopt(multi_handle, option, param.ptr);
Dout(dc::curl, "curl_easy_setopt(" << (AICURLM*)multi_handle << ", " << option << ", (object*)0x" << std::hex << (size_t)param.ptr << std::dec << ") = " << ret);
break;
case CURLOPTTYPE_FUNCTIONPOINT:
ret = curl_multi_setopt(multi_handle, option, param.ptr);
Dout(dc::curl, "curl_easy_setopt(" << (AICURLM*)multi_handle << ", " << option << ", (function*)0x" << std::hex << (size_t)param.ptr << std::dec << ") = " << ret);
break;
case CURLOPTTYPE_OFF_T:
ret = curl_multi_setopt(multi_handle, option, param.offset);
Dout(dc::curl, "curl_easy_setopt(" << (AICURLM*)multi_handle << ", " << option << ", (curl_off_t)" << param.offset << ") = " << ret);
break;
default: // Stop compiler complaining about no default.
break;
}
return ret;
}
CURLMcode debug_curl_multi_socket_action(CURLM* multi_handle, curl_socket_t sockfd, int ev_bitmask, int* running_handles)
{
CURLMcode ret;
ret = curl_multi_socket_action(multi_handle, sockfd, ev_bitmask, running_handles);
Dout(dc::curl, "curl_multi_socket_action(" << (AICURLM*)multi_handle << ", " << Socket(sockfd) <<
", " << EvBitmask(ev_bitmask) << ", {" << (ret == CURLM_OK ? *running_handles : 0) << "}) = " << ret);
return ret;
}
char const* debug_curl_multi_strerror(CURLMcode errornum)
{
char const* ret;
ret = curl_multi_strerror(errornum);
Dout(dc::curl, "curl_multi_strerror(" << errornum << ") = \"" << ret << '"');
return ret;
}
struct curl_slist* debug_curl_slist_append(struct curl_slist* list, char const* string)
{
struct curl_slist* ret;
ret = curl_slist_append(list, string);
Dout(dc::curl, "curl_slist_append((curl_slist)@0x" << std::hex << (size_t)list << std::dec << ", \"" << string << "\") = " << *ret);
return ret;
}
void debug_curl_slist_free_all(struct curl_slist* list)
{
curl_slist_free_all(list);
Dout(dc::curl, "curl_slist_free_all((curl_slist)@0x" << std::hex << (size_t)list << std::dec << ")");
}
char* debug_curl_unescape(char const* url, int length)
{
char* ret;
ret = curl_unescape(url, length);
Dout(dc::curl, "curl_unescape(\"" << url << "\", " << length << ") = \"" << ret << '"');
return ret;
}
char* debug_curl_version(void)
{
char* ret;
ret = curl_version();
Dout(dc::curl, "curl_version() = \"" << ret << '"');
return ret;
}
}
#else // DEBUG_CURLIO
int debug_libcurl_dummy; // I thought some OS didn't like empty source files.
#endif // DEBUG_CURLIO

View File

@@ -0,0 +1,89 @@
#ifndef DEBUG_LIBCURL
#define DEBUG_LIBCURL
#ifndef DEBUG_CURLIO
#error "Don't include debug_libcurl.h unless DEBUG_CURLIO is defined."
#endif
#ifndef CURLINFO_TYPEMASK
#error "<curl/curl.h> must be included before including debug_libcurl.h!"
#endif
#ifndef LLPREPROCESSOR_H
// CURL_STATICLIB is needed on windows namely, which is defined in llpreprocessor.h (but only on windows).
#error "llpreprocessor.h must be included before <curl/curl.h>."
#endif
extern "C" {
extern void debug_curl_easy_cleanup(CURL* handle);
extern CURL* debug_curl_easy_duphandle(CURL* handle);
extern char* debug_curl_easy_escape(CURL* curl, char* url, int length);
extern CURLcode debug_curl_easy_getinfo(CURL* curl, CURLINFO info, ...);
extern CURL* debug_curl_easy_init(void);
extern CURLcode debug_curl_easy_pause(CURL* handle, int bitmask);
extern CURLcode debug_curl_easy_perform(CURL* handle);
extern void debug_curl_easy_reset(CURL* handle);
extern CURLcode debug_curl_easy_setopt(CURL* handle, CURLoption option, ...);
extern char const* debug_curl_easy_strerror(CURLcode errornum);
extern char* debug_curl_easy_unescape(CURL* curl, char* url, int inlength, int* outlength);
extern void debug_curl_free(char* ptr);
extern time_t debug_curl_getdate(char const* datestring, time_t* now);
extern void debug_curl_global_cleanup(void);
extern CURLcode debug_curl_global_init(long flags);
extern CURLMcode debug_curl_multi_add_handle(CURLM* multi_handle, CURL* easy_handle);
extern CURLMcode debug_curl_multi_assign(CURLM* multi_handle, curl_socket_t sockfd, void* sockptr);
extern CURLMcode debug_curl_multi_cleanup(CURLM* multi_handle);
extern CURLMsg* debug_curl_multi_info_read(CURLM* multi_handle, int* msgs_in_queue);
extern CURLM* debug_curl_multi_init(void);
extern CURLMcode debug_curl_multi_remove_handle(CURLM* multi_handle, CURL* easy_handle);
extern CURLMcode debug_curl_multi_setopt(CURLM* multi_handle, CURLMoption option, ...);
extern CURLMcode debug_curl_multi_socket_action(CURLM* multi_handle, curl_socket_t sockfd, int ev_bitmask, int* running_handles);
extern char const* debug_curl_multi_strerror(CURLMcode errornum);
extern struct curl_slist* debug_curl_slist_append(struct curl_slist* list, char const* string);
extern void debug_curl_slist_free_all(struct curl_slist* list);
extern char* debug_curl_unescape(char const* url, int length);
extern char* debug_curl_version(void);
}
#ifndef COMPILING_DEBUG_LIBCURL_CC
#ifdef curl_easy_setopt
#undef curl_easy_setopt
#undef curl_easy_getinfo
#undef curl_multi_setopt
#endif
#define curl_easy_cleanup(handle) debug_curl_easy_cleanup(handle)
#define curl_easy_duphandle(handle) debug_curl_easy_duphandle(handle)
#define curl_easy_escape(curl, url, length) debug_curl_easy_escape(curl, url, length)
#define curl_easy_getinfo(curl, info, param) debug_curl_easy_getinfo(curl, info, param)
#define curl_easy_init() debug_curl_easy_init()
#define curl_easy_pause(handle, bitmask) debug_curl_easy_pause(handle, bitmask)
#define curl_easy_perform(handle) debug_curl_easy_perform(handle)
#define curl_easy_reset(handle) debug_curl_easy_reset(handle)
#define curl_easy_setopt(handle, option, param) debug_curl_easy_setopt(handle, option, param)
#define curl_easy_strerror(errornum) debug_curl_easy_strerror(errornum)
#define curl_easy_unescape(curl, url, inlength, outlength) debug_curl_easy_unescape(curl, url, inlength, outlength)
#define curl_free(ptr) debug_curl_free(ptr)
#define curl_getdate(datestring, now) debug_curl_getdate(datestring, now)
#define curl_global_cleanup() debug_curl_global_cleanup()
#define curl_global_init(flags) debug_curl_global_init(flags)
#define curl_multi_add_handle(multi_handle, easy_handle) debug_curl_multi_add_handle(multi_handle, easy_handle)
#define curl_multi_assign(multi_handle, sockfd, sockptr) debug_curl_multi_assign(multi_handle, sockfd, sockptr)
#define curl_multi_cleanup(multi_handle) debug_curl_multi_cleanup(multi_handle)
#define curl_multi_info_read(multi_handle, msgs_in_queue) debug_curl_multi_info_read(multi_handle, msgs_in_queue)
#define curl_multi_init() debug_curl_multi_init()
#define curl_multi_remove_handle(multi_handle, easy_handle) debug_curl_multi_remove_handle(multi_handle, easy_handle)
#define curl_multi_setopt(multi_handle, option, param) debug_curl_multi_setopt(multi_handle, option, param)
#define curl_multi_socket_action(multi_handle, sockfd, ev_bitmask, running_handles) debug_curl_multi_socket_action(multi_handle, sockfd, ev_bitmask, running_handles)
#define curl_multi_strerror(errornum) debug_curl_multi_strerror(errornum)
#define curl_slist_append(list, string) debug_curl_slist_append(list, string)
#define curl_slist_free_all(list) debug_curl_slist_free_all(list)
#define curl_unescape(url, length) debug_curl_unescape(url, length)
#define curl_version() debug_curl_version()
#endif // !COMPILING_DEBUG_LIBCURL_CC
#endif // DEBUG_LIBCURL

View File

@@ -28,7 +28,6 @@
#include "linden_common.h"
#include "llares.h"
#include "llscopedvolatileaprpool.h"
#include <ares_dns.h>
#include <ares_version.h>
@@ -38,6 +37,7 @@
#include "apr_poll.h"
#include "llapr.h"
#include "llaprpool.h"
#include "llareslistener.h"
#if defined(LL_WINDOWS)

View File

@@ -401,7 +401,7 @@ bool LLAssetStorage::findInStaticVFSAndInvokeCallback(const LLUUID& uuid, LLAsse
if (user_data)
{
// The *user_data should not be passed without a callback to clean it up.
llassert(callback != NULL)
llassert(callback != NULL);
}
BOOL exists = mStaticVFS->getExists(uuid, type);
@@ -441,7 +441,7 @@ void LLAssetStorage::getAssetData(const LLUUID uuid, LLAssetType::EType type, LL
if (user_data)
{
// The *user_data should not be passed without a callback to clean it up.
llassert(callback != NULL)
llassert(callback != NULL);
}
if (mShutDown)

View File

@@ -56,6 +56,7 @@
#include "llstl.h"
#include "lltransfermanager.h"
#include "llmodularmath.h"
#include "llpacketring.h"
const S32 PING_START_BLOCK = 3; // How many pings behind we have to be to consider ourself blocked.
const S32 PING_RELEASE_BLOCK = 2; // How many pings behind we have to be to consider ourself unblocked.
@@ -346,7 +347,7 @@ S32 LLCircuitData::resendUnackedPackets(const F64 now)
packetp->mBuffer[0] |= LL_RESENT_FLAG; // tag packet id as being a resend
gMessageSystem->mPacketRing.sendPacket(packetp->mSocket,
gMessageSystem->mPacketRing->sendPacket(packetp->mSocket,
(char *)packetp->mBuffer, packetp->mBufferLength,
packetp->mHost);

File diff suppressed because it is too large Load Diff

View File

@@ -1,383 +1,39 @@
/**
/**
* @file llcurl.h
* @author Zero / Donovan
* @date 2006-10-15
* @brief A wrapper around libcurl.
* @brief Drop in replacement for old llcurl.h.
*
* $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,
* 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
* 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$
* 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.
*
* 22/06/2012
* Initial version, written by Aleric Inglewood @ SL
*/
#ifndef LL_LLCURL_H
#define LL_LLCURL_H
#include "linden_common.h"
#include "aicurl.h"
#include <sstream>
#include <string>
#include <vector>
#include <boost/intrusive_ptr.hpp>
#include <curl/curl.h> // TODO: remove dependency
#include "llbuffer.h"
#include "lliopipe.h"
#include "llsd.h"
#include "llthread.h"
class LLMutex;
// For whatever reason, this is not typedef'd in curl.h
typedef size_t (*curl_header_callback)(void *ptr, size_t size, size_t nmemb, void *stream);
class LLCurl
{
LOG_CLASS(LLCurl);
public:
class Easy;
class Multi;
static bool sMultiThreaded;
struct TransferInfo
{
TransferInfo() : mSizeDownload(0.0), mTotalTime(0.0), mSpeedDownload(0.0) {}
F64 mSizeDownload;
F64 mTotalTime;
F64 mSpeedDownload;
};
class Responder
{
//LOG_CLASS(Responder);
public:
Responder();
virtual ~Responder();
/**
* @brief return true if the status code indicates success.
*/
static bool isGoodStatus(U32 status)
{
return((200 <= status) && (status < 300));
}
virtual void errorWithContent(
U32 status,
const std::string& reason,
const LLSD& content);
//< called by completed() on bad status
virtual void error(U32 status, const std::string& reason);
//< called by default error(status, reason, content)
virtual void result(const LLSD& content);
//< called by completed for good status codes.
virtual void completedRaw(
U32 status,
const std::string& reason,
const LLChannelDescriptors& channels,
const LLIOPipe::buffer_ptr_t& buffer);
/**< Override point for clients that may want to use this
class when the response is some other format besides LLSD
*/
virtual void completed(
U32 status,
const std::string& reason,
const LLSD& content);
/**< The default implemetnation calls
either:
* result(), or
* error()
*/
// Override to handle parsing of the header only. Note: this is the only place where the contents
// of the header can be parsed. In the ::completed call above only the body is contained in the LLSD.
virtual void completedHeader(U32 status, const std::string& reason, const LLSD& content);
// Used internally to set the url for debugging later.
void setURL(const std::string& url);
virtual bool followRedir()
{
return false;
}
public: /* but not really -- don't touch this */
U32 mReferenceCount;
private:
std::string mURL;
};
typedef boost::intrusive_ptr<Responder> ResponderPtr;
/**
* @ brief Set certificate authority file used to verify HTTPS certs.
*/
static void setCAFile(const std::string& file);
/**
* @ brief Set certificate authority path used to verify HTTPS certs.
*/
static void setCAPath(const std::string& path);
/**
* @ brief Return human-readable string describing libcurl version.
*/
static std::string getVersionString();
/**
* @ brief Get certificate authority file used to verify HTTPS certs.
*/
static const std::string& getCAFile() { return sCAFile; }
/**
* @ brief Get certificate authority path used to verify HTTPS certs.
*/
static const std::string& getCAPath() { return sCAPath; }
/**
* @ brief Initialize LLCurl class
*/
static void initClass(bool multi_threaded = false);
/**
* @ brief Cleanup LLCurl class
*/
static void cleanupClass();
/**
* @ brief curl error code -> string
*/
static std::string strerror(CURLcode errorcode);
// For OpenSSL callbacks
static std::vector<LLMutex*> sSSLMutex;
// OpenSSL callbacks
static void ssl_locking_callback(int mode, int type, const char *file, int line);
static unsigned long ssl_thread_id(void);
private:
static std::string sCAPath;
static std::string sCAFile;
static const unsigned int MAX_REDIRECTS;
};
class LLCurl::Easy
{
LOG_CLASS(Easy);
private:
Easy();
public:
static Easy* getEasy();
~Easy();
CURL* getCurlHandle() const { return mCurlEasyHandle; }
void setErrorBuffer();
void setCA();
void setopt(CURLoption option, S32 value);
// These assume the setter does not free value!
void setopt(CURLoption option, void* value);
void setopt(CURLoption option, char* value);
// Copies the string so that it is gauranteed to stick around
void setoptString(CURLoption option, const std::string& value);
void slist_append(const char* str);
void setHeaders();
U32 report(CURLcode);
void getTransferInfo(LLCurl::TransferInfo* info);
void prepRequest(const std::string& url, const std::vector<std::string>& headers, ResponderPtr, S32 time_out = 0, bool post = false);
const char* getErrorBuffer();
std::stringstream& getInput() { return mInput; }
std::stringstream& getHeaderOutput() { return mHeaderOutput; }
LLIOPipe::buffer_ptr_t& getOutput() { return mOutput; }
const LLChannelDescriptors& getChannels() { return mChannels; }
void resetState();
static CURL* allocEasyHandle();
static void releaseEasyHandle(CURL* handle);
private:
friend class LLCurl;
friend class LLCurl::Multi;
CURL* mCurlEasyHandle;
struct curl_slist* mHeaders;
std::stringstream mRequest;
LLChannelDescriptors mChannels;
LLIOPipe::buffer_ptr_t mOutput;
std::stringstream mInput;
std::stringstream mHeaderOutput;
char mErrorBuffer[CURL_ERROR_SIZE];
// Note: char*'s not strings since we pass pointers to curl
std::vector<char*> mStrings;
ResponderPtr mResponder;
static std::set<CURL*> sFreeHandles;
static std::set<CURL*> sActiveHandles;
static LLMutex* sHandleMutex;
static LLMutex* sMultiMutex;
};
class LLCurl::Multi : public LLThread
{
LOG_CLASS(Multi);
public:
typedef enum
{
PERFORM_STATE_READY=0,
PERFORM_STATE_PERFORMING=1,
PERFORM_STATE_COMPLETED=2
} ePerformState;
Multi();
~Multi();
Easy* allocEasy();
bool addEasy(Easy* easy);
void removeEasy(Easy* easy);
S32 process();
void perform();
void doPerform();
virtual void run();
CURLMsg* info_read(S32* msgs_in_queue);
S32 mQueued;
S32 mErrorCount;
S32 mPerformState;
LLCondition* mSignal;
bool mQuitting;
bool mThreaded;
private:
void easyFree(Easy*);
CURLM* mCurlMultiHandle;
typedef std::set<Easy*> easy_active_list_t;
easy_active_list_t mEasyActiveList;
typedef std::map<CURL*, Easy*> easy_active_map_t;
easy_active_map_t mEasyActiveMap;
typedef std::set<Easy*> easy_free_list_t;
easy_free_list_t mEasyFreeList;
};
// DONT UNCOMMENT BREAKS GCC47
//namespace boost
//{
void intrusive_ptr_add_ref(LLCurl::Responder* p);
void intrusive_ptr_release(LLCurl::Responder* p);
//};
class LLCurlRequest
{
public:
typedef std::vector<std::string> headers_t;
LLCurlRequest();
~LLCurlRequest();
void get(const std::string& url, LLCurl::ResponderPtr responder);
bool getByteRange(const std::string& url, const headers_t& headers, S32 offset, S32 length, LLCurl::ResponderPtr responder);
bool post(const std::string& url, const headers_t& headers, const LLSD& data, LLCurl::ResponderPtr responder, S32 time_out = 0);
bool post(const std::string& url, const headers_t& headers, const std::string& data, LLCurl::ResponderPtr responder, S32 time_out = 0);
S32 process();
S32 getQueued();
private:
void addMulti();
LLCurl::Easy* allocEasy();
bool addEasy(LLCurl::Easy* easy);
private:
typedef std::set<LLCurl::Multi*> curlmulti_set_t;
curlmulti_set_t mMultiSet;
LLCurl::Multi* mActiveMulti;
S32 mActiveRequestCount;
BOOL mProcessing;
U32 mThreadID; // debug
};
class LLCurlEasyRequest
{
public:
LLCurlEasyRequest();
~LLCurlEasyRequest();
void setopt(CURLoption option, S32 value);
void setoptString(CURLoption option, const std::string& value);
void setPost(char* postdata, S32 size);
void setHeaderCallback(curl_header_callback callback, void* userdata);
void setWriteCallback(curl_write_callback callback, void* userdata);
void setReadCallback(curl_read_callback callback, void* userdata);
void setSSLCtxCallback(curl_ssl_ctx_callback callback, void* userdata);
void slist_append(const char* str);
void sendRequest(const std::string& url);
void requestComplete();
void perform();
bool getResult(CURLcode* result, LLCurl::TransferInfo* info = NULL);
std::string getErrorString();
LLCurl::Easy* getEasy() const { return mEasy; }
private:
CURLMsg* info_read(S32* queue, LLCurl::TransferInfo* info);
private:
LLCurl::Multi* mMulti;
LLCurl::Easy* mEasy;
bool mRequestSent;
bool mResultReturned;
};
// Provide access to LLCurl free functions outside of llcurl.cpp without polluting the global namespace.
namespace LLCurlFF
{
void check_easy_code(CURLcode code);
void check_multi_code(CURLMcode code);
}
// Map interface to old LLCurl names so this can be used as a drop-in replacement.
namespace LLCurl = AICurlInterface;
#endif // LL_LLCURL_H

View File

@@ -25,7 +25,6 @@
*/
#include "linden_common.h"
#include "llhttpclient.h"
#include "llassetstorage.h"
@@ -38,10 +37,8 @@
#include "lluri.h"
#include "message.h"
#include <curl/curl.h>
const F32 HTTP_REQUEST_EXPIRY_SECS = 60.0f;
////////////////////////////////////////////////////////////////////////////
// Responder class moved to LLCurl
@@ -122,7 +119,7 @@ namespace
{
public:
RawInjector(const U8* data, S32 size) : mData(data), mSize(size) {}
virtual ~RawInjector() {delete mData;}
virtual ~RawInjector() {delete [] mData;}
const char* contentType() { return "application/octet-stream"; }
@@ -156,9 +153,9 @@ namespace
if(fstream.is_open())
{
fstream.seekg(0, std::ios::end);
U32 fileSize = (U32)fstream.tellg();
U32 fileSize = fstream.tellg();
fstream.seekg(0, std::ios::beg);
std::vector<char> fileBuffer(fileSize); //Mem leak fix'd
std::vector<char> fileBuffer(fileSize);
fstream.read(&fileBuffer[0], fileSize);
ostream.write(&fileBuffer[0], fileSize);
fstream.close();
@@ -209,20 +206,45 @@ static void request(
const F32 timeout = HTTP_REQUEST_EXPIRY_SECS,
const LLSD& headers = LLSD())
{
if (responder)
{
// For possible debug output from within the responder.
responder->setURL(url);
}
if (!LLHTTPClient::hasPump())
{
responder->completed(U32_MAX, "No pump", LLSD());
responder->fatalError("No pump");
return;
}
LLPumpIO::chain_t chain;
LLURLRequest* req = new LLURLRequest(method, url);
LLURLRequest* req;
try
{
req = new LLURLRequest(method, url);
}
catch(AICurlNoEasyHandle& error)
{
llwarns << "Failed to create LLURLRequest: " << error.what() << llendl;
// This is what the old LL code did: no recovery whatsoever (but also no leaks or crash).
return ;
}
req->checkRootCertificate(true);
// Insert custom headers is the caller sent any
if (headers.isMap())
{
lldebugs << LLURLRequest::actionAsVerb(method) << " " << url << " "
<< headers << llendl;
// Insert custom headers if the caller sent any
if (headers.isMap())
{
if (headers.has("Cookie"))
{
req->allowCookies();
}
LLSD::map_const_iterator iter = headers.beginMap();
LLSD::map_const_iterator end = headers.endMap();
@@ -257,11 +279,6 @@ static void request(
}
}
if (responder)
{
responder->setURL(url);
}
req->setCallback(new LLHTTPClientURLAdaptor(responder));
if (method == LLURLRequest::HTTP_POST && gMessageSystem)
@@ -308,7 +325,7 @@ void LLHTTPClient::getByteRange(
std::string range = llformat("bytes=%d-%d", offset, offset+bytes-1);
headers["Range"] = range;
}
request(url,LLURLRequest::HTTP_GET, NULL, responder, timeout, headers);
request(url, LLURLRequest::HTTP_GET, NULL, responder, timeout, headers);
}
void LLHTTPClient::head(
@@ -347,12 +364,12 @@ class LLHTTPBuffer
public:
LLHTTPBuffer() { }
static size_t curl_write( void *ptr, size_t size, size_t nmemb, void *user_data)
static size_t curl_write(char* ptr, size_t size, size_t nmemb, void* user_data)
{
LLHTTPBuffer* self = (LLHTTPBuffer*)user_data;
size_t bytes = (size * nmemb);
self->mBuffer.append((char*)ptr,bytes);
self->mBuffer.append(ptr,bytes);
return nmemb;
}
@@ -403,99 +420,82 @@ static LLSD blocking_request(
)
{
lldebugs << "blockingRequest of " << url << llendl;
char curl_error_buffer[CURL_ERROR_SIZE] = "\0";
CURL* curlp = curl_easy_init();
LLHTTPBuffer http_buffer;
std::string body_str;
// other request method checks root cert first, we skip?
// * Set curl handle options
curl_easy_setopt(curlp, CURLOPT_NOSIGNAL, 1); // don't use SIGALRM for timeouts
curl_easy_setopt(curlp, CURLOPT_TIMEOUT, timeout); // seconds, see warning at top of function.
curl_easy_setopt(curlp, CURLOPT_WRITEFUNCTION, LLHTTPBuffer::curl_write);
curl_easy_setopt(curlp, CURLOPT_WRITEDATA, &http_buffer);
curl_easy_setopt(curlp, CURLOPT_URL, url.c_str());
curl_easy_setopt(curlp, CURLOPT_ERRORBUFFER, curl_error_buffer);
// * Setup headers (don't forget to free them after the call!)
curl_slist* headers_list = NULL;
if (headers.isMap())
S32 http_status = 499;
LLSD response = LLSD::emptyMap();
try
{
LLSD::map_const_iterator iter = headers.beginMap();
LLSD::map_const_iterator end = headers.endMap();
for (; iter != end; ++iter)
AICurlEasyRequest easy_request(false);
AICurlEasyRequest_wat curlEasyRequest_w(*easy_request);
LLHTTPBuffer http_buffer;
// * Set curl handle options
curlEasyRequest_w->setopt(CURLOPT_TIMEOUT, (long)timeout); // seconds, see warning at top of function.
curlEasyRequest_w->setWriteCallback(&LLHTTPBuffer::curl_write, &http_buffer);
// * Setup headers.
if (headers.isMap())
{
std::ostringstream header;
header << iter->first << ": " << iter->second.asString() ;
lldebugs << "header = " << header.str() << llendl;
headers_list = curl_slist_append(headers_list, header.str().c_str());
LLSD::map_const_iterator iter = headers.beginMap();
LLSD::map_const_iterator end = headers.endMap();
for (; iter != end; ++iter)
{
std::ostringstream header;
header << iter->first << ": " << iter->second.asString() ;
lldebugs << "header = " << header.str() << llendl;
curlEasyRequest_w->addHeader(header.str().c_str());
}
}
// Needs to stay alive until after the call to perform().
std::ostringstream ostr;
// * Setup specific method / "verb" for the URI (currently only GET and POST supported + poppy)
if (method == LLURLRequest::HTTP_GET)
{
curlEasyRequest_w->setopt(CURLOPT_HTTPGET, 1);
}
else if (method == LLURLRequest::HTTP_POST)
{
//copied from PHP libs, correct?
curlEasyRequest_w->addHeader("Content-Type: application/llsd+xml");
LLSDSerialize::toXML(body, ostr);
curlEasyRequest_w->setPost(ostr.str().c_str(), ostr.str().length());
}
// * Do the action using curl, handle results
curlEasyRequest_w->addHeader("Accept: application/llsd+xml");
curlEasyRequest_w->finalizeRequest(url);
S32 curl_success = curlEasyRequest_w->perform();
curlEasyRequest_w->getinfo(CURLINFO_RESPONSE_CODE, &http_status);
// if we get a non-404 and it's not a 200 OR maybe it is but you have error bits,
if ( http_status != 404 && (http_status != 200 || curl_success != 0) )
{
// We expect 404s, don't spam for them.
llwarns << "CURL REQ URL: " << url << llendl;
llwarns << "CURL REQ METHOD TYPE: " << method << llendl;
llwarns << "CURL REQ HEADERS: " << headers.asString() << llendl;
llwarns << "CURL REQ BODY: " << ostr.str() << llendl;
llwarns << "CURL HTTP_STATUS: " << http_status << llendl;
llwarns << "CURL ERROR: " << curlEasyRequest_w->getErrorString() << llendl;
llwarns << "CURL ERROR BODY: " << http_buffer.asString() << llendl;
response["body"] = http_buffer.asString();
}
else
{
response["body"] = http_buffer.asLLSD();
lldebugs << "CURL response: " << http_buffer.asString() << llendl;
}
}
// * Setup specific method / "verb" for the URI (currently only GET and POST supported + poppy)
if (method == LLURLRequest::HTTP_GET)
catch(AICurlNoEasyHandle const& error)
{
curl_easy_setopt(curlp, CURLOPT_HTTPGET, 1);
}
else if (method == LLURLRequest::HTTP_POST)
{
curl_easy_setopt(curlp, CURLOPT_POST, 1);
//serialize to ostr then copy to str - need to because ostr ptr is unstable :(
std::ostringstream ostr;
LLSDSerialize::toXML(body, ostr);
body_str = ostr.str();
curl_easy_setopt(curlp, CURLOPT_POSTFIELDS, body_str.c_str());
//copied from PHP libs, correct?
headers_list = curl_slist_append(headers_list, "Content-Type: application/llsd+xml");
// copied from llurlrequest.cpp
// it appears that apache2.2.3 or django in etch is busted. If
// we do not clear the expect header, we get a 500. May be
// limited to django/mod_wsgi.
headers_list = curl_slist_append(headers_list, "Expect:");
}
// * Do the action using curl, handle results
lldebugs << "HTTP body: " << body_str << llendl;
headers_list = curl_slist_append(headers_list, "Accept: application/llsd+xml");
CURLcode curl_result = curl_easy_setopt(curlp, CURLOPT_HTTPHEADER, headers_list);
if ( curl_result != CURLE_OK )
{
llinfos << "Curl is hosed - can't add headers" << llendl;
response["body"] = error.what();
}
LLSD response = LLSD::emptyMap();
S32 curl_success = curl_easy_perform(curlp);
S32 http_status = 499;
curl_easy_getinfo(curlp, CURLINFO_RESPONSE_CODE, &http_status);
response["status"] = http_status;
// if we get a non-404 and it's not a 200 OR maybe it is but you have error bits,
if ( http_status != 404 && (http_status != 200 || curl_success != 0) )
{
// We expect 404s, don't spam for them.
llwarns << "CURL REQ URL: " << url << llendl;
llwarns << "CURL REQ METHOD TYPE: " << method << llendl;
llwarns << "CURL REQ HEADERS: " << headers.asString() << llendl;
llwarns << "CURL REQ BODY: " << body_str << llendl;
llwarns << "CURL HTTP_STATUS: " << http_status << llendl;
llwarns << "CURL ERROR: " << curl_error_buffer << llendl;
llwarns << "CURL ERROR BODY: " << http_buffer.asString() << llendl;
response["body"] = http_buffer.asString();
}
else
{
response["body"] = http_buffer.asLLSD();
lldebugs << "CURL response: " << http_buffer.asString() << llendl;
}
if(headers_list)
{ // free the header list
curl_slist_free_all(headers_list);
}
// * Cleanup
curl_easy_cleanup(curlp);
return response;
}
@@ -595,7 +595,8 @@ bool LLHTTPClient::hasPump()
return theClientPump != NULL;
}
LLPumpIO &LLHTTPClient::getPump()
//static
LLPumpIO& LLHTTPClient::getPump()
{
return *theClientPump;
}

View File

@@ -34,7 +34,6 @@
#include <string>
#include <boost/intrusive_ptr.hpp>
#include "llassettype.h"
#include "llcurl.h"
#include "lliopipe.h"
@@ -56,6 +55,9 @@ public:
typedef LLCurl::Responder Responder;
typedef LLCurl::ResponderPtr ResponderPtr;
// The default actually already ignores responses.
class ResponderIgnore : public Responder { };
/** @name non-blocking API */
//@{
static void head(

View File

@@ -76,7 +76,14 @@ LLIOPipe::~LLIOPipe()
}
//virtual
bool LLIOPipe::isValid()
bool LLIOPipe::hasExpiration(void) const
{
// LLIOPipe::hasNotExpired always returns true.
return false;
}
//virtual
bool LLIOPipe::hasNotExpired(void) const
{
return true ;
}

View File

@@ -149,7 +149,7 @@ public:
// The connection was lost.
STATUS_LOST_CONNECTION = -5,
// The totoal process time has exceeded the timeout.
// The total process time has exceeded the timeout.
STATUS_EXPIRED = -6,
// Keep track of the count of codes here.
@@ -231,7 +231,16 @@ public:
*/
virtual ~LLIOPipe();
virtual bool isValid() ;
/**
* @brief External expiration facility.
*
* If hasExpiration() returns true, then we need to check hasNotExpired()
* to see if the LLIOPipe is still valid. In the legacy LL code the
* latter was called isValid() and was overloaded for two purposes:
* either expiration or failure to initialize.
*/
virtual bool hasExpiration(void) const;
virtual bool hasNotExpired(void) const;
protected:
/**

View File

@@ -32,7 +32,7 @@
#include "llhost.h"
#include "llpacketbuffer.h"
#include "llproxy.h"
//#include "llproxy.h"
#include "llthrottle.h"
#include "net.h"

View File

@@ -29,7 +29,6 @@
#include "llproxy.h"
#include <string>
#include <curl/curl.h>
#include "llapr.h"
#include "llcurl.h"
@@ -47,23 +46,22 @@ static apr_status_t tcp_blocking_handshake(LLSocket::ptr_t handle, char * dataou
static LLSocket::ptr_t tcp_open_channel(LLHost host); // Open a TCP channel to a given host
static void tcp_close_channel(LLSocket::ptr_t* handle_ptr); // Close an open TCP channel
LLProxy::LLProxy():
mHTTPProxyEnabled(false),
mProxyMutex(),
mUDPProxy(),
mTCPProxy(),
mHTTPProxy(),
ProxyShared::ProxyShared(void):
mProxyType(LLPROXY_SOCKS),
mAuthMethodSelected(METHOD_NOAUTH),
mSocksUsername(),
mSocksPassword()
mAuthMethodSelected(METHOD_NOAUTH)
{
}
LLProxy::LLProxy():
mHTTPProxyEnabled(false)
{
}
LLProxy::~LLProxy()
{
stopSOCKSProxy();
disableHTTPProxy();
Shared_wat shared_w(mShared);
disableHTTPProxy(shared_w);
}
/**
@@ -78,15 +76,18 @@ S32 LLProxy::proxyHandshake(LLHost proxy)
{
S32 result;
Unshared_rat unshared_r(mUnshared);
Shared_rat shared_r(mShared);
/* SOCKS 5 Auth request */
socks_auth_request_t socks_auth_request;
socks_auth_response_t socks_auth_response;
socks_auth_request.version = SOCKS_VERSION; // SOCKS version 5
socks_auth_request.num_methods = 1; // Sending 1 method.
socks_auth_request.methods = getSelectedAuthMethod(); // Send only the selected method.
socks_auth_request.methods = getSelectedAuthMethod(shared_r); // Send only the selected method.
result = tcp_blocking_handshake(mProxyControlChannel,
result = tcp_blocking_handshake(unshared_r->mProxyControlChannel,
static_cast<char*>(static_cast<void*>(&socks_auth_request)),
sizeof(socks_auth_request),
static_cast<char*>(static_cast<void*>(&socks_auth_response)),
@@ -109,8 +110,8 @@ S32 LLProxy::proxyHandshake(LLHost proxy)
if (socks_auth_response.method == METHOD_PASSWORD)
{
// The server has requested a username/password combination
std::string socks_username(getSocksUser());
std::string socks_password(getSocksPwd());
std::string socks_username(getSocksUser(shared_r));
std::string socks_password(getSocksPwd(shared_r));
U32 request_size = socks_username.size() + socks_password.size() + 3;
char * password_auth = new char[request_size];
password_auth[0] = 0x01;
@@ -121,7 +122,7 @@ S32 LLProxy::proxyHandshake(LLHost proxy)
authmethod_password_reply_t password_reply;
result = tcp_blocking_handshake(mProxyControlChannel,
result = tcp_blocking_handshake(unshared_r->mProxyControlChannel,
password_auth,
request_size,
static_cast<char*>(static_cast<void*>(&password_reply)),
@@ -157,7 +158,7 @@ S32 LLProxy::proxyHandshake(LLHost proxy)
// "If the client is not in possession of the information at the time of the UDP ASSOCIATE,
// the client MUST use a port number and address of all zeros. RFC 1928"
result = tcp_blocking_handshake(mProxyControlChannel,
result = tcp_blocking_handshake(unshared_r->mProxyControlChannel,
static_cast<char*>(static_cast<void*>(&connect_request)),
sizeof(connect_request),
static_cast<char*>(static_cast<void*>(&connect_reply)),
@@ -176,10 +177,14 @@ S32 LLProxy::proxyHandshake(LLHost proxy)
return SOCKS_UDP_FWD_NOT_GRANTED;
}
mUDPProxy.setPort(ntohs(connect_reply.port)); // reply port is in network byte order
mUDPProxy.setAddress(proxy.getAddress());
{
// Write access type and read access type are really the same, so unshared_w must be simply a reference.
Unshared_wat& unshared_w = unshared_r;
unshared_w->mUDPProxy.setPort(ntohs(connect_reply.port)); // reply port is in network byte order
unshared_w->mUDPProxy.setAddress(proxy.getAddress());
}
// The connection was successful. We now have the UDP port to send requests that need forwarding to.
LL_INFOS("Proxy") << "SOCKS 5 UDP proxy connected on " << mUDPProxy << LL_ENDL;
LL_INFOS("Proxy") << "SOCKS 5 UDP proxy connected on " << unshared_r->mUDPProxy << LL_ENDL;
return SOCKS_OK;
}
@@ -197,9 +202,11 @@ S32 LLProxy::proxyHandshake(LLHost proxy)
*/
S32 LLProxy::startSOCKSProxy(LLHost host)
{
Unshared_wat unshared_w(mUnshared);
if (host.isOk())
{
mTCPProxy = host;
unshared_w->mTCPProxy = host;
}
else
{
@@ -209,13 +216,13 @@ S32 LLProxy::startSOCKSProxy(LLHost host)
// Close any running SOCKS connection.
stopSOCKSProxy();
mProxyControlChannel = tcp_open_channel(mTCPProxy);
if (!mProxyControlChannel)
unshared_w->mProxyControlChannel = tcp_open_channel(unshared_w->mTCPProxy);
if (!unshared_w->mProxyControlChannel)
{
return SOCKS_HOST_CONNECT_FAILED;
}
S32 status = proxyHandshake(mTCPProxy);
S32 status = proxyHandshake(unshared_w->mTCPProxy);
if (status != SOCKS_OK)
{
@@ -246,14 +253,16 @@ void LLProxy::stopSOCKSProxy()
// then we must shut down any HTTP proxy operations. But it is allowable if web
// proxy is being used to continue proxying HTTP.
if (LLPROXY_SOCKS == getHTTPProxyType())
Shared_rat shared_r(mShared);
if (LLPROXY_SOCKS == getHTTPProxyType(shared_r))
{
disableHTTPProxy();
Shared_wat shared_w(shared_r);
disableHTTPProxy(shared_w);
}
if (mProxyControlChannel)
Unshared_wat unshared_w(mUnshared);
if (unshared_w->mProxyControlChannel)
{
tcp_close_channel(&mProxyControlChannel);
tcp_close_channel(&unshared_w->mProxyControlChannel);
}
}
@@ -262,9 +271,7 @@ void LLProxy::stopSOCKSProxy()
*/
void LLProxy::setAuthNone()
{
LLMutexLock lock(&mProxyMutex);
mAuthMethodSelected = METHOD_NOAUTH;
Shared_wat(mShared)->mAuthMethodSelected = METHOD_NOAUTH;
}
/**
@@ -288,11 +295,10 @@ bool LLProxy::setAuthPassword(const std::string &username, const std::string &pa
return false;
}
LLMutexLock lock(&mProxyMutex);
mAuthMethodSelected = METHOD_PASSWORD;
mSocksUsername = username;
mSocksPassword = password;
Shared_wat shared_w(mShared);
shared_w->mAuthMethodSelected = METHOD_PASSWORD;
shared_w->mSocksUsername = username;
shared_w->mSocksPassword = password;
return true;
}
@@ -314,12 +320,10 @@ bool LLProxy::enableHTTPProxy(LLHost httpHost, LLHttpProxyType type)
return false;
}
LLMutexLock lock(&mProxyMutex);
mHTTPProxy = httpHost;
mProxyType = type;
Shared_wat shared_w(mShared);
mHTTPProxyEnabled = true;
shared_w->mHTTPProxy = httpHost;
shared_w->mProxyType = type;
return true;
}
@@ -335,9 +339,8 @@ bool LLProxy::enableHTTPProxy()
{
bool ok;
LLMutexLock lock(&mProxyMutex);
ok = (mHTTPProxy.isOk());
Shared_rat shared_r(mShared);
ok = (shared_r->mHTTPProxy.isOk());
if (ok)
{
mHTTPProxyEnabled = true;
@@ -346,54 +349,6 @@ bool LLProxy::enableHTTPProxy()
return ok;
}
/**
* @brief Disable the HTTP proxy.
*/
void LLProxy::disableHTTPProxy()
{
LLMutexLock lock(&mProxyMutex);
mHTTPProxyEnabled = false;
}
/**
* @brief Get the currently selected HTTP proxy type
*/
LLHttpProxyType LLProxy::getHTTPProxyType() const
{
LLMutexLock lock(&mProxyMutex);
return mProxyType;
}
/**
* @brief Get the SOCKS 5 password.
*/
std::string LLProxy::getSocksPwd() const
{
LLMutexLock lock(&mProxyMutex);
return mSocksPassword;
}
/**
* @brief Get the SOCKS 5 username.
*/
std::string LLProxy::getSocksUser() const
{
LLMutexLock lock(&mProxyMutex);
return mSocksUsername;
}
/**
* @brief Get the currently selected SOCKS 5 authentication method.
*
* @return Returns either none or password.
*/
LLSocks5AuthType LLProxy::getSelectedAuthMethod() const
{
LLMutexLock lock(&mProxyMutex);
return mAuthMethodSelected;
}
/**
* @brief Stop the LLProxy and make certain that any APR pools and classes are deleted before terminating APR.
*
@@ -406,57 +361,6 @@ void LLProxy::cleanupClass()
deleteSingleton();
}
void LLProxy::applyProxySettings(LLCurlEasyRequest* handle)
{
applyProxySettings(handle->getEasy());
}
void LLProxy::applyProxySettings(LLCurl::Easy* handle)
{
applyProxySettings(handle->getCurlHandle());
}
/**
* @brief Apply proxy settings to a CuRL request if an HTTP proxy is enabled.
*
* This method has been designed to be safe to call from
* any thread in the viewer. This allows requests in the
* texture fetch thread to be aware of the proxy settings.
* When the HTTP proxy is enabled, the proxy mutex will
* be locked every time this method is called.
*
* @param handle A pointer to a valid CURL request, before it has been performed.
*/
void LLProxy::applyProxySettings(CURL* handle)
{
// Do a faster unlocked check to see if we are supposed to proxy.
if (mHTTPProxyEnabled)
{
// We think we should proxy, lock the proxy mutex.
LLMutexLock lock(&mProxyMutex);
// Now test again to verify that the proxy wasn't disabled between the first check and the lock.
if (mHTTPProxyEnabled)
{
LLCurlFF::check_easy_code(curl_easy_setopt(handle, CURLOPT_PROXY, mHTTPProxy.getIPString().c_str()));
LLCurlFF::check_easy_code(curl_easy_setopt(handle, CURLOPT_PROXYPORT, mHTTPProxy.getPort()));
if (mProxyType == LLPROXY_SOCKS)
{
LLCurlFF::check_easy_code(curl_easy_setopt(handle, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5));
if (mAuthMethodSelected == METHOD_PASSWORD)
{
std::string auth_string = mSocksUsername + ":" + mSocksPassword;
LLCurlFF::check_easy_code(curl_easy_setopt(handle, CURLOPT_PROXYUSERPWD, auth_string.c_str()));
}
}
else
{
LLCurlFF::check_easy_code(curl_easy_setopt(handle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP));
}
}
}
}
/**
* @brief Send one TCP packet and receive one in return.
*

View File

@@ -33,6 +33,7 @@
#include "llmemory.h"
#include "llsingleton.h"
#include "llthread.h"
#include "aithreadsafe.h"
#include <string>
// SOCKS error codes returned from the StartProxy method
@@ -206,41 +207,92 @@ enum LLSocks5AuthType
* The implementation of HTTP proxying is handled by libcurl. LLProxy
* is responsible for managing the HTTP proxy options and provides a
* thread-safe method to apply those options to a curl request
* (LLProxy::applyProxySettings()). This method is overloaded
* to accommodate the various abstraction libcurl layers that exist
* throughout the viewer (LLCurlEasyRequest, LLCurl::Easy, and CURL).
*
* If you are working with LLCurl or LLCurlEasyRequest objects,
* the configured proxy settings will be applied in the constructors
* of those request handles. If you are working with CURL objects
* directly, you will need to pass the handle of the request to
* applyProxySettings() before issuing the request.
* (LLProxy::applyProxySettings()).
*
* To ensure thread safety, all LLProxy members that relate to the HTTP
* proxy require the LLProxyMutex to be locked before accessing.
*/
struct ProxyUnshared
{
/*###########################################################################################
MEMBERS READ AND WRITTEN ONLY IN THE MAIN THREAD.
###########################################################################################*/
// UDP proxy address and port
LLHost mUDPProxy;
// TCP proxy control channel address and port
LLHost mTCPProxy;
// socket handle to proxy TCP control channel
LLSocket::ptr_t mProxyControlChannel;
/*###########################################################################################
END OF UNSHARED MEMBERS
###########################################################################################*/
};
struct ProxyShared
{
ProxyShared(void);
/*###########################################################################################
MEMBERS WRITTEN IN MAIN THREAD AND READ IN ANY THREAD.
###########################################################################################*/
// HTTP proxy address and port
LLHost mHTTPProxy;
// Currently selected HTTP proxy type. Can be web or SOCKS.
LLHttpProxyType mProxyType;
// SOCKS 5 selected authentication method.
LLSocks5AuthType mAuthMethodSelected;
// SOCKS 5 username
std::string mSocksUsername;
// SOCKS 5 password
std::string mSocksPassword;
/*###########################################################################################
END OF SHARED MEMBERS
###########################################################################################*/
};
class LLProxy: public LLSingleton<LLProxy>
{
LOG_CLASS(LLProxy);
public:
typedef AISTAccessConst<ProxyUnshared> Unshared_crat; // Constant Read Access Type for Unshared (cannot be converted to write access).
typedef AISTAccess<ProxyUnshared> Unshared_rat; // Read Access Type for Unshared (same as write access type, since we don't lock at all).
typedef AISTAccess<ProxyUnshared> Unshared_wat; // Write Access Type, for Unshared.
typedef AIReadAccessConst<ProxyShared> Shared_crat; // Constant Read Access Type for Shared (cannot be converted to write access).
typedef AIReadAccess<ProxyShared> Shared_rat; // Read Access Type for Shared.
typedef AIWriteAccess<ProxyShared> Shared_wat; // Write Access Type for Shared.
/*###########################################################################################
METHODS THAT DO NOT LOCK mProxyMutex!
Public methods that only access variables not shared between threads.
###########################################################################################*/
// Constructor, cannot have parameters due to LLSingleton parent class. Call from main thread only.
LLProxy();
// Static check for enabled status for UDP packets. Call from main thread only.
static bool isSOCKSProxyEnabled() { return sUDPProxyEnabled; }
// Static check for enabled status for UDP packets. Called from main thread only.
static bool isSOCKSProxyEnabled(void) { llassert(is_main_thread()); return sUDPProxyEnabled; }
// Get the UDP proxy address and port. Call from main thread only.
LLHost getUDPProxy() const { return mUDPProxy; }
// Get the UDP proxy address and port. Called from main thread only.
LLHost getUDPProxy(void) const { return Unshared_crat(mUnshared)->mUDPProxy; }
/*###########################################################################################
END OF NON-LOCKING METHODS
End of methods that only access variables not shared between threads.
###########################################################################################*/
// Return true if there is a good chance that the HTTP proxy is currently enabled.
bool HTTPProxyEnabled(void) const { return mHTTPProxyEnabled; }
/*###########################################################################################
METHODS THAT LOCK mProxyMutex! DO NOT CALL WHILE mProxyMutex IS LOCKED!
Public methods that access variables shared between threads.
###########################################################################################*/
// Destructor, closes open connections. Do not call directly, use cleanupClass().
~LLProxy();
@@ -251,9 +303,7 @@ public:
// Apply the current proxy settings to a curl request. Doesn't do anything if mHTTPProxyEnabled is false.
// Safe to call from any thread.
void applyProxySettings(CURL* handle);
void applyProxySettings(LLCurl::Easy* handle);
void applyProxySettings(LLCurlEasyRequest* handle);
void applyProxySettings(AICurlEasyRequest_wat const& curlEasyRequest_w);
// Start a connection to the SOCKS 5 proxy. Call from main thread only.
S32 startSOCKSProxy(LLHost host);
@@ -273,30 +323,37 @@ public:
bool enableHTTPProxy();
// Stop proxying HTTP packets. Call from main thread only.
void disableHTTPProxy();
// Note that this needs shared_w to be passed because we want the shared members to be locked when this is reset to false.
void disableHTTPProxy(Shared_wat const& shared_w) { mHTTPProxyEnabled = false; }
void disableHTTPProxy(void) { disableHTTPProxy(Shared_wat(mShared)); }
// Get the currently selected HTTP proxy address and port
LLHost const& getHTTPProxy(Shared_crat const& shared_r) const { return shared_r->mHTTPProxy; }
// Get the currently selected HTTP proxy type
LLHttpProxyType getHTTPProxyType(Shared_crat const& shared_r) const { return shared_r->mProxyType; }
// Get the currently selected auth method.
LLSocks5AuthType getSelectedAuthMethod(Shared_crat const& shared_r) const { return shared_r->mAuthMethodSelected; }
// SOCKS 5 username and password accessors.
std::string getSocksUser(Shared_crat const& shared_r) const { return shared_r->mSocksUsername; }
std::string getSocksPwd(Shared_crat const& shared_r) const { return shared_r->mSocksPassword; }
/*###########################################################################################
END OF LOCKING METHODS
End of methods that access variables shared between threads.
###########################################################################################*/
private:
/*###########################################################################################
METHODS THAT LOCK mProxyMutex! DO NOT CALL WHILE mProxyMutex IS LOCKED!
Private methods that access variables shared between threads.
###########################################################################################*/
// Perform a SOCKS 5 authentication and UDP association with the proxy server.
S32 proxyHandshake(LLHost proxy);
// Get the currently selected auth method.
LLSocks5AuthType getSelectedAuthMethod() const;
// Get the currently selected HTTP proxy type
LLHttpProxyType getHTTPProxyType() const;
std::string getSocksPwd() const;
std::string getSocksUser() const;
/*###########################################################################################
END OF LOCKING METHODS
End of methods that access variables shared between threads.
###########################################################################################*/
private:
@@ -304,49 +361,16 @@ private:
// Instead use enableHTTPProxy() and disableHTTPProxy() instead.
mutable LLAtomic32<bool> mHTTPProxyEnabled;
// Mutex to protect shared members in non-main thread calls to applyProxySettings().
mutable LLMutex mProxyMutex;
/*###########################################################################################
MEMBERS READ AND WRITTEN ONLY IN THE MAIN THREAD. DO NOT SHARE!
###########################################################################################*/
// Is the UDP proxy enabled?
static bool sUDPProxyEnabled;
// UDP proxy address and port
LLHost mUDPProxy;
// TCP proxy control channel address and port
LLHost mTCPProxy;
AIThreadSafeSingleThreadDC<ProxyUnshared> mUnshared;
AIThreadSafeDC<ProxyShared> mShared;
// socket handle to proxy TCP control channel
LLSocket::ptr_t mProxyControlChannel;
/*###########################################################################################
END OF UNSHARED MEMBERS
###########################################################################################*/
/*###########################################################################################
MEMBERS WRITTEN IN MAIN THREAD AND READ IN ANY THREAD. ONLY READ OR WRITE AFTER LOCKING mProxyMutex!
###########################################################################################*/
// HTTP proxy address and port
LLHost mHTTPProxy;
// Currently selected HTTP proxy type. Can be web or socks.
LLHttpProxyType mProxyType;
// SOCKS 5 selected authentication method.
LLSocks5AuthType mAuthMethodSelected;
// SOCKS 5 username
std::string mSocksUsername;
// SOCKS 5 password
std::string mSocksPassword;
/*###########################################################################################
END OF SHARED MEMBERS
###########################################################################################*/
public:
// For thread-safe read access. Use the _crat access types with these.
AIThreadSafeSingleThreadDC<ProxyUnshared> const& unshared_lockobj(void) const { return mUnshared; }
AIThreadSafeDC<ProxyShared> const& shared_lockobj(void) const { return mShared; }
};
#endif

View File

@@ -4,31 +4,25 @@
* @date 2004-11-21
* @brief Implementation of the i/o pump and related functions.
*
* $LicenseInfo:firstyear=2004&license=viewergpl$
*
* Copyright (c) 2004-2009, Linden Research, Inc.
*
* $LicenseInfo:firstyear=2004&license=viewerlgpl$
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
* Copyright (C) 2010, Linden Research, Inc.
*
* 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, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
* 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.
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
* 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.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* 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$
*/
@@ -40,12 +34,11 @@
#include "apr_poll.h"
#include "llapr.h"
#include "llfasttimer.h"
#include "llmemtype.h"
#include "llstl.h"
#include "llstat.h"
#include "llthread.h"
#include "llfasttimer.h"
#include <iterator> //VS2010
// These should not be enabled in production, but they can be
// intensely useful during development for finding certain kinds of
@@ -191,17 +184,28 @@ LLPumpIO::LLPumpIO(void) :
LLPumpIO::~LLPumpIO()
{
LLMemType m1(LLMemType::MTYPE_IO_PUMP);
cleanup();
#if LL_THREADS_APR
if (mChainsMutex) apr_thread_mutex_destroy(mChainsMutex);
if (mCallbackMutex) apr_thread_mutex_destroy(mCallbackMutex);
#endif
mChainsMutex = NULL;
mCallbackMutex = NULL;
if(mPollset)
{
// lldebugs << "cleaning up pollset" << llendl;
apr_pollset_destroy(mPollset);
mPollset = NULL;
}
}
bool LLPumpIO::addChain(const chain_t& chain, F32 timeout)
bool LLPumpIO::addChain(chain_t const& chain, F32 timeout)
{
LLMemType m1(LLMemType::MTYPE_IO_PUMP);
if(chain.empty()) return false;
#if LL_THREADS_APR
LLScopedLock lock(mChainsMutex);
#endif
chain_t::const_iterator it = chain.begin();
chain_t::const_iterator const end = chain.end();
if (it == end) return false;
LLChainInfo info;
info.setTimeoutSeconds(timeout);
info.mData = LLIOPipe::buffer_ptr_t(new LLBufferArray);
@@ -212,14 +216,17 @@ bool LLPumpIO::addChain(const chain_t& chain, F32 timeout)
#else
lldebugs << "LLPumpIO::addChain() " << chain[0] <<llendl;
#endif
chain_t::const_iterator it = chain.begin();
chain_t::const_iterator end = chain.end();
for(; it != end; ++it)
{
info.mHasExpiration = info.mHasExpiration || (*it)->hasExpiration();
link.mPipe = (*it);
link.mChannels = info.mData->nextChannel();
info.mChainLinks.push_back(link);
}
#if LL_THREADS_APR
LLScopedLock lock(mChainsMutex);
#endif
mPendingChains.push_back(info);
return true;
}
@@ -236,11 +243,10 @@ bool LLPumpIO::addChain(
// description, we need to have that description matched to a
// particular buffer.
if(!data) return false;
if(links.empty()) return false;
links_t::const_iterator link = links.begin();
links_t::const_iterator const end = links.end();
if (link == end) return false;
#if LL_THREADS_APR
LLScopedLock lock(mChainsMutex);
#endif
#if LL_DEBUG_PIPE_TYPE_IN_PUMP
lldebugs << "LLPumpIO::addChain() " << links[0].mPipe << " '"
<< typeid(*(links[0].mPipe)).name() << "'" << llendl;
@@ -252,6 +258,17 @@ bool LLPumpIO::addChain(
info.mChainLinks = links;
info.mData = data;
info.mContext = context;
for (; link != end; ++link)
{
if (link->mPipe->hasExpiration())
{
info.mHasExpiration = true;
break;
}
}
#if LL_THREADS_APR
LLScopedLock lock(mChainsMutex);
#endif
mPendingChains.push_back(info);
return true;
}
@@ -438,6 +455,15 @@ void LLPumpIO::pump()
static LLFastTimer::DeclareTimer FTM_PUMP_IO("Pump IO");
LLPumpIO::current_chain_t LLPumpIO::removeRunningChain(LLPumpIO::current_chain_t& run_chain)
{
std::for_each(
(*run_chain).mDescriptors.begin(),
(*run_chain).mDescriptors.end(),
ll_delete_apr_pollset_fd_client_data());
return mRunningChains.erase(run_chain);
}
//timeout is in microseconds
void LLPumpIO::pump(const S32& poll_timeout)
{
@@ -583,10 +609,16 @@ void LLPumpIO::pump(const S32& poll_timeout)
// << (*run_chain).mChainLinks[0].mPipe
// << " because we reached the end." << llendl;
#endif
run_chain = mRunningChains.erase(run_chain);
run_chain = removeRunningChain(run_chain);
continue;
}
}
else if(isChainExpired(*run_chain))
{
run_chain = removeRunningChain(run_chain);
continue;
}
PUMP_DEBUG;
if((*run_chain).mLock)
{
@@ -694,11 +726,7 @@ void LLPumpIO::pump(const S32& poll_timeout)
PUMP_DEBUG;
// This chain is done. Clean up any allocated memory and
// erase the chain info.
std::for_each(
(*run_chain).mDescriptors.begin(),
(*run_chain).mDescriptors.end(),
ll_delete_apr_pollset_fd_client_data());
run_chain = mRunningChains.erase(run_chain);
run_chain = removeRunningChain(run_chain);
// *NOTE: may not always need to rebuild the pollset.
mRebuildPollset = true;
@@ -833,22 +861,6 @@ void LLPumpIO::initialize(void)
apr_thread_mutex_create(&mCallbackMutex, APR_THREAD_MUTEX_UNNESTED, mPool());
#endif
}
void LLPumpIO::cleanup()
{
LLMemType m1(LLMemType::MTYPE_IO_PUMP);
#if LL_THREADS_APR
if (mChainsMutex) apr_thread_mutex_destroy(mChainsMutex);
if (mCallbackMutex) apr_thread_mutex_destroy(mCallbackMutex);
#endif
mChainsMutex = NULL;
mCallbackMutex = NULL;
if(mPollset)
{
// lldebugs << "cleaning up pollset" << llendl;
apr_pollset_destroy(mPollset);
mPollset = NULL;
}
}
void LLPumpIO::rebuildPollset()
{
@@ -1083,10 +1095,30 @@ void LLPumpIO::processChain(LLChainInfo& chain)
PUMP_DEBUG;
}
bool LLPumpIO::isChainExpired(LLChainInfo& chain)
{
if(!chain.mHasExpiration)
{
return false ;
}
for(links_t::iterator iter = chain.mChainLinks.begin(); iter != chain.mChainLinks.end(); ++iter)
{
if(!(*iter).mPipe->hasNotExpired())
{
return true ;
}
}
return false ;
}
bool LLPumpIO::handleChainError(
LLChainInfo& chain,
LLIOPipe::EStatus error)
{
DoutEntering(dc::notice, "LLPumpIO::handleChainError(" << (void*)&chain << ", " << LLIOPipe::lookupStatusString(error) << ")");
LLMemType m1(LLMemType::MTYPE_IO_PUMP);
links_t::reverse_iterator rit;
if(chain.mHead == chain.mChainLinks.end())
@@ -1124,6 +1156,9 @@ bool LLPumpIO::handleChainError(
#endif
keep_going = false;
break;
case LLIOPipe::STATUS_EXPIRED:
keep_going = false;
break ;
default:
if(LLIOPipe::isSuccess(error))
{
@@ -1146,6 +1181,7 @@ LLPumpIO::LLChainInfo::LLChainInfo() :
mInit(false),
mLock(0),
mEOS(false),
mHasExpiration(false),
mDescriptorsPool(new LLAPRPool(LLThread::tldata().mRootPool))
{
LLMemType m1(LLMemType::MTYPE_IO_PUMP);

View File

@@ -4,31 +4,25 @@
* @date 2004-11-19
* @brief Declaration of pump class which manages io chains.
*
* $LicenseInfo:firstyear=2004&license=viewergpl$
*
* Copyright (c) 2004-2009, Linden Research, Inc.
*
* $LicenseInfo:firstyear=2004&license=viewerlgpl$
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
* Copyright (C) 2010, Linden Research, Inc.
*
* 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, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
* 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.
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
* 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.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* 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$
*/
@@ -351,12 +345,13 @@ protected:
// basic member data
bool mInit;
bool mEOS;
bool mHasExpiration;
S32 mLock;
LLFrameTimer mTimer;
links_t::iterator mHead;
links_t mChainLinks;
LLIOPipe::buffer_ptr_t mData;
bool mEOS;
LLIOPipe::buffer_ptr_t mData;
LLSD mContext;
// tracking inside the pump
@@ -397,8 +392,8 @@ protected:
protected:
void initialize();
void cleanup();
current_chain_t removeRunningChain(current_chain_t& chain) ;
/**
* @brief Given the internal state of the chains, rebuild the pollset
* @see setConditional()
@@ -425,6 +420,9 @@ protected:
*/
bool handleChainError(LLChainInfo& chain, LLIOPipe::EStatus error);
//if the chain is expired, remove it
bool isChainExpired(LLChainInfo& chain) ;
public:
/**
* @brief Return number of running chains.

View File

@@ -151,11 +151,11 @@ bool LLSDMessage::ResponderAdapter::listener(const LLSD& payload, bool success)
{
if (success)
{
mResponder->result(payload);
mResponder->pubResult(payload);
}
else
{
mResponder->errorWithContent(payload["status"].asInteger(), payload["reason"], payload["content"]);
mResponder->pubErrorWithContent(payload["status"].asInteger(), payload["reason"], payload["content"]);
}
/*---------------- MUST BE LAST STATEMENT BEFORE RETURN ----------------*/

View File

@@ -1,255 +0,0 @@
/**
* @file llsdrpcclient.cpp
* @author Phoenix
* @date 2005-11-05
* @brief Implementation of the llsd client classes.
*
* $LicenseInfo:firstyear=2005&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 "linden_common.h"
#include "llsdrpcclient.h"
#include "llbufferstream.h"
#include "llfasttimer.h"
#include "llfiltersd2xmlrpc.h"
#include "llmemtype.h"
#include "llpumpio.h"
#include "llsd.h"
#include "llsdserialize.h"
#include "llurlrequest.h"
/**
* String constants
*/
static std::string LLSDRPC_RESPONSE_NAME("response");
static std::string LLSDRPC_FAULT_NAME("fault");
/**
* LLSDRPCResponse
*/
LLSDRPCResponse::LLSDRPCResponse() :
mIsError(false),
mIsFault(false)
{
LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT);
}
// virtual
LLSDRPCResponse::~LLSDRPCResponse()
{
LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT);
}
bool LLSDRPCResponse::extractResponse(const LLSD& sd)
{
LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT);
bool rv = true;
if(sd.has(LLSDRPC_RESPONSE_NAME))
{
mReturnValue = sd[LLSDRPC_RESPONSE_NAME];
mIsFault = false;
}
else if(sd.has(LLSDRPC_FAULT_NAME))
{
mReturnValue = sd[LLSDRPC_FAULT_NAME];
mIsFault = true;
}
else
{
mReturnValue.clear();
mIsError = true;
rv = false;
}
return rv;
}
static LLFastTimer::DeclareTimer FTM_SDRPC_RESPONSE("SDRPC Response");
// virtual
LLIOPipe::EStatus LLSDRPCResponse::process_impl(
const LLChannelDescriptors& channels,
buffer_ptr_t& buffer,
bool& eos,
LLSD& context,
LLPumpIO* pump)
{
LLFastTimer t(FTM_SDRPC_RESPONSE);
PUMP_DEBUG;
LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT);
if(mIsError)
{
error(pump);
}
else if(mIsFault)
{
fault(pump);
}
else
{
response(pump);
}
PUMP_DEBUG;
return STATUS_DONE;
}
/**
* LLSDRPCClient
*/
LLSDRPCClient::LLSDRPCClient() :
mState(STATE_NONE),
mQueue(EPBQ_PROCESS)
{
LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT);
}
// virtual
LLSDRPCClient::~LLSDRPCClient()
{
LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT);
}
bool LLSDRPCClient::call(
const std::string& uri,
const std::string& method,
const LLSD& parameter,
LLSDRPCResponse* response,
EPassBackQueue queue)
{
LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT);
//llinfos << "RPC: " << uri << "." << method << "(" << *parameter << ")"
// << llendl;
if(method.empty() || !response)
{
return false;
}
mState = STATE_READY;
mURI.assign(uri);
std::stringstream req;
req << LLSDRPC_REQUEST_HEADER_1 << method
<< LLSDRPC_REQUEST_HEADER_2;
LLSDSerialize::toNotation(parameter, req);
req << LLSDRPC_REQUEST_FOOTER;
mRequest = req.str();
mQueue = queue;
mResponse = response;
return true;
}
bool LLSDRPCClient::call(
const std::string& uri,
const std::string& method,
const std::string& parameter,
LLSDRPCResponse* response,
EPassBackQueue queue)
{
LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT);
//llinfos << "RPC: " << uri << "." << method << "(" << parameter << ")"
// << llendl;
if(method.empty() || parameter.empty() || !response)
{
return false;
}
mState = STATE_READY;
mURI.assign(uri);
std::stringstream req;
req << LLSDRPC_REQUEST_HEADER_1 << method
<< LLSDRPC_REQUEST_HEADER_2 << parameter
<< LLSDRPC_REQUEST_FOOTER;
mRequest = req.str();
mQueue = queue;
mResponse = response;
return true;
}
static LLFastTimer::DeclareTimer FTM_PROCESS_SDRPC_CLIENT("SDRPC Client");
// virtual
LLIOPipe::EStatus LLSDRPCClient::process_impl(
const LLChannelDescriptors& channels,
buffer_ptr_t& buffer,
bool& eos,
LLSD& context,
LLPumpIO* pump)
{
LLFastTimer t(FTM_PROCESS_SDRPC_CLIENT);
PUMP_DEBUG;
LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT);
if((STATE_NONE == mState) || (!pump))
{
// You should have called the call() method already.
return STATUS_PRECONDITION_NOT_MET;
}
EStatus rv = STATUS_DONE;
switch(mState)
{
case STATE_READY:
{
PUMP_DEBUG;
// lldebugs << "LLSDRPCClient::process_impl STATE_READY" << llendl;
buffer->append(
channels.out(),
(U8*)mRequest.c_str(),
mRequest.length());
context[CONTEXT_DEST_URI_SD_LABEL] = mURI;
mState = STATE_WAITING_FOR_RESPONSE;
break;
}
case STATE_WAITING_FOR_RESPONSE:
{
PUMP_DEBUG;
// The input channel has the sd response in it.
//lldebugs << "LLSDRPCClient::process_impl STATE_WAITING_FOR_RESPONSE"
// << llendl;
LLBufferStream resp(channels, buffer.get());
LLSD sd;
LLSDSerialize::fromNotation(sd, resp, buffer->count(channels.in()));
LLSDRPCResponse* response = (LLSDRPCResponse*)mResponse.get();
if (!response)
{
mState = STATE_DONE;
break;
}
response->extractResponse(sd);
if(EPBQ_PROCESS == mQueue)
{
LLPumpIO::chain_t chain;
chain.push_back(mResponse);
pump->addChain(chain, DEFAULT_CHAIN_EXPIRY_SECS);
}
else
{
pump->respond(mResponse.get());
}
mState = STATE_DONE;
break;
}
case STATE_DONE:
default:
PUMP_DEBUG;
llinfos << "invalid state to process" << llendl;
rv = STATUS_ERROR;
break;
}
return rv;
}

View File

@@ -1,323 +0,0 @@
/**
* @file llsdrpcclient.h
* @author Phoenix
* @date 2005-11-05
* @brief Implementation and helpers for structure data RPC clients.
*
* $LicenseInfo:firstyear=2005&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_LLSDRPCCLIENT_H
#define LL_LLSDRPCCLIENT_H
/**
* This file declares classes to encapsulate a basic structured data
* remote procedure client.
*/
#include "llchainio.h"
#include "llfiltersd2xmlrpc.h"
#include "lliopipe.h"
#include "llurlrequest.h"
/**
* @class LLSDRPCClientResponse
* @brief Abstract base class to represent a response from an SD server.
*
* This is used as a base class for callbacks generated from an
* structured data remote procedure call. The
* <code>extractResponse</code> method will deal with the llsdrpc method
* call overhead, and keep track of what to call during the next call
* into <code>process</code>. If you use this as a base class, you
* need to implement <code>response</code>, <code>fault</code>, and
* <code>error</code> to do something useful. When in those methods,
* you can parse and utilize the mReturnValue member data.
*/
class LLSDRPCResponse : public LLIOPipe
{
public:
LLSDRPCResponse();
virtual ~LLSDRPCResponse();
/**
* @brief This method extracts the response out of the sd passed in
*
* Any appropriate data found in the sd passed in will be
* extracted and managed by this object - not copied or cloned. It
* will still be up to the caller to delete the pointer passed in.
* @param sd The raw structured data response from the remote server.
* @return Returns true if this was able to parse the structured data.
*/
bool extractResponse(const LLSD& sd);
protected:
/**
* @brief Method called when the response is ready.
*/
virtual bool response(LLPumpIO* pump) = 0;
/**
* @brief Method called when a fault is generated by the remote server.
*/
virtual bool fault(LLPumpIO* pump) = 0;
/**
* @brief Method called when there was an error
*/
virtual bool error(LLPumpIO* pump) = 0;
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);
//@}
protected:
LLSD mReturnValue;
bool mIsError;
bool mIsFault;
};
/**
* @class LLSDRPCClient
* @brief Client class for a structured data remote procedure call.
*
* This class helps deal with making structured data calls to a remote
* server. You can visualize the calls as:
* <code>
* response = uri.method(parameter)
* </code>
* where you pass in everything to <code>call</code> and this class
* takes care of the rest of the details.
* In typical usage, you will derive a class from this class and
* provide an API more useful for the specific application at
* hand. For example, if you were writing a service to send an instant
* message, you could create an API for it to send the messsage, and
* that class would do the work of translating it into the method and
* parameter, find the destination, and invoke <code>call</call> with
* a useful implementation of LLSDRPCResponse passed in to handle the
* response from the network.
*/
class LLSDRPCClient : public LLIOPipe
{
public:
LLSDRPCClient();
virtual ~LLSDRPCClient();
/**
* @brief Enumeration for tracking which queue to process the
* response.
*/
enum EPassBackQueue
{
EPBQ_PROCESS,
EPBQ_CALLBACK,
};
/**
* @brief Call a method on a remote LLSDRPCServer
*
* @param uri The remote object to call, eg,
* http://localhost/usher. If you are using a factory with a fixed
* url, the uri passed in will probably be ignored.
* @param method The method to call on the remote object
* @param parameter The parameter to pass into the remote
* object. It is up to the caller to delete the value passed in.
* @param response The object which gets the response.
* @param queue Specifies to call the response on the process or
* callback queue.
* @return Returns true if this object will be able to make the RPC call.
*/
bool call(
const std::string& uri,
const std::string& method,
const LLSD& parameter,
LLSDRPCResponse* response,
EPassBackQueue queue);
/**
* @brief Call a method on a remote LLSDRPCServer
*
* @param uri The remote object to call, eg,
* http://localhost/usher. If you are using a factory with a fixed
* url, the uri passed in will probably be ignored.
* @param method The method to call on the remote object
* @param parameter The seriailized parameter to pass into the
* remote object.
* @param response The object which gets the response.
* @param queue Specifies to call the response on the process or
* callback queue.
* @return Returns true if this object will be able to make the RPC call.
*/
bool call(
const std::string& uri,
const std::string& method,
const std::string& parameter,
LLSDRPCResponse* response,
EPassBackQueue queue);
protected:
/**
* @brief Enumeration for tracking client state.
*/
enum EState
{
STATE_NONE,
STATE_READY,
STATE_WAITING_FOR_RESPONSE,
STATE_DONE
};
/* @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);
//@}
protected:
EState mState;
std::string mURI;
std::string mRequest;
EPassBackQueue mQueue;
LLIOPipe::ptr_t mResponse;
};
/**
* @class LLSDRPCClientFactory
* @brief Basic implementation for making an SD RPC client factory
*
* This class eases construction of a basic sd rpc client. Here is an
* example of it's use:
* <code>
* class LLUsefulService : public LLService { ... }
* LLService::registerCreator(
* "useful",
* LLService::creator_t(new LLSDRPCClientFactory<LLUsefulService>))
* </code>
*/
template<class Client>
class LLSDRPCClientFactory : public LLChainIOFactory
{
public:
LLSDRPCClientFactory() {}
LLSDRPCClientFactory(const std::string& fixed_url) : mURL(fixed_url) {}
virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const
{
lldebugs << "LLSDRPCClientFactory::build" << llendl;
LLURLRequest* http(new LLURLRequest(LLURLRequest::HTTP_POST));
if(!http->isValid())
{
llwarns << "Creating LLURLRequest failed." << llendl ;
delete http;
return false;
}
LLIOPipe::ptr_t service(new Client);
chain.push_back(service);
LLIOPipe::ptr_t http_pipe(http);
http->addHeader("Content-Type: text/llsd");
if(mURL.empty())
{
chain.push_back(LLIOPipe::ptr_t(new LLContextURLExtractor(http)));
}
else
{
http->setURL(mURL);
}
chain.push_back(http_pipe);
chain.push_back(service);
return true;
}
protected:
std::string mURL;
};
/**
* @class LLXMLSDRPCClientFactory
* @brief Basic implementation for making an XMLRPC to SD RPC client factory
*
* This class eases construction of a basic sd rpc client which uses
* xmlrpc as a serialization grammar. Here is an example of it's use:
* <code>
* class LLUsefulService : public LLService { ... }
* LLService::registerCreator(
* "useful",
* LLService::creator_t(new LLXMLSDRPCClientFactory<LLUsefulService>))
* </code>
*/
template<class Client>
class LLXMLSDRPCClientFactory : public LLChainIOFactory
{
public:
LLXMLSDRPCClientFactory() {}
LLXMLSDRPCClientFactory(const std::string& fixed_url) : mURL(fixed_url) {}
virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const
{
lldebugs << "LLXMLSDRPCClientFactory::build" << llendl;
LLURLRequest* http(new LLURLRequest(LLURLRequest::HTTP_POST));
if(!http->isValid())
{
llwarns << "Creating LLURLRequest failed." << llendl ;
delete http;
return false ;
}
LLIOPipe::ptr_t service(new Client);
chain.push_back(service);
LLIOPipe::ptr_t http_pipe(http);
http->addHeader("Content-Type: text/xml");
if(mURL.empty())
{
chain.push_back(LLIOPipe::ptr_t(new LLContextURLExtractor(http)));
}
else
{
http->setURL(mURL);
}
chain.push_back(LLIOPipe::ptr_t(new LLFilterSD2XMLRPCRequest(NULL)));
chain.push_back(http_pipe);
chain.push_back(LLIOPipe::ptr_t(new LLFilterXMLRPCResponse2LLSD));
chain.push_back(service);
return true;
}
protected:
std::string mURL;
};
#endif // LL_LLSDRPCCLIENT_H

View File

@@ -1,347 +0,0 @@
/**
* @file llsdrpcserver.cpp
* @author Phoenix
* @date 2005-10-11
* @brief Implementation of the LLSDRPCServer and related classes.
*
* $LicenseInfo:firstyear=2005&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 "linden_common.h"
#include "llsdrpcserver.h"
#include "llbuffer.h"
#include "llbufferstream.h"
#include "llfasttimer.h"
#include "llmemtype.h"
#include "llpumpio.h"
#include "llsdserialize.h"
#include "llstl.h"
static const char FAULT_PART_1[] = "{'fault':{'code':i";
static const char FAULT_PART_2[] = ", 'description':'";
static const char FAULT_PART_3[] = "'}}";
static const char RESPONSE_PART_1[] = "{'response':";
static const char RESPONSE_PART_2[] = "}";
static const S32 FAULT_GENERIC = 1000;
static const S32 FAULT_METHOD_NOT_FOUND = 1001;
static const std::string LLSDRPC_METHOD_SD_NAME("method");
static const std::string LLSDRPC_PARAMETER_SD_NAME("parameter");
/**
* LLSDRPCServer
*/
LLSDRPCServer::LLSDRPCServer() :
mState(LLSDRPCServer::STATE_NONE),
mPump(NULL),
mLock(0)
{
LLMemType m1(LLMemType::MTYPE_IO_SD_SERVER);
}
LLSDRPCServer::~LLSDRPCServer()
{
LLMemType m1(LLMemType::MTYPE_IO_SD_SERVER);
std::for_each(
mMethods.begin(),
mMethods.end(),
llcompose1(
DeletePointerFunctor<LLSDRPCMethodCallBase>(),
llselect2nd<method_map_t::value_type>()));
std::for_each(
mCallbackMethods.begin(),
mCallbackMethods.end(),
llcompose1(
DeletePointerFunctor<LLSDRPCMethodCallBase>(),
llselect2nd<method_map_t::value_type>()));
}
// virtual
ESDRPCSStatus LLSDRPCServer::deferredResponse(
const LLChannelDescriptors& channels,
LLBufferArray* data) {
// subclass should provide a sane implementation
return ESDRPCS_DONE;
}
void LLSDRPCServer::clearLock()
{
if(mLock && mPump)
{
mPump->clearLock(mLock);
mPump = NULL;
mLock = 0;
}
}
static LLFastTimer::DeclareTimer FTM_PROCESS_SDRPC_SERVER("SDRPC Server");
// virtual
LLIOPipe::EStatus LLSDRPCServer::process_impl(
const LLChannelDescriptors& channels,
buffer_ptr_t& buffer,
bool& eos,
LLSD& context,
LLPumpIO* pump)
{
LLFastTimer t(FTM_PROCESS_SDRPC_SERVER);
PUMP_DEBUG;
LLMemType m1(LLMemType::MTYPE_IO_SD_SERVER);
// lldebugs << "LLSDRPCServer::process_impl" << llendl;
// Once we have all the data, We need to read the sd on
// the the in channel, and respond on the out channel
if(!eos) return STATUS_BREAK;
if(!pump || !buffer) return STATUS_PRECONDITION_NOT_MET;
std::string method_name;
LLIOPipe::EStatus status = STATUS_DONE;
switch(mState)
{
case STATE_DEFERRED:
PUMP_DEBUG;
if(ESDRPCS_DONE != deferredResponse(channels, buffer.get()))
{
buildFault(
channels,
buffer.get(),
FAULT_GENERIC,
"deferred response failed.");
}
mState = STATE_DONE;
return STATUS_DONE;
case STATE_DONE:
// lldebugs << "STATE_DONE" << llendl;
break;
case STATE_CALLBACK:
// lldebugs << "STATE_CALLBACK" << llendl;
PUMP_DEBUG;
method_name = mRequest[LLSDRPC_METHOD_SD_NAME].asString();
if(!method_name.empty() && mRequest.has(LLSDRPC_PARAMETER_SD_NAME))
{
if(ESDRPCS_DONE != callbackMethod(
method_name,
mRequest[LLSDRPC_PARAMETER_SD_NAME],
channels,
buffer.get()))
{
buildFault(
channels,
buffer.get(),
FAULT_GENERIC,
"Callback method call failed.");
}
}
else
{
// this should never happen, since we should not be in
// this state unless we originally found a method and
// params during the first call to process.
buildFault(
channels,
buffer.get(),
FAULT_GENERIC,
"Invalid LLSDRPC sever state - callback without method.");
}
pump->clearLock(mLock);
mLock = 0;
mState = STATE_DONE;
break;
case STATE_NONE:
// lldebugs << "STATE_NONE" << llendl;
default:
{
// First time we got here - process the SD request, and call
// the method.
PUMP_DEBUG;
LLBufferStream istr(channels, buffer.get());
mRequest.clear();
LLSDSerialize::fromNotation(
mRequest,
istr,
buffer->count(channels.in()));
// { 'method':'...', 'parameter': ... }
method_name = mRequest[LLSDRPC_METHOD_SD_NAME].asString();
if(!method_name.empty() && mRequest.has(LLSDRPC_PARAMETER_SD_NAME))
{
ESDRPCSStatus rv = callMethod(
method_name,
mRequest[LLSDRPC_PARAMETER_SD_NAME],
channels,
buffer.get());
switch(rv)
{
case ESDRPCS_DEFERRED:
mPump = pump;
mLock = pump->setLock();
mState = STATE_DEFERRED;
status = STATUS_BREAK;
break;
case ESDRPCS_CALLBACK:
{
mState = STATE_CALLBACK;
LLPumpIO::LLLinkInfo link;
link.mPipe = LLIOPipe::ptr_t(this);
link.mChannels = channels;
LLPumpIO::links_t links;
links.push_back(link);
pump->respond(links, buffer, context);
mLock = pump->setLock();
status = STATUS_BREAK;
break;
}
case ESDRPCS_DONE:
mState = STATE_DONE;
break;
case ESDRPCS_ERROR:
default:
buildFault(
channels,
buffer.get(),
FAULT_GENERIC,
"Method call failed.");
break;
}
}
else
{
// send a fault
buildFault(
channels,
buffer.get(),
FAULT_GENERIC,
"Unable to find method and parameter in request.");
}
break;
}
}
PUMP_DEBUG;
return status;
}
// virtual
ESDRPCSStatus LLSDRPCServer::callMethod(
const std::string& method,
const LLSD& params,
const LLChannelDescriptors& channels,
LLBufferArray* response)
{
LLMemType m1(LLMemType::MTYPE_IO_SD_SERVER);
// Try to find the method in the method table.
ESDRPCSStatus rv = ESDRPCS_DONE;
method_map_t::iterator it = mMethods.find(method);
if(it != mMethods.end())
{
rv = (*it).second->call(params, channels, response);
}
else
{
it = mCallbackMethods.find(method);
if(it == mCallbackMethods.end())
{
// method not found.
std::ostringstream message;
message << "rpc server unable to find method: " << method;
buildFault(
channels,
response,
FAULT_METHOD_NOT_FOUND,
message.str());
}
else
{
// we found it in the callback methods - tell the process
// to coordinate calling on the pump callback.
return ESDRPCS_CALLBACK;
}
}
return rv;
}
// virtual
ESDRPCSStatus LLSDRPCServer::callbackMethod(
const std::string& method,
const LLSD& params,
const LLChannelDescriptors& channels,
LLBufferArray* response)
{
LLMemType m1(LLMemType::MTYPE_IO_SD_SERVER);
// Try to find the method in the callback method table.
ESDRPCSStatus rv = ESDRPCS_DONE;
method_map_t::iterator it = mCallbackMethods.find(method);
if(it != mCallbackMethods.end())
{
rv = (*it).second->call(params, channels, response);
}
else
{
std::ostringstream message;
message << "pcserver unable to find callback method: " << method;
buildFault(
channels,
response,
FAULT_METHOD_NOT_FOUND,
message.str());
}
return rv;
}
// static
void LLSDRPCServer::buildFault(
const LLChannelDescriptors& channels,
LLBufferArray* data,
S32 code,
const std::string& msg)
{
LLMemType m1(LLMemType::MTYPE_IO_SD_SERVER);
LLBufferStream ostr(channels, data);
ostr << FAULT_PART_1 << code << FAULT_PART_2 << msg << FAULT_PART_3;
llinfos << "LLSDRPCServer::buildFault: " << code << ", " << msg << llendl;
}
// static
void LLSDRPCServer::buildResponse(
const LLChannelDescriptors& channels,
LLBufferArray* data,
const LLSD& response)
{
LLMemType m1(LLMemType::MTYPE_IO_SD_SERVER);
LLBufferStream ostr(channels, data);
ostr << RESPONSE_PART_1;
LLSDSerialize::toNotation(response, ostr);
ostr << RESPONSE_PART_2;
#if LL_DEBUG
std::ostringstream debug_ostr;
debug_ostr << "LLSDRPCServer::buildResponse: ";
LLSDSerialize::toNotation(response, debug_ostr);
llinfos << debug_ostr.str() << llendl;
#endif
}

View File

@@ -1,360 +0,0 @@
/**
* @file llsdrpcserver.h
* @author Phoenix
* @date 2005-10-11
* @brief Declaration of the structured data remote procedure call server.
*
* $LicenseInfo:firstyear=2005&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_LLSDRPCSERVER_H
#define LL_LLSDRPCSERVER_H
/**
* I've set this up to be pretty easy to use when you want to make a
* structured data rpc server which responds to methods by
* name. Derive a class from the LLSDRPCServer, and during
* construction (or initialization if you have the luxury) map method
* names to pointers to member functions. This will look a lot like:
*
* <code>
* class LLMessageAgents : public LLSDRPCServer {<br>
* public:<br>
* typedef LLSDRPCServer<LLUsher> mem_fn_t;<br>
* LLMessageAgents() {<br>
* mMethods["message"] = new mem_fn_t(this, &LLMessageAgents::rpc_IM);<br>
* mMethods["alert"] = new mem_fn_t(this, &LLMessageAgents::rpc_Alrt);<br>
* }<br>
* protected:<br>
* rpc_IM(const LLSD& params,
* const LLChannelDescriptors& channels,
* LLBufferArray* data)
* {...}<br>
* rpc_Alert(const LLSD& params,
* const LLChannelDescriptors& channels,
* LLBufferArray* data)
* {...}<br>
* };<br>
* </code>
*
* The params are an array where each element in the array is a single
* parameter in the call.
*
* It is up to you to pack a valid serialized llsd response into the
* data object passed into the method, but you can use the helper
* methods below to help.
*/
#include <map>
#include "lliopipe.h"
#include "lliohttpserver.h"
#include "llfiltersd2xmlrpc.h"
class LLSD;
/**
* @brief Enumeration for specifying server method call status. This
* enumeration controls how the server class will manage the pump
* process/callback mechanism.
*/
enum ESDRPCSStatus
{
// The call went ok, but the response is not yet ready. The
// method will arrange for the clearLock() call to be made at
// a later date, after which, once the chain is being pumped
// again, deferredResponse() will be called to gather the result
ESDRPCS_DEFERRED,
// The LLSDRPCServer would like to handle the method on the
// callback queue of the pump.
ESDRPCS_CALLBACK,
// The method call finished and generated output.
ESDRPCS_DONE,
// Method failed for some unspecified reason - you should avoid
// this. A generic fault will be sent to the output.
ESDRPCS_ERROR,
ESDRPCS_COUNT,
};
/**
* @class LLSDRPCMethodCallBase
* @brief Base class for calling a member function in an sd rpcserver
* implementation.
*/
class LLSDRPCMethodCallBase
{
public:
LLSDRPCMethodCallBase() {}
virtual ~LLSDRPCMethodCallBase() {}
virtual ESDRPCSStatus call(
const LLSD& params,
const LLChannelDescriptors& channels,
LLBufferArray* response) = 0;
protected:
};
/**
* @class LLSDRPCMethodCall
* @brief Class which implements member function calls.
*/
template<class Server>
class LLSDRPCMethodCall : public LLSDRPCMethodCallBase
{
public:
typedef ESDRPCSStatus (Server::*mem_fn)(
const LLSD& params,
const LLChannelDescriptors& channels,
LLBufferArray* data);
LLSDRPCMethodCall(Server* s, mem_fn fn) :
mServer(s),
mMemFn(fn)
{
}
virtual ~LLSDRPCMethodCall() {}
virtual ESDRPCSStatus call(
const LLSD& params,
const LLChannelDescriptors& channels,
LLBufferArray* data)
{
return (*mServer.*mMemFn)(params, channels, data);
}
protected:
Server* mServer;
mem_fn mMemFn;
//bool (Server::*mMemFn)(const LLSD& params, LLBufferArray& data);
};
/**
* @class LLSDRPCServer
* @brief Basic implementation of a structure data rpc server
*
* The rpc server is also designed to appropriately straddle the pump
* <code>process()</code> and <code>callback()</code> to specify which
* thread you want to work on when handling a method call. The
* <code>mMethods</code> methods are called from
* <code>process()</code>, while the <code>mCallbackMethods</code> are
* called when a pump is in a <code>callback()</code> cycle.
*/
class LLSDRPCServer : public LLIOPipe
{
public:
LLSDRPCServer();
virtual ~LLSDRPCServer();
/**
* enumeration for generic fault codes
*/
enum
{
FAULT_BAD_REQUEST = 2000,
FAULT_NO_RESPONSE = 2001,
};
/**
* @brief Call this method to return an rpc fault.
*
* @param channel The channel for output on the data buffer
* @param data buffer which will recieve the final output
* @param code The fault code
* @param msg The fault message
*/
static void buildFault(
const LLChannelDescriptors& channels,
LLBufferArray* data,
S32 code,
const std::string& msg);
/**
* @brief Call this method to build an rpc response.
*
* @param channel The channel for output on the data buffer
* @param data buffer which will recieve the final output
* @param response The return value from the method call
*/
static void buildResponse(
const LLChannelDescriptors& channels,
LLBufferArray* data,
const LLSD& response);
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);
//@}
protected:
/**
* @brief Enumeration to track the state of the rpc server instance
*/
enum EState
{
STATE_NONE,
STATE_CALLBACK,
STATE_DEFERRED,
STATE_DONE
};
/**
* @brief This method is called when an http post comes in.
*
* The default behavior is to look at the method name, look up the
* method in the method table, and call it. If the method is not
* found, this function will build a fault response. You can
* implement your own version of this function if you want to hard
* wire some behavior or optimize things a bit.
* @param method The method name being called
* @param params The parameters
* @param channel The channel for output on the data buffer
* @param data The http data
* @return Returns the status of the method call, done/deferred/etc
*/
virtual ESDRPCSStatus callMethod(
const std::string& method,
const LLSD& params,
const LLChannelDescriptors& channels,
LLBufferArray* data);
/**
* @brief This method is called when a pump callback is processed.
*
* The default behavior is to look at the method name, look up the
* method in the callback method table, and call it. If the method
* is not found, this function will build a fault response. You
* can implement your own version of this function if you want to
* hard wire some behavior or optimize things a bit.
* @param method The method name being called
* @param params The parameters
* @param channel The channel for output on the data buffer
* @param data The http data
* @return Returns the status of the method call, done/deferred/etc
*/
virtual ESDRPCSStatus callbackMethod(
const std::string& method,
const LLSD& params,
const LLChannelDescriptors& channels,
LLBufferArray* data);
/**
* @brief Called after a deferred service is unlocked
*
* If a method returns ESDRPCS_DEFERRED, then the service chain
* will be locked and not processed until some other system calls
* clearLock() on the service instance again. At that point,
* once the pump starts processing the chain again, this method
* will be called so the service can output the final result
* into the buffers.
*/
virtual ESDRPCSStatus deferredResponse(
const LLChannelDescriptors& channels,
LLBufferArray* data);
// donovan put this public here 7/27/06
public:
/**
* @brief unlock a service that as ESDRPCS_DEFERRED
*/
void clearLock();
protected:
EState mState;
LLSD mRequest;
LLPumpIO* mPump;
S32 mLock;
typedef std::map<std::string, LLSDRPCMethodCallBase*> method_map_t;
method_map_t mMethods;
method_map_t mCallbackMethods;
};
/**
* @name Helper Templates for making LLHTTPNodes
*
* These templates help in creating nodes for handing a service from
* either SDRPC or XMLRPC, given a single implementation of LLSDRPCServer.
*
* To use it:
* \code
* class LLUsefulServer : public LLSDRPCServer { ... }
*
* LLHTTPNode& root = LLCreateHTTPWireServer(...);
* root.addNode("llsdrpc/useful", new LLSDRPCNode<LLUsefulServer>);
* root.addNode("xmlrpc/useful", new LLXMLRPCNode<LLUsefulServer>);
* \endcode
*/
//@{
template<class Server>
class LLSDRPCServerFactory : public LLChainIOFactory
{
public:
virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const
{
lldebugs << "LLXMLSDRPCServerFactory::build" << llendl;
chain.push_back(LLIOPipe::ptr_t(new Server));
return true;
}
};
template<class Server>
class LLSDRPCNode : public LLHTTPNodeForFactory<
LLSDRPCServerFactory<Server> >
{
};
template<class Server>
class LLXMLRPCServerFactory : public LLChainIOFactory
{
public:
virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const
{
lldebugs << "LLXMLSDRPCServerFactory::build" << llendl;
chain.push_back(LLIOPipe::ptr_t(new LLFilterXMLRPCRequest2LLSD));
chain.push_back(LLIOPipe::ptr_t(new Server));
chain.push_back(LLIOPipe::ptr_t(new LLFilterSD2XMLRPCResponse));
return true;
}
};
template<class Server>
class LLXMLRPCNode : public LLHTTPNodeForFactory<
LLXMLRPCServerFactory<Server> >
{
};
//@}
#endif // LL_LLSDRPCSERVER_H

View File

@@ -29,6 +29,10 @@
#include "linden_common.h"
#include "llurlrequest.h"
#ifdef CWDEBUG
#include <libcwd/buf2str.h>
#endif
#include <algorithm>
#include <openssl/x509_vfy.h>
#include <openssl/ssl.h>
@@ -48,13 +52,10 @@ static const U32 HTTP_STATUS_PIPE_ERROR = 499;
/**
* String constants
*/
const std::string CONTEXT_DEST_URI_SD_LABEL("dest_uri");
const std::string CONTEXT_TRANSFERED_BYTES("transfered_bytes");
static size_t headerCallback(void* data, size_t size, size_t nmemb, void* user);
static size_t headerCallback(char* data, size_t size, size_t nmemb, void* user);
/**
* class LLURLRequestDetail
@@ -65,33 +66,26 @@ public:
LLURLRequestDetail();
~LLURLRequestDetail();
std::string mURL;
LLCurlEasyRequest* mCurlRequest;
LLBufferArray* mResponseBuffer;
AICurlEasyRequest mCurlEasyRequest;
LLIOPipe::buffer_ptr_t mResponseBuffer;
LLChannelDescriptors mChannels;
U8* mLastRead;
U32 mBodyLimit;
S32 mByteAccumulator;
bool mIsBodyLimitSet;
LLURLRequest::SSLCertVerifyCallback mSSLVerifyCallback;
};
LLURLRequestDetail::LLURLRequestDetail() :
mCurlRequest(NULL),
mResponseBuffer(NULL),
mCurlEasyRequest(false),
mLastRead(NULL),
mBodyLimit(0),
mByteAccumulator(0),
mIsBodyLimitSet(false)
{
LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
mCurlRequest = new LLCurlEasyRequest();
}
LLURLRequestDetail::~LLURLRequestDetail()
{
LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
delete mCurlRequest;
mResponseBuffer = NULL;
mLastRead = NULL;
}
@@ -123,6 +117,7 @@ LLURLRequest::LLURLRequest(LLURLRequest::ERequestAction action) :
mAction(action)
{
LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
// This might throw AICurlNoEasyHandle.
initialize();
}
@@ -132,6 +127,7 @@ LLURLRequest::LLURLRequest(
mAction(action)
{
LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
// This might throw AICurlNoEasyHandle.
initialize();
setURL(url);
}
@@ -139,13 +135,16 @@ LLURLRequest::LLURLRequest(
LLURLRequest::~LLURLRequest()
{
LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
{
AICurlEasyRequest_wat curl_easy_request_w(*mDetail->mCurlEasyRequest);
curl_easy_request_w->revokeCallbacks();
curl_easy_request_w->send_events_to(NULL);
}
delete mDetail;
mDetail = NULL ;
}
void LLURLRequest::setURL(const std::string& url)
{
LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
mDetail->mURL = url;
}
@@ -153,19 +152,21 @@ std::string LLURLRequest::getURL() const
{
return mDetail->mURL;
}
void LLURLRequest::addHeader(const char* header)
{
LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
mDetail->mCurlRequest->slist_append(header);
AICurlEasyRequest_wat curlEasyRequest_w(*mDetail->mCurlEasyRequest);
curlEasyRequest_w->addHeader(header);
}
void LLURLRequest::checkRootCertificate(bool check)
{
mDetail->mCurlRequest->setopt(CURLOPT_SSL_VERIFYPEER, (check? TRUE : FALSE));
mDetail->mCurlRequest->setoptString(CURLOPT_ENCODING, "");
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;
@@ -176,7 +177,8 @@ void LLURLRequest::setCallback(LLURLRequestComplete* callback)
{
LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
mCompletionCallback = callback;
mDetail->mCurlRequest->setHeaderCallback(&headerCallback, (void*)callback);
AICurlEasyRequest_wat curlEasyRequest_w(*mDetail->mCurlEasyRequest);
curlEasyRequest_w->setHeaderCallback(&headerCallback, (void*)callback);
}
// Added to mitigate the effect of libcurl looking
@@ -210,26 +212,41 @@ 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;
LL_DEBUGS("Proxy") << "use_proxy = " << (use_proxy?'Y':'N') << ", env_proxy = " << (!env_proxy.empty() ? env_proxy : "(null)") << LL_ENDL;
if (use_proxy && !env_proxy.empty())
{
mDetail->mCurlRequest->setoptString(CURLOPT_PROXY, env_proxy);
}
else
{
mDetail->mCurlRequest->setoptString(CURLOPT_PROXY, "");
}
AICurlEasyRequest_wat curlEasyRequest_w(*mDetail->mCurlEasyRequest);
curlEasyRequest_w->setoptString(CURLOPT_PROXY, (use_proxy && !env_proxy.empty()) ? env_proxy : std::string(""));
}
void LLURLRequest::useProxy(const std::string &proxy)
{
mDetail->mCurlRequest->setoptString(CURLOPT_PROXY, proxy);
AICurlEasyRequest_wat curlEasyRequest_w(*mDetail->mCurlEasyRequest);
curlEasyRequest_w->setoptString(CURLOPT_PROXY, proxy);
}
void LLURLRequest::allowCookies()
{
mDetail->mCurlRequest->setoptString(CURLOPT_COOKIEFILE, "");
AICurlEasyRequest_wat curlEasyRequest_w(*mDetail->mCurlEasyRequest);
curlEasyRequest_w->setoptString(CURLOPT_COOKIEFILE, "");
}
//virtual
bool LLURLRequest::hasExpiration(void) const
{
// 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
@@ -237,7 +254,27 @@ 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;
@@ -252,9 +289,21 @@ LLIOPipe::EStatus LLURLRequest::handleError(
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");
static LLFastTimer::DeclareTimer FTM_PROCESS_URL_REQUEST_GET_RESULT("Get Result");
static LLFastTimer::DeclareTimer FTM_URL_PERFORM("Perform");
// virtual
LLIOPipe::EStatus LLURLRequest::process_impl(
@@ -269,9 +318,10 @@ LLIOPipe::EStatus LLURLRequest::process_impl(
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 prcessing, check how many
// we're still waiting or processing, check how many
// bytes we have accumulated.
const S32 MIN_ACCUMULATION = 100000;
if(pump && (mDetail->mByteAccumulator > MIN_ACCUMULATION))
@@ -309,44 +359,42 @@ LLIOPipe::EStatus LLURLRequest::process_impl(
// *FIX: bit of a hack, but it should work. The configure and
// callback method expect this information to be ready.
mDetail->mResponseBuffer = buffer.get();
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.
// *FIX: Maybe we should just go to the next state now...
return STATUS_BREAK;
}
case STATE_WAITING_FOR_RESPONSE:
case STATE_PROCESSING_RESPONSE:
{
PUMP_DEBUG;
LLIOPipe::EStatus status = STATUS_BREAK;
if (!mRemoved) // Not removed from multi handle yet?
{
LLFastTimer t(FTM_URL_PERFORM);
mDetail->mCurlRequest->perform();
// 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.
while(1)
// Left braces in order not to change indentation.
{
CURLcode result;
bool newmsg = false;
{
LLFastTimer t(FTM_PROCESS_URL_REQUEST_GET_RESULT);
newmsg = mDetail->mCurlRequest->getResult(&result);
}
if(!newmsg)
{
// keep processing
break;
}
AICurlEasyRequest_wat(*mDetail->mCurlEasyRequest)->getResult(&result);
mState = STATE_HAVE_RESPONSE;
context[CONTEXT_REQUEST][CONTEXT_TRANSFERED_BYTES] = mRequestTransferedBytes;
context[CONTEXT_RESPONSE][CONTEXT_TRANSFERED_BYTES] = mResponseTransferedBytes;
@@ -378,6 +426,7 @@ LLIOPipe::EStatus LLURLRequest::process_impl(
}
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:
@@ -419,10 +468,15 @@ void LLURLRequest::initialize()
{
LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
mState = STATE_INITIALIZED;
// This might throw AICurlNoEasyHandle.
mDetail = new LLURLRequestDetail;
mDetail->mCurlRequest->setopt(CURLOPT_NOSIGNAL, 1);
mDetail->mCurlRequest->setWriteCallback(&downCallback, (void*)this);
mDetail->mCurlRequest->setReadCallback(&upCallback, (void*)this);
{
AICurlEasyRequest_wat curlEasyRequest_w(*mDetail->mCurlEasyRequest);
curlEasyRequest_w->setWriteCallback(&downCallback, (void*)this);
curlEasyRequest_w->setReadCallback(&upCallback, (void*)this);
}
mRequestTransferedBytes = 0;
mResponseTransferedBytes = 0;
}
@@ -437,70 +491,66 @@ bool LLURLRequest::configure()
S32 bytes = mDetail->mResponseBuffer->countAfter(
mDetail->mChannels.in(),
NULL);
switch(mAction)
{
case HTTP_HEAD:
mDetail->mCurlRequest->setopt(CURLOPT_HEADER, 1);
mDetail->mCurlRequest->setopt(CURLOPT_NOBODY, 1);
mDetail->mCurlRequest->setopt(CURLOPT_FOLLOWLOCATION, 1);
rv = true;
break;
case HTTP_GET:
mDetail->mCurlRequest->setopt(CURLOPT_HTTPGET, 1);
mDetail->mCurlRequest->setopt(CURLOPT_FOLLOWLOCATION, 1);
AICurlEasyRequest_wat curlEasyRequest_w(*mDetail->mCurlEasyRequest);
switch(mAction)
{
case HTTP_HEAD:
curlEasyRequest_w->setopt(CURLOPT_HEADER, 1);
curlEasyRequest_w->setopt(CURLOPT_NOBODY, 1);
curlEasyRequest_w->setopt(CURLOPT_FOLLOWLOCATION, 1);
rv = true;
break;
// Set Accept-Encoding to allow response compression
mDetail->mCurlRequest->setoptString(CURLOPT_ENCODING, "");
rv = true;
break;
case HTTP_GET:
curlEasyRequest_w->setopt(CURLOPT_HTTPGET, 1);
curlEasyRequest_w->setopt(CURLOPT_FOLLOWLOCATION, 1);
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.
addHeader("Expect:");
// Set Accept-Encoding to allow response compression
curlEasyRequest_w->setoptString(CURLOPT_ENCODING, "");
rv = true;
break;
mDetail->mCurlRequest->setopt(CURLOPT_UPLOAD, 1);
mDetail->mCurlRequest->setopt(CURLOPT_INFILESIZE, bytes);
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.
curlEasyRequest_w->addHeader("Expect:");
curlEasyRequest_w->setopt(CURLOPT_UPLOAD, 1);
curlEasyRequest_w->setopt(CURLOPT_INFILESIZE, bytes);
rv = true;
break;
case HTTP_POST:
// Disable the expect http 1.1 extension. POST and PUT default
// to turning this on, and I am not too sure what it means.
addHeader("Expect:");
case HTTP_POST:
// Set the handle for an http post
curlEasyRequest_w->setPost(bytes);
// Disable the content type http header.
// *FIX: what should it be?
addHeader("Content-Type:");
// Set Accept-Encoding to allow response compression
curlEasyRequest_w->setoptString(CURLOPT_ENCODING, "");
rv = true;
break;
// Set the handle for an http post
mDetail->mCurlRequest->setPost(NULL, bytes);
case HTTP_DELETE:
// Set the handle for an http post
curlEasyRequest_w->setoptString(CURLOPT_CUSTOMREQUEST, "DELETE");
rv = true;
break;
// Set Accept-Encoding to allow response compression
mDetail->mCurlRequest->setoptString(CURLOPT_ENCODING, "");
rv = true;
break;
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;
case HTTP_DELETE:
// Set the handle for an http post
mDetail->mCurlRequest->setoptString(CURLOPT_CUSTOMREQUEST, "DELETE");
rv = true;
break;
case HTTP_MOVE:
// Set the handle for an http post
mDetail->mCurlRequest->setoptString(CURLOPT_CUSTOMREQUEST, "MOVE");
// *NOTE: should we check for the Destination header?
rv = true;
break;
default:
llwarns << "Unhandled URLRequest action: " << mAction << llendl;
break;
}
if(rv)
{
mDetail->mCurlRequest->sendRequest(mDetail->mURL);
default:
llwarns << "Unhandled URLRequest action: " << mAction << llendl;
break;
}
if(rv)
{
curlEasyRequest_w->finalizeRequest(mDetail->mURL);
curlEasyRequest_w->send_events_to(this);
}
}
return rv;
}
@@ -564,9 +614,8 @@ size_t LLURLRequest::upCallback(
return bytes;
}
static size_t headerCallback(void* data, size_t size, size_t nmemb, void* user)
static size_t headerCallback(char* header_line, size_t size, size_t nmemb, void* user)
{
const char* header_line = (const char*)data;
size_t header_len = size * nmemb;
LLURLRequestComplete* complete = (LLURLRequestComplete*)user;
@@ -632,42 +681,6 @@ static size_t headerCallback(void* data, size_t size, size_t nmemb, void* user)
return header_len;
}
static LLFastTimer::DeclareTimer FTM_PROCESS_URL_EXTRACTOR("URL Extractor");
/**
* LLContextURLExtractor
*/
// virtual
LLIOPipe::EStatus LLContextURLExtractor::process_impl(
const LLChannelDescriptors& channels,
buffer_ptr_t& buffer,
bool& eos,
LLSD& context,
LLPumpIO* pump)
{
LLFastTimer t(FTM_PROCESS_URL_EXTRACTOR);
PUMP_DEBUG;
LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
// The destination host is in the context.
if(context.isUndefined() || !mRequest)
{
return STATUS_PRECONDITION_NOT_MET;
}
// copy in to out, since this just extract the URL and does not
// actually change the data.
LLChangeChannel change(channels.in(), channels.out());
std::for_each(buffer->beginSegment(), buffer->endSegment(), change);
// find the context url
if(context.has(CONTEXT_DEST_URI_SD_LABEL))
{
mRequest->setURL(context[CONTEXT_DEST_URI_SD_LABEL].asString());
return STATUS_DONE;
}
return STATUS_ERROR;
}
/**
* LLURLRequestComplete
*/

View File

@@ -41,7 +41,6 @@
#include "llcurl.h"
extern const std::string CONTEXT_REQUEST;
extern const std::string CONTEXT_DEST_URI_SD_LABEL;
extern const std::string CONTEXT_RESPONSE;
extern const std::string CONTEXT_TRANSFERED_BYTES;
@@ -65,7 +64,7 @@ typedef struct x509_store_ctx_st X509_STORE_CTX;
* worth the time and effort to eventually port this to a raw client
* socket.
*/
class LLURLRequest : public LLIOPipe
class LLURLRequest : public LLIOPipe, protected AICurlEasyHandleEvents
{
LOG_CLASS(LLURLRequest);
public:
@@ -188,6 +187,9 @@ public:
*/
void allowCookies();
/*virtual*/ bool hasExpiration(void) const;
/*virtual*/ bool hasNotExpired(void) const;
public:
/**
* @brief Give this pipe a chance to handle a generated error
@@ -212,6 +214,7 @@ protected:
STATE_INITIALIZED,
STATE_WAITING_FOR_RESPONSE,
STATE_PROCESSING_RESPONSE,
STATE_CURL_FINISHED,
STATE_HAVE_RESPONSE,
};
EState mState;
@@ -221,6 +224,14 @@ protected:
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.
@@ -259,42 +270,6 @@ private:
LLURLRequest(const LLURLRequest&);
};
/**
* @class LLContextURLExtractor
* @brief This class unpacks the url out of a agent usher service so
* it can be packed into a LLURLRequest object.
* @see LLIOPipe
*
* This class assumes that the context is a map that contains an entry
* named CONTEXT_DEST_URI_SD_LABEL.
*/
class LLContextURLExtractor : public LLIOPipe
{
public:
LLContextURLExtractor(LLURLRequest* req) : mRequest(req) {}
~LLContextURLExtractor() {}
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);
//@}
protected:
LLURLRequest* mRequest;
};
/**
* @class LLURLRequestComplete
* @brief Class which can optionally be used with an LLURLRequest to
@@ -367,11 +342,4 @@ protected:
EStatus mRequestStatus;
};
/**
* External constants
*/
extern const std::string CONTEXT_DEST_URI_SD_LABEL;
#endif // LL_LLURLREQUEST_H

View File

@@ -81,6 +81,7 @@
#include "v4math.h"
#include "lltransfertargetvfile.h"
#include "llmemtype.h"
#include "llpacketring.h"
// Constants
//const char* MESSAGE_LOG_FILENAME = "message.log";
@@ -243,7 +244,8 @@ LLMessageSystem::LLMessageSystem(const std::string& filename, U32 port,
bool failure_is_fatal,
const F32 circuit_heartbeat_interval, const F32 circuit_timeout) :
mCircuitInfo(circuit_heartbeat_interval, circuit_timeout),
mLastMessageFromTrustedMessageService(false)
mLastMessageFromTrustedMessageService(false),
mPacketRing(new LLPacketRing)
{
init();
@@ -383,6 +385,9 @@ LLMessageSystem::~LLMessageSystem()
delete mPollInfop;
mPollInfop = NULL;
delete mPacketRing;
mPacketRing = NULL;
mIncomingCompressedSize = 0;
mCurrentRecvPacketID = 0;
}
@@ -548,13 +553,13 @@ BOOL LLMessageSystem::checkMessages( S64 frame_count )
U8* buffer = mTrueReceiveBuffer;
mTrueReceiveSize = mPacketRing.receivePacket(mSocket, (char *)mTrueReceiveBuffer);
mTrueReceiveSize = mPacketRing->receivePacket(mSocket, (char *)mTrueReceiveBuffer);
// If you want to dump all received packets into SecondLife.log, uncomment this
//dumpPacketToLog();
receive_size = mTrueReceiveSize;
mLastSender = mPacketRing.getLastSender();
mLastReceivingIF = mPacketRing.getLastReceivingInterface();
mLastSender = mPacketRing->getLastSender();
mLastReceivingIF = mPacketRing->getLastReceivingInterface();
if (receive_size < (S32) LL_MINIMUM_VALID_PACKET_SIZE)
{
@@ -1129,7 +1134,7 @@ S32 LLMessageSystem::flushReliable(const LLHost &host)
return send_bytes;
}
LLHTTPClient::ResponderPtr LLMessageSystem::createResponder(const std::string& name)
LLFnPtrResponder* LLMessageSystem::createResponder(const std::string& name)
{
if(mSendReliable)
{
@@ -1328,7 +1333,7 @@ S32 LLMessageSystem::sendMessage(const LLHost &host)
}
BOOL success;
success = mPacketRing.sendPacket(mSocket, (char *)buf_ptr, buffer_length, host);
success = mPacketRing->sendPacket(mSocket, (char *)buf_ptr, buffer_length, host);
if (!success)
{
@@ -3361,7 +3366,7 @@ void LLMessageSystem::establishBidirectionalTrust(const LLHost &host, S64 frame_
void LLMessageSystem::dumpPacketToLog()
{
LL_WARNS("Messaging") << "Packet Dump from:" << mPacketRing.getLastSender() << llendl;
LL_WARNS("Messaging") << "Packet Dump from:" << mPacketRing->getLastSender() << llendl;
LL_WARNS("Messaging") << "Packet Size:" << mTrueReceiveSize << llendl;
char line_buffer[256]; /* Flawfinder: ignore */
S32 i;

View File

@@ -48,9 +48,9 @@
#include "string_table.h"
#include "llcircuit.h"
#include "lltimer.h"
#include "llpacketring.h"
//#include "llpacketring.h"
#include "llhost.h"
#include "llhttpclient.h"
//#include "llhttpclient.h"
#include "llhttpnode.h"
#include "llpacketack.h"
#include "llsingleton.h"
@@ -61,6 +61,12 @@
#include "llstoredmessage.h"
class LLPacketRing;
namespace
{
class LLFnPtrResponder;
}
const U32 MESSAGE_MAX_STRINGS_LENGTH = 64;
const U32 MESSAGE_NUMBER_OF_HASH_BUCKETS = 8192;
@@ -213,7 +219,7 @@ class LLMessageSystem : public LLMessageSenderInterface
LLHost mUntrustedInterface;
public:
LLPacketRing mPacketRing;
LLPacketRing* mPacketRing;
LLReliablePacketParams mReliablePacketParams;
// Set this flag to TRUE when you want *very* verbose logs.
@@ -494,7 +500,7 @@ public:
void (*callback)(void **,S32),
void ** callback_data);
LLHTTPClient::ResponderPtr createResponder(const std::string& name);
LLFnPtrResponder* createResponder(const std::string& name);
S32 sendMessage(const LLHost &host);
S32 sendMessage(const U32 circuit);
private:

View File

@@ -125,7 +125,7 @@ int LLPluginInstance::load(const std::string& plugin_dir, std::string &plugin_fi
buf[0] = 0;
if (error)
{
strncpy(buf, dlerror(), sizeof(buf));
strncpy(buf, error, sizeof(buf));
}
buf[sizeof(buf) - 1] = 0;
}

View File

@@ -379,6 +379,7 @@ void LLPluginProcessParent::idle(void)
}
else
{
// Set PluginAttachDebuggerToPlugins to TRUE to use this. You might also want to set DebugPluginDisableTimeout to TRUE.
if(mDebug)
{
// If we're set to debug, start up a gdb instance in a new terminal window and have it attach to the plugin process and continue.
@@ -400,14 +401,37 @@ void LLPluginProcessParent::idle(void)
mDebugger.launch();
#elif LL_LINUX
// The command we're constructing would look like this on the command line:
// /usr/bin/xterm -geometry 160x24-0+0 -e '/usr/bin/gdb -n /proc/12345/exe 12345'
// This can be changed by setting the following environment variables, for example:
// export LL_DEBUG_TERMINAL_COMMAND="/usr/bin/gnome-terminal --geometry=165x24-0+0 -e %s"
// /usr/bin/xterm -geometry 160x24-0+0 -e /usr/bin/gdb -n /proc/12345/exe 12345
// Note that most terminals demand that all arguments to the process that is
// started with -e are passed as arguments to the terminal: there are no quotes
// around '/usr/bin/gdb -n /proc/12345/exe 12345'. This is the case for xterm,
// uxterm, konsole etc. The exception might be gnome-terminal.
//
// The constructed command can be changed by setting the following environment
// variables, for example:
//
// export LL_DEBUG_GDB_PATH=/usr/bin/gdb
// export LL_DEBUG_TERMINAL_COMMAND='/usr/bin/gnome-terminal --geometry=165x24-0+0 -e "%s"'
//
// Or, as second example, if you are running the viewer on host 'A', and you want
// to open the gdb terminal on the X display of host 'B', you would run on host B:
// 'ssh -X A' (and then start the viewer, or just leave the terminal open), and
// then use:
//
// export LL_DEBUG_TERMINAL_COMMAND="/usr/bin/uxterm -fs 9 -fa 'DejaVu Sans Mono' -display localhost:10 -geometry 209x31+0-50 -e %s"
//
// which would open the terminal on B (no quotes around the %s, since this uses uxterm!).
// For a list of available strings to pass to the -fa, run in a terminal: fc-list :scalable=true:spacing=mono: family
char const* env;
std::string const terminal_command = (env = getenv("LL_DEBUG_TERMINAL_COMMAND")) ? env : "/usr/bin/xterm -geometry 160x24+0+0 -e %s";
std::string terminal_command = (env = getenv("LL_DEBUG_TERMINAL_COMMAND")) ? env : "/usr/bin/xterm -geometry 160x24+0+0 -e %s";
char const* const gdb_path = (env = getenv("LL_DEBUG_GDB_PATH")) ? env : "/usr/bin/gdb";
cmd << gdb_path << " -n /proc/" << mProcess.getProcessID() << "/exe " << mProcess.getProcessID();
std::string::size_type pos = terminal_command.find("%s");
if (pos != std::string::npos)
{
terminal_command.replace(pos, 2, cmd.str());
}
typedef boost::tokenizer< boost::escaped_list_separator<
char>, std::basic_string<
@@ -429,14 +453,7 @@ void LLPluginProcessParent::idle(void)
mDebugger.setExecutable(*token);
while (++token != tokens.end())
{
if (*token == "%s")
{
mDebugger.addArgument(cmd.str());
}
else
{
mDebugger.addArgument(*token);
}
mDebugger.addArgument(*token);
}
mDebugger.launch();
#endif

View File

@@ -1416,7 +1416,7 @@ BOOL LLWindowWin32::switchContext(BOOL fullscreen, const LLCoordScreen &size, BO
{
found_format = TRUE;
}
else if(cur_format >= num_formats-1)
else if(cur_format >= (S32)num_formats-1)
{
cur_format = 0;
found_format = TRUE;

View File

@@ -42,6 +42,7 @@
#include "v4color.h"
#include "v4coloru.h"
#include "llinstancetracker.h"
#include "llrefcount.h"
#include "llcontrolgroupreader.h"

View File

@@ -133,6 +133,7 @@ set(viewer_SOURCE_FILES
llconfirmationmanager.cpp
llconsole.cpp
llcontainerview.cpp
llcurlrequest.cpp
llcurrencyuimanager.cpp
llcylinder.cpp
lldaycyclemanager.cpp
@@ -634,6 +635,7 @@ set(viewer_HEADER_FILES
llconfirmationmanager.h
llconsole.h
llcontainerview.h
llcurlrequest.h
llcurrencyuimanager.h
llcylinder.h
lldaycyclemanager.h

View File

@@ -4227,17 +4227,6 @@
<key>Value</key>
<real>120.0</real>
</map>
<key>CurlUseMultipleThreads</key>
<map>
<key>Comment</key>
<string>Use background threads for executing curl_multi_perform (requires restart)</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>1</integer>
</map>
<key>Cursor3D</key>
<map>
<key>Comment</key>

View File

@@ -6,15 +6,18 @@
#ifndef CURL_STATICLIB
#define CURL_STATICLIB 1
#endif
#include <curl/curl.h>
#include <stdtypes.h>
#include <llbufferstream.h>
#include <llerror.h>
#include <llhttpclient.h>
#include <llurlrequest.h>
#include <llxmltree.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
// ********************************************************************
@@ -255,7 +258,16 @@ static void request(const std::string &url,
}
LLPumpIO::chain_t chain;
LLURLRequest *req = new LLURLRequest(method, url);
LLURLRequest *req;
try
{
req = new LLURLRequest(method, url);
}
catch(AICurlNoEasyHandle const& error)
{
llwarns << "Failed to create LLURLRequest: " << error.what() << llendl;
return;
}
req->checkRootCertificate(true);
/*
@@ -324,10 +336,11 @@ int HippoRestRequest::getBlocking(const std::string &url, std::string *result)
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_CAINFO, gDirUtilp->getCAFile().c_str());
curl_easy_setopt(curlp, CURLOPT_WRITEFUNCTION, curlWrite);
curl_easy_setopt(curlp, CURLOPT_WRITEDATA, result);
@@ -337,7 +350,7 @@ int HippoRestRequest::getBlocking(const std::string &url, std::string *result)
*result = "";
S32 curlSuccess = curl_easy_perform(curlp);
S32 httpStatus = 499;
long httpStatus = 499L; // curl_easy_getinfo demands pointer to long.
curl_easy_getinfo(curlp, CURLINFO_RESPONSE_CODE, &httpStatus);
if (curlSuccess != 0) {

View File

@@ -139,15 +139,11 @@ fi
export VIEWER_BINARY='singularity-do-not-run-directly'
BINARY_TYPE=$(expr match "$(file -b bin/$VIEWER_BINARY)" '\(.*executable\)')
QPP=qt4/plugins/imageformats/
if [ "${BINARY_TYPE}" == "ELF 64-bit LSB executable" ]; then
QTPLUGINS=/usr/lib64/$QPP:/lib64/$QPP:/usr/local/lib64/$QPP
SL_ENV+='LD_LIBRARY_PATH="`pwd`/lib64:`pwd`/lib32:$QTPLUGINS:$LD_LIBRARY_PATH"'
SL_ENV+='LD_LIBRARY_PATH="`pwd`/lib64:`pwd`/lib32:$LD_LIBRARY_PATH"'
else
QTPLUGINS=/usr/lib/$QPP:/lib/$QPP:/usr/local/lib/$QPP
SL_ENV+='LD_LIBRARY_PATH="`pwd`/lib:$QTPLUGINS:$LD_LIBRARY_PATH"'
SL_ENV+='LD_LIBRARY_PATH="`pwd`/lib:$LD_LIBRARY_PATH"'
fi
export SL_CMD='$LL_WRAPPER bin/$VIEWER_BINARY'
export SL_OPT="`cat gridargs.dat` $@"

View File

@@ -2401,7 +2401,7 @@ bool LLAgent::sendMaturityPreferenceToServer(int preferredMaturity)
body["access_prefs"] = access_prefs;
llinfos << "Sending access prefs update to " << (access_prefs["max"].asString()) << " via capability to: "
<< url << llendl;
LLHTTPClient::post(url, body, new LLHTTPClient::Responder()); // Ignore response
LLHTTPClient::post(url, body, new LLHTTPClient::ResponderIgnore); // Ignore response
return true;
}
return false;

View File

@@ -61,7 +61,7 @@ bool LLAgentLanguage::update()
body["language"] = language;
body["language_is_public"] = gSavedSettings.getBOOL("LanguageIsPublic");
LLHTTPClient::post(url, body, new LLHTTPClient::Responder);
LLHTTPClient::post(url, body, new LLHTTPClient::ResponderIgnore);
}
return true;
}

View File

@@ -63,7 +63,6 @@
#include "llviewerjoystick.h"
#include "llfloaterjoystick.h"
#include "llares.h"
#include "llcurl.h"
#include "llfloatersnapshot.h"
#include "lltexturestats.h"
#include "llviewerwindow.h"
@@ -611,6 +610,9 @@ bool LLAppViewer::init()
initLogging();
// 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();
@@ -635,11 +637,6 @@ bool LLAppViewer::init()
LLPrivateMemoryPoolManager::initClass((BOOL)gSavedSettings.getBOOL("MemoryPrivatePoolEnabled"), (U32)gSavedSettings.getU32("MemoryPrivatePoolSize")) ;
mAlloc.setProfilingEnabled(gSavedSettings.getBOOL("MemProfiling"));
// *NOTE:Mani - LLCurl::initClass is not thread safe.
// Called before threads are created.
LLCurl::initClass(gSavedSettings.getF32("CurlRequestTimeOut"));
LL_INFOS("InitInfo") << "LLCurl initialized." << LL_ENDL ;
initThreads();
LL_INFOS("InitInfo") << "Threads initialized." << LL_ENDL ;
@@ -1742,22 +1739,20 @@ bool LLAppViewer::cleanup()
delete gVFS;
gVFS = NULL;
// Cleanup settings last in case other clases reference them
gSavedSettings.cleanup();
gColors.cleanup();
gCrashSettings.cleanup();
LLWatchdog::getInstance()->cleanup();
llinfos << "Shutting down message system" << llendflush;
end_messaging_system();
llinfos << "Message system deleted." << llendflush;
LLUserAuth::getInstance()->reset(); //reset before LLCurl::cleanupClass, else LLCURL::sHandleMutex == NULL
// *NOTE:Mani - The following call is not thread safe.
LLCurl::cleanupClass();
llinfos << "LLCurl cleaned up." << llendflush;
LLApp::stopErrorThread(); // The following call is not thread-safe. Have to stop all threads.
AICurlInterface::cleanupCurl();
// Cleanup settings last in case other classes reference them.
gSavedSettings.cleanup();
gColors.cleanup();
gCrashSettings.cleanup();
// If we're exiting to launch an URL, do that here so the screen
// is at the right resolution before we launch IE.
if (!gLaunchFileOnQuit.empty())
@@ -1825,6 +1820,8 @@ bool LLAppViewer::initThreads()
LLWatchdog::getInstance()->init(watchdog_killer_callback);
}
AICurlInterface::startCurlThread();
LLImage::initClass();
LLVFSThread::initClass(enable_threads && false);

View File

@@ -0,0 +1,146 @@
/**
* @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

@@ -0,0 +1,57 @@
/**
* @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

@@ -266,7 +266,7 @@ bool LLCurrencyUIManager::Impl::checkTransaction()
return false;
}
if (!mTransaction->process())
if (!mTransaction->is_finished())
{
return false;
}

View File

@@ -866,7 +866,7 @@ bool LLFloaterBuyLandUI::checkTransaction()
return false;
}
if (!mTransaction->process())
if (!mTransaction->is_finished())
{
return false;
}

View File

@@ -762,7 +762,7 @@ BOOL LLPanelRegionGeneralInfo::sendUpdate()
body["allow_parcel_changes"] = childGetValue("allow_parcel_changes_check");
body["block_parcel_search"] = childGetValue("block_parcel_search_check");
LLHTTPClient::post(url, body, new LLHTTPClient::Responder());
LLHTTPClient::post(url, body, new LLHTTPClient::ResponderIgnore);
}
else
{

View File

@@ -43,6 +43,7 @@
#include "lluictrlfactory.h"
#include "llwindow.h"
#include "llviewerwindow.h"
#include "llhttpclient.h"
static LLFloaterURLEntry* sInstance = NULL;

View File

@@ -99,7 +99,7 @@ std::string LLLogChat::timestamp(bool withdate)
//static
void LLLogChat::saveHistory(std::string filename, std::string line)
void LLLogChat::saveHistory(std::string const& filename, std::string line)
{
if(!filename.size())
{
@@ -120,7 +120,7 @@ void LLLogChat::saveHistory(std::string filename, std::string line)
}
}
void LLLogChat::loadHistory(std::string filename , void (*callback)(ELogLineType,std::string,void*), void* userdata)
void LLLogChat::loadHistory(std::string const& filename , void (*callback)(ELogLineType,std::string,void*), void* userdata)
{
if(!filename.size())
{

View File

@@ -34,6 +34,8 @@
#ifndef LL_LLLOGCHAT_H
#define LL_LLLOGCHAT_H
#include <string>
class LLLogChat
{
public:
@@ -44,9 +46,9 @@ public:
LOG_END
};
static std::string timestamp(bool withdate = false);
static std::string makeLogFileName(std::string(filename));
static void saveHistory(std::string filename, std::string line);
static void loadHistory(std::string filename,
static std::string makeLogFileName(std::string filename);
static void saveHistory(std::string const& filename, std::string line);
static void loadHistory(std::string const& filename,
void (*callback)(ELogLineType,std::string,void*),
void* userdata);
private:

View File

@@ -35,7 +35,7 @@
#include "llappviewer.h"
#include "llbufferstream.h"
#include "llcallbacklist.h"
#include "llcurl.h"
#include "llcurlrequest.h"
#include "lldatapacker.h"
#include "llfasttimer.h"
#if MESH_IMPORT
@@ -465,7 +465,6 @@ public:
LLMeshRepoThread::LLMeshRepoThread()
: LLThread("mesh repo")
{
mWaiting = false;
mMutex = new LLMutex();
mHeaderMutex = new LLMutex();
mSignal = new LLCondition();
@@ -483,7 +482,7 @@ LLMeshRepoThread::~LLMeshRepoThread()
void LLMeshRepoThread::run()
{
mCurlRequest = new LLCurlRequest();
mCurlRequest = new AICurlInterface::Request;
#if MESH_IMPORT
LLCDResult res = LLConvexDecomposition::initThread();
if (res != LLCD_OK)
@@ -492,13 +491,10 @@ void LLMeshRepoThread::run()
}
#endif //MESH_IMPORT
mSignal->lock();
while (!LLApp::isQuitting())
{
mWaiting = true;
mSignal->wait();
mWaiting = false;
if (!LLApp::isQuitting())
// Left braces in order not to change the indentation.
{
static U32 count = 0;
@@ -511,38 +507,53 @@ void LLMeshRepoThread::run()
}
// NOTE: throttling intentionally favors LOD requests over header requests
while (!mLODReqQ.empty() && count < MAX_MESH_REQUESTS_PER_SECOND && sActiveLODRequests < (S32)sMaxConcurrentRequests)
{
if (mMutex)
{
mMutex->lock();
LODRequest req = mLODReqQ.front();
mLODReqQ.pop();
LLMeshRepository::sLODProcessing--;
mMutex->unlock();
if (!fetchMeshLOD(req.mMeshParams, req.mLOD, count))//failed, resubmit
try
{
fetchMeshLOD(req.mMeshParams, req.mLOD, count);
}
catch(AICurlNoEasyHandle const& error)
{
llwarns << "fetchMeshLOD() failed: " << error.what() << llendl;
mMutex->lock();
mLODReqQ.push(req) ;
LLMeshRepository::sLODProcessing++;
mLODReqQ.push(req);
mMutex->unlock();
break;
}
}
}
while (!mHeaderReqQ.empty() && count < MAX_MESH_REQUESTS_PER_SECOND && sActiveHeaderRequests < (S32)sMaxConcurrentRequests)
{
if (mMutex)
{
mMutex->lock();
HeaderRequest req = mHeaderReqQ.front();
mHeaderReqQ.pop();
mMutex->unlock();
if (!fetchMeshHeader(req.mMeshParams, count))//failed, resubmit
bool success = false;
try
{
success = fetchMeshHeader(req.mMeshParams, count);
}
catch(AICurlNoEasyHandle const& error)
{
llwarns << "fetchMeshHeader() failed: " << error.what() << llendl;
}
if (!success)
{
mMutex->lock();
mHeaderReqQ.push(req) ;
mMutex->unlock();
break;
}
}
}
@@ -552,7 +563,16 @@ void LLMeshRepoThread::run()
for (std::set<LLUUID>::iterator iter = mSkinRequests.begin(); iter != mSkinRequests.end(); ++iter)
{
LLUUID mesh_id = *iter;
if (!fetchMeshSkinInfo(mesh_id))
bool success = false;
try
{
success = fetchMeshSkinInfo(mesh_id);
}
catch(AICurlNoEasyHandle const& error)
{
llwarns << "fetchMeshSkinInfo(" << mesh_id << ") failed: " << error.what() << llendl;
}
if (!success)
{
incomplete.insert(mesh_id);
}
@@ -565,7 +585,16 @@ void LLMeshRepoThread::run()
for (std::set<LLUUID>::iterator iter = mDecompositionRequests.begin(); iter != mDecompositionRequests.end(); ++iter)
{
LLUUID mesh_id = *iter;
if (!fetchMeshDecomposition(mesh_id))
bool success = false;
try
{
success = fetchMeshDecomposition(mesh_id);
}
catch(AICurlNoEasyHandle const& error)
{
llwarns << "fetchMeshDecomposition(" << mesh_id << ") failed: " << error.what() << llendl;
}
if (!success)
{
incomplete.insert(mesh_id);
}
@@ -578,7 +607,16 @@ void LLMeshRepoThread::run()
for (std::set<LLUUID>::iterator iter = mPhysicsShapeRequests.begin(); iter != mPhysicsShapeRequests.end(); ++iter)
{
LLUUID mesh_id = *iter;
if (!fetchMeshPhysicsShape(mesh_id))
bool success = false;
try
{
success = fetchMeshPhysicsShape(mesh_id);
}
catch(AICurlNoEasyHandle const& error)
{
llwarns << "fetchMeshPhysicsShape(" << mesh_id << ") failed: " << error.what() << llendl;
}
if (!success)
{
incomplete.insert(mesh_id);
}
@@ -586,14 +624,11 @@ void LLMeshRepoThread::run()
mPhysicsShapeRequests = incomplete;
}
mCurlRequest->process();
}
mSignal->wait();
}
if (mSignal->isLocked())
{ //make sure to let go of the mutex associated with the given signal before shutting down
mSignal->unlock();
}
mSignal->unlock();
#if MESH_IMPORT
res = LLConvexDecomposition::quitThread();
@@ -645,7 +680,7 @@ void LLMeshRepoThread::loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod)
if (pending != mPendingLOD.end())
{ //append this lod request to existing header request
pending->second.push_back(lod);
llassert(pending->second.size() <= LLModel::NUM_LODS)
llassert(pending->second.size() <= LLModel::NUM_LODS);
}
else
{ //if no header request is pending, fetch header
@@ -681,21 +716,15 @@ std::string LLMeshRepoThread::constructUrl(LLUUID mesh_id)
bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id)
{ //protected by mMutex
if (!mHeaderMutex)
{
return false;
}
mHeaderMutex->lock();
if (mMeshHeader.find(mesh_id) == mMeshHeader.end())
{ //we have no header info for this mesh, do nothing
{
// We have no header info for this mesh, try again later.
mHeaderMutex->unlock();
return false;
}
bool ret = true ;
U32 header_size = mMeshHeaderSize[mesh_id];
if (header_size > 0)
@@ -743,12 +772,10 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id)
std::string http_url = constructUrl(mesh_id);
if (!http_url.empty())
{
ret = mCurlRequest->getByteRange(http_url, headers, offset, size,
new LLMeshSkinInfoResponder(mesh_id, offset, size));
if(ret)
{
LLMeshRepository::sHTTPRequestCount++;
}
// This might throw AICurlNoEasyHandle.
mCurlRequest->getByteRange(http_url, headers, offset, size,
new LLMeshSkinInfoResponder(mesh_id, offset, size));
LLMeshRepository::sHTTPRequestCount++;
}
}
}
@@ -758,26 +785,22 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id)
}
//early out was not hit, effectively fetched
return ret;
return true;
}
//return false if failed to get header
bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id)
{ //protected by mMutex
if (!mHeaderMutex)
{
return false;
}
mHeaderMutex->lock();
if (mMeshHeader.find(mesh_id) == mMeshHeader.end())
{ //we have no header info for this mesh, do nothing
{
// We have no header info for this mesh, try again later.
mHeaderMutex->unlock();
return false;
}
U32 header_size = mMeshHeaderSize[mesh_id];
bool ret = true ;
if (header_size > 0)
{
@@ -824,12 +847,10 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id)
std::string http_url = constructUrl(mesh_id);
if (!http_url.empty())
{
ret = mCurlRequest->getByteRange(http_url, headers, offset, size,
new LLMeshDecompositionResponder(mesh_id, offset, size));
if(ret)
{
LLMeshRepository::sHTTPRequestCount++;
}
// This might throw AICurlNoEasyHandle.
mCurlRequest->getByteRange(http_url, headers, offset, size,
new LLMeshDecompositionResponder(mesh_id, offset, size));
LLMeshRepository::sHTTPRequestCount++;
}
}
}
@@ -839,26 +860,22 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id)
}
//early out was not hit, effectively fetched
return ret;
return true;
}
//return false if failed to get header
bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id)
{ //protected by mMutex
if (!mHeaderMutex)
{
return false;
}
mHeaderMutex->lock();
if (mMeshHeader.find(mesh_id) == mMeshHeader.end())
{ //we have no header info for this mesh, do nothing
{
// We have no header info for this mesh, retry later.
mHeaderMutex->unlock();
return false;
}
U32 header_size = mMeshHeaderSize[mesh_id];
bool ret = true ;
if (header_size > 0)
{
@@ -905,13 +922,10 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id)
std::string http_url = constructUrl(mesh_id);
if (!http_url.empty())
{
ret = mCurlRequest->getByteRange(http_url, headers, offset, size,
new LLMeshPhysicsShapeResponder(mesh_id, offset, size));
if(ret)
{
LLMeshRepository::sHTTPRequestCount++;
}
// This might throw AICurlNoEasyHandle.
mCurlRequest->getByteRange(http_url, headers, offset, size,
new LLMeshPhysicsShapeResponder(mesh_id, offset, size));
LLMeshRepository::sHTTPRequestCount++;
}
}
else
@@ -925,7 +939,7 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id)
}
//early out was not hit, effectively fetched
return ret;
return true;
}
//return false if failed to get header
@@ -944,14 +958,14 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, U32& c
LLMeshRepository::sCacheBytesRead += bytes;
file.read(buffer, bytes);
if (headerReceived(mesh_params, buffer, bytes))
{ //did not do an HTTP request, return false
{
// Already have header, no need to retry.
return true;
}
}
}
//either cache entry doesn't exist or is corrupt, request header from simulator
bool retval = true ;
std::vector<std::string> headers;
headers.push_back("Accept: application/octet-stream");
@@ -961,29 +975,19 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, U32& c
//grab first 4KB if we're going to bother with a fetch. Cache will prevent future fetches if a full mesh fits
//within the first 4KB
//NOTE -- this will break of headers ever exceed 4KB
retval = mCurlRequest->getByteRange(http_url, headers, 0, 4096, new LLMeshHeaderResponder(mesh_params));
if(retval)
{
LLMeshRepository::sHTTPRequestCount++;
}
// This might throw AICurlNoEasyHandle.
mCurlRequest->getByteRange(http_url, headers, 0, 4096, new LLMeshHeaderResponder(mesh_params));
LLMeshRepository::sHTTPRequestCount++;
count++;
}
return retval;
return true;
}
//return false if failed to get mesh lod.
bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, U32& count)
void LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, U32& count)
{ //protected by mMutex
if (!mHeaderMutex)
{
return false;
}
mHeaderMutex->lock();
bool retval = true;
LLUUID mesh_id = mesh_params.getSculptID();
U32 header_size = mMeshHeaderSize[mesh_id];
@@ -1019,7 +1023,7 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod,
if (lodReceived(mesh_params, lod, buffer, size))
{
delete[] buffer;
return true;
return;
}
}
@@ -1033,13 +1037,10 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod,
std::string http_url = constructUrl(mesh_id);
if (!http_url.empty())
{
retval = mCurlRequest->getByteRange(constructUrl(mesh_id), headers, offset, size,
// This might throw AICurlNoEasyHandle.
mCurlRequest->getByteRange(constructUrl(mesh_id), headers, offset, size,
new LLMeshLODResponder(mesh_params, lod, offset, size));
if(retval)
{
LLMeshRepository::sHTTPRequestCount++;
}
LLMeshRepository::sHTTPRequestCount++;
count++;
}
else
@@ -1056,8 +1057,6 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod,
{
mHeaderMutex->unlock();
}
return retval;
}
bool LLMeshRepoThread::headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size)
@@ -1102,7 +1101,7 @@ bool LLMeshRepoThread::headerReceived(const LLVolumeParams& mesh_params, U8* dat
LLMutexLock lock(mHeaderMutex);
mMeshHeaderSize[mesh_id] = header_size;
mMeshHeader[mesh_id] = header;
}
}
LLMutexLock lock(mMutex); // <FS:ND/> FIRE-7182, make sure only one thread access mPendingLOD at the same time.
@@ -1604,7 +1603,7 @@ void LLMeshUploadThread::generateHulls()
void LLMeshUploadThread::doWholeModelUpload()
{
mCurlRequest = new LLCurlRequest();
mCurlRequest = new AICurlInterface::Request();
if (mWholeModelUploadURL.empty())
{
@@ -1619,11 +1618,14 @@ void LLMeshUploadThread::doWholeModelUpload()
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();
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);
@@ -1640,7 +1642,7 @@ void LLMeshUploadThread::requestWholeModelFee()
{
dump_num++;
mCurlRequest = new LLCurlRequest();
mCurlRequest = new AICurlInterface::Request;
generateHulls();
@@ -1650,6 +1652,7 @@ void LLMeshUploadThread::requestWholeModelFee()
mPendingUploads++;
LLCurlRequest::headers_t headers;
// This might throw AICurlNoEasyHandle.
mCurlRequest->post(mWholeModelFeeCapability, headers, model_data,
new LLWholeModelFeeResponder(this,model_data, mFeeObserverHandle), mMeshUploadTimeOut);
@@ -1670,11 +1673,6 @@ void LLMeshUploadThread::requestWholeModelFee()
void LLMeshRepoThread::notifyLoadedMeshes()
{//called via gMeshRepo.notifyLoadedMeshes(). mMutex already locked
if (!mMutex)
{
return;
}
while (!mLoadedQ.empty())
{
mMutex->lock();
@@ -2378,17 +2376,17 @@ void LLMeshRepository::notifyLoadedMeshes()
mInventoryQ.pop();
}
}
#endif //MESH_IMPORT
//call completed callbacks on finished decompositions
mDecompThread->notifyCompleted();
if (!mThread->mWaiting)
{ //curl thread is churning, wait for it to go idle
if (!mThread->mSignal->tryLock())
{
// Curl thread is churning, wait for it to go idle.
return;
}
mThread->mSignal->unlock();
static std::string region_name("never name a region this");
@@ -3334,6 +3332,7 @@ void LLPhysicsDecomp::run()
mStageID[stages[i].mName] = i;
}
mSignal->lock();
while (!mQuitting)
{
mSignal->wait();
@@ -3362,14 +3361,10 @@ void LLPhysicsDecomp::run()
}
}
}
mSignal->unlock();
decomp->quitThread();
if (mSignal->isLocked())
{ //let go of mSignal's associated mutex
mSignal->unlock();
}
mDone = true;
#endif //MESH_IMPORT
}

View File

@@ -32,6 +32,7 @@
#include "lluuid.h"
#include "llviewertexture.h"
#include "llvolume.h"
#include "llcurlrequest.h"
#if MESH_IMPORT
#define LLCONVEXDECOMPINTER_STATIC 1
@@ -67,7 +68,6 @@ struct LLCDHull
class LLVOVolume;
class LLMeshResponder;
class LLCurlRequest;
class LLMutex;
class LLCondition;
class LLVFS;
@@ -246,13 +246,11 @@ public:
static S32 sActiveLODRequests;
static U32 sMaxConcurrentRequests;
LLCurlRequest* mCurlRequest;
AICurlInterface::Request* mCurlRequest;
LLMutex* mMutex;
LLMutex* mHeaderMutex;
LLCondition* mSignal;
bool mWaiting;
//map of known mesh headers
typedef std::map<LLUUID, LLSD> mesh_header_map;
mesh_header_map mMeshHeader;
@@ -351,7 +349,7 @@ public:
void loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod);
bool fetchMeshHeader(const LLVolumeParams& mesh_params, U32& count);
bool fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, U32& count);
void fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, U32& count);
bool headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size);
bool lodReceived(const LLVolumeParams& mesh_params, S32 lod, U8* data, S32 data_size);
bool skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 data_size);

View File

@@ -999,7 +999,7 @@ void LLPanelClassified::sendClassifiedClickMessage(const std::string& type)
std::string url = gAgent.getRegion()->getCapability("SearchStatTracking");
llinfos << "LLPanelClassified::sendClassifiedClickMessage via capability" << llendl;
LLHTTPClient::post(url, body, new LLHTTPClient::Responder());
LLHTTPClient::post(url, body, new LLHTTPClient::ResponderIgnore);
}
////////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -49,7 +49,6 @@
#include "llcheckboxctrl.h"
#include "llcommandhandler.h" // for secondlife:///app/login/
#include "llcombobox.h"
#include "llcurl.h"
#include "llviewercontrol.h"
#include "llfloaterabout.h"
#include "llfloatertest.h"

View File

@@ -1998,7 +1998,7 @@ public:
virtual void processGroup(LLSpatialGroup* group)
{
llassert(!group->isState(LLSpatialGroup::DIRTY) && !group->isEmpty())
llassert(!group->isState(LLSpatialGroup::DIRTY) && !group->isEmpty());
if (mRes < 2)
{

View File

@@ -218,6 +218,7 @@
#include "llfloaterblacklist.h"
#include "scriptcounter.h"
#include "shfloatermediaticker.h"
#include "llpacketring.h"
// </edit>
#include "llpathfindingmanager.h"
@@ -256,7 +257,9 @@ extern S32 gStartImageHeight;
// local globals
//
#if defined(CWDEBUG) || defined(DEBUG_CURLIO)
static bool gCurlIo;
#endif
static LLHost gAgentSimHost;
static BOOL gSkipOptionalUpdate = FALSE;
@@ -628,21 +631,21 @@ bool idle_startup()
F32 dropPercent = gSavedSettings.getF32("PacketDropPercentage");
msg->mPacketRing.setDropPercentage(dropPercent);
msg->mPacketRing->setDropPercentage(dropPercent);
F32 inBandwidth = gSavedSettings.getF32("InBandwidth");
F32 outBandwidth = gSavedSettings.getF32("OutBandwidth");
if (inBandwidth != 0.f)
{
LL_DEBUGS("AppInit") << "Setting packetring incoming bandwidth to " << inBandwidth << LL_ENDL;
msg->mPacketRing.setUseInThrottle(TRUE);
msg->mPacketRing.setInBandwidth(inBandwidth);
msg->mPacketRing->setUseInThrottle(TRUE);
msg->mPacketRing->setInBandwidth(inBandwidth);
}
if (outBandwidth != 0.f)
{
LL_DEBUGS("AppInit") << "Setting packetring outgoing bandwidth to " << outBandwidth << LL_ENDL;
msg->mPacketRing.setUseOutThrottle(TRUE);
msg->mPacketRing.setOutBandwidth(outBandwidth);
msg->mPacketRing->setUseOutThrottle(TRUE);
msg->mPacketRing->setOutBandwidth(outBandwidth);
}
}
@@ -1348,6 +1351,9 @@ bool idle_startup()
llinfos << "Authenticating with " << grid_uri << llendl;
// Always write curl I/O debug info for the login attempt.
Debug(gCurlIo = dc::curl.is_on() && !dc::curlio.is_on(); if (gCurlIo) dc::curlio.on());
// TODO if statement here to use web_login_key
// OGPX : which routine would this end up in? the LLSD or XMLRPC, or ....?
LLUserAuth::getInstance()->authenticate(
@@ -1422,6 +1428,7 @@ bool idle_startup()
LL_DEBUGS("AppInit") << "downloading..." << LL_ENDL;
return FALSE;
}
Debug(if (gCurlIo) dc::curlio.off()); // Login succeeded: restore dc::curlio to original state.
LLStartUp::setStartupState( STATE_LOGIN_PROCESS_RESPONSE );
progress += 0.01f;
set_startup_status(progress, LLTrans::getString("LoginProcessingResponse"), auth_message);
@@ -3607,6 +3614,7 @@ std::string LLStartUp::startupStateToString(EStartupState state)
#define RTNENUM(E) case E: return #E
switch(state){
RTNENUM( STATE_FIRST );
RTNENUM( STATE_BROWSER_INIT );
RTNENUM( STATE_LOGIN_SHOW );
RTNENUM( STATE_LOGIN_WAIT );
RTNENUM( STATE_LOGIN_CLEANUP );
@@ -3614,10 +3622,13 @@ std::string LLStartUp::startupStateToString(EStartupState state)
RTNENUM( STATE_UPDATE_CHECK );
RTNENUM( STATE_LOGIN_AUTH_INIT );
RTNENUM( STATE_LOGIN_AUTHENTICATE );
RTNENUM( STATE_WAIT_LEGACY_LOGIN );
RTNENUM( STATE_XMLRPC_LEGACY_LOGIN );
RTNENUM( STATE_LOGIN_NO_DATA_YET );
RTNENUM( STATE_LOGIN_DOWNLOADING );
RTNENUM( STATE_LOGIN_PROCESS_RESPONSE );
RTNENUM( STATE_WORLD_INIT );
RTNENUM( STATE_MULTIMEDIA_INIT );
RTNENUM( STATE_FONT_INIT );
RTNENUM( STATE_SEED_GRANTED_WAIT );
RTNENUM( STATE_SEED_CAP_GRANTED );
@@ -3630,10 +3641,10 @@ std::string LLStartUp::startupStateToString(EStartupState state)
RTNENUM( STATE_WEARABLES_WAIT );
RTNENUM( STATE_CLEANUP );
RTNENUM( STATE_STARTED );
default:
return llformat("(state #%d)", state);
}
#undef RTNENUM
// Never reached.
return llformat("(state #%d)", state);
}

View File

@@ -779,7 +779,7 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher,
calcWorkPriority();
mType = host.isOk() ? LLImageBase::TYPE_AVATAR_BAKE : LLImageBase::TYPE_NORMAL;
// llinfos << "Create: " << mID << " mHost:" << host << " Discard=" << discard << llendl;
llinfos << "Create: " << mID << " mHost:" << host << " Discard=" << discard << " URL:"<< mUrl << llendl;
if (!mFetcher->mDebugPause)
{
U32 work_priority = mWorkPriority | LLWorkerThread::PRIORITY_HIGH;
@@ -1320,8 +1320,15 @@ bool LLTextureFetchWorker::doWork(S32 param)
// Will call callbackHttpGet when curl request completes
std::vector<std::string> headers;
headers.push_back("Accept: image/x-j2c");
res = mFetcher->mCurlGetRequest->getByteRange(mUrl, headers, offset, mRequestedSize,
new HTTPGetResponder(mFetcher, mID, LLTimer::getTotalTime(), mRequestedSize, offset, true));
try
{
res = mFetcher->mCurlGetRequest->getByteRange(mUrl, headers, offset, mRequestedSize,
new HTTPGetResponder(mFetcher, mID, LLTimer::getTotalTime(), mRequestedSize, offset, true));
}
catch(AICurlNoEasyHandle const& error)
{
llwarns << error.what() << llendl;
}
}
if (!res)
{
@@ -2100,7 +2107,7 @@ bool LLTextureFetch::createRequest(const std::string& url, const LLUUID& id, con
worker->unlockWorkMutex();
}
// llinfos << "REQUESTED: " << id << " Discard: " << desired_discard << llendl;
llinfos << "REQUESTED: " << id << " Discard: " << desired_discard << llendl;
return true;
}
@@ -2357,14 +2364,6 @@ void LLTextureFetch::commonUpdate()
// Run a cross-thread command, if any.
cmdDoWork();
#endif
// Update Curl on same thread as mCurlGetRequest was constructed
llassert_always(mCurlGetRequest);
S32 processed = mCurlGetRequest->process();
if (processed > 0)
{
lldebugs << "processed: " << processed << " messages." << llendl;
}
}
@@ -2429,7 +2428,7 @@ void LLTextureFetch::shutDownImageDecodeThread()
void LLTextureFetch::startThread()
{
// Construct mCurlGetRequest from Worker Thread
mCurlGetRequest = new LLCurlRequest();
mCurlGetRequest = new AICurlInterface::Request;
}
// WORKER THREAD

View File

@@ -37,7 +37,7 @@
#include "llimage.h"
#include "lluuid.h"
#include "llworkerthread.h"
#include "llcurl.h"
#include "llcurlrequest.h"
#include "lltextureinfo.h"
#include "llapr.h"
@@ -107,7 +107,7 @@ public:
LLViewerAssetStats * main_stats);
void commandDataBreak();
LLCurlRequest & getCurlRequest() { return *mCurlGetRequest; }
AICurlInterface::Request& getCurlRequest() { return *mCurlGetRequest; }
bool isQAMode() const { return mQAMode; }
@@ -178,7 +178,7 @@ private:
LLTextureCache* mTextureCache;
LLImageDecodeThread* mImageDecodeThread;
LLCurlRequest* mCurlGetRequest;
AICurlInterface::Request* mCurlGetRequest;
// Map of all requests by UUID
typedef std::map<LLUUID,LLTextureFetchWorker*> map_t;

View File

@@ -33,6 +33,7 @@
#include "llviewerprecompiledheaders.h"
#include "lltexturestatsuploader.h"
#include "llhttpclient.h"
LLTextureStatsUploader::LLTextureStatsUploader()
{

View File

@@ -38,7 +38,10 @@
#include "llpanellogin.h"
#include "llviewercontrol.h"
#include "curl/curl.h"
#include <curl/curl.h> // curl_unescape, curl_free
#ifdef DEBUG_CURLIO
#include "debug_libcurl.h"
#endif
//static
LLURLSimString LLURLSimString::sInstance;

View File

@@ -47,9 +47,12 @@
#include "stringize.h"
// NOTE: MUST include these after otherincludes since queue gets redefined!?!!
#include <curl/curl.h>
#include <xmlrpc-epi/xmlrpc.h>
#include <curl/curl.h>
#ifdef DEBUG_CURLIO
#include "debug_libcurl.h"
#endif
// Don't define PLATFORM_STRING for unknown platforms - they need
// to get added to the login cgi script, so we want this to cause an
@@ -270,7 +273,7 @@ LLUserAuth::UserAuthcode LLUserAuth::authResponse()
return mAuthResponse;
}
bool done = mTransaction->process();
bool done = mTransaction->is_finished();
if (!done) {
if (LLXMLRPCTransaction::StatusDownloading == mTransaction->status(0))

View File

@@ -34,7 +34,6 @@
#define LLVIEWERASSETSTORAGE_H
#include "llassetstorage.h"
//#include "curl/curl.h"
class LLVFile;

View File

@@ -250,6 +250,7 @@
#include "llfloatervfs.h"
#include "llfloatervfsexplorer.h"
#include "shfloatermediaticker.h"
#include "llpacketring.h"
// </edit>
#include "scriptcounter.h"
@@ -4048,7 +4049,7 @@ void print_packets_lost(void*)
void drop_packet(void*)
{
gMessageSystem->mPacketRing.dropPackets(1);
gMessageSystem->mPacketRing->dropPackets(1);
}

View File

@@ -467,7 +467,7 @@ void LLViewerParcelMedia::sendMediaNavigateMessage(const std::string& url)
body["agent-id"] = gAgent.getID();
body["local-id"] = LLViewerParcelMgr::getInstance()->getAgentParcel()->getLocalID();
body["url"] = url;
LLHTTPClient::post(region_url, body, new LLHTTPClient::Responder);
LLHTTPClient::post(region_url, body, new LLHTTPClient::ResponderIgnore);
}
else
{

View File

@@ -1310,7 +1310,7 @@ void LLViewerParcelMgr::sendParcelPropertiesUpdate(LLParcel* parcel, bool use_ag
parcel->packMessage(body);
llinfos << "Sending parcel properties update via capability to: "
<< url << llendl;
LLHTTPClient::post(url, body, new LLHTTPClient::Responder());
LLHTTPClient::post(url, body, new LLHTTPClient::ResponderIgnore);
}
else
{

View File

@@ -151,7 +151,7 @@
#include "llnamevalue.h"
#include "llpacketack.h"
#include "llpacketbuffer.h"
#include "llpacketring.h"
//#include "llpacketring.h"
#include "llpartdata.h"
#include "llregionhandle.h"
#include "lltaskname.h"

View File

@@ -277,7 +277,7 @@ void LLViewerTextureList::shutdown()
break;
}
if (count > 0 && !gDirUtilp->getLindenUserDir(true).empty())
if (count > 0 && !gDirUtilp->getLindenUserDir(true).empty())
{
std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, get_texture_list_name());
llofstream file;

View File

@@ -76,8 +76,6 @@
#include "llagentcamera.h"
#include "curl/curl.h"
LLWaterParamManager::LLWaterParamManager() :
mFogColor(22.f/255.f, 43.f/255.f, 54.f/255.f, 0.0f, 0.0f, "waterFogColor", "WaterFogColor"),
mFogDensity(4, "waterFogDensity", 2),

View File

@@ -77,7 +77,6 @@
#include "llviewerregion.h"
#include "llassetuploadresponders.h"
#include "curl/curl.h"
#include "llstreamtools.h"
// [RLVa:KB] - Checked: 2011-09-04 (RLVa-1.4.1a) | Added: RLVa-1.4.1a

View File

@@ -61,6 +61,7 @@
#include "message.h"
#include "pipeline.h"
#include "llappviewer.h" // for do_disconnect()
#include "llpacketring.h"
#include <deque>
#include <queue>
@@ -779,8 +780,8 @@ void LLWorld::updateNetStats()
S32 packets_out = gMessageSystem->mPacketsOut - mLastPacketsOut;
S32 packets_lost = gMessageSystem->mDroppedPackets - mLastPacketsLost;
S32 actual_in_bits = gMessageSystem->mPacketRing.getAndResetActualInBits();
S32 actual_out_bits = gMessageSystem->mPacketRing.getAndResetActualOutBits();
S32 actual_in_bits = gMessageSystem->mPacketRing->getAndResetActualInBits();
S32 actual_out_bits = gMessageSystem->mPacketRing->getAndResetActualOutBits();
LLViewerStats::getInstance()->mActualInKBitStat.addValue(actual_in_bits/1024.f);
LLViewerStats::getInstance()->mActualOutKBitStat.addValue(actual_out_bits/1024.f);
LLViewerStats::getInstance()->mKBitStat.addValue(bits/1024.f);

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