Files
SingularityViewer/indra/newview/hipporestrequest.cpp
Aleric Inglewood 1d5a63c180 Fixed showing TOS floater.
This creates a separate events interface structure
for CurlResponderBuffer (AICurlResponderBufferEvents)
for dealing with received HTTP headers.

The headers are passed to the Responder, but only
if the class derived from Responder implements
completedHeaders (otherwise it makes little sense
to even decode the headers).

Basically this is a reimplementation of the functionality
of the old LLHTTPClientURLAdaptor class.
2012-09-18 23:59:09 +02:00

344 lines
9.1 KiB
C++

#include "llviewerprecompiledheaders.h"
#include "hipporestrequest.h"
#ifndef CURL_STATICLIB
#define CURL_STATICLIB 1
#endif
#include <stdtypes.h>
#include "llbufferstream.h"
#include "llerror.h"
#include "llhttpclient.h"
#include "llurlrequest.h"
#include "llxmltree.h"
#include <curl/curl.h>
#ifdef DEBUG_CURLIO
#include "debug_libcurl.h"
#endif
// ********************************************************************
class HippoRestComplete /* AIFIXME: 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);
}
#if 0 // AIFIXME: doesn't compile
// 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);
}
#endif
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 BodyDataRaw : public Injector
{
public:
explicit BodyDataRaw(const std::string &data) : mData(data) { }
/*virtual*/ char const* contentType(void) const { return "application/octet-stream"; }
/*virtual*/ U32 get_body(LLChannelDescriptors const& channels, buffer_ptr_t& buffer)
{
LLBufferStream ostream(channels, buffer.get());
ostream.write(mData.data(), mData.size());
return mData.size();
}
private:
std::string mData;
};
class BodyDataXml : public Injector
{
public:
explicit BodyDataXml(const LLXmlTree *tree) :
mTree(tree)
{
}
virtual ~BodyDataXml()
{
if (mTree) delete mTree;
}
/*virtual*/ char const* contentType(void) const { return "application/xml"; }
/*virtual*/ U32 get_body(LLChannelDescriptors const& channels, buffer_ptr_t& buffer)
{
std::string data;
mTree->write(data);
LLBufferStream ostream(channels, buffer.get());
ostream.write(data.data(), data.size());
return data.size();
}
private:
const LLXmlTree *mTree;
};
// ********************************************************************
static void request(const std::string &url,
LLURLRequest::ERequestAction method,
Injector *body,
HippoRestHandler *handler, float timeout);
// static
void HippoRestRequest::get5(const std::string &url,
HippoRestHandler *handler, float timeout)
{
request(url, LLURLRequest::HTTP_GET, 0, handler, timeout);
}
// static
void HippoRestRequest::put5(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::put5(const std::string &url, const LLXmlTree *body,
HippoRestHandler *handler, float timeout)
{
request(url, LLURLRequest::HTTP_PUT, new BodyDataXml(body), handler, timeout);
}
// static
void HippoRestRequest::post5(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::post5(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,
Injector *body,
HippoRestHandler *handler, float timeout)
{
LLURLRequest *req;
try
{
AIHTTPHeaders empty_headers;
//AIFIXME (doesn't compile): req = new LLURLRequest(method, url, body, handler, empty_headers);
}
catch(AICurlNoEasyHandle const& error)
{
llwarns << "Failed to create LLURLRequest: " << error.what() << llendl;
return;
}
// Already done by default. req->checkRootCertificate(true);
/*
// 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();
//AIFIXME req is not defined: req->addHeader(accept.c_str());
}
//AIFIXME: req->setCallback(new HippoRestComplete(handler));
if ((method == LLURLRequest::HTTP_PUT) || (method == LLURLRequest::HTTP_POST)) {
std::string content = "Content-Type: ";
content += body->contentType();
//AIFIXME req is not defined: req->addHeader(content.c_str());
//AIFIXME: chain.push_back(LLIOPipe::ptr_t(body));
}
//AIFIXME: 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();
llassert_always(curlp);
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);
long httpStatus = 499L; // curl_easy_getinfo demands pointer to long.
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;
}