#include "llviewerprecompiledheaders.h" #include "hipporestrequest.h" #ifndef CURL_STATICLIB #define CURL_STATICLIB 1 #endif #include #include #include #include #include #include #include // ******************************************************************** 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::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 &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 &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->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(); 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_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; }