phoenix-firestorm/indra/llmessage/llcurl.cpp

344 lines
6.8 KiB
C++

/*
* 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();
}