diff options
author | Steven Bennetts <steve@lindenlab.com> | 2007-03-02 21:25:50 +0000 |
---|---|---|
committer | Steven Bennetts <steve@lindenlab.com> | 2007-03-02 21:25:50 +0000 |
commit | 4dabd9c0472deb49573fdafef2fa413e59703f19 (patch) | |
tree | 06c680d6a2047e03838d6548bccd26c7baf9d652 /indra/llmessage/llcurl.cpp | |
parent | d4462963c6ba5db2088723bbedc7b60f1184c594 (diff) |
merge release@58699 beta-1-14-0@58707 -> release
Diffstat (limited to 'indra/llmessage/llcurl.cpp')
-rw-r--r-- | indra/llmessage/llcurl.cpp | 343 |
1 files changed, 343 insertions, 0 deletions
diff --git a/indra/llmessage/llcurl.cpp b/indra/llmessage/llcurl.cpp new file mode 100644 index 0000000000..886697ed41 --- /dev/null +++ b/indra/llmessage/llcurl.cpp @@ -0,0 +1,343 @@ +/* + * llcurl.cpp + * MacTester + * + * Created by Zero Linden on 10/15/06. + * Copyright 2006 __MyCompanyName__. All rights reserved. + * + */ + +#include "llcurl.h" + +#include <iomanip> + +#include "llsdserialize.h" + +////////////////////////////////////////////////////////////////////////////// +/* + The trick to getting curl to do keep-alives is to reuse the + same easy handle for the requests. It appears that curl + keeps a pool of connections alive for each easy handle, but + doesn't share them between easy handles. Therefore it is + important to keep a pool of easy handles and reuse them, + rather than create and destroy them with each request. This + code does this. + + Furthermore, it would behoove us to keep track of which + hosts an easy handle was used for and pick an easy handle + that matches the next request. This code does not current + do this. + */ + +using namespace std; + +LLCurl::Responder::Responder() + : mReferenceCount(0) +{ +} +LLCurl::Responder::~Responder() +{ +} + +// virtual +void LLCurl::Responder::error(U32 status, const std::stringstream& content) +{ + llinfos << "LLCurl::Responder::error " << status << ": " << content.str() << llendl; +} + +// virtual +void LLCurl::Responder::result(const std::stringstream& content) +{ +} + +// virtual +void LLCurl::Responder::completed(U32 status, const std::stringstream& content) +{ + if (200 <= status && status < 300) + { + result(content); + } + else + { + error(status, content); + } +} + + +namespace boost +{ + void intrusive_ptr_add_ref(LLCurl::Responder* p) + { + ++p->mReferenceCount; + } + + void intrusive_ptr_release(LLCurl::Responder* p) + { + if(0 == --p->mReferenceCount) + { + delete p; + } + } +}; + +////////////////////////////////////////////////////////////////////////////// + +size_t +curlOutputCallback(void* data, size_t size, size_t nmemb, void* user_data) +{ + stringstream& output = *(stringstream*)user_data; + + size_t n = size * nmemb; + output.write((const char*)data, n); + if (!((istream&)output).good()) { + std::cerr << "WHAT!?!?!? istream side bad" << std::endl; + } + if (!((ostream&)output).good()) { + std::cerr << "WHAT!?!?!? ostream side bad" << std::endl; + } + + return n; +} + +// Only used if request contained a body (post or put), Not currently implemented. +// size_t +// curlRequestCallback(void* data, size_t size, size_t nmemb, void* user_data) +// { +// stringstream& request = *(stringstream*)user_data; + +// size_t n = size * nmemb; +// request.read((char*)data, n); +// return request.gcount(); +// } + + + + + +LLCurl::Easy::Easy() +{ + mHeaders = 0; + mHeaders = curl_slist_append(mHeaders, "Connection: keep-alive"); + mHeaders = curl_slist_append(mHeaders, "Keep-alive: 300"); + mHeaders = curl_slist_append(mHeaders, "Content-Type: application/xml"); + // FIXME: shouldn't be there for GET/DELETE + // FIXME: should have ACCEPT headers + + mHandle = curl_easy_init(); +} + +LLCurl::Easy::~Easy() +{ + curl_easy_cleanup(mHandle); + curl_slist_free_all(mHeaders); +} + +void +LLCurl::Easy::get(const string& url, ResponderPtr responder) +{ + prep(url, responder); + curl_easy_setopt(mHandle, CURLOPT_HTTPGET, 1); +} + +void +LLCurl::Easy::getByteRange(const string& url, S32 offset, S32 length, ResponderPtr responder) +{ + mRange = llformat("Range: bytes=%d-%d", offset,offset+length-1); + mHeaders = curl_slist_append(mHeaders, mRange.c_str()); + prep(url, responder); + curl_easy_setopt(mHandle, CURLOPT_HTTPGET, 1); +} + +void +LLCurl::Easy::perform() +{ + report(curl_easy_perform(mHandle)); +} + +void +LLCurl::Easy::prep(const std::string& url, ResponderPtr responder) +{ +#if !LL_DARWIN + curl_easy_reset(mHandle); // SJB: doesn't exisit on OSX 10.3.9 +#else + // SJB: equivalent? fast? + curl_easy_cleanup(mHandle); + mHandle = curl_easy_init(); +#endif + + curl_easy_setopt(mHandle, CURLOPT_PRIVATE, this); + +// curl_easy_setopt(mHandle, CURLOPT_VERBOSE, 1); // usefull for debugging + curl_easy_setopt(mHandle, CURLOPT_NOSIGNAL, 1); + curl_easy_setopt(mHandle, CURLOPT_WRITEFUNCTION, &curlOutputCallback); + curl_easy_setopt(mHandle, CURLOPT_WRITEDATA, &mOutput); +#if 1 // For debug + curl_easy_setopt(mHandle, CURLOPT_HEADERFUNCTION, &curlOutputCallback); + curl_easy_setopt(mHandle, CURLOPT_HEADERDATA, &mHeaderOutput); +#endif + curl_easy_setopt(mHandle, CURLOPT_ERRORBUFFER, &mErrorBuffer); + curl_easy_setopt(mHandle, CURLOPT_ENCODING, ""); + curl_easy_setopt(mHandle, CURLOPT_SSL_VERIFYPEER, false); + curl_easy_setopt(mHandle, CURLOPT_HTTPHEADER, mHeaders); + + mOutput.str(""); + if (!((istream&)mOutput).good()) { + std::cerr << "WHAT!?!?!? istream side bad" << std::endl; + } + if (!((ostream&)mOutput).good()) { + std::cerr << "WHAT!?!?!? ostream side bad" << std::endl; + } + + mURL = url; + curl_easy_setopt(mHandle, CURLOPT_URL, mURL.c_str()); + + mResponder = responder; +} + +void +LLCurl::Easy::report(CURLcode code) +{ + if (!mResponder) return; + + long responseCode; + + if (code == CURLE_OK) + { + curl_easy_getinfo(mHandle, CURLINFO_RESPONSE_CODE, &responseCode); + } + else + { + responseCode = 499; + } + + mResponder->completed(responseCode, mOutput); + mResponder = NULL; +} + + + + + + +LLCurl::Multi::Multi() +{ + mHandle = curl_multi_init(); +} + +LLCurl::Multi::~Multi() +{ + // FIXME: should clean up excess handles in mFreeEasy + curl_multi_cleanup(mHandle); +} + + +void +LLCurl::Multi::get(const std::string& url, ResponderPtr responder) +{ + LLCurl::Easy* easy = easyAlloc(); + easy->get(url, responder); + curl_multi_add_handle(mHandle, easy->mHandle); +} + +void +LLCurl::Multi::getByteRange(const std::string& url, S32 offset, S32 length, ResponderPtr responder) +{ + LLCurl::Easy* easy = easyAlloc(); + easy->getByteRange(url, offset, length, responder); + curl_multi_add_handle(mHandle, easy->mHandle); +} + +void +LLCurl::Multi::process() +{ + int count; + for (int call_count = 0; call_count < 5; call_count += 1) + { + if (CURLM_CALL_MULTI_PERFORM != curl_multi_perform(mHandle, &count)) + { + break; + } + } + + CURLMsg* msg; + int msgs_in_queue; + while ((msg = curl_multi_info_read(mHandle, &msgs_in_queue))) + { + if (msg->msg != CURLMSG_DONE) continue; + Easy* easy = 0; + curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &easy); + if (!easy) continue; + easy->report(msg->data.result); + + curl_multi_remove_handle(mHandle, easy->mHandle); + easyFree(easy); + } +} + + + +LLCurl::Easy* +LLCurl::Multi::easyAlloc() +{ + Easy* easy = 0; + + if (mFreeEasy.empty()) + { + easy = new Easy(); + } + else + { + easy = mFreeEasy.back(); + mFreeEasy.pop_back(); + } + + return easy; +} + +void +LLCurl::Multi::easyFree(Easy* easy) +{ + if (mFreeEasy.size() < 5) + { + mFreeEasy.push_back(easy); + } + else + { + delete easy; + } +} + + + +namespace +{ + static LLCurl::Multi* sMainMulti = 0; + + LLCurl::Multi* + mainMulti() + { + if (!sMainMulti) { + sMainMulti = new LLCurl::Multi(); + } + return sMainMulti; + } +} + +void +LLCurl::get(const std::string& url, ResponderPtr responder) +{ + mainMulti()->get(url, responder); +} + +void +LLCurl::getByteRange(const std::string& url, S32 offset, S32 length, ResponderPtr responder) +{ + mainMulti()->getByteRange(url, offset, length, responder); +} + +void +LLCurl::process() +{ + mainMulti()->process(); +} + |