summaryrefslogtreecommitdiff
path: root/indra/llmessage/llhttpclient.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llmessage/llhttpclient.cpp')
-rw-r--r--indra/llmessage/llhttpclient.cpp673
1 files changed, 0 insertions, 673 deletions
diff --git a/indra/llmessage/llhttpclient.cpp b/indra/llmessage/llhttpclient.cpp
deleted file mode 100644
index 60f17e6870..0000000000
--- a/indra/llmessage/llhttpclient.cpp
+++ /dev/null
@@ -1,673 +0,0 @@
-/**
- * @file llhttpclient.cpp
- * @brief Implementation of classes for making HTTP requests.
- *
- * $LicenseInfo:firstyear=2006&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-#include <openssl/x509_vfy.h>
-#include "llhttpclient.h"
-
-#include "llassetstorage.h"
-#include "lliopipe.h"
-#include "llurlrequest.h"
-#include "llbufferstream.h"
-#include "llsdserialize.h"
-#include "llvfile.h"
-#include "llvfs.h"
-#include "lluri.h"
-
-#include "message.h"
-#include <curl/curl.h>
-
-
-const F32 HTTP_REQUEST_EXPIRY_SECS = 60.0f;
-LLURLRequest::SSLCertVerifyCallback LLHTTPClient::mCertVerifyCallback = NULL;
-
-////////////////////////////////////////////////////////////////////////////
-
-// Responder class moved to LLCurl
-
-namespace
-{
- class LLHTTPClientURLAdaptor : public LLURLRequestComplete
- {
- public:
- LLHTTPClientURLAdaptor(LLCurl::ResponderPtr responder)
- : LLURLRequestComplete(), mResponder(responder), mStatus(HTTP_INTERNAL_ERROR),
- mReason("LLURLRequest complete w/no status")
- {
- }
-
- ~LLHTTPClientURLAdaptor()
- {
- }
-
- virtual void httpStatus(S32 status, const std::string& reason)
- {
- LLURLRequestComplete::httpStatus(status,reason);
-
- mStatus = status;
- mReason = reason;
- }
-
- virtual void complete(const LLChannelDescriptors& channels,
- const buffer_ptr_t& buffer)
- {
- // *TODO: Re-interpret mRequestStatus codes?
- // Would like to detect curl errors, such as
- // connection errors, write erros, etc.
- if (mResponder.get())
- {
- mResponder->setResult(mStatus, mReason);
- mResponder->completedRaw(channels, buffer);
- }
- }
- virtual void header(const std::string& header, const std::string& value)
- {
- if (mResponder.get())
- {
- mResponder->setResponseHeader(header, value);
- }
- }
-
- private:
- LLCurl::ResponderPtr mResponder;
- S32 mStatus;
- std::string mReason;
- };
-
- class Injector : public LLIOPipe
- {
- public:
- virtual const std::string& contentType() = 0;
- };
-
- class LLSDInjector : public Injector
- {
- public:
- LLSDInjector(const LLSD& sd) : mSD(sd) {}
- virtual ~LLSDInjector() {}
-
- const std::string& contentType() { return HTTP_CONTENT_LLSD_XML; }
-
- virtual EStatus process_impl(const LLChannelDescriptors& channels,
- buffer_ptr_t& buffer, bool& eos, LLSD& context, LLPumpIO* pump)
- {
- LLBufferStream ostream(channels, buffer.get());
- LLSDSerialize::toXML(mSD, ostream);
- eos = true;
- return STATUS_DONE;
- }
-
- const LLSD mSD;
- };
-
- class RawInjector : public Injector
- {
- public:
- RawInjector(const U8* data, S32 size) : mData(data), mSize(size) {}
- virtual ~RawInjector() {delete [] mData;}
-
- const std::string& contentType() { return HTTP_CONTENT_OCTET_STREAM; }
-
- virtual EStatus process_impl(const LLChannelDescriptors& channels,
- buffer_ptr_t& buffer, bool& eos, LLSD& context, LLPumpIO* pump)
- {
- LLBufferStream ostream(channels, buffer.get());
- ostream.write((const char *)mData, mSize); // hopefully chars are always U8s
- eos = true;
- return STATUS_DONE;
- }
-
- const U8* mData;
- S32 mSize;
- };
-
- class FileInjector : public Injector
- {
- public:
- FileInjector(const std::string& filename) : mFilename(filename) {}
- virtual ~FileInjector() {}
-
- const std::string& contentType() { return HTTP_CONTENT_OCTET_STREAM; }
-
- virtual EStatus process_impl(const LLChannelDescriptors& channels,
- buffer_ptr_t& buffer, bool& eos, LLSD& context, LLPumpIO* pump)
- {
- LLBufferStream ostream(channels, buffer.get());
-
- llifstream fstream(mFilename.c_str(), std::iostream::binary | std::iostream::out);
- if(fstream.is_open())
- {
- fstream.seekg(0, std::ios::end);
- U32 fileSize = (U32)fstream.tellg();
- fstream.seekg(0, std::ios::beg);
- std::vector<char> fileBuffer(fileSize);
- fstream.read(&fileBuffer[0], fileSize);
- ostream.write(&fileBuffer[0], fileSize);
- fstream.close();
- eos = true;
- return STATUS_DONE;
- }
-
- return STATUS_ERROR;
- }
-
- const std::string mFilename;
- };
-
- class VFileInjector : public Injector
- {
- public:
- VFileInjector(const LLUUID& uuid, LLAssetType::EType asset_type) : mUUID(uuid), mAssetType(asset_type) {}
- virtual ~VFileInjector() {}
-
- const std::string& contentType() { return HTTP_CONTENT_OCTET_STREAM; }
-
- virtual EStatus process_impl(const LLChannelDescriptors& channels,
- buffer_ptr_t& buffer, bool& eos, LLSD& context, LLPumpIO* pump)
- {
- LLBufferStream ostream(channels, buffer.get());
-
- LLVFile vfile(gVFS, mUUID, mAssetType, LLVFile::READ);
- S32 fileSize = vfile.getSize();
- U8* fileBuffer;
- fileBuffer = new U8 [fileSize];
- vfile.read(fileBuffer, fileSize);
- ostream.write((char*)fileBuffer, fileSize);
- delete [] fileBuffer;
- eos = true;
- return STATUS_DONE;
- }
-
- const LLUUID mUUID;
- LLAssetType::EType mAssetType;
- };
-
-
- LLPumpIO* theClientPump = NULL;
-}
-
-void LLHTTPClient::setCertVerifyCallback(LLURLRequest::SSLCertVerifyCallback callback)
-{
- LLHTTPClient::mCertVerifyCallback = callback;
-}
-
-static void request(
- const std::string& url,
- EHTTPMethod method,
- Injector* body_injector,
- LLCurl::ResponderPtr responder,
- const F32 timeout = HTTP_REQUEST_EXPIRY_SECS,
- const LLSD& headers = LLSD(),
- bool follow_redirects = true
- )
-{
- if (!LLHTTPClient::hasPump())
- {
- if (responder)
- {
- responder->completeResult(HTTP_INTERNAL_ERROR, "No pump");
- }
- delete body_injector;
- return;
- }
- LLPumpIO::chain_t chain;
-
- LLURLRequest* req = new LLURLRequest(method, url, follow_redirects);
- if(!req->isValid())//failed
- {
- if (responder)
- {
- responder->completeResult(HTTP_INTERNAL_CURL_ERROR, "Internal Error - curl failure");
- }
- delete req;
- delete body_injector;
- return;
- }
-
- req->setSSLVerifyCallback(LLHTTPClient::getCertVerifyCallback(), (void *)req);
-
- LL_DEBUGS("LLHTTPClient") << httpMethodAsVerb(method) << " " << url << " " << headers << LL_ENDL;
-
- // Insert custom headers if the caller sent any
- if (headers.isMap())
- {
- if (headers.has(HTTP_OUT_HEADER_COOKIE))
- {
- req->allowCookies();
- }
-
- LLSD::map_const_iterator iter = headers.beginMap();
- LLSD::map_const_iterator end = headers.endMap();
-
- for (; iter != end; ++iter)
- {
- //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)
- if ((iter->first == HTTP_OUT_HEADER_PRAGMA) && (iter->second.asString().empty()))
- {
- req->useProxy(false);
- }
- LL_DEBUGS("LLHTTPClient") << "header = " << iter->first
- << ": " << iter->second.asString() << LL_ENDL;
- req->addHeader(iter->first, iter->second.asString());
- }
- }
-
- // Check to see if we have already set Accept or not. If no one
- // set it, set it to application/llsd+xml since that's what we
- // almost always want.
- if( method != HTTP_PUT && method != HTTP_POST )
- {
- if(!headers.has(HTTP_OUT_HEADER_ACCEPT))
- {
- req->addHeader(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_LLSD_XML);
- }
- }
-
- if (responder)
- {
- responder->setURL(url);
- responder->setHTTPMethod(method);
- }
-
- req->setCallback(new LLHTTPClientURLAdaptor(responder));
-
- if (method == HTTP_POST && gMessageSystem)
- {
- req->addHeader("X-SecondLife-UDP-Listen-Port", llformat("%d",
- gMessageSystem->mPort));
- }
-
- if (method == HTTP_PUT || method == HTTP_POST || method == HTTP_PATCH)
- {
- if(!headers.has(HTTP_OUT_HEADER_CONTENT_TYPE))
- {
- // If the Content-Type header was passed in, it has
- // already been added as a header through req->addHeader
- // in the loop above. We defer to the caller's wisdom, but
- // if they did not specify a Content-Type, then ask the
- // injector.
- req->addHeader(HTTP_OUT_HEADER_CONTENT_TYPE, body_injector->contentType());
- }
- chain.push_back(LLIOPipe::ptr_t(body_injector));
- }
-
- chain.push_back(LLIOPipe::ptr_t(req));
-
- theClientPump->addChain(chain, timeout);
-}
-
-
-void LLHTTPClient::getByteRange(
- const std::string& url,
- S32 offset,
- S32 bytes,
- ResponderPtr responder,
- const LLSD& hdrs,
- const F32 timeout,
- bool follow_redirects /* = true */)
-{
- LLSD headers = hdrs;
- if(offset > 0 || bytes > 0)
- {
- std::string range = llformat("bytes=%d-%d", offset, offset+bytes-1);
- headers[HTTP_OUT_HEADER_RANGE] = range;
- }
- request(url,HTTP_GET, NULL, responder, timeout, headers, follow_redirects);
-}
-
-void LLHTTPClient::head(
- const std::string& url,
- ResponderPtr responder,
- const LLSD& headers,
- const F32 timeout,
- bool follow_redirects /* = true */)
-{
- request(url, HTTP_HEAD, NULL, responder, timeout, headers, follow_redirects);
-}
-
-void LLHTTPClient::get(const std::string& url, ResponderPtr responder, const LLSD& headers, const F32 timeout,
- bool follow_redirects /* = true */)
-{
- request(url, HTTP_GET, NULL, responder, timeout, headers, follow_redirects);
-}
-void LLHTTPClient::getHeaderOnly(const std::string& url, ResponderPtr responder, const LLSD& headers,
- const F32 timeout, bool follow_redirects /* = true */)
-{
- request(url, HTTP_HEAD, NULL, responder, timeout, headers, follow_redirects);
-}
-void LLHTTPClient::getHeaderOnly(const std::string& url, ResponderPtr responder, const F32 timeout,
- bool follow_redirects /* = true */)
-{
- getHeaderOnly(url, responder, LLSD(), timeout, follow_redirects);
-}
-
-void LLHTTPClient::get(const std::string& url, const LLSD& query, ResponderPtr responder, const LLSD& headers,
- const F32 timeout, bool follow_redirects /* = true */)
-{
- LLURI uri;
-
- uri = LLURI::buildHTTP(url, LLSD::emptyArray(), query);
- get(uri.asString(), responder, headers, timeout, follow_redirects);
-}
-
-// A simple class for managing data returned from a curl http request.
-class LLHTTPBuffer
-{
-public:
- LLHTTPBuffer() { }
-
- static size_t curl_write( void *ptr, size_t size, size_t nmemb, void *user_data)
- {
- LLHTTPBuffer* self = (LLHTTPBuffer*)user_data;
-
- size_t bytes = (size * nmemb);
- self->mBuffer.append((char*)ptr,bytes);
- return nmemb;
- }
-
- LLSD asLLSD()
- {
- LLSD content;
-
- if (mBuffer.empty()) return content;
-
- std::istringstream istr(mBuffer);
- LLSDSerialize::fromXML(content, istr);
- return content;
- }
-
- const std::string& asString()
- {
- return mBuffer;
- }
-
-private:
- std::string mBuffer;
-};
-
-// These calls are blocking! This is usually bad, unless you're a dataserver. Then it's awesome.
-
-/**
- @brief does a blocking request on the url, returning the data or bad status.
-
- @param url URI to verb on.
- @param method the verb to hit the URI with.
- @param body the body of the call (if needed - for instance not used for GET and DELETE, but is for POST and PUT)
- @param headers HTTP headers to use for the request.
- @param timeout Curl timeout to use. Defaults to 5. Rationale:
- Without this timeout, blockingGet() calls have been observed to take
- up to 90 seconds to complete. Users of blockingGet() already must
- check the HTTP return code for validity, so this will not introduce
- new errors. A 5 second timeout will succeed > 95% of the time (and
- probably > 99% of the time) based on my statistics. JC
-
- @returns an LLSD map: {status: integer, body: map}
- */
-static LLSD blocking_request(
- const std::string& url,
- EHTTPMethod method,
- const LLSD& body,
- const LLSD& headers = LLSD(),
- const F32 timeout = 5
-)
-{
- LL_DEBUGS() << "blockingRequest of " << url << LL_ENDL;
- char curl_error_buffer[CURL_ERROR_SIZE] = "\0";
- CURL* curlp = LLCurl::newEasyHandle();
- llassert_always(curlp != NULL) ;
-
- LLHTTPBuffer http_buffer;
- std::string body_str;
-
- // other request method checks root cert first, we skip?
-
- // Apply configured proxy settings
- LLProxy::getInstance()->applyProxySettings(curlp);
-
- // * Set curl handle options
- curl_easy_setopt(curlp, CURLOPT_NOSIGNAL, 1); // don't use SIGALRM for timeouts
- curl_easy_setopt(curlp, CURLOPT_TIMEOUT, timeout); // seconds, see warning at top of function.
- curl_easy_setopt(curlp, CURLOPT_WRITEFUNCTION, LLHTTPBuffer::curl_write);
- curl_easy_setopt(curlp, CURLOPT_WRITEDATA, &http_buffer);
- curl_easy_setopt(curlp, CURLOPT_URL, url.c_str());
- curl_easy_setopt(curlp, CURLOPT_ERRORBUFFER, curl_error_buffer);
-
- // * Setup headers (don't forget to free them after the call!)
- curl_slist* headers_list = NULL;
- if (headers.isMap())
- {
- LLSD::map_const_iterator iter = headers.beginMap();
- LLSD::map_const_iterator end = headers.endMap();
- for (; iter != end; ++iter)
- {
- std::ostringstream header;
- header << iter->first << ": " << iter->second.asString() ;
- LL_DEBUGS() << "header = " << header.str() << LL_ENDL;
- headers_list = curl_slist_append(headers_list, header.str().c_str());
- }
- }
-
- // * Setup specific method / "verb" for the URI (currently only GET and POST supported + poppy)
- if (method == HTTP_GET)
- {
- curl_easy_setopt(curlp, CURLOPT_HTTPGET, 1);
- }
- else if (method == HTTP_POST)
- {
- curl_easy_setopt(curlp, CURLOPT_POST, 1);
- //serialize to ostr then copy to str - need to because ostr ptr is unstable :(
- std::ostringstream ostr;
- LLSDSerialize::toXML(body, ostr);
- body_str = ostr.str();
- curl_easy_setopt(curlp, CURLOPT_POSTFIELDS, body_str.c_str());
- //copied from PHP libs, correct?
- headers_list = curl_slist_append(headers_list,
- llformat("%s: %s", HTTP_OUT_HEADER_CONTENT_TYPE.c_str(), HTTP_CONTENT_LLSD_XML.c_str()).c_str());
-
- // copied from llurlrequest.cpp
- // it appears that apache2.2.3 or django in etch is busted. If
- // we do not clear the expect header, we get a 500. May be
- // limited to django/mod_wsgi.
- headers_list = curl_slist_append(headers_list, llformat("%s:", HTTP_OUT_HEADER_EXPECT.c_str()).c_str());
- }
-
- // * Do the action using curl, handle results
- LL_DEBUGS() << "HTTP body: " << body_str << LL_ENDL;
- headers_list = curl_slist_append(headers_list,
- llformat("%s: %s", HTTP_OUT_HEADER_ACCEPT.c_str(), HTTP_CONTENT_LLSD_XML.c_str()).c_str());
- CURLcode curl_result = curl_easy_setopt(curlp, CURLOPT_HTTPHEADER, headers_list);
- if ( curl_result != CURLE_OK )
- {
- LL_INFOS() << "Curl is hosed - can't add headers" << LL_ENDL;
- }
-
- LLSD response = LLSD::emptyMap();
- S32 curl_success = curl_easy_perform(curlp);
- S32 http_status = HTTP_INTERNAL_ERROR;
- curl_easy_getinfo(curlp, CURLINFO_RESPONSE_CODE, &http_status);
- response["status"] = http_status;
- // if we get a non-404 and it's not a 200 OR maybe it is but you have error bits,
- if ( http_status != HTTP_NOT_FOUND && (http_status != HTTP_OK || curl_success != 0) )
- {
- // We expect 404s, don't spam for them.
- LL_WARNS() << "CURL REQ URL: " << url << LL_ENDL;
- LL_WARNS() << "CURL REQ METHOD TYPE: " << method << LL_ENDL;
- LL_WARNS() << "CURL REQ HEADERS: " << headers.asString() << LL_ENDL;
- LL_WARNS() << "CURL REQ BODY: " << body_str << LL_ENDL;
- LL_WARNS() << "CURL HTTP_STATUS: " << http_status << LL_ENDL;
- LL_WARNS() << "CURL ERROR: " << curl_error_buffer << LL_ENDL;
- LL_WARNS() << "CURL ERROR BODY: " << http_buffer.asString() << LL_ENDL;
- response["body"] = http_buffer.asString();
- }
- else
- {
- response["body"] = http_buffer.asLLSD();
- LL_DEBUGS() << "CURL response: " << http_buffer.asString() << LL_ENDL;
- }
-
- if(headers_list)
- { // free the header list
- curl_slist_free_all(headers_list);
- }
-
- // * Cleanup
- LLCurl::deleteEasyHandle(curlp);
- return response;
-}
-
-LLSD LLHTTPClient::blockingGet(const std::string& url)
-{
- return blocking_request(url, HTTP_GET, LLSD());
-}
-
-LLSD LLHTTPClient::blockingPost(const std::string& url, const LLSD& body)
-{
- return blocking_request(url, HTTP_POST, body);
-}
-
-void LLHTTPClient::put(
- const std::string& url,
- const LLSD& body,
- ResponderPtr responder,
- const LLSD& headers,
- const F32 timeout)
-{
- request(url, HTTP_PUT, new LLSDInjector(body), responder, timeout, headers);
-}
-
-void LLHTTPClient::patch(
- const std::string& url,
- const LLSD& body,
- ResponderPtr responder,
- const LLSD& headers,
- const F32 timeout)
-{
- request(url, HTTP_PATCH, new LLSDInjector(body), responder, timeout, headers);
-}
-
-void LLHTTPClient::putRaw(
- const std::string& url,
- const U8* data,
- S32 size,
- ResponderPtr responder,
- const LLSD& headers,
- const F32 timeout)
-{
- request(url, HTTP_PUT, new RawInjector(data, size), responder, timeout, headers);
-}
-
-void LLHTTPClient::post(
- const std::string& url,
- const LLSD& body,
- ResponderPtr responder,
- const LLSD& headers,
- const F32 timeout)
-{
- request(url, HTTP_POST, new LLSDInjector(body), responder, timeout, headers);
-}
-
-void LLHTTPClient::postRaw(
- const std::string& url,
- const U8* data,
- S32 size,
- ResponderPtr responder,
- const LLSD& headers,
- const F32 timeout)
-{
- request(url, HTTP_POST, new RawInjector(data, size), responder, timeout, headers);
-}
-
-void LLHTTPClient::postFile(
- const std::string& url,
- const std::string& filename,
- ResponderPtr responder,
- const LLSD& headers,
- const F32 timeout)
-{
- request(url, HTTP_POST, new FileInjector(filename), responder, timeout, headers);
-}
-
-void LLHTTPClient::postFile(
- const std::string& url,
- const LLUUID& uuid,
- LLAssetType::EType asset_type,
- ResponderPtr responder,
- const LLSD& headers,
- const F32 timeout)
-{
- request(url, HTTP_POST, new VFileInjector(uuid, asset_type), responder, timeout, headers);
-}
-
-// static
-void LLHTTPClient::del(
- const std::string& url,
- ResponderPtr responder,
- const LLSD& headers,
- const F32 timeout)
-{
- request(url, HTTP_DELETE, NULL, responder, timeout, headers);
-}
-
-// static
-void LLHTTPClient::move(
- const std::string& url,
- const std::string& destination,
- ResponderPtr responder,
- const LLSD& hdrs,
- const F32 timeout)
-{
- LLSD headers = hdrs;
- headers[HTTP_OUT_HEADER_DESTINATION] = destination;
- request(url, HTTP_MOVE, NULL, responder, timeout, headers);
-}
-
-// static
-void LLHTTPClient::copy(
- const std::string& url,
- const std::string& destination,
- ResponderPtr responder,
- const LLSD& hdrs,
- const F32 timeout)
-{
- LLSD headers = hdrs;
- headers[HTTP_OUT_HEADER_DESTINATION] = destination;
- request(url, HTTP_COPY, NULL, responder, timeout, headers);
-}
-
-
-void LLHTTPClient::setPump(LLPumpIO& pump)
-{
- theClientPump = &pump;
-}
-
-bool LLHTTPClient::hasPump()
-{
- return theClientPump != NULL;
-}