diff --git a/indra/aistatemachine/aicurl.cpp b/indra/aistatemachine/aicurl.cpp index c9f393ff5..65457cd80 100644 --- a/indra/aistatemachine/aicurl.cpp +++ b/indra/aistatemachine/aicurl.cpp @@ -456,14 +456,6 @@ AIHTTPTimeoutPolicy const& Responder::getHTTPTimeoutPolicy(void) const return AIHTTPTimeoutPolicy::getDebugSettingsCurlTimeout(); } -// Called with HTML header. -// virtual -void Responder::completedHeaders(U32, std::string const&, AIHTTPReceivedHeaders const&) -{ - // This should not be called unless a derived class implemented it. - llerrs << "Unexpected call to completedHeaders()." << llendl; -} - // Called with HTML body. // virtual void Responder::completedRaw(U32 status, std::string const& reason, LLChannelDescriptors const& channels, LLIOPipe::buffer_ptr_t const& buffer) @@ -529,6 +521,16 @@ void intrusive_ptr_release(Responder* responder) } } +// virtual +void LegacyPolledResponder::completed_headers(U32 status, std::string const& reason, CURLcode code, AICurlInterface::TransferInfo* info) +{ + mCode = code; + mStatus = status; + mReason = reason; + // Call base class implementation. + Responder::completed_headers(status, reason, code, info); +} + } // namespace AICurlInterface //================================================================================== diff --git a/indra/aistatemachine/aicurl.h b/indra/aistatemachine/aicurl.h index db1da9a10..767a6dabc 100644 --- a/indra/aistatemachine/aicurl.h +++ b/indra/aistatemachine/aicurl.h @@ -52,6 +52,7 @@ #include "stdtypes.h" // U16, S32, U32, F64 #include "llatomic.h" // LLAtomicU32 #include "aithreadsafe.h" +#include "llhttpstatuscodes.h" #include "aihttpheaders.h" extern bool gNoVerifySSLCert; @@ -244,11 +245,13 @@ class Responder : public AICurlResponderBufferEvents { } // Derived classes can override this to get the HTML headers that were received, when the message is completed. - // The default does nothing. - virtual void completedHeaders(U32 status, std::string const& reason, AIHTTPReceivedHeaders const& headers); + virtual void completedHeaders(U32 status, std::string const& reason, AIHTTPReceivedHeaders const& headers) + { + // The default does nothing. + } public: - // Derived classes that implement completedHeaders() should return true here. + // Derived classes that implement completed_headers()/completedHeaders() should return true here. virtual bool needsHeaders(void) const { return false; } // Timeout policy to use. @@ -299,6 +302,32 @@ class Responder : public AICurlResponderBufferEvents { // destruct when there are no pointers left pointing to it. typedef boost::intrusive_ptr ResponderPtr; +// Same as above except that this class stores the result, allowing old polling +// code to poll if the transaction finished by calling is_finished() (from the +// main the thread) and then access the results-- as opposed to immediately +// digesting the results when any of the virtual functions are called. +class LegacyPolledResponder : public Responder { + protected: + CURLcode mCode; + U32 mStatus; + std::string mReason; + bool mFinished; + + public: + LegacyPolledResponder(void) : mCode(CURLE_FAILED_INIT), mStatus(HTTP_INTERNAL_ERROR), mFinished(false) { } + + // Accessors. + CURLcode result_code(void) const { return mCode; } + U32 http_status(void) const { return mStatus; } + bool is_finished(void) const { return mFinished; } + std::string const& reason(void) const { return mReason; } + + /*virtual*/ bool needsHeaders(void) const { return true; } + /*virtual*/ void completed_headers(U32 status, std::string const& reason, CURLcode code, AICurlInterface::TransferInfo* info); + // This must be defined by the derived class and set mFinished = true at the end. + /*virtual*/ void completedRaw(U32 status, std::string const& reason, LLChannelDescriptors const& channels, buffer_ptr_t const& buffer) = 0; +}; + } // namespace AICurlInterface // Forward declaration (see aicurlprivate.h). diff --git a/indra/llmessage/llhttpclient.cpp b/indra/llmessage/llhttpclient.cpp index 03e9e50e5..6f7db978d 100644 --- a/indra/llmessage/llhttpclient.cpp +++ b/indra/llmessage/llhttpclient.cpp @@ -230,39 +230,68 @@ void LLHTTPClient::get4(std::string const& url, LLSD const& query, ResponderPtr get4(uri.asString(), responder, headers); } -// A simple class for managing data returned from a curl http request. -class LLHTTPBuffer -{ -public: - LLHTTPBuffer() { } - - static size_t curl_write(char* ptr, size_t size, size_t nmemb, void* user_data) - { - LLHTTPBuffer* self = (LLHTTPBuffer*)user_data; - - size_t bytes = (size * nmemb); - self->mBuffer.append(ptr,bytes); - return nmemb; - } - - LLSD asLLSD() - { - LLSD content; - - if (mBuffer.empty()) return content; - - std::istringstream istr(mBuffer); - LLSDSerialize::fromXML(content, istr); - return content; - } - - std::string asString() - { - return mBuffer; - } - +class BlockingResponder : public AICurlInterface::LegacyPolledResponder { private: - std::string mBuffer; + LLCondition mSignal; + LLSD mResponse; + std::ostringstream mBody; + +public: + void wait(void); + LLSD const& response(void) const { llassert(mFinished && mCode == CURLE_OK && mStatus == HTTP_OK); return mResponse; } + std::string const& body(void) const { llassert(mFinished && mCode == CURLE_OK && mStatus != HTTP_OK); return mBody.str(); } + + /*virtual*/ void completedRaw(U32 status, std::string const& reason, LLChannelDescriptors const& channels, buffer_ptr_t const& buffer); +}; + +void BlockingResponder::completedRaw(U32, std::string const&, LLChannelDescriptors const& channels, buffer_ptr_t const& buffer) +{ + if (mCode == CURLE_OK) + { + LLBufferStream istr(channels, buffer.get()); + if (mStatus == HTTP_OK) + { + LLSDSerialize::fromXML(mResponse, istr); + } + else + { + mBody << istr; + } + } + mSignal.lock(); + mFinished = true; + mSignal.unlock(); + mSignal.signal(); +} + +void BlockingResponder::wait(void) +{ + if (AIThreadID::in_main_thread()) + { + // We're the main thread, so we have to give AIStateMachine CPU cycles. + while (!mFinished) + { + AIStateMachine::mainloop(); + ms_sleep(10); + } + } + else // Hopefully not the curl thread :p + { + mSignal.lock(); + while (!mFinished) + mSignal.wait(); + mSignal.unlock(); + } +} + +class BlockingPostResponder : public BlockingResponder { +public: + /*virtual*/ AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return blockingPost_timeout; } +}; + +class BlockingGetResponder : public BlockingResponder { +public: + /*virtual*/ AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return blockingGet_timeout; } }; // These calls are blocking! This is usually bad, unless you're a dataserver. Then it's awesome. @@ -287,85 +316,54 @@ static LLSD blocking_request( std::string const& url, LLURLRequest::ERequestAction method, LLSD const& body, - AIHTTPHeaders& headers, - AIHTTPTimeoutPolicy const& timeout) + AIHTTPHeaders& headers) { lldebugs << "blockingRequest of " << url << llendl; - S32 http_status = 499; - LLSD response = LLSD::emptyMap(); - -#if 0 // AIFIXME: rewrite to use AICurlEasyRequestStateMachine - try + boost::intrusive_ptr responder; + if (method == LLURLRequest::HTTP_POST) { - AICurlEasyRequest easy_request(false); - AICurlEasyRequest_wat curlEasyRequest_w(*easy_request); + responder = new BlockingPostResponder; + LLHTTPClient::post4(url, body, responder, headers); + } + else + { + llassert(method == LLURLRequest::HTTP_GET); + responder = new BlockingGetResponder; + LLHTTPClient::get4(url, responder, headers); + } - LLHTTPBuffer http_buffer; - - // * Set curl handle options - curlEasyRequest_w->setopt(CURLOPT_TIMEOUT, (long)timeout); // seconds, see warning at top of function. - curlEasyRequest_w->setWriteCallback(&LLHTTPBuffer::curl_write, &http_buffer); + responder->wait(); - // * Setup headers. - if (headers.isMap()) - { - LLSD::map_const_iterator iter = headers.beginMap(); - LLSD::map_const_iterator end = headers.endMap(); - for (; iter != end; ++iter) - { - std::ostringstream header; - header << iter->first << ": " << iter->second.asString() ; - lldebugs << "header = " << header.str() << llendl; - curlEasyRequest_w->addHeader(header.str().c_str()); - } - } - - // Needs to stay alive until after the call to perform(). - std::ostringstream ostr; + S32 http_status = HTTP_INTERNAL_ERROR; + LLSD response = LLSD::emptyMap(); + CURLcode result = responder->result_code(); - // * Setup specific method / "verb" for the URI (currently only GET and POST supported + poppy) - if (method == LLURLRequest::HTTP_GET) + if (result == CURLE_OK && (http_status = responder->http_status()) == HTTP_OK) + { + response["body"] = responder->response(); + } + else if (result == CURLE_OK) + { + // We expect 404s, don't spam for them. + if (http_status != 404) { - curlEasyRequest_w->setopt(CURLOPT_HTTPGET, 1); - } - else if (method == LLURLRequest::HTTP_POST) - { - //copied from PHP libs, correct? - curlEasyRequest_w->addHeader("Content-Type: application/llsd+xml"); - LLSDSerialize::toXML(body, ostr); - curlEasyRequest_w->setPost(ostr.str().c_str(), ostr.str().length()); - } - - // * Do the action using curl, handle results - curlEasyRequest_w->addHeader("Accept: application/llsd+xml"); - curlEasyRequest_w->finalizeRequest(url); - - S32 curl_success = curlEasyRequest_w->perform(); - curlEasyRequest_w->getinfo(CURLINFO_RESPONSE_CODE, &http_status); - // if we get a non-404 and it's not a 200 OR maybe it is but you have error bits, - if ( http_status != 404 && (http_status != 200 || curl_success != 0) ) - { - // We expect 404s, don't spam for them. llwarns << "CURL REQ URL: " << url << llendl; llwarns << "CURL REQ METHOD TYPE: " << LLURLRequest::actionAsVerb(method) << llendl; - llwarns << "CURL REQ HEADERS: " << headers.asString() << llendl; - llwarns << "CURL REQ BODY: " << ostr.str() << llendl; + llwarns << "CURL REQ HEADERS: " << headers << llendl; + if (method == LLURLRequest::HTTP_POST) + { + llwarns << "CURL REQ BODY: " << body.asString() << llendl; + } llwarns << "CURL HTTP_STATUS: " << http_status << llendl; - llwarns << "CURL ERROR BODY: " << http_buffer.asString() << llendl; - response["body"] = http_buffer.asString(); - } - else - { - response["body"] = http_buffer.asLLSD(); - lldebugs << "CURL response: " << http_buffer.asString() << llendl; + llwarns << "CURL ERROR BODY: " << responder->body() << llendl; } + response["body"] = responder->body(); } - catch(AICurlNoEasyHandle const& error) + else { - response["body"] = error.what(); + response["body"] = responder->reason(); } -#endif response["status"] = http_status; return response; @@ -374,13 +372,13 @@ static LLSD blocking_request( LLSD LLHTTPClient::blockingGet(const std::string& url) { AIHTTPHeaders empty_headers; - return blocking_request(url, LLURLRequest::HTTP_GET, LLSD(), empty_headers, blockingGet_timeout); + return blocking_request(url, LLURLRequest::HTTP_GET, LLSD(), empty_headers); } LLSD LLHTTPClient::blockingPost(const std::string& url, const LLSD& body) { AIHTTPHeaders empty_headers; - return blocking_request(url, LLURLRequest::HTTP_POST, body, empty_headers, blockingPost_timeout); + return blocking_request(url, LLURLRequest::HTTP_POST, body, empty_headers); } void LLHTTPClient::put4(std::string const& url, LLSD const& body, ResponderPtr responder, AIHTTPHeaders& headers) diff --git a/indra/newview/llcurrencyuimanager.cpp b/indra/newview/llcurrencyuimanager.cpp index 5ba0c5980..80a70a152 100644 --- a/indra/newview/llcurrencyuimanager.cpp +++ b/indra/newview/llcurrencyuimanager.cpp @@ -265,7 +265,7 @@ bool LLCurrencyUIManager::Impl::checkTransaction() return false; } - if (mResponder->result_code() != CURLE_OK || mResponder->http_result() < 200 || mResponder->http_result() >= 400) + if (mResponder->result_code() != CURLE_OK || mResponder->http_status() < 200 || mResponder->http_status() >= 400) { setError(mResponder->reason(), mResponder->getURL()); } diff --git a/indra/newview/llfloaterbuyland.cpp b/indra/newview/llfloaterbuyland.cpp index a5a1b62a2..2c1631987 100644 --- a/indra/newview/llfloaterbuyland.cpp +++ b/indra/newview/llfloaterbuyland.cpp @@ -863,7 +863,7 @@ bool LLFloaterBuyLandUI::checkTransaction() return false; } - if (mResponder->result_code() != CURLE_OK || mResponder->http_result() < 200 || mResponder->http_result() >= 400) + if (mResponder->result_code() != CURLE_OK || mResponder->http_status() < 200 || mResponder->http_status() >= 400) { tellUserError(mResponder->reason(), mResponder->getURL()); } diff --git a/indra/newview/llxmlrpcresponder.cpp b/indra/newview/llxmlrpcresponder.cpp index ddf4afee9..a19bcdd9e 100644 --- a/indra/newview/llxmlrpcresponder.cpp +++ b/indra/newview/llxmlrpcresponder.cpp @@ -158,14 +158,12 @@ XMLRPC_VALUE LLXMLRPCValue::getValue() const void XMLRPCResponder::completed_headers(U32 status, std::string const& reason, CURLcode code, AICurlInterface::TransferInfo* info) { - mCode = code; if (info) { mTransferInfo = *info; } - mStatus = status; - mReason = reason; - // Note: 'status' and 'reason' are the same as what is passed to completedRaw. + // Call base class implementation. + LegacyPolledResponder::completed_headers(status, reason, code, info); } void XMLRPCResponder::completedRaw(U32 status, std::string const& reason, LLChannelDescriptors const& channels, buffer_ptr_t const& buffer) diff --git a/indra/newview/llxmlrpcresponder.h b/indra/newview/llxmlrpcresponder.h index 23973b6db..ffb06f14d 100644 --- a/indra/newview/llxmlrpcresponder.h +++ b/indra/newview/llxmlrpcresponder.h @@ -91,31 +91,20 @@ private: XMLRPC_VALUE mV; }; -class XMLRPCResponder : public LLCurl::Responder { +class XMLRPCResponder : public AICurlInterface::LegacyPolledResponder { private: - CURLcode mCode; - U32 mStatus; AICurlInterface::TransferInfo mTransferInfo; S32 mBufferSize; bool mReceivedHTTPHeader; - bool mFinished; - std::string mReason; XMLRPC_REQUEST mResponse; public: - XMLRPCResponder(void) : mCode(CURLE_FAILED_INIT), mStatus(HTTP_INTERNAL_ERROR), mReceivedHTTPHeader(false), mFinished(false) { } - // Accessors. - CURLcode result_code(void) const { return mCode; } - U32 http_result(void) const { return mStatus; } F64 transferRate(void) const; bool is_downloading(void) const { return mReceivedHTTPHeader; } - bool is_finished(void) const { return mFinished; } - std::string const& reason(void) const { return mReason; } XMLRPC_REQUEST response(void) const { return mResponse; } LLXMLRPCValue responseValue(void) const; - /*virtual*/ bool needsHeaders(void) const { return true; } /*virtual*/ void received_HTTP_header(void) { mReceivedHTTPHeader = true; LLCurl::Responder::received_HTTP_header(); } /*virtual*/ void completed_headers(U32 status, std::string const& reason, CURLcode code, AICurlInterface::TransferInfo* info); /*virtual*/ void completedRaw(U32 status, std::string const& reason, LLChannelDescriptors const& channels, buffer_ptr_t const& buffer);