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:
Aleric Inglewood
2012-11-02 18:41:23 +01:00
parent 1e1f5e8193
commit 72bde5234a
8 changed files with 170 additions and 272 deletions

View File

@@ -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);

View File

@@ -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

View File

@@ -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)

View File

@@ -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.

View File

@@ -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.

View File

@@ -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();

View File

@@ -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:

View File

@@ -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)
{