diff options
Diffstat (limited to 'indra/llmessage/llcurl.h')
-rwxr-xr-x[-rw-r--r--] | indra/llmessage/llcurl.h | 404 |
1 files changed, 355 insertions, 49 deletions
diff --git a/indra/llmessage/llcurl.h b/indra/llmessage/llcurl.h index 64dadd6640..385d9fffa8 100644..100755 --- a/indra/llmessage/llcurl.h +++ b/indra/llmessage/llcurl.h @@ -39,10 +39,16 @@ #include <curl/curl.h> // TODO: remove dependency #include "llbuffer.h" +#include "llhttpconstants.h" #include "lliopipe.h" #include "llsd.h" +#include "llqueuedthread.h" +#include "llframetimer.h" +#include "llpointer.h" +#include "llsingleton.h" class LLMutex; +class LLCurlThread; // For whatever reason, this is not typedef'd in curl.h typedef size_t (*curl_header_callback)(void *ptr, size_t size, size_t nmemb, void *stream); @@ -63,7 +69,7 @@ public: F64 mSpeedDownload; }; - class Responder + class Responder : public LLThreadSafeRefCount { //LOG_CLASS(Responder); public: @@ -71,64 +77,94 @@ public: Responder(); virtual ~Responder(); - /** - * @brief return true if the status code indicates success. - */ - static bool isGoodStatus(U32 status) + virtual bool followRedir() { - return((200 <= status) && (status < 300)); + return false; } - - virtual void errorWithContent( - U32 status, - const std::string& reason, - const LLSD& content); - //< called by completed() on bad status - - virtual void error(U32 status, const std::string& reason); - //< called by default error(status, reason, content) - - virtual void result(const LLSD& content); - //< called by completed for good status codes. + /** + * @brief return true if the status code indicates success. + */ + bool isGoodStatus() const { return isHttpGoodStatus(mStatus); } + + S32 getStatus() const { return mStatus; } + const std::string& getReason() const { return mReason; } + const LLSD& getContent() const { return mContent; } + bool hasResponseHeader(const std::string& header) const; + const std::string& getResponseHeader(const std::string& header) const; + const LLSD& getResponseHeaders() const { return mResponseHeaders; } + const std::string& getURL() const { return mURL; } + EHTTPMethod getHTTPMethod() const { return mHTTPMethod; } + + // This formats response information for use in log spam. Includes content spam. + std::string dumpResponse() const; + + // Allows direct triggering of success/error with different results. + void completeResult(S32 status, const std::string& reason, const LLSD& content = LLSD()); + void successResult(const LLSD& content); + void failureResult(S32 status, const std::string& reason, const LLSD& content = LLSD()); + + // The default implementation will try to parse body content as an LLSD, however + // it should not spam about parsing failures unless the server sent a + // Content-Type: application/llsd+xml header. virtual void completedRaw( - U32 status, - const std::string& reason, const LLChannelDescriptors& channels, const LLIOPipe::buffer_ptr_t& buffer); /**< Override point for clients that may want to use this class when the response is some other format besides LLSD */ - - virtual void completed( - U32 status, - const std::string& reason, - const LLSD& content); - /**< The default implemetnation calls - either: - * result(), or - * error() - */ - // Override to handle parsing of the header only. Note: this is the only place where the contents - // of the header can be parsed. In the ::completed call above only the body is contained in the LLSD. - virtual void completedHeader(U32 status, const std::string& reason, const LLSD& content); - // Used internally to set the url for debugging later. - void setURL(const std::string& url); + // The http* methods are not public since these should be triggered internally + // after status, reason, content, etc have been set. + // If you need to trigger a completion method, use the *Result methods, above. + protected: + // These methods are the preferred way to process final results. + // By default, when one of these is called the following information will be resolved: + // * HTTP status code - getStatus() + // * Reason string - getReason() + // * Content - getContent() + // * Response Headers - getResponseHeaders() + + // By default, httpSuccess is triggered whenever httpCompleted is called with a 2xx status code. + virtual void httpSuccess(); + //< called by completed for good status codes. - virtual bool followRedir() - { - return false; - } + // By default, httpFailure is triggered whenever httpCompleted is called with a non-2xx status code. + virtual void httpFailure(); + //< called by httpCompleted() on bad status - public: /* but not really -- don't touch this */ - U32 mReferenceCount; + // httpCompleted does not generally need to be overridden, unless + // you don't care about the status code (which determine httpFailure or httpSuccess) + // or if you want to re-interpret what a 'good' vs' bad' status code is. + virtual void httpCompleted(); + /**< The default implementation calls + either: + * httpSuccess(), or + * httpFailure() + */ + + public: + void setHTTPMethod(EHTTPMethod method); + void setURL(const std::string& url); + void setResult(S32 status, const std::string& reason, const LLSD& content = LLSD()); + void setResponseHeader(const std::string& header, const std::string& value); private: + // These can be accessed by the get* methods. Treated as 'read-only' during completion handlers. + EHTTPMethod mHTTPMethod; std::string mURL; + LLSD mResponseHeaders; + + protected: + // These should also generally be treated as 'read-only' during completion handlers + // and should be accessed by the get* methods. The exception to this rule would + // be when overriding the completedRaw method in preparation for calling httpCompleted(). + S32 mStatus; + std::string mReason; + LLSD mContent; }; - typedef boost::intrusive_ptr<Responder> ResponderPtr; + typedef LLPointer<Responder> ResponderPtr; /** @@ -159,7 +195,7 @@ public: /** * @ brief Initialize LLCurl class */ - static void initClass(); + static void initClass(F32 curl_reuest_timeout = 120.f, S32 max_number_handles = 256, bool multi_threaded = false); /** * @ brief Cleanup LLCurl class @@ -178,18 +214,209 @@ public: static void ssl_locking_callback(int mode, int type, const char *file, int line); static unsigned long ssl_thread_id(void); + static LLCurlThread* getCurlThread() { return sCurlThread ;} + + static CURLM* newMultiHandle() ; + static CURLMcode deleteMultiHandle(CURLM* handle) ; + static CURL* newEasyHandle() ; + static void deleteEasyHandle(CURL* handle) ; + + static CURL* createStandardCurlHandle(); + private: static std::string sCAPath; static std::string sCAFile; static const unsigned int MAX_REDIRECTS; + static LLCurlThread* sCurlThread; + + static LLMutex* sHandleMutexp ; + static S32 sTotalHandles ; + static S32 sMaxHandles; + static CURL* sCurlTemplateStandardHandle; +public: + static bool sNotQuitting; + static F32 sCurlRequestTimeOut; }; -namespace boost +class LLCurl::Easy { - void intrusive_ptr_add_ref(LLCurl::Responder* p); - void intrusive_ptr_release(LLCurl::Responder* p); + LOG_CLASS(Easy); + +private: + Easy(); + +public: + static Easy* getEasy(); + ~Easy(); + + CURL* getCurlHandle() const { return mCurlEasyHandle; } + + void setErrorBuffer(); + void setCA(); + + void setopt(CURLoption option, S32 value); + // These assume the setter does not free value! + void setopt(CURLoption option, void* value); + void setopt(CURLoption option, char* value); + // Copies the string so that it is guaranteed to stick around + void setoptString(CURLoption option, const std::string& value); + + void slist_append(const std::string& header, const std::string& value); + void slist_append(const char* str); + void setHeaders(); + + S32 report(CURLcode); + void getTransferInfo(LLCurl::TransferInfo* info); + + void prepRequest(const std::string& url, const std::vector<std::string>& headers, LLCurl::ResponderPtr, S32 time_out = 0, bool post = false); + + const char* getErrorBuffer(); + + std::stringstream& getInput() { return mInput; } + std::stringstream& getHeaderOutput() { return mHeaderOutput; } + LLIOPipe::buffer_ptr_t& getOutput() { return mOutput; } + const LLChannelDescriptors& getChannels() { return mChannels; } + + void resetState(); + + static CURL* allocEasyHandle(); + static void releaseEasyHandle(CURL* handle); + +private: + friend class LLCurl; + friend class LLCurl::Multi; + + CURL* mCurlEasyHandle; + struct curl_slist* mHeaders; + + std::stringstream mRequest; + LLChannelDescriptors mChannels; + LLIOPipe::buffer_ptr_t mOutput; + std::stringstream mInput; + std::stringstream mHeaderOutput; + char mErrorBuffer[CURL_ERROR_SIZE]; + + // Note: char*'s not strings since we pass pointers to curl + std::vector<char*> mStrings; + + LLCurl::ResponderPtr mResponder; + + static std::set<CURL*> sFreeHandles; + static std::set<CURL*> sActiveHandles; + static LLMutex* sHandleMutexp ; + + static void deleteAllActiveHandles(); + static void deleteAllFreeHandles(); +}; + +class LLCurl::Multi +{ + LOG_CLASS(Multi); + + friend class LLCurlThread ; + +private: + ~Multi(); + + void markDead() ; + bool doPerform(); + +public: + + typedef enum + { + STATE_READY=0, + STATE_PERFORMING=1, + STATE_COMPLETED=2 + } ePerformState; + + Multi(F32 idle_time_out = 0.f); + + LLCurl::Easy* allocEasy(); + bool addEasy(LLCurl::Easy* easy); + void removeEasy(LLCurl::Easy* easy); + + void lock() ; + void unlock() ; + + void setState(ePerformState state) ; + ePerformState getState() ; + + bool isCompleted() ; + bool isValid() {return mCurlMultiHandle != NULL && mValid;} + bool isDead() {return mDead;} + + bool waitToComplete() ; + + S32 process(); + + CURLMsg* info_read(S32* msgs_in_queue); + + S32 mQueued; + S32 mErrorCount; + +private: + void easyFree(LLCurl::Easy*); + void cleanup(bool deleted = false) ; + + CURLM* mCurlMultiHandle; + + typedef std::set<LLCurl::Easy*> easy_active_list_t; + easy_active_list_t mEasyActiveList; + typedef std::map<CURL*, LLCurl::Easy*> easy_active_map_t; + easy_active_map_t mEasyActiveMap; + typedef std::set<LLCurl::Easy*> easy_free_list_t; + easy_free_list_t mEasyFreeList; + + LLQueuedThread::handle_t mHandle ; + ePerformState mState; + + BOOL mDead ; + BOOL mValid ; + LLMutex* mMutexp ; + LLMutex* mDeletionMutexp ; + LLMutex* mEasyMutexp ; + LLFrameTimer mIdleTimer ; + F32 mIdleTimeOut; }; +class LLCurlThread : public LLQueuedThread +{ +public: + + class CurlRequest : public LLQueuedThread::QueuedRequest + { + protected: + virtual ~CurlRequest(); // use deleteRequest() + + public: + CurlRequest(handle_t handle, LLCurl::Multi* multi, LLCurlThread* curl_thread); + + /*virtual*/ bool processRequest(); + /*virtual*/ void finishRequest(bool completed); + + private: + // input + LLCurl::Multi* mMulti; + LLCurlThread* mCurlThread; + }; + friend class CurlRequest; + +public: + LLCurlThread(bool threaded = true) ; + virtual ~LLCurlThread() ; + + S32 update(F32 max_time_ms); + + void addMulti(LLCurl::Multi* multi) ; + void killMulti(LLCurl::Multi* multi) ; + +private: + bool doMultiPerform(LLCurl::Multi* multi) ; + void deleteMulti(LLCurl::Multi* multi) ; + void cleanupMulti(LLCurl::Multi* multi) ; +} ; + class LLCurlRequest { @@ -201,7 +428,9 @@ public: void get(const std::string& url, LLCurl::ResponderPtr responder); bool getByteRange(const std::string& url, const headers_t& headers, S32 offset, S32 length, LLCurl::ResponderPtr responder); - bool post(const std::string& url, const headers_t& headers, const LLSD& data, LLCurl::ResponderPtr responder); + bool post(const std::string& url, const headers_t& headers, const LLSD& data, LLCurl::ResponderPtr responder, S32 time_out = 0); + bool post(const std::string& url, const headers_t& headers, const std::string& data, LLCurl::ResponderPtr responder, S32 time_out = 0); + S32 process(); S32 getQueued(); @@ -215,7 +444,72 @@ private: curlmulti_set_t mMultiSet; LLCurl::Multi* mActiveMulti; S32 mActiveRequestCount; - U32 mThreadID; // debug + BOOL mProcessing; +}; + +//for texture fetch only +class LLCurlTextureRequest : public LLCurlRequest +{ +public: + LLCurlTextureRequest(S32 concurrency); + ~LLCurlTextureRequest(); + + U32 getByteRange(const std::string& url, const headers_t& headers, S32 offset, S32 length, U32 pri, LLCurl::ResponderPtr responder, F32 delay_time = -1.f); + void nextRequests(); + void completeRequest(S32 received_bytes); + + void updatePriority(U32 handle, U32 pri); + void removeRequest(U32 handle); + + U32 getTotalReceivedBits(); + U32 getTotalIssuedRequests(); + S32 getNumRequests(); + bool isWaiting(U32 handle); + +private: + LLMutex mMutex; + S32 mConcurrency; + S32 mInQueue; //request currently in queue. + U32 mHandleCounter; + U32 mTotalIssuedRequests; + U32 mTotalReceivedBits; + + typedef struct _request_t + { + _request_t(U32 handle, const std::string& url, const headers_t& headers, S32 offset, S32 length, U32 pri, LLCurl::ResponderPtr responder) : + mHandle(handle), mUrl(url), mHeaders(headers), mOffset(offset), mLength(length), mPriority(pri), mResponder(responder), mStartTime(0.f) + {} + + U32 mHandle; + std::string mUrl; + LLCurlRequest::headers_t mHeaders; + S32 mOffset; + S32 mLength; + LLCurl::ResponderPtr mResponder; + U32 mPriority; + F32 mStartTime; //start time to issue this request + } request_t; + + struct request_compare + { + bool operator()(const request_t* lhs, const request_t* rhs) const + { + if(lhs->mPriority != rhs->mPriority) + { + return lhs->mPriority > rhs->mPriority; // higher priority in front of queue (set) + } + else + { + return (U32)lhs < (U32)rhs; + } + } + }; + + typedef std::set<request_t*, request_compare> req_queue_t; + req_queue_t mCachedRequests; + std::map<S32, request_t*> mRequestMap; + + LLFrameTimer mGlobalTimer; }; class LLCurlEasyRequest @@ -230,12 +524,17 @@ public: void setWriteCallback(curl_write_callback callback, void* userdata); void setReadCallback(curl_read_callback callback, void* userdata); void setSSLCtxCallback(curl_ssl_ctx_callback callback, void* userdata); + void slist_append(const std::string& header, const std::string& value); void slist_append(const char* str); void sendRequest(const std::string& url); void requestComplete(); - S32 perform(); bool getResult(CURLcode* result, LLCurl::TransferInfo* info = NULL); std::string getErrorString(); + bool isCompleted() {return mMulti->isCompleted() ;} + bool wait() { return mMulti->waitToComplete(); } + bool isValid() {return mMulti && mMulti->isValid(); } + + LLCurl::Easy* getEasy() const { return mEasy; } private: CURLMsg* info_read(S32* queue, LLCurl::TransferInfo* info); @@ -247,4 +546,11 @@ private: bool mResultReturned; }; +// Provide access to LLCurl free functions outside of llcurl.cpp without polluting the global namespace. +namespace LLCurlFF +{ + void check_easy_code(CURLcode code); + void check_multi_code(CURLMcode code); +} + #endif // LL_LLCURL_H |