Rename CurlResponderBuffer to BufferedCurlEasyRequest and derive it from CurlEasyRequest
Every curl transaction is a AICurlEasyRequestStateMachine which has a AICurlEasyRequest as member, which is a reference counting pointer to a ThreadSafeBufferedCurlEasyRequest. And now BufferedCurlEasyRequest is derived from CurlEasyRequest which is derived from CurlEasyHandle, but neither are used separatedly.
This commit is contained in:
@@ -610,12 +610,12 @@ CURLMcode CurlEasyHandle::remove_handle_from_multi(AICurlEasyRequest_wat& curl_e
|
||||
return res;
|
||||
}
|
||||
|
||||
void intrusive_ptr_add_ref(ThreadSafeCurlEasyRequest* threadsafe_curl_easy_request)
|
||||
void intrusive_ptr_add_ref(ThreadSafeBufferedCurlEasyRequest* threadsafe_curl_easy_request)
|
||||
{
|
||||
threadsafe_curl_easy_request->mReferenceCount++;
|
||||
}
|
||||
|
||||
void intrusive_ptr_release(ThreadSafeCurlEasyRequest* threadsafe_curl_easy_request)
|
||||
void intrusive_ptr_release(ThreadSafeBufferedCurlEasyRequest* threadsafe_curl_easy_request)
|
||||
{
|
||||
if (--threadsafe_curl_easy_request->mReferenceCount == 0)
|
||||
{
|
||||
@@ -731,21 +731,11 @@ void CurlEasyRequest::setPost_raw(U32 size, char const* data)
|
||||
setopt(CURLOPT_POSTFIELDS, data); // Implies CURLOPT_POST
|
||||
}
|
||||
|
||||
ThreadSafeCurlEasyRequest* CurlEasyRequest::get_lockobj(void)
|
||||
{
|
||||
return static_cast<ThreadSafeCurlEasyRequest*>(AIThreadSafeSimpleDC<CurlEasyRequest>::wrapper_cast(this));
|
||||
}
|
||||
|
||||
ThreadSafeCurlEasyRequest const* CurlEasyRequest::get_lockobj(void) const
|
||||
{
|
||||
return static_cast<ThreadSafeCurlEasyRequest const*>(AIThreadSafeSimpleDC<CurlEasyRequest>::wrapper_cast(this));
|
||||
}
|
||||
|
||||
//static
|
||||
size_t CurlEasyRequest::headerCallback(char* ptr, size_t size, size_t nmemb, void* userdata)
|
||||
{
|
||||
CurlEasyRequest* self = static_cast<CurlEasyRequest*>(userdata);
|
||||
ThreadSafeCurlEasyRequest* lockobj = self->get_lockobj();
|
||||
ThreadSafeBufferedCurlEasyRequest* lockobj = self->get_lockobj();
|
||||
AICurlEasyRequest_wat lock_self(*lockobj);
|
||||
return self->mHeaderCallback(ptr, size, nmemb, self->mHeaderCallbackUserData);
|
||||
}
|
||||
@@ -762,7 +752,7 @@ void CurlEasyRequest::setHeaderCallback(curl_write_callback callback, void* user
|
||||
size_t CurlEasyRequest::writeCallback(char* ptr, size_t size, size_t nmemb, void* userdata)
|
||||
{
|
||||
CurlEasyRequest* self = static_cast<CurlEasyRequest*>(userdata);
|
||||
ThreadSafeCurlEasyRequest* lockobj = self->get_lockobj();
|
||||
ThreadSafeBufferedCurlEasyRequest* lockobj = self->get_lockobj();
|
||||
AICurlEasyRequest_wat lock_self(*lockobj);
|
||||
return self->mWriteCallback(ptr, size, nmemb, self->mWriteCallbackUserData);
|
||||
}
|
||||
@@ -779,7 +769,7 @@ void CurlEasyRequest::setWriteCallback(curl_write_callback callback, void* userd
|
||||
size_t CurlEasyRequest::readCallback(char* ptr, size_t size, size_t nmemb, void* userdata)
|
||||
{
|
||||
CurlEasyRequest* self = static_cast<CurlEasyRequest*>(userdata);
|
||||
ThreadSafeCurlEasyRequest* lockobj = self->get_lockobj();
|
||||
ThreadSafeBufferedCurlEasyRequest* lockobj = self->get_lockobj();
|
||||
AICurlEasyRequest_wat lock_self(*lockobj);
|
||||
return self->mReadCallback(ptr, size, nmemb, self->mReadCallbackUserData);
|
||||
}
|
||||
@@ -796,7 +786,7 @@ void CurlEasyRequest::setReadCallback(curl_read_callback callback, void* userdat
|
||||
CURLcode CurlEasyRequest::SSLCtxCallback(CURL* curl, void* sslctx, void* userdata)
|
||||
{
|
||||
CurlEasyRequest* self = static_cast<CurlEasyRequest*>(userdata);
|
||||
ThreadSafeCurlEasyRequest* lockobj = self->get_lockobj();
|
||||
ThreadSafeBufferedCurlEasyRequest* lockobj = self->get_lockobj();
|
||||
AICurlEasyRequest_wat lock_self(*lockobj);
|
||||
return self->mSSLCtxCallback(curl, sslctx, self->mSSLCtxCallbackUserData);
|
||||
}
|
||||
@@ -864,7 +854,7 @@ CurlEasyRequest::~CurlEasyRequest()
|
||||
// If the CurlEasyRequest object is destructed then we need to revoke all callbacks, because
|
||||
// we can't set the lock anymore, and neither will mHeaderCallback, mWriteCallback etc,
|
||||
// be available anymore.
|
||||
send_events_to(NULL);
|
||||
send_handle_events_to(NULL);
|
||||
revokeCallbacks();
|
||||
// This wasn't freed yet if the request never finished.
|
||||
curl_slist_free_all(mHeaders);
|
||||
@@ -1094,12 +1084,12 @@ void CurlEasyRequest::set_timeout_opts(void)
|
||||
setopt(CURLOPT_TIMEOUT, mTimeoutPolicy->getCurlTransaction());
|
||||
}
|
||||
|
||||
void CurlEasyRequest::create_timeout_object(ThreadSafeCurlEasyRequest* lockobj)
|
||||
void CurlEasyRequest::create_timeout_object(ThreadSafeBufferedCurlEasyRequest* lockobj)
|
||||
{
|
||||
mTimeout = new curlthread::HTTPTimeout(mTimeoutPolicy, lockobj);
|
||||
}
|
||||
|
||||
LLPointer<curlthread::HTTPTimeout>& CurlEasyRequest::get_timeout_object(ThreadSafeCurlEasyRequest* lockobj)
|
||||
LLPointer<curlthread::HTTPTimeout>& CurlEasyRequest::get_timeout_object(ThreadSafeBufferedCurlEasyRequest* lockobj)
|
||||
{
|
||||
if (mTimeoutIsOrphan)
|
||||
{
|
||||
@@ -1168,7 +1158,7 @@ void CurlEasyRequest::removed_from_multi_handle(AICurlEasyRequest_wat& curl_easy
|
||||
mHandleEventsTarget->removed_from_multi_handle(curl_easy_request_w);
|
||||
}
|
||||
|
||||
void CurlEasyRequest::print_diagnostics(AICurlEasyRequest_wat const& curlEasyRequest_w, CURLcode code)
|
||||
void CurlEasyRequest::print_diagnostics(CURLcode code)
|
||||
{
|
||||
if (code == CURLE_OPERATION_TIMEDOUT)
|
||||
{
|
||||
@@ -1176,48 +1166,39 @@ void CurlEasyRequest::print_diagnostics(AICurlEasyRequest_wat const& curlEasyReq
|
||||
// this is far from the code that guaranteeds that it is set.
|
||||
if (mTimeout)
|
||||
{
|
||||
mTimeout->print_diagnostics(curlEasyRequest_w);
|
||||
mTimeout->print_diagnostics(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CurlResponderBuffer
|
||||
// BufferedCurlEasyRequest
|
||||
|
||||
static int const HTTP_REDIRECTS_DEFAULT = 10;
|
||||
|
||||
LLChannelDescriptors const CurlResponderBuffer::sChannels;
|
||||
LLChannelDescriptors const BufferedCurlEasyRequest::sChannels;
|
||||
|
||||
CurlResponderBuffer::CurlResponderBuffer() : mRequestTransferedBytes(0), mResponseTransferedBytes(0), mBufferEventsTarget(NULL)
|
||||
BufferedCurlEasyRequest::BufferedCurlEasyRequest() : mRequestTransferedBytes(0), mResponseTransferedBytes(0), mBufferEventsTarget(NULL)
|
||||
{
|
||||
ThreadSafeBufferedCurlEasyRequest* lockobj = get_lockobj();
|
||||
AICurlEasyRequest_wat curl_easy_request_w(*lockobj);
|
||||
curl_easy_request_w->send_events_to(this);
|
||||
}
|
||||
|
||||
#define llmaybeerrs lllog(LLApp::isRunning() ? LLError::LEVEL_ERROR : LLError::LEVEL_WARN, NULL, NULL, false, true)
|
||||
|
||||
// The callbacks need to be revoked when the CurlResponderBuffer is destructed (because that is what the callbacks use).
|
||||
// The AIThreadSafeSimple<CurlResponderBuffer> is destructed first (right to left), so when we get here then the
|
||||
// ThreadSafeCurlEasyRequest base class of ThreadSafeBufferedCurlEasyRequest is still intact and we can create
|
||||
// and use curl_easy_request_w.
|
||||
CurlResponderBuffer::~CurlResponderBuffer()
|
||||
BufferedCurlEasyRequest::~BufferedCurlEasyRequest()
|
||||
{
|
||||
ThreadSafeBufferedCurlEasyRequest* lockobj = get_lockobj();
|
||||
AICurlEasyRequest_wat curl_easy_request_w(*lockobj); // Wait 'til possible callbacks have returned.
|
||||
send_buffer_events_to(NULL);
|
||||
curl_easy_request_w->revokeCallbacks();
|
||||
revokeCallbacks();
|
||||
if (mResponder)
|
||||
{
|
||||
// If the responder is still alive, then that means that CurlResponderBuffer::processOutput was
|
||||
// If the responder is still alive, then that means that BufferedCurlEasyRequest::processOutput was
|
||||
// never called, which means that the removed_from_multi_handle event never happened.
|
||||
// This is definitely an internal error as it can only happen when libcurl is too slow,
|
||||
// in which case AICurlEasyRequestStateMachine::mTimer times out, but that already
|
||||
// calls CurlResponderBuffer::timed_out().
|
||||
llmaybeerrs << "Calling ~CurlResponderBuffer() with active responder!" << llendl;
|
||||
// calls BufferedCurlEasyRequest::timed_out().
|
||||
llmaybeerrs << "Calling ~BufferedCurlEasyRequest() with active responder!" << llendl;
|
||||
if (!LLApp::isRunning())
|
||||
{
|
||||
// It might happen if some CurlResponderBuffer escaped clean up somehow :/
|
||||
// It might happen if some BufferedCurlEasyRequest escaped clean up somehow :/
|
||||
mResponder = NULL;
|
||||
}
|
||||
else
|
||||
@@ -1228,33 +1209,38 @@ CurlResponderBuffer::~CurlResponderBuffer()
|
||||
}
|
||||
}
|
||||
|
||||
void CurlResponderBuffer::timed_out(void)
|
||||
void BufferedCurlEasyRequest::timed_out(void)
|
||||
{
|
||||
mResponder->finished(CURLE_OK, HTTP_INTERNAL_ERROR, "Request timeout, aborted.", sChannels, mOutput);
|
||||
mResponder = NULL;
|
||||
mResponder->finished(CURLE_OK, HTTP_INTERNAL_ERROR, "Request timeout, aborted.", sChannels, mOutput);
|
||||
if (mResponder->needsHeaders())
|
||||
{
|
||||
send_buffer_events_to(NULL); // Revoke buffer events: we sent them to the responder.
|
||||
}
|
||||
mResponder = NULL;
|
||||
}
|
||||
|
||||
void CurlResponderBuffer::resetState(AICurlEasyRequest_wat& curl_easy_request_w)
|
||||
void BufferedCurlEasyRequest::resetState(void)
|
||||
{
|
||||
llassert(!mResponder);
|
||||
|
||||
curl_easy_request_w->resetState();
|
||||
// Call base class implementation.
|
||||
CurlEasyRequest::resetState();
|
||||
|
||||
mOutput.reset();
|
||||
mInput.reset();
|
||||
}
|
||||
|
||||
ThreadSafeBufferedCurlEasyRequest* CurlResponderBuffer::get_lockobj(void)
|
||||
ThreadSafeBufferedCurlEasyRequest* BufferedCurlEasyRequest::get_lockobj(void)
|
||||
{
|
||||
return static_cast<ThreadSafeBufferedCurlEasyRequest*>(AIThreadSafeSimple<CurlResponderBuffer>::wrapper_cast(this));
|
||||
return static_cast<ThreadSafeBufferedCurlEasyRequest*>(AIThreadSafeSimple<BufferedCurlEasyRequest>::wrapper_cast(this));
|
||||
}
|
||||
|
||||
ThreadSafeBufferedCurlEasyRequest const* CurlResponderBuffer::get_lockobj(void) const
|
||||
ThreadSafeBufferedCurlEasyRequest const* BufferedCurlEasyRequest::get_lockobj(void) const
|
||||
{
|
||||
return static_cast<ThreadSafeBufferedCurlEasyRequest const*>(AIThreadSafeSimple<CurlResponderBuffer>::wrapper_cast(this));
|
||||
return static_cast<ThreadSafeBufferedCurlEasyRequest const*>(AIThreadSafeSimple<BufferedCurlEasyRequest>::wrapper_cast(this));
|
||||
}
|
||||
|
||||
void CurlResponderBuffer::prepRequest(AICurlEasyRequest_wat& curl_easy_request_w, AIHTTPHeaders const& headers, LLHTTPClient::ResponderPtr responder)
|
||||
void BufferedCurlEasyRequest::prepRequest(AICurlEasyRequest_wat& curl_easy_request_w, AIHTTPHeaders const& headers, LLHTTPClient::ResponderPtr responder)
|
||||
{
|
||||
mInput.reset(new LLBufferArray);
|
||||
mInput->setThreaded(true);
|
||||
|
||||
@@ -162,7 +162,7 @@ void setCAPath(std::string const& file);
|
||||
|
||||
// Forward declaration (see aicurlprivate.h).
|
||||
namespace AICurlPrivate {
|
||||
class CurlEasyRequest;
|
||||
class BufferedCurlEasyRequest;
|
||||
} // namespace AICurlPrivate
|
||||
|
||||
// Define access types (_crat = Const Read Access Type, _rat = Read Access Type, _wat = Write Access Type).
|
||||
@@ -170,13 +170,13 @@ namespace AICurlPrivate {
|
||||
// AICurlEasyRequest h1; // Create easy handle.
|
||||
// AICurlEasyRequest h2(h1); // Make lightweight copies.
|
||||
// AICurlEasyRequest_wat h2_w(*h2); // Lock and obtain write access to the easy handle.
|
||||
// Use *h2_w, which is a reference to the locked CurlEasyRequest instance.
|
||||
// Use *h2_w, which is a reference to the locked BufferedCurlEasyRequest instance.
|
||||
// Note: As it is not allowed to use curl easy handles in any way concurrently,
|
||||
// read access would at most give access to a CURL const*, which will turn out
|
||||
// to be completely useless; therefore it is sufficient and efficient to use
|
||||
// an AIThreadSafeSimple and it's unlikely that AICurlEasyRequest_rat will be used.
|
||||
typedef AIAccessConst<AICurlPrivate::CurlEasyRequest> AICurlEasyRequest_rat;
|
||||
typedef AIAccess<AICurlPrivate::CurlEasyRequest> AICurlEasyRequest_wat;
|
||||
typedef AIAccessConst<AICurlPrivate::BufferedCurlEasyRequest> AICurlEasyRequest_rat;
|
||||
typedef AIAccess<AICurlPrivate::BufferedCurlEasyRequest> AICurlEasyRequest_wat;
|
||||
|
||||
// Events generated by AICurlPrivate::CurlEasyHandle.
|
||||
struct AICurlEasyHandleEvents {
|
||||
@@ -205,38 +205,38 @@ typedef LLPointer<AIPostField> AIPostFieldPtr;
|
||||
|
||||
#include "aicurlprivate.h"
|
||||
|
||||
// AICurlPrivate::CurlEasyRequestPtr, a boost::intrusive_ptr, is no more threadsafe than a
|
||||
// AICurlPrivate::BufferedCurlEasyRequestPtr, a boost::intrusive_ptr, is no more threadsafe than a
|
||||
// builtin type, but wrapping it in AIThreadSafe is obviously not going to help here.
|
||||
// Therefore we use the following trick: we wrap CurlEasyRequestPtr too, and only allow
|
||||
// Therefore we use the following trick: we wrap BufferedCurlEasyRequestPtr too, and only allow
|
||||
// read accesses on it.
|
||||
|
||||
// AICurlEasyRequest: a thread safe, reference counting, auto-cleaning curl easy handle.
|
||||
// AICurlEasyRequest: a thread safe, reference counting, buffered, auto-cleaning curl easy handle.
|
||||
class AICurlEasyRequest {
|
||||
private:
|
||||
// Use AICurlEasyRequestStateMachine, not AICurlEasyRequest.
|
||||
friend class AICurlEasyRequestStateMachine;
|
||||
|
||||
// Initial construction is allowed (thread-safe).
|
||||
// Note: If ThreadSafeCurlEasyRequest() throws then the memory allocated is still freed.
|
||||
// 'new' never returned however and neither the constructor nor destructor of mCurlEasyRequest is called in this case.
|
||||
// Note: If ThreadSafeBufferedCurlEasyRequest() throws then the memory allocated is still freed.
|
||||
// 'new' never returned however and neither the constructor nor destructor of mBufferedCurlEasyRequest is called in this case.
|
||||
// This might throw AICurlNoEasyHandle.
|
||||
AICurlEasyRequest(bool buffered) :
|
||||
mCurlEasyRequest(buffered ? new AICurlPrivate::ThreadSafeBufferedCurlEasyRequest : new AICurlPrivate::ThreadSafeCurlEasyRequest) { }
|
||||
AICurlEasyRequest(void) :
|
||||
mBufferedCurlEasyRequest(new AICurlPrivate::ThreadSafeBufferedCurlEasyRequest) { }
|
||||
|
||||
public:
|
||||
// Used for storing this object in a standard container (see MultiHandle::add_easy_request).
|
||||
AICurlEasyRequest(AICurlEasyRequest const& orig) : mCurlEasyRequest(orig.mCurlEasyRequest) { }
|
||||
AICurlEasyRequest(AICurlEasyRequest const& orig) : mBufferedCurlEasyRequest(orig.mBufferedCurlEasyRequest) { }
|
||||
|
||||
// For the rest, only allow read operations.
|
||||
AIThreadSafeSimple<AICurlPrivate::CurlEasyRequest>& operator*(void) const { llassert(mCurlEasyRequest.get()); return *mCurlEasyRequest; }
|
||||
AIThreadSafeSimple<AICurlPrivate::CurlEasyRequest>* operator->(void) const { llassert(mCurlEasyRequest.get()); return mCurlEasyRequest.get(); }
|
||||
AIThreadSafeSimple<AICurlPrivate::CurlEasyRequest>* get(void) const { return mCurlEasyRequest.get(); }
|
||||
AIThreadSafeSimple<AICurlPrivate::BufferedCurlEasyRequest>& operator*(void) const { llassert(mBufferedCurlEasyRequest.get()); return *mBufferedCurlEasyRequest; }
|
||||
AIThreadSafeSimple<AICurlPrivate::BufferedCurlEasyRequest>* operator->(void) const { llassert(mBufferedCurlEasyRequest.get()); return mBufferedCurlEasyRequest.get(); }
|
||||
AIThreadSafeSimple<AICurlPrivate::BufferedCurlEasyRequest>* get(void) const { return mBufferedCurlEasyRequest.get(); }
|
||||
|
||||
// Returns true if this object points to the same CurlEasyRequest object.
|
||||
bool operator==(AICurlEasyRequest const& cer) const { return mCurlEasyRequest == cer.mCurlEasyRequest; }
|
||||
// Returns true if this object points to the same BufferedCurlEasyRequest object.
|
||||
bool operator==(AICurlEasyRequest const& cer) const { return mBufferedCurlEasyRequest == cer.mBufferedCurlEasyRequest; }
|
||||
|
||||
// Returns true if this object points to a different CurlEasyRequest object.
|
||||
bool operator!=(AICurlEasyRequest const& cer) const { return mCurlEasyRequest != cer.mCurlEasyRequest; }
|
||||
// Returns true if this object points to a different BufferedCurlEasyRequest object.
|
||||
bool operator!=(AICurlEasyRequest const& cer) const { return mBufferedCurlEasyRequest != cer.mBufferedCurlEasyRequest; }
|
||||
|
||||
// Queue this request for insertion in the multi session.
|
||||
void addRequest(void);
|
||||
@@ -244,12 +244,9 @@ class AICurlEasyRequest {
|
||||
// Queue a command to remove this request from the multi session (or cancel a queued command to add it).
|
||||
void removeRequest(void);
|
||||
|
||||
// Returns true when this AICurlEasyRequest wraps a AICurlPrivate::ThreadSafeBufferedCurlEasyRequest.
|
||||
bool isBuffered(void) const { return mCurlEasyRequest->isBuffered(); }
|
||||
|
||||
private:
|
||||
// The actual pointer to the ThreadSafeCurlEasyRequest instance.
|
||||
AICurlPrivate::CurlEasyRequestPtr mCurlEasyRequest;
|
||||
// The actual pointer to the ThreadSafeBufferedCurlEasyRequest instance.
|
||||
AICurlPrivate::BufferedCurlEasyRequestPtr mBufferedCurlEasyRequest;
|
||||
|
||||
private:
|
||||
// Assignment would not be thread-safe; we may create this object and read from it.
|
||||
@@ -259,7 +256,7 @@ class AICurlEasyRequest {
|
||||
|
||||
public:
|
||||
// Instead of assignment, it might be helpful to use swap.
|
||||
void swap(AICurlEasyRequest& cer) { mCurlEasyRequest.swap(cer.mCurlEasyRequest); }
|
||||
void swap(AICurlEasyRequest& cer) { mBufferedCurlEasyRequest.swap(cer.mBufferedCurlEasyRequest); }
|
||||
|
||||
public:
|
||||
// The more exotic member functions of this class, to deal with passing this class
|
||||
@@ -267,26 +264,18 @@ class AICurlEasyRequest {
|
||||
// For "internal use" only; don't use things from AICurlPrivate yourself.
|
||||
|
||||
// It's thread-safe to give read access the underlaying boost::intrusive_ptr.
|
||||
// It's not OK to then call get() on that and store the AICurlPrivate::ThreadSafeCurlEasyRequest* separately.
|
||||
AICurlPrivate::CurlEasyRequestPtr const& get_ptr(void) const { return mCurlEasyRequest; }
|
||||
// It's not OK to then call get() on that and store the AICurlPrivate::ThreadSafeBufferedCurlEasyRequest* separately.
|
||||
AICurlPrivate::BufferedCurlEasyRequestPtr const& get_ptr(void) const { return mBufferedCurlEasyRequest; }
|
||||
|
||||
// If we have a correct (with regard to reference counting) AICurlPrivate::CurlEasyRequestPtr,
|
||||
// If we have a correct (with regard to reference counting) AICurlPrivate::BufferedCurlEasyRequestPtr,
|
||||
// then it's OK to construct a AICurlEasyRequest from it.
|
||||
// Note that the external AICurlPrivate::CurlEasyRequestPtr needs its own locking, because
|
||||
// Note that the external AICurlPrivate::BufferedCurlEasyRequestPtr needs its own locking, because
|
||||
// it's not thread-safe in itself.
|
||||
AICurlEasyRequest(AICurlPrivate::CurlEasyRequestPtr const& ptr) : mCurlEasyRequest(ptr) { }
|
||||
AICurlEasyRequest(AICurlPrivate::BufferedCurlEasyRequestPtr const& ptr) : mBufferedCurlEasyRequest(ptr) { }
|
||||
|
||||
// This one is obviously dangerous. It's for use only in MultiHandle::check_run_count.
|
||||
// See also the long comment in CurlEasyRequest::finalizeRequest with regard to CURLOPT_PRIVATE.
|
||||
explicit AICurlEasyRequest(AICurlPrivate::ThreadSafeCurlEasyRequest* ptr) : mCurlEasyRequest(ptr) { }
|
||||
};
|
||||
|
||||
// Write Access Type for the buffer.
|
||||
struct AICurlResponderBuffer_wat : public AIAccess<AICurlPrivate::CurlResponderBuffer> {
|
||||
explicit AICurlResponderBuffer_wat(AICurlPrivate::ThreadSafeBufferedCurlEasyRequest& lockobj) :
|
||||
AIAccess<AICurlPrivate::CurlResponderBuffer>(lockobj) { }
|
||||
AICurlResponderBuffer_wat(AIThreadSafeSimple<AICurlPrivate::CurlEasyRequest>& lockobj) :
|
||||
AIAccess<AICurlPrivate::CurlResponderBuffer>(static_cast<AICurlPrivate::ThreadSafeBufferedCurlEasyRequest&>(lockobj)) { }
|
||||
// See also the long comment in BufferedCurlEasyRequest::finalizeRequest with regard to CURLOPT_PRIVATE.
|
||||
explicit AICurlEasyRequest(AICurlPrivate::ThreadSafeBufferedCurlEasyRequest* ptr) : mBufferedCurlEasyRequest(ptr) { }
|
||||
};
|
||||
|
||||
#define AICurlPrivate DONTUSE_AICurlPrivate
|
||||
|
||||
@@ -63,7 +63,7 @@ void AICurlEasyRequestStateMachine::initialize_impl(void)
|
||||
{
|
||||
AICurlEasyRequest_wat curlEasyRequest_w(*mCurlEasyRequest);
|
||||
llassert(curlEasyRequest_w->is_finalized()); // Call finalizeRequest() before calling run().
|
||||
curlEasyRequest_w->send_events_to(this);
|
||||
curlEasyRequest_w->send_handle_events_to(this);
|
||||
}
|
||||
mAdded = false;
|
||||
mTimedOut = false;
|
||||
@@ -90,7 +90,7 @@ void AICurlEasyRequestStateMachine::removed_from_multi_handle(AICurlEasyRequest_
|
||||
{
|
||||
llassert(mFinished || mTimedOut); // If we neither finished nor timed out, then why is this being removed?
|
||||
// Note that allowing this would cause an assertion later on for removing
|
||||
// a CurlResponderBuffer with a still active Responder.
|
||||
// a BufferedCurlEasyRequest with a still active Responder.
|
||||
set_state(mFinished ? AICurlEasyRequestStateMachine_removed_after_finished : AICurlEasyRequestStateMachine_removed);
|
||||
}
|
||||
|
||||
@@ -175,12 +175,8 @@ void AICurlEasyRequestStateMachine::multiplex_impl(void)
|
||||
}
|
||||
|
||||
// The request finished and either data or an error code is available.
|
||||
if (mBuffered)
|
||||
{
|
||||
AICurlEasyRequest_wat easy_request_w(*mCurlEasyRequest);
|
||||
AICurlResponderBuffer_wat buffered_easy_request_w(*mCurlEasyRequest);
|
||||
buffered_easy_request_w->processOutput(easy_request_w);
|
||||
}
|
||||
AICurlEasyRequest_wat easy_request_w(*mCurlEasyRequest);
|
||||
easy_request_w->processOutput();
|
||||
}
|
||||
|
||||
if (current_state == AICurlEasyRequestStateMachine_finished)
|
||||
@@ -196,11 +192,10 @@ void AICurlEasyRequestStateMachine::multiplex_impl(void)
|
||||
case AICurlEasyRequestStateMachine_removed:
|
||||
{
|
||||
// The request was removed from the multi handle.
|
||||
if (mBuffered && mTimedOut)
|
||||
if (mTimedOut)
|
||||
{
|
||||
AICurlEasyRequest_wat easy_request_w(*mCurlEasyRequest);
|
||||
AICurlResponderBuffer_wat buffered_easy_request_w(*mCurlEasyRequest);
|
||||
buffered_easy_request_w->timed_out();
|
||||
easy_request_w->timed_out();
|
||||
}
|
||||
|
||||
// We're done. If we timed out, abort -- or else the application will
|
||||
@@ -235,12 +230,8 @@ void AICurlEasyRequestStateMachine::finish_impl(void)
|
||||
// Revoke callbacks.
|
||||
{
|
||||
AICurlEasyRequest_wat curl_easy_request_w(*mCurlEasyRequest);
|
||||
if (mBuffered)
|
||||
{
|
||||
AICurlResponderBuffer_wat buffered_easy_request_w(*mCurlEasyRequest);
|
||||
buffered_easy_request_w->send_buffer_events_to(NULL);
|
||||
}
|
||||
curl_easy_request_w->send_events_to(NULL);
|
||||
curl_easy_request_w->send_buffer_events_to(NULL);
|
||||
curl_easy_request_w->send_handle_events_to(NULL);
|
||||
curl_easy_request_w->revokeCallbacks();
|
||||
}
|
||||
if (mTimer)
|
||||
@@ -254,14 +245,10 @@ void AICurlEasyRequestStateMachine::finish_impl(void)
|
||||
kill();
|
||||
}
|
||||
|
||||
AICurlEasyRequestStateMachine::AICurlEasyRequestStateMachine(bool buffered) :
|
||||
mBuffered(buffered), mCurlEasyRequest(buffered), mTimer(NULL), mTotalDelayTimeout(AIHTTPTimeoutPolicy::getDebugSettingsCurlTimeout().getTotalDelay())
|
||||
AICurlEasyRequestStateMachine::AICurlEasyRequestStateMachine(void) :
|
||||
mTimer(NULL), mTotalDelayTimeout(AIHTTPTimeoutPolicy::getDebugSettingsCurlTimeout().getTotalDelay())
|
||||
{
|
||||
Dout(dc::statemachine, "Calling AICurlEasyRequestStateMachine(" << (buffered ? "true" : "false") << ") [" << (void*)this << "] [" << (void*)mCurlEasyRequest.get() << "]");
|
||||
if (!mBuffered)
|
||||
{
|
||||
llwarns << "Using unbuffered AICurlEasyRequestStateMachine" << llendl;
|
||||
}
|
||||
Dout(dc::statemachine, "Calling AICurlEasyRequestStateMachine(void) [" << (void*)this << "] [" << (void*)mCurlEasyRequest.get() << "]");
|
||||
}
|
||||
|
||||
void AICurlEasyRequestStateMachine::setTotalDelayTimeout(F32 totalDelayTimeout)
|
||||
|
||||
@@ -52,13 +52,12 @@
|
||||
// Construction of a AICurlEasyRequestStateMachine might throw AICurlNoEasyHandle.
|
||||
class AICurlEasyRequestStateMachine : public AIStateMachine, public AICurlEasyHandleEvents {
|
||||
public:
|
||||
AICurlEasyRequestStateMachine(bool buffered);
|
||||
AICurlEasyRequestStateMachine(void);
|
||||
|
||||
// Transparent access.
|
||||
AICurlEasyRequest mCurlEasyRequest;
|
||||
|
||||
private:
|
||||
bool mBuffered; // Argument used for construction of mCurlEasyRequest.
|
||||
bool mAdded; // Set when the last command to the curl thread was to add the request.
|
||||
bool mTimedOut; // Set if the expiration timer timed out.
|
||||
bool mFinished; // Set by the curl thread to signal it finished.
|
||||
|
||||
@@ -41,7 +41,8 @@ class AICurlEasyRequestStateMachine;
|
||||
|
||||
namespace AICurlPrivate {
|
||||
|
||||
class ThreadSafeCurlEasyRequest;
|
||||
class CurlEasyRequest;
|
||||
class ThreadSafeBufferedCurlEasyRequest;
|
||||
|
||||
namespace curlthread {
|
||||
|
||||
@@ -64,11 +65,11 @@ class HTTPTimeout : public LLRefCount {
|
||||
static F64 const sClockWidth; // Time between two clock ticks in seconds.
|
||||
static U64 sClockCount; // Clock count used as 'now' during one loop of the main loop.
|
||||
#if defined(CWDEBUG) || defined(DEBUG_CURLIO)
|
||||
ThreadSafeCurlEasyRequest* mLockObj;
|
||||
ThreadSafeBufferedCurlEasyRequest* mLockObj;
|
||||
#endif
|
||||
|
||||
public:
|
||||
HTTPTimeout(AIHTTPTimeoutPolicy const* policy, ThreadSafeCurlEasyRequest* lock_obj) :
|
||||
HTTPTimeout(AIHTTPTimeoutPolicy const* policy, ThreadSafeBufferedCurlEasyRequest* lock_obj) :
|
||||
mPolicy(policy), mNothingReceivedYet(true), mLowSpeedOn(false), mUploadFinished(false), mStalled((U64)-1)
|
||||
#if defined(CWDEBUG) || defined(DEBUG_CURLIO)
|
||||
, mLockObj(lock_obj)
|
||||
@@ -94,7 +95,7 @@ class HTTPTimeout : public LLRefCount {
|
||||
bool has_stalled(void) const { return mStalled < sClockCount; }
|
||||
|
||||
// Called from CurlResponderBuffer::processOutput if a timeout occurred.
|
||||
void print_diagnostics(AICurlEasyRequest_wat const& curlEasyRequest_w);
|
||||
void print_diagnostics(CurlEasyRequest const* curl_easy_request);
|
||||
|
||||
#if defined(CWDEBUG) || defined(DEBUG_CURLIO)
|
||||
void* get_lockobj(void) const { return mLockObj; }
|
||||
@@ -129,9 +130,6 @@ 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)
|
||||
|
||||
@@ -275,9 +273,9 @@ class CurlEasyHandle : public boost::noncopyable, protected AICurlEasyHandleEven
|
||||
// the data exchange. Use getResult() to determine if an error occurred.
|
||||
//
|
||||
// Note that the life cycle of a CurlEasyRequest is controlled by AICurlEasyRequest:
|
||||
// a CurlEasyRequest is only ever created as base class of a ThreadSafeCurlEasyRequest,
|
||||
// a CurlEasyRequest is only ever created as base class of a ThreadSafeBufferedCurlEasyRequest,
|
||||
// which is only created by creating a AICurlEasyRequest. When the last copy of such
|
||||
// AICurlEasyRequest is deleted, then also the ThreadSafeCurlEasyRequest is deleted
|
||||
// AICurlEasyRequest is deleted, then also the ThreadSafeBufferedCurlEasyRequest is deleted
|
||||
// and the CurlEasyRequest destructed.
|
||||
class CurlEasyRequest : public CurlEasyHandle {
|
||||
private:
|
||||
@@ -315,7 +313,9 @@ class CurlEasyRequest : public CurlEasyHandle {
|
||||
// Call this if the set callbacks are about to be invalidated.
|
||||
void revokeCallbacks(void);
|
||||
|
||||
protected:
|
||||
// Reset everything to the state it was in when this object was just created.
|
||||
// Called by BufferedCurlEasyRequest::resetState.
|
||||
void resetState(void);
|
||||
|
||||
private:
|
||||
@@ -326,7 +326,7 @@ class CurlEasyRequest : public CurlEasyHandle {
|
||||
static CURLcode curlCtxCallback(CURL* curl, void* sslctx, void* parm);
|
||||
|
||||
// Called from get_timeout_object and httptimeout.
|
||||
void create_timeout_object(ThreadSafeCurlEasyRequest* lockobj);
|
||||
void create_timeout_object(ThreadSafeBufferedCurlEasyRequest* lockobj);
|
||||
|
||||
public:
|
||||
// Set default options that we want applied to all curl easy handles.
|
||||
@@ -355,7 +355,7 @@ class CurlEasyRequest : public CurlEasyHandle {
|
||||
}
|
||||
|
||||
// Called by in case of an error.
|
||||
void print_diagnostics(AICurlEasyRequest_wat const& curlEasyRequest_w, CURLcode code);
|
||||
void print_diagnostics(CURLcode code);
|
||||
|
||||
// Called by MultiHandle::check_run_count() to fill info with the transfer info.
|
||||
void getTransferInfo(AITransferInfo* info);
|
||||
@@ -386,15 +386,14 @@ class CurlEasyRequest : public CurlEasyHandle {
|
||||
std::string const& getLowercaseHostname(void) const { return mLowercaseHostname; }
|
||||
// Called by CurlSocketInfo to allow access to the last (after a redirect) HTTPTimeout object related to this request.
|
||||
// This creates mTimeout (unless mTimeoutIsOrphan is set in which case it adopts the orphan).
|
||||
LLPointer<curlthread::HTTPTimeout>& get_timeout_object(ThreadSafeCurlEasyRequest* lockobj);
|
||||
LLPointer<curlthread::HTTPTimeout>& get_timeout_object(ThreadSafeBufferedCurlEasyRequest* lockobj);
|
||||
// Accessor for mTimeout with optional creation of orphaned object (if lockobj != NULL).
|
||||
LLPointer<curlthread::HTTPTimeout>& httptimeout(ThreadSafeCurlEasyRequest* lockobj = NULL) { if (lockobj && !mTimeout) create_timeout_object(lockobj); return mTimeout; }
|
||||
LLPointer<curlthread::HTTPTimeout>& httptimeout(ThreadSafeBufferedCurlEasyRequest* lockobj = NULL) { if (lockobj && !mTimeout) create_timeout_object(lockobj); return mTimeout; }
|
||||
// Return true if no data has been received on the latest socket (if any) for too long.
|
||||
bool has_stalled(void) const { return mTimeout && mTimeout->has_stalled(); }
|
||||
|
||||
private:
|
||||
// This class may only be created by constructing a ThreadSafeCurlEasyRequest.
|
||||
friend class ThreadSafeCurlEasyRequest;
|
||||
protected:
|
||||
// This class may only be created as base class of BufferedCurlEasyRequest.
|
||||
// Throws AICurlNoEasyHandle.
|
||||
CurlEasyRequest(void) : mHeaders(NULL), mHandleEventsTarget(NULL), mResult(CURLE_FAILED_INIT), mTimeoutPolicy(NULL), mTimeoutIsOrphan(false)
|
||||
#if defined(CWDEBUG) || defined(DEBUG_CURLIO)
|
||||
@@ -406,14 +405,14 @@ class CurlEasyRequest : public CurlEasyHandle {
|
||||
|
||||
public:
|
||||
// Post-initialization, set the parent to pass the events to.
|
||||
void send_events_to(AICurlEasyHandleEvents* target) { mHandleEventsTarget = target; }
|
||||
void send_handle_events_to(AICurlEasyHandleEvents* target) { mHandleEventsTarget = target; }
|
||||
|
||||
// For debugging purposes
|
||||
bool is_finalized(void) const { return mTimeoutPolicy; }
|
||||
|
||||
// Return pointer to the ThreadSafe (wrapped) version of this object.
|
||||
ThreadSafeCurlEasyRequest* get_lockobj(void);
|
||||
ThreadSafeCurlEasyRequest const* get_lockobj(void) const;
|
||||
inline ThreadSafeBufferedCurlEasyRequest* get_lockobj(void);
|
||||
inline ThreadSafeBufferedCurlEasyRequest const* get_lockobj(void) const;
|
||||
|
||||
protected:
|
||||
// Pass events to parent.
|
||||
@@ -422,27 +421,14 @@ class CurlEasyRequest : public CurlEasyHandle {
|
||||
/*virtual*/ void removed_from_multi_handle(AICurlEasyRequest_wat& curl_easy_request_w);
|
||||
};
|
||||
|
||||
// Buffers used by the AICurlInterface::Request API.
|
||||
// Curl callbacks write into and read from these buffers.
|
||||
// The interface with the rest of the code is through
|
||||
// AICurlInterface::ResponderBase and derived classes.
|
||||
//
|
||||
// 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 AICurlResponderBufferEvents, protected AICurlEasyHandleEvents {
|
||||
// This class adds input/output buffers to the request and hooks up the libcurl callbacks to use those buffers.
|
||||
// Received data is partially decoded and made available through various member functions.
|
||||
class BufferedCurlEasyRequest : public CurlEasyRequest {
|
||||
public:
|
||||
// The type of the used buffers.
|
||||
typedef boost::shared_ptr<LLBufferArray> buffer_ptr_t;
|
||||
|
||||
void resetState(AICurlEasyRequest_wat& curl_easy_request_w);
|
||||
void resetState(void);
|
||||
void prepRequest(AICurlEasyRequest_wat& buffered_curl_easy_request_w, AIHTTPHeaders const& headers, LLHTTPClient::ResponderPtr responder);
|
||||
|
||||
buffer_ptr_t& getInput(void) { return mInput; }
|
||||
@@ -452,7 +438,7 @@ class CurlResponderBuffer : protected AICurlResponderBufferEvents, protected AIC
|
||||
void timed_out(void);
|
||||
|
||||
// Called after removed_from_multi_handle was called.
|
||||
void processOutput(AICurlEasyRequest_wat& curl_easy_request_w);
|
||||
void processOutput(void);
|
||||
|
||||
// Do not write more than this amount.
|
||||
//void setBodyLimit(U32 size) { mBodyLimit = size; }
|
||||
@@ -466,11 +452,6 @@ class CurlResponderBuffer : protected AICurlResponderBufferEvents, protected AIC
|
||||
/*virtual*/ void received_header(std::string const& key, std::string const& value);
|
||||
/*virtual*/ void completed_headers(U32 status, std::string const& reason, AITransferInfo* info);
|
||||
|
||||
// CurlEasyHandle events.
|
||||
/*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:
|
||||
buffer_ptr_t mInput;
|
||||
U8* mLastRead; // Pointer into mInput where we last stopped reading (or NULL to start at the beginning).
|
||||
@@ -489,9 +470,9 @@ class CurlResponderBuffer : protected AICurlResponderBufferEvents, protected AIC
|
||||
private:
|
||||
// This class may only be created by constructing a ThreadSafeBufferedCurlEasyRequest.
|
||||
friend class ThreadSafeBufferedCurlEasyRequest;
|
||||
CurlResponderBuffer(void);
|
||||
BufferedCurlEasyRequest(void);
|
||||
public:
|
||||
~CurlResponderBuffer();
|
||||
~BufferedCurlEasyRequest();
|
||||
|
||||
private:
|
||||
static size_t curlWriteCallback(char* data, size_t size, size_t nmemb, void* user_data);
|
||||
@@ -511,46 +492,38 @@ class CurlResponderBuffer : protected AICurlResponderBufferEvents, protected AIC
|
||||
bool isValid(void) const { return mResponder; }
|
||||
};
|
||||
|
||||
// This class wraps CurlEasyRequest for thread-safety and adds a reference counter so we can
|
||||
inline ThreadSafeBufferedCurlEasyRequest* CurlEasyRequest::get_lockobj(void)
|
||||
{
|
||||
return static_cast<BufferedCurlEasyRequest*>(this)->get_lockobj();
|
||||
}
|
||||
|
||||
inline ThreadSafeBufferedCurlEasyRequest const* CurlEasyRequest::get_lockobj(void) const
|
||||
{
|
||||
return static_cast<BufferedCurlEasyRequest const*>(this)->get_lockobj();
|
||||
}
|
||||
|
||||
// This class wraps BufferedCurlEasyRequest for thread-safety and adds a reference counter so we can
|
||||
// copy it around cheaply and it gets destructed automatically when the last instance is deleted.
|
||||
// It guarantees that the CURL* handle is never used concurrently, which is not allowed by libcurl.
|
||||
// As AIThreadSafeSimpleDC contains a mutex, it cannot be copied. Therefore we need a reference counter for this object.
|
||||
class ThreadSafeCurlEasyRequest : public AIThreadSafeSimple<CurlEasyRequest> {
|
||||
class ThreadSafeBufferedCurlEasyRequest : public AIThreadSafeSimple<BufferedCurlEasyRequest> {
|
||||
public:
|
||||
// Throws AICurlNoEasyHandle.
|
||||
ThreadSafeCurlEasyRequest(void) : mReferenceCount(0)
|
||||
{ new (ptr()) CurlEasyRequest;
|
||||
Dout(dc::curl, "Creating ThreadSafeCurlEasyRequest with this = " << (void*)this); }
|
||||
virtual ~ThreadSafeCurlEasyRequest()
|
||||
{ Dout(dc::curl, "Destructing ThreadSafeCurlEasyRequest with this = " << (void*)this); }
|
||||
|
||||
// Returns true if this is a base class of ThreadSafeBufferedCurlEasyRequest.
|
||||
virtual bool isBuffered(void) const { return false; }
|
||||
ThreadSafeBufferedCurlEasyRequest(void) : mReferenceCount(0)
|
||||
{ new (ptr()) BufferedCurlEasyRequest;
|
||||
Dout(dc::curl, "Creating ThreadSafeBufferedCurlEasyRequest with this = " << (void*)this); }
|
||||
virtual ~ThreadSafeBufferedCurlEasyRequest()
|
||||
{ Dout(dc::curl, "Destructing ThreadSafeBufferedCurlEasyRequest with this = " << (void*)this); }
|
||||
|
||||
private:
|
||||
LLAtomicU32 mReferenceCount;
|
||||
|
||||
friend void intrusive_ptr_add_ref(ThreadSafeCurlEasyRequest* p); // Called by boost::intrusive_ptr when a new copy of a boost::intrusive_ptr<ThreadSafeCurlEasyRequest> is made.
|
||||
friend void intrusive_ptr_release(ThreadSafeCurlEasyRequest* p); // Called by boost::intrusive_ptr when a boost::intrusive_ptr<ThreadSafeCurlEasyRequest> is destroyed.
|
||||
};
|
||||
|
||||
// Same as the above but adds a CurlResponderBuffer. The latter has its own locking in order to
|
||||
// allow casting the underlying CurlEasyRequest to ThreadSafeCurlEasyRequest, independent of
|
||||
// what class it is part of: ThreadSafeCurlEasyRequest or ThreadSafeBufferedCurlEasyRequest.
|
||||
// The virtual destructor of ThreadSafeCurlEasyRequest allows to treat each easy handle transparently
|
||||
// as a ThreadSafeCurlEasyRequest object, or optionally dynamic_cast it to a ThreadSafeBufferedCurlEasyRequest.
|
||||
// Note: the order of these base classes is important: AIThreadSafeSimple<CurlResponderBuffer> is now
|
||||
// destructed before ThreadSafeCurlEasyRequest is.
|
||||
class ThreadSafeBufferedCurlEasyRequest : public ThreadSafeCurlEasyRequest, public AIThreadSafeSimple<CurlResponderBuffer> {
|
||||
public:
|
||||
// Throws AICurlNoEasyHandle.
|
||||
ThreadSafeBufferedCurlEasyRequest(void) { new (AIThreadSafeSimple<CurlResponderBuffer>::ptr()) CurlResponderBuffer; }
|
||||
|
||||
/*virtual*/ bool isBuffered(void) const { return true; }
|
||||
friend void intrusive_ptr_add_ref(ThreadSafeBufferedCurlEasyRequest* p); // Called by boost::intrusive_ptr when a new copy of a boost::intrusive_ptr<ThreadSafeBufferedCurlEasyRequest> is made.
|
||||
friend void intrusive_ptr_release(ThreadSafeBufferedCurlEasyRequest* p); // Called by boost::intrusive_ptr when a boost::intrusive_ptr<ThreadSafeBufferedCurlEasyRequest> is destroyed.
|
||||
};
|
||||
|
||||
// The curl easy request type wrapped in a reference counting pointer.
|
||||
typedef boost::intrusive_ptr<AICurlPrivate::ThreadSafeCurlEasyRequest> CurlEasyRequestPtr;
|
||||
typedef boost::intrusive_ptr<AICurlPrivate::ThreadSafeBufferedCurlEasyRequest> BufferedCurlEasyRequestPtr;
|
||||
|
||||
// This class wraps CURLM*'s.
|
||||
// It guarantees that a pointer is cleaned up when no longer needed, as required by libcurl.
|
||||
|
||||
@@ -215,14 +215,14 @@ class Command {
|
||||
Command(AICurlEasyRequest const& easy_request, command_st command) : mCurlEasyRequest(easy_request.get_ptr()), mCommand(command) { }
|
||||
|
||||
command_st command(void) const { return mCommand; }
|
||||
CurlEasyRequestPtr const& easy_request(void) const { return mCurlEasyRequest; }
|
||||
BufferedCurlEasyRequestPtr const& easy_request(void) const { return mCurlEasyRequest; }
|
||||
|
||||
bool operator==(AICurlEasyRequest const& easy_request) const { return mCurlEasyRequest == easy_request.get_ptr(); }
|
||||
|
||||
void reset(void);
|
||||
|
||||
private:
|
||||
CurlEasyRequestPtr mCurlEasyRequest;
|
||||
BufferedCurlEasyRequestPtr mCurlEasyRequest;
|
||||
command_st mCommand;
|
||||
};
|
||||
|
||||
@@ -237,11 +237,11 @@ void Command::reset(void)
|
||||
//
|
||||
// MAIN-THREAD (AICurlEasyRequest::addRequest)
|
||||
// * command_queue locked
|
||||
// - A non-active (mActiveMultiHandle is NULL) ThreadSafeCurlEasyRequest (by means of an AICurlEasyRequest pointing to it) is added to command_queue with as command cmd_add.
|
||||
// - A non-active (mActiveMultiHandle is NULL) ThreadSafeBufferedCurlEasyRequest (by means of an AICurlEasyRequest pointing to it) is added to command_queue with as command cmd_add.
|
||||
// * command_queue unlocked
|
||||
//
|
||||
// If at this point addRequest is called again, then it is detected that the last command added to the queue
|
||||
// for this ThreadSafeCurlEasyRequest is cmd_add.
|
||||
// for this ThreadSafeBufferedCurlEasyRequest is cmd_add.
|
||||
//
|
||||
// CURL-THREAD (AICurlThread::wakeup):
|
||||
// * command_queue locked
|
||||
@@ -251,7 +251,7 @@ void Command::reset(void)
|
||||
// - The command is removed from command_queue
|
||||
// * command_queue unlocked
|
||||
//
|
||||
// If at this point addRequest is called again, then it is detected that command_being_processed adds the same ThreadSafeCurlEasyRequest.
|
||||
// If at this point addRequest is called again, then it is detected that command_being_processed adds the same ThreadSafeBufferedCurlEasyRequest.
|
||||
//
|
||||
// * command_being_processed is read-locked
|
||||
// - mActiveMultiHandle is set to point to the curl multi handle
|
||||
@@ -260,7 +260,7 @@ void Command::reset(void)
|
||||
// - command_being_processed is reset
|
||||
// * command_being_processed is unlocked
|
||||
//
|
||||
// If at this point addRequest is called again, then it is detected that the ThreadSafeCurlEasyRequest is active.
|
||||
// If at this point addRequest is called again, then it is detected that the ThreadSafeBufferedCurlEasyRequest is active.
|
||||
|
||||
// Multi-threaded queue for passing Command objects from the main-thread to the curl-thread.
|
||||
AIThreadSafeSimpleDC<std::deque<Command> > command_queue;
|
||||
@@ -746,7 +746,7 @@ std::ostream& operator<<(std::ostream& os, DebugFdSet const& s)
|
||||
class CurlSocketInfo
|
||||
{
|
||||
public:
|
||||
CurlSocketInfo(MultiHandle& multi_handle, CURL* easy, curl_socket_t s, int action, ThreadSafeCurlEasyRequest* lockobj);
|
||||
CurlSocketInfo(MultiHandle& multi_handle, CURL* easy, curl_socket_t s, int action, ThreadSafeBufferedCurlEasyRequest* lockobj);
|
||||
~CurlSocketInfo();
|
||||
|
||||
void set_action(int action);
|
||||
@@ -760,7 +760,7 @@ class CurlSocketInfo
|
||||
LLPointer<HTTPTimeout> mTimeout;
|
||||
};
|
||||
|
||||
CurlSocketInfo::CurlSocketInfo(MultiHandle& multi_handle, CURL* easy, curl_socket_t s, int action, ThreadSafeCurlEasyRequest* lockobj) :
|
||||
CurlSocketInfo::CurlSocketInfo(MultiHandle& multi_handle, CURL* easy, curl_socket_t s, int action, ThreadSafeBufferedCurlEasyRequest* lockobj) :
|
||||
mMultiHandle(multi_handle), mEasy(easy), mSocketFd(s), mAction(CURL_POLL_NONE), mEasyRequest(lockobj)
|
||||
{
|
||||
llassert(*AICurlEasyRequest_wat(*mEasyRequest) == easy);
|
||||
@@ -1464,7 +1464,7 @@ void MultiHandle::handle_stalls(void)
|
||||
int MultiHandle::socket_callback(CURL* easy, curl_socket_t s, int action, void* userp, void* socketp)
|
||||
{
|
||||
#ifdef CWDEBUG
|
||||
ThreadSafeCurlEasyRequest* lockobj = NULL;
|
||||
ThreadSafeBufferedCurlEasyRequest* lockobj = NULL;
|
||||
curl_easy_getinfo(easy, CURLINFO_PRIVATE, &lockobj);
|
||||
DoutEntering(dc::curl, "MultiHandle::socket_callback((CURL*)" << (void*)easy << ", " << s <<
|
||||
", " << action_str(action) << ", " << (void*)userp << ", " << (void*)socketp << ") [CURLINFO_PRIVATE = " << (void*)lockobj << "]");
|
||||
@@ -1479,7 +1479,7 @@ int MultiHandle::socket_callback(CURL* easy, curl_socket_t s, int action, void*
|
||||
{
|
||||
if (!sock_info)
|
||||
{
|
||||
ThreadSafeCurlEasyRequest* ptr;
|
||||
ThreadSafeBufferedCurlEasyRequest* ptr;
|
||||
CURLcode rese = curl_easy_getinfo(easy, CURLINFO_PRIVATE, &ptr);
|
||||
llassert_always(rese == CURLE_OK);
|
||||
sock_info = new CurlSocketInfo(self, easy, s, action, ptr);
|
||||
@@ -1599,7 +1599,7 @@ CURLMcode MultiHandle::remove_easy_request(addedEasyRequests_type::iterator cons
|
||||
#endif
|
||||
}
|
||||
#if CWDEBUG
|
||||
ThreadSafeCurlEasyRequest* lockobj = iter->get_ptr().get();
|
||||
ThreadSafeBufferedCurlEasyRequest* lockobj = iter->get_ptr().get();
|
||||
#endif
|
||||
mAddedEasyRequests.erase(iter);
|
||||
Dout(dc::curl, "MultiHandle::remove_easy_request: Removed AICurlEasyRequest " << (void*)lockobj << "; now processing " << mAddedEasyRequests.size() << " easy handles.");
|
||||
@@ -1626,7 +1626,7 @@ void MultiHandle::check_run_count(void)
|
||||
if (msg->msg == CURLMSG_DONE)
|
||||
{
|
||||
CURL* easy = msg->easy_handle;
|
||||
ThreadSafeCurlEasyRequest* ptr;
|
||||
ThreadSafeBufferedCurlEasyRequest* ptr;
|
||||
CURLcode rese = curl_easy_getinfo(easy, CURLINFO_PRIVATE, &ptr);
|
||||
llassert_always(rese == CURLE_OK);
|
||||
AICurlEasyRequest easy_request(ptr);
|
||||
@@ -1973,9 +1973,9 @@ void HTTPTimeout::done(AICurlEasyRequest_wat const& curlEasyRequest_w, CURLcode
|
||||
DoutCurl("done: mStalled set to -1");
|
||||
}
|
||||
|
||||
void HTTPTimeout::print_diagnostics(AICurlEasyRequest_wat const& curlEasyRequest_w)
|
||||
void HTTPTimeout::print_diagnostics(CurlEasyRequest const* curl_easy_request)
|
||||
{
|
||||
llwarns << "Request to " << curlEasyRequest_w->getLowercaseHostname() << " timed out for " << curlEasyRequest_w->getTimeoutPolicy()->name() << llendl;
|
||||
llwarns << "Request to " << curl_easy_request->getLowercaseHostname() << " timed out for " << curl_easy_request->getTimeoutPolicy()->name() << llendl;
|
||||
}
|
||||
|
||||
} // namespace curlthread
|
||||
@@ -2039,48 +2039,25 @@ void stopCurlThread(void)
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CurlResponderBuffer
|
||||
// BufferedCurlEasyRequest
|
||||
|
||||
void CurlResponderBuffer::setStatusAndReason(U32 status, std::string const& reason)
|
||||
void BufferedCurlEasyRequest::setStatusAndReason(U32 status, std::string const& reason)
|
||||
{
|
||||
mStatus = status;
|
||||
mReason = reason;
|
||||
}
|
||||
|
||||
void CurlResponderBuffer::added_to_multi_handle(AICurlEasyRequest_wat& curl_easy_request_w)
|
||||
{
|
||||
llerrs << "Unexpected call to added_to_multi_handle()." << llendl;
|
||||
}
|
||||
|
||||
void CurlResponderBuffer::finished(AICurlEasyRequest_wat& curl_easy_request_w)
|
||||
{
|
||||
llerrs << "Unexpected call to finished()." << llendl;
|
||||
}
|
||||
|
||||
void CurlResponderBuffer::removed_from_multi_handle(AICurlEasyRequest_wat& curl_easy_request_w)
|
||||
{
|
||||
DoutCurl("Calling CurlResponderBuffer::removed_from_multi_handle(@" << (void*)&*curl_easy_request_w << ") for this = " << (void*)this);
|
||||
|
||||
// Lock self.
|
||||
ThreadSafeBufferedCurlEasyRequest* lockobj = get_lockobj();
|
||||
llassert(dynamic_cast<ThreadSafeBufferedCurlEasyRequest*>(static_cast<ThreadSafeCurlEasyRequest*>(ThreadSafeCurlEasyRequest::wrapper_cast(&*curl_easy_request_w))) == lockobj);
|
||||
AICurlResponderBuffer_wat buffer_w(*lockobj);
|
||||
llassert(&*buffer_w == this);
|
||||
|
||||
processOutput(curl_easy_request_w);
|
||||
}
|
||||
|
||||
void CurlResponderBuffer::processOutput(AICurlEasyRequest_wat& curl_easy_request_w)
|
||||
void BufferedCurlEasyRequest::processOutput(void)
|
||||
{
|
||||
U32 responseCode = 0;
|
||||
std::string responseReason;
|
||||
|
||||
CURLcode code;
|
||||
AITransferInfo info;
|
||||
curl_easy_request_w->getResult(&code, &info);
|
||||
getResult(&code, &info);
|
||||
if (code == CURLE_OK)
|
||||
{
|
||||
curl_easy_request_w->getinfo(CURLINFO_RESPONSE_CODE, &responseCode);
|
||||
getinfo(CURLINFO_RESPONSE_CODE, &responseCode);
|
||||
// If getResult code is CURLE_OK then we should have decoded the first header line ourselves.
|
||||
llassert(responseCode == mStatus);
|
||||
if (responseCode == mStatus)
|
||||
@@ -2092,12 +2069,12 @@ void CurlResponderBuffer::processOutput(AICurlEasyRequest_wat& curl_easy_request
|
||||
{
|
||||
responseCode = HTTP_INTERNAL_ERROR;
|
||||
responseReason = curl_easy_strerror(code);
|
||||
curl_easy_request_w->setopt(CURLOPT_FRESH_CONNECT, TRUE);
|
||||
setopt(CURLOPT_FRESH_CONNECT, TRUE);
|
||||
}
|
||||
|
||||
if (code != CURLE_OK)
|
||||
{
|
||||
curl_easy_request_w->print_diagnostics(curl_easy_request_w, code);
|
||||
print_diagnostics(code);
|
||||
}
|
||||
if (mBufferEventsTarget)
|
||||
{
|
||||
@@ -2110,43 +2087,42 @@ void CurlResponderBuffer::processOutput(AICurlEasyRequest_wat& curl_easy_request
|
||||
mResponder->finished(code, responseCode, responseReason, sChannels, mOutput);
|
||||
mResponder = NULL;
|
||||
|
||||
resetState(curl_easy_request_w);
|
||||
resetState();
|
||||
}
|
||||
|
||||
void CurlResponderBuffer::received_HTTP_header(void)
|
||||
void BufferedCurlEasyRequest::received_HTTP_header(void)
|
||||
{
|
||||
if (mBufferEventsTarget)
|
||||
mBufferEventsTarget->received_HTTP_header();
|
||||
}
|
||||
|
||||
void CurlResponderBuffer::received_header(std::string const& key, std::string const& value)
|
||||
void BufferedCurlEasyRequest::received_header(std::string const& key, std::string const& value)
|
||||
{
|
||||
if (mBufferEventsTarget)
|
||||
mBufferEventsTarget->received_header(key, value);
|
||||
}
|
||||
|
||||
void CurlResponderBuffer::completed_headers(U32 status, std::string const& reason, AITransferInfo* info)
|
||||
void BufferedCurlEasyRequest::completed_headers(U32 status, std::string const& reason, AITransferInfo* info)
|
||||
{
|
||||
if (mBufferEventsTarget)
|
||||
mBufferEventsTarget->completed_headers(status, reason, info);
|
||||
}
|
||||
|
||||
//static
|
||||
size_t CurlResponderBuffer::curlWriteCallback(char* data, size_t size, size_t nmemb, void* user_data)
|
||||
size_t BufferedCurlEasyRequest::curlWriteCallback(char* data, size_t size, size_t nmemb, void* user_data)
|
||||
{
|
||||
ThreadSafeBufferedCurlEasyRequest* lockobj = static_cast<ThreadSafeBufferedCurlEasyRequest*>(user_data);
|
||||
|
||||
// We need to lock the curl easy request object too, because that lock is used
|
||||
// to make sure that callbacks and destruction aren't done simultaneously.
|
||||
AICurlEasyRequest_wat buffered_easy_request_w(*lockobj);
|
||||
AICurlEasyRequest_wat self_w(*lockobj);
|
||||
|
||||
S32 bytes = size * nmemb; // The amount to write.
|
||||
AICurlResponderBuffer_wat buffer_w(*lockobj);
|
||||
// CurlResponderBuffer::setBodyLimit is never called, so buffer_w->mBodyLimit is infinite.
|
||||
// BufferedCurlEasyRequest::setBodyLimit is never called, so buffer_w->mBodyLimit is infinite.
|
||||
//S32 bytes = llmin(size * nmemb, buffer_w->mBodyLimit); buffer_w->mBodyLimit -= bytes;
|
||||
buffer_w->getOutput()->append(sChannels.in(), (U8 const*)data, bytes);
|
||||
buffer_w->mResponseTransferedBytes += bytes; // Accumulate data received from the server.
|
||||
if (buffered_easy_request_w->httptimeout()->data_received(bytes)) // Update timeout administration.
|
||||
self_w->getOutput()->append(sChannels.in(), (U8 const*)data, bytes);
|
||||
self_w->mResponseTransferedBytes += bytes; // Accumulate data received from the server.
|
||||
if (self_w->httptimeout()->data_received(bytes)) // Update timeout administration.
|
||||
{
|
||||
// Transfer timed out. Return 0 which will abort with error CURLE_WRITE_ERROR.
|
||||
return 0;
|
||||
@@ -2155,23 +2131,22 @@ size_t CurlResponderBuffer::curlWriteCallback(char* data, size_t size, size_t nm
|
||||
}
|
||||
|
||||
//static
|
||||
size_t CurlResponderBuffer::curlReadCallback(char* data, size_t size, size_t nmemb, void* user_data)
|
||||
size_t BufferedCurlEasyRequest::curlReadCallback(char* data, size_t size, size_t nmemb, void* user_data)
|
||||
{
|
||||
ThreadSafeBufferedCurlEasyRequest* lockobj = static_cast<ThreadSafeBufferedCurlEasyRequest*>(user_data);
|
||||
|
||||
// We need to lock the curl easy request object too, because that lock is used
|
||||
// to make sure that callbacks and destruction aren't done simultaneously.
|
||||
AICurlEasyRequest_wat buffered_easy_request_w(*lockobj);
|
||||
AICurlEasyRequest_wat self_w(*lockobj);
|
||||
|
||||
S32 bytes = size * nmemb; // The maximum amount to read.
|
||||
AICurlResponderBuffer_wat buffer_w(*lockobj);
|
||||
buffer_w->mLastRead = buffer_w->getInput()->readAfter(sChannels.out(), buffer_w->mLastRead, (U8*)data, bytes);
|
||||
buffer_w->mRequestTransferedBytes += bytes; // Accumulate data sent to the server.
|
||||
self_w->mLastRead = self_w->getInput()->readAfter(sChannels.out(), self_w->mLastRead, (U8*)data, bytes);
|
||||
self_w->mRequestTransferedBytes += bytes; // Accumulate data sent to the server.
|
||||
// Timeout administration. Note that it can happen that we get here
|
||||
// before the socket callback has been called, because the silly libcurl
|
||||
// writes headers without informing us. In that case it's OK to create
|
||||
// the Timeout object on the fly, so pass lockobj.
|
||||
if (buffered_easy_request_w->httptimeout(lockobj)->data_sent(bytes))
|
||||
if (self_w->httptimeout(lockobj)->data_sent(bytes))
|
||||
{
|
||||
// Transfer timed out. Return CURL_READFUNC_ABORT which will abort with error CURLE_ABORTED_BY_CALLBACK.
|
||||
return CURL_READFUNC_ABORT;
|
||||
@@ -2180,19 +2155,19 @@ size_t CurlResponderBuffer::curlReadCallback(char* data, size_t size, size_t nme
|
||||
}
|
||||
|
||||
//static
|
||||
size_t CurlResponderBuffer::curlHeaderCallback(char* data, size_t size, size_t nmemb, void* user_data)
|
||||
size_t BufferedCurlEasyRequest::curlHeaderCallback(char* data, size_t size, size_t nmemb, void* user_data)
|
||||
{
|
||||
ThreadSafeBufferedCurlEasyRequest* lockobj = static_cast<ThreadSafeBufferedCurlEasyRequest*>(user_data);
|
||||
|
||||
// We need to lock the curl easy request object, because that lock is used
|
||||
// to make sure that callbacks and destruction aren't done simultaneously.
|
||||
AICurlEasyRequest_wat buffered_easy_request_w(*lockobj);
|
||||
AICurlEasyRequest_wat self_w(*lockobj);
|
||||
|
||||
// This used to be headerCallback() in llurlrequest.cpp.
|
||||
|
||||
char const* const header_line = static_cast<char const*>(data);
|
||||
size_t const header_len = size * nmemb;
|
||||
if (buffered_easy_request_w->httptimeout()->data_received(header_len)) // Update timeout administration.
|
||||
if (self_w->httptimeout()->data_received(header_len)) // Update timeout administration.
|
||||
{
|
||||
// Transfer timed out. Return 0 which will abort with error CURLE_WRITE_ERROR.
|
||||
return 0;
|
||||
@@ -2231,9 +2206,8 @@ size_t CurlResponderBuffer::curlHeaderCallback(char* data, size_t size, size_t n
|
||||
llwarns << "Received broken header line from server: \"" << header << "\"" << llendl;
|
||||
}
|
||||
{
|
||||
AICurlResponderBuffer_wat curl_responder_buffer_w(*lockobj);
|
||||
curl_responder_buffer_w->received_HTTP_header();
|
||||
curl_responder_buffer_w->setStatusAndReason(status, reason);
|
||||
self_w->received_HTTP_header();
|
||||
self_w->setStatusAndReason(status, reason);
|
||||
}
|
||||
return header_len;
|
||||
}
|
||||
@@ -2248,7 +2222,7 @@ size_t CurlResponderBuffer::curlHeaderCallback(char* data, size_t size, size_t n
|
||||
key = utf8str_tolower(utf8str_trim(key));
|
||||
value = utf8str_trim(value);
|
||||
|
||||
AICurlResponderBuffer_wat(*lockobj)->received_header(key, value);
|
||||
self_w->received_header(key, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2268,7 +2242,7 @@ int debug_callback(CURL*, curl_infotype infotype, char* buf, size_t size, void*
|
||||
#ifdef CWDEBUG
|
||||
using namespace ::libcwd;
|
||||
|
||||
CurlEasyRequest* request = (CurlEasyRequest*)user_ptr;
|
||||
BufferedCurlEasyRequest* request = (BufferedCurlEasyRequest*)user_ptr;
|
||||
std::ostringstream marker;
|
||||
marker << (void*)request->get_lockobj();
|
||||
libcw_do.push_marker();
|
||||
|
||||
@@ -84,15 +84,6 @@ public:
|
||||
* 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 ResponderBase : public AICurlResponderBufferEvents {
|
||||
public:
|
||||
|
||||
@@ -78,7 +78,7 @@ std::string LLURLRequest::actionAsVerb(LLURLRequest::ERequestAction action)
|
||||
// This might throw AICurlNoEasyHandle.
|
||||
LLURLRequest::LLURLRequest(LLURLRequest::ERequestAction action, std::string const& url, Injector* body,
|
||||
LLHTTPClient::ResponderPtr responder, AIHTTPHeaders& headers, bool is_auth, bool no_compression) :
|
||||
AICurlEasyRequestStateMachine(true), mAction(action), mURL(url), mIsAuth(is_auth), mNoCompression(no_compression),
|
||||
mAction(action), mURL(url), mIsAuth(is_auth), mNoCompression(no_compression),
|
||||
mBody(body), mResponder(responder), mHeaders(headers)
|
||||
{
|
||||
}
|
||||
@@ -121,17 +121,16 @@ void LLURLRequest::initialize_impl(void)
|
||||
bool success = false;
|
||||
try
|
||||
{
|
||||
AICurlEasyRequest_wat buffered_easy_request_w(*mCurlEasyRequest);
|
||||
AICurlResponderBuffer_wat buffer_w(*mCurlEasyRequest);
|
||||
buffer_w->prepRequest(buffered_easy_request_w, mHeaders, mResponder);
|
||||
AICurlEasyRequest_wat easy_request_w(*mCurlEasyRequest);
|
||||
easy_request_w->prepRequest(easy_request_w, mHeaders, mResponder);
|
||||
|
||||
if (mBody)
|
||||
{
|
||||
// This might throw AICurlNoBody.
|
||||
mBodySize = mBody->get_body(buffer_w->sChannels, buffer_w->getInput());
|
||||
mBodySize = mBody->get_body(easy_request_w->sChannels, easy_request_w->getInput());
|
||||
}
|
||||
|
||||
success = configure(buffered_easy_request_w);
|
||||
success = configure(easy_request_w);
|
||||
}
|
||||
catch (AICurlNoBody const& error)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user