diff --git a/indra/llmessage/aicurl.cpp b/indra/llmessage/aicurl.cpp index 54093e5a9..06e13e0f0 100644 --- a/indra/llmessage/aicurl.cpp +++ b/indra/llmessage/aicurl.cpp @@ -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(AIThreadSafeSimpleDC::wrapper_cast(this)); -} - -ThreadSafeCurlEasyRequest const* CurlEasyRequest::get_lockobj(void) const -{ - return static_cast(AIThreadSafeSimpleDC::wrapper_cast(this)); -} - //static size_t CurlEasyRequest::headerCallback(char* ptr, size_t size, size_t nmemb, void* userdata) { CurlEasyRequest* self = static_cast(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(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(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(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& CurlEasyRequest::get_timeout_object(ThreadSafeCurlEasyRequest* lockobj) +LLPointer& 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 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(AIThreadSafeSimple::wrapper_cast(this)); + return static_cast(AIThreadSafeSimple::wrapper_cast(this)); } -ThreadSafeBufferedCurlEasyRequest const* CurlResponderBuffer::get_lockobj(void) const +ThreadSafeBufferedCurlEasyRequest const* BufferedCurlEasyRequest::get_lockobj(void) const { - return static_cast(AIThreadSafeSimple::wrapper_cast(this)); + return static_cast(AIThreadSafeSimple::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); diff --git a/indra/llmessage/aicurl.h b/indra/llmessage/aicurl.h index dfd64a80c..4424e4299 100644 --- a/indra/llmessage/aicurl.h +++ b/indra/llmessage/aicurl.h @@ -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 AICurlEasyRequest_rat; -typedef AIAccess AICurlEasyRequest_wat; +typedef AIAccessConst AICurlEasyRequest_rat; +typedef AIAccess AICurlEasyRequest_wat; // Events generated by AICurlPrivate::CurlEasyHandle. struct AICurlEasyHandleEvents { @@ -205,38 +205,38 @@ typedef LLPointer 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& operator*(void) const { llassert(mCurlEasyRequest.get()); return *mCurlEasyRequest; } - AIThreadSafeSimple* operator->(void) const { llassert(mCurlEasyRequest.get()); return mCurlEasyRequest.get(); } - AIThreadSafeSimple* get(void) const { return mCurlEasyRequest.get(); } + AIThreadSafeSimple& operator*(void) const { llassert(mBufferedCurlEasyRequest.get()); return *mBufferedCurlEasyRequest; } + AIThreadSafeSimple* operator->(void) const { llassert(mBufferedCurlEasyRequest.get()); return mBufferedCurlEasyRequest.get(); } + AIThreadSafeSimple* 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 { - explicit AICurlResponderBuffer_wat(AICurlPrivate::ThreadSafeBufferedCurlEasyRequest& lockobj) : - AIAccess(lockobj) { } - AICurlResponderBuffer_wat(AIThreadSafeSimple& lockobj) : - AIAccess(static_cast(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 diff --git a/indra/llmessage/aicurleasyrequeststatemachine.cpp b/indra/llmessage/aicurleasyrequeststatemachine.cpp index 910bbae1d..a5c9f9d44 100644 --- a/indra/llmessage/aicurleasyrequeststatemachine.cpp +++ b/indra/llmessage/aicurleasyrequeststatemachine.cpp @@ -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) diff --git a/indra/llmessage/aicurleasyrequeststatemachine.h b/indra/llmessage/aicurleasyrequeststatemachine.h index 3878a597e..8ece6fc26 100644 --- a/indra/llmessage/aicurleasyrequeststatemachine.h +++ b/indra/llmessage/aicurleasyrequeststatemachine.h @@ -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. diff --git a/indra/llmessage/aicurlprivate.h b/indra/llmessage/aicurlprivate.h index 5ccac0450..9fb62ca15 100644 --- a/indra/llmessage/aicurlprivate.h +++ b/indra/llmessage/aicurlprivate.h @@ -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& get_timeout_object(ThreadSafeCurlEasyRequest* lockobj); + LLPointer& get_timeout_object(ThreadSafeBufferedCurlEasyRequest* lockobj); // Accessor for mTimeout with optional creation of orphaned object (if lockobj != NULL). - LLPointer& httptimeout(ThreadSafeCurlEasyRequest* lockobj = NULL) { if (lockobj && !mTimeout) create_timeout_object(lockobj); return mTimeout; } + LLPointer& 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 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(this)->get_lockobj(); +} + +inline ThreadSafeBufferedCurlEasyRequest const* CurlEasyRequest::get_lockobj(void) const +{ + return static_cast(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 { +class ThreadSafeBufferedCurlEasyRequest : public AIThreadSafeSimple { 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 is made. - friend void intrusive_ptr_release(ThreadSafeCurlEasyRequest* p); // Called by boost::intrusive_ptr when a boost::intrusive_ptr 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 is now -// destructed before ThreadSafeCurlEasyRequest is. -class ThreadSafeBufferedCurlEasyRequest : public ThreadSafeCurlEasyRequest, public AIThreadSafeSimple { - public: - // Throws AICurlNoEasyHandle. - ThreadSafeBufferedCurlEasyRequest(void) { new (AIThreadSafeSimple::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 is made. + friend void intrusive_ptr_release(ThreadSafeBufferedCurlEasyRequest* p); // Called by boost::intrusive_ptr when a boost::intrusive_ptr is destroyed. }; // The curl easy request type wrapped in a reference counting pointer. -typedef boost::intrusive_ptr CurlEasyRequestPtr; +typedef boost::intrusive_ptr BufferedCurlEasyRequestPtr; // This class wraps CURLM*'s. // It guarantees that a pointer is cleaned up when no longer needed, as required by libcurl. diff --git a/indra/llmessage/aicurlthread.cpp b/indra/llmessage/aicurlthread.cpp index a0196b07a..91744ce23 100644 --- a/indra/llmessage/aicurlthread.cpp +++ b/indra/llmessage/aicurlthread.cpp @@ -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 > 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 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(static_cast(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(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(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(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(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(); diff --git a/indra/llmessage/llhttpclient.h b/indra/llmessage/llhttpclient.h index 05f3d4b63..1c8fe9c65 100644 --- a/indra/llmessage/llhttpclient.h +++ b/indra/llmessage/llhttpclient.h @@ -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: diff --git a/indra/llmessage/llurlrequest.cpp b/indra/llmessage/llurlrequest.cpp index 3653f7058..e6b4ee4c3 100644 --- a/indra/llmessage/llurlrequest.cpp +++ b/indra/llmessage/llurlrequest.cpp @@ -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) {