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:
@@ -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
1502
indra/llmessage/aicurl.cpp
Normal file
File diff suppressed because it is too large
Load Diff
343
indra/llmessage/aicurl.h
Normal file
343
indra/llmessage/aicurl.h
Normal 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
|
||||
478
indra/llmessage/aicurlprivate.h
Normal file
478
indra/llmessage/aicurlprivate.h
Normal 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
|
||||
1710
indra/llmessage/aicurlthread.cpp
Normal file
1710
indra/llmessage/aicurlthread.cpp
Normal file
File diff suppressed because it is too large
Load Diff
128
indra/llmessage/aicurlthread.h
Normal file
128
indra/llmessage/aicurlthread.h
Normal 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
|
||||
942
indra/llmessage/debug_libcurl.cpp
Normal file
942
indra/llmessage/debug_libcurl.cpp
Normal 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
|
||||
|
||||
89
indra/llmessage/debug_libcurl.h
Normal file
89
indra/llmessage/debug_libcurl.h
Normal 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
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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 ;
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
/**
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
#include "llhost.h"
|
||||
#include "llpacketbuffer.h"
|
||||
#include "llproxy.h"
|
||||
//#include "llproxy.h"
|
||||
#include "llthrottle.h"
|
||||
#include "net.h"
|
||||
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 ----------------*/
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user