353 lines
9.0 KiB
C++
353 lines
9.0 KiB
C++
|
|
#include "llviewerprecompiledheaders.h"
|
|
|
|
#include "hipporestrequest.h"
|
|
|
|
#ifndef CURL_STATICLIB
|
|
#define CURL_STATICLIB 1
|
|
#endif
|
|
#include <curl/curl.h>
|
|
|
|
#include <stdtypes.h>
|
|
#include <llbufferstream.h>
|
|
#include <llerror.h>
|
|
#include <llhttpclient.h>
|
|
#include <llurlrequest.h>
|
|
#include <llxmltree.h>
|
|
|
|
|
|
// ********************************************************************
|
|
|
|
|
|
class HippoRestComplete : public LLURLRequestComplete
|
|
{
|
|
public:
|
|
HippoRestComplete(HippoRestHandler *handler) :
|
|
mHandler(handler),
|
|
mStatus(499),
|
|
mReason("Request completed w/o status")
|
|
{
|
|
}
|
|
|
|
~HippoRestComplete()
|
|
{
|
|
delete mHandler;
|
|
}
|
|
|
|
// Called once for each header received, prior to httpStatus
|
|
void header(const std::string& header, const std::string& value)
|
|
{
|
|
mHandler->addHeader(header, value);
|
|
}
|
|
|
|
// Always called on request completion, prior to complete
|
|
void httpStatus(U32 status, const std::string& reason)
|
|
{
|
|
LLURLRequestComplete::httpStatus(status, reason);
|
|
mStatus = status;
|
|
mReason = reason;
|
|
}
|
|
|
|
void complete(const LLChannelDescriptors &channels, const buffer_ptr_t &buffer)
|
|
{
|
|
mHandler->handle(mStatus, mReason, channels, buffer);
|
|
}
|
|
|
|
private:
|
|
HippoRestHandler *mHandler;
|
|
int mStatus;
|
|
std::string mReason;
|
|
};
|
|
|
|
|
|
// ********************************************************************
|
|
|
|
|
|
static std::string gEmptyString;
|
|
|
|
void HippoRestHandler::addHeader(const std::string &header, const std::string &content)
|
|
{
|
|
mHeaders[header] = content;
|
|
}
|
|
|
|
bool HippoRestHandler::hasHeader(const std::string &header) const
|
|
{
|
|
return (mHeaders.find(header) != mHeaders.end());
|
|
}
|
|
|
|
const std::string &HippoRestHandler::getHeader(const std::string &header) const
|
|
{
|
|
std::map<std::string, std::string>::const_iterator it;
|
|
it = mHeaders.find(header);
|
|
if (it != mHeaders.end()) {
|
|
return it->second;
|
|
} else {
|
|
return gEmptyString;
|
|
}
|
|
}
|
|
|
|
|
|
// ********************************************************************
|
|
|
|
|
|
void HippoRestHandlerRaw::handle(int status, const std::string &reason,
|
|
const LLChannelDescriptors &channels,
|
|
const boost::shared_ptr<LLBufferArray> &body)
|
|
{
|
|
if (status == 200) {
|
|
std::string data;
|
|
LLBufferArray *buffer = body.get();
|
|
LLBufferArray::segment_iterator_t it, end = buffer->endSegment();
|
|
for (it=buffer->beginSegment(); it!=end; ++it)
|
|
if (it->isOnChannel(channels.in()))
|
|
data.append((char*)it->data(), it->size());
|
|
result(data);
|
|
} else {
|
|
llwarns << "Rest request error " << status << ": " << reason << llendl;
|
|
}
|
|
}
|
|
|
|
void HippoRestHandlerXml::handle(int status, const std::string &reason,
|
|
const LLChannelDescriptors &channels,
|
|
const boost::shared_ptr<LLBufferArray> &body)
|
|
{
|
|
if (status == 200) {
|
|
LLXmlTree *tree = new LLXmlTree();
|
|
bool success = tree->parseBufferStart();
|
|
LLBufferArray *buffer = body.get();
|
|
LLBufferArray::segment_iterator_t it, end = buffer->endSegment();
|
|
for (it=buffer->beginSegment(); success && (it!=end); ++it)
|
|
if (it->isOnChannel(channels.in()))
|
|
success = success && tree->parseBuffer((char*)it->data(), it->size());
|
|
success = success && tree->parseBufferFinalize();
|
|
if (success) result(tree);
|
|
delete tree;
|
|
} else {
|
|
llwarns << "Rest request error " << status << ": " << reason << llendl;
|
|
}
|
|
}
|
|
|
|
|
|
// ********************************************************************
|
|
|
|
|
|
class BodyData : public LLIOPipe
|
|
{
|
|
public:
|
|
virtual ~BodyData() { }
|
|
virtual const char *getContentMimeType() const = 0;
|
|
};
|
|
|
|
class BodyDataRaw : public BodyData
|
|
{
|
|
public:
|
|
explicit BodyDataRaw(const std::string &data) :
|
|
mData(data)
|
|
{
|
|
}
|
|
virtual ~BodyDataRaw() { }
|
|
|
|
const char *getContentMimeType() const { return "application/octet-stream"; }
|
|
|
|
EStatus process_impl(const LLChannelDescriptors &channels,
|
|
buffer_ptr_t &buffer, bool &eos,
|
|
LLSD &context, LLPumpIO *pump)
|
|
{
|
|
LLBufferStream ostream(channels, buffer.get());
|
|
ostream.write(mData.data(), mData.size());
|
|
eos = true;
|
|
return STATUS_DONE;
|
|
}
|
|
|
|
private:
|
|
std::string mData;
|
|
};
|
|
|
|
class BodyDataXml : public BodyData
|
|
{
|
|
public:
|
|
explicit BodyDataXml(const LLXmlTree *tree) :
|
|
mTree(tree)
|
|
{
|
|
}
|
|
|
|
virtual ~BodyDataXml()
|
|
{
|
|
if (mTree) delete mTree;
|
|
}
|
|
|
|
const char *getContentMimeType() const { return "application/xml"; }
|
|
|
|
EStatus process_impl(const LLChannelDescriptors &channels,
|
|
buffer_ptr_t &buffer, bool &eos,
|
|
LLSD &context, LLPumpIO *pump)
|
|
{
|
|
std::string data;
|
|
mTree->write(data);
|
|
LLBufferStream ostream(channels, buffer.get());
|
|
ostream.write(data.data(), data.size());
|
|
eos = true;
|
|
return STATUS_DONE;
|
|
}
|
|
|
|
private:
|
|
const LLXmlTree *mTree;
|
|
};
|
|
|
|
|
|
// ********************************************************************
|
|
|
|
|
|
static void request(const std::string &url,
|
|
LLURLRequest::ERequestAction method,
|
|
BodyData *body,
|
|
HippoRestHandler *handler, float timeout);
|
|
|
|
|
|
// static
|
|
void HippoRestRequest::get(const std::string &url,
|
|
HippoRestHandler *handler, float timeout)
|
|
{
|
|
request(url, LLURLRequest::HTTP_GET, 0, handler, timeout);
|
|
}
|
|
|
|
// static
|
|
void HippoRestRequest::put(const std::string &url, const std::string &body,
|
|
HippoRestHandler *handler, float timeout)
|
|
{
|
|
request(url, LLURLRequest::HTTP_PUT, new BodyDataRaw(body), handler, timeout);
|
|
}
|
|
|
|
// static
|
|
void HippoRestRequest::put(const std::string &url, const LLXmlTree *body,
|
|
HippoRestHandler *handler, float timeout)
|
|
{
|
|
request(url, LLURLRequest::HTTP_PUT, new BodyDataXml(body), handler, timeout);
|
|
}
|
|
|
|
// static
|
|
void HippoRestRequest::post(const std::string &url, const std::string &body,
|
|
HippoRestHandler *handler, float timeout)
|
|
{
|
|
request(url, LLURLRequest::HTTP_POST, new BodyDataRaw(body), handler, timeout);
|
|
}
|
|
|
|
// static
|
|
void HippoRestRequest::post(const std::string &url, const LLXmlTree *body,
|
|
HippoRestHandler *handler, float timeout)
|
|
{
|
|
request(url, LLURLRequest::HTTP_POST, new BodyDataXml(body), handler, timeout);
|
|
}
|
|
|
|
|
|
// ********************************************************************
|
|
|
|
|
|
static void request(const std::string &url,
|
|
LLURLRequest::ERequestAction method,
|
|
BodyData *body,
|
|
HippoRestHandler *handler, float timeout)
|
|
{
|
|
if (!LLHTTPClient::hasPump())
|
|
{
|
|
// !!! responder->completed(U32_MAX, "No pump", LLSD());
|
|
return;
|
|
}
|
|
LLPumpIO::chain_t chain;
|
|
|
|
LLURLRequest *req = new LLURLRequest(method, url);
|
|
req->setSSLVerifyCallback(LLHTTPClient::getCertVerifyCallback(), (void *)req);
|
|
|
|
/*
|
|
// Insert custom headers if the caller sent any
|
|
if (headers.isMap())
|
|
{
|
|
LLSD::map_const_iterator iter = headers.beginMap();
|
|
LLSD::map_const_iterator end = headers.endMap();
|
|
|
|
for (; iter != end; ++iter)
|
|
{
|
|
std::ostringstream header;
|
|
//if the header is "Pragma" with no value
|
|
//the caller intends to force libcurl to drop
|
|
//the Pragma header it so gratuitously inserts
|
|
//Before inserting the header, force libcurl
|
|
//to not use the proxy (read: llurlrequest.cpp)
|
|
static const std::string PRAGMA("Pragma");
|
|
if ((iter->first == PRAGMA) && (iter->second.asString().empty()))
|
|
{
|
|
req->useProxy(false);
|
|
}
|
|
header << iter->first << ": " << iter->second.asString() ;
|
|
lldebugs << "header = " << header.str() << llendl;
|
|
req->addHeader(header.str().c_str());
|
|
}
|
|
}
|
|
*/
|
|
|
|
if ((method != LLURLRequest::HTTP_PUT) && (method != LLURLRequest::HTTP_POST)) {
|
|
std::string accept = "Accept: ";
|
|
accept += handler->getAcceptMimeType();
|
|
req->addHeader(accept.c_str());
|
|
}
|
|
|
|
req->setCallback(new HippoRestComplete(handler));
|
|
|
|
if ((method == LLURLRequest::HTTP_PUT) || (method == LLURLRequest::HTTP_POST)) {
|
|
std::string content = "Content-Type: ";
|
|
content += body->getContentMimeType();
|
|
req->addHeader(content.c_str());
|
|
chain.push_back(LLIOPipe::ptr_t(body));
|
|
}
|
|
|
|
chain.push_back(LLIOPipe::ptr_t(req));
|
|
LLHTTPClient::getPump().addChain(chain, timeout);
|
|
}
|
|
|
|
|
|
// ********************************************************************
|
|
|
|
|
|
static size_t curlWrite(void *ptr, size_t size, size_t nmemb, void *userData)
|
|
{
|
|
std::string *result = (std::string*)userData;
|
|
size_t bytes = (size * nmemb);
|
|
result->append((char*)ptr, bytes);
|
|
return nmemb;
|
|
}
|
|
|
|
|
|
// static
|
|
int HippoRestRequest::getBlocking(const std::string &url, std::string *result)
|
|
{
|
|
llinfos << "Requesting: " << url << llendl;
|
|
|
|
char curlErrorBuffer[CURL_ERROR_SIZE];
|
|
CURL* curlp = curl_easy_init();
|
|
|
|
curl_easy_setopt(curlp, CURLOPT_NOSIGNAL, 1); // don't use SIGALRM for timeouts
|
|
curl_easy_setopt(curlp, CURLOPT_TIMEOUT, 5); // seconds
|
|
curl_easy_setopt(curlp, CURLOPT_CAINFO, gDirUtilp->getCAFile().c_str());
|
|
|
|
curl_easy_setopt(curlp, CURLOPT_WRITEFUNCTION, curlWrite);
|
|
curl_easy_setopt(curlp, CURLOPT_WRITEDATA, result);
|
|
curl_easy_setopt(curlp, CURLOPT_URL, url.c_str());
|
|
curl_easy_setopt(curlp, CURLOPT_ERRORBUFFER, curlErrorBuffer);
|
|
curl_easy_setopt(curlp, CURLOPT_FAILONERROR, 1);
|
|
|
|
*result = "";
|
|
S32 curlSuccess = curl_easy_perform(curlp);
|
|
S32 httpStatus = 499;
|
|
curl_easy_getinfo(curlp, CURLINFO_RESPONSE_CODE, &httpStatus);
|
|
|
|
if (curlSuccess != 0) {
|
|
llwarns << "CURL ERROR (HTTP Status " << httpStatus << "): " << curlErrorBuffer << llendl;
|
|
} else if (httpStatus != 200) {
|
|
llwarns << "HTTP Error " << httpStatus << ", but no Curl error." << llendl;
|
|
}
|
|
|
|
curl_easy_cleanup(curlp);
|
|
return httpStatus;
|
|
}
|
|
|