diff options
author | Oz Linden <oz@lindenlab.com> | 2016-04-04 15:53:09 -0400 |
---|---|---|
committer | Oz Linden <oz@lindenlab.com> | 2016-04-04 15:53:09 -0400 |
commit | 9be58e915a6c69de280ccabd3019e9ac40beed26 (patch) | |
tree | ad6f8f1f754a865afb761d0f17744cc7e1ef8b74 /indra/llmessage | |
parent | ab46c9226bd9048fa218f54bc8668594401529e7 (diff) | |
parent | 18928ea6c6f2830a0d45ec412c915eceff1b76b0 (diff) |
merge with 4.0.3-release
Diffstat (limited to 'indra/llmessage')
50 files changed, 3769 insertions, 11520 deletions
diff --git a/indra/llmessage/CMakeLists.txt b/indra/llmessage/CMakeLists.txt index 0a308fbf10..87bec60d95 100644 --- a/indra/llmessage/CMakeLists.txt +++ b/indra/llmessage/CMakeLists.txt @@ -14,6 +14,7 @@ include(LLAddBuildTest) include(Python) include(Tut) include(Python) +include(JsonCpp) include_directories (${CMAKE_CURRENT_SOURCE_DIR}) @@ -23,11 +24,10 @@ include_directories( ${LLMATH_INCLUDE_DIRS} ${LLMESSAGE_INCLUDE_DIRS} ${LLVFS_INCLUDE_DIRS} + ${JSONCPP_INCLUDE_DIR} ) set(llmessage_SOURCE_FILES - llares.cpp - llareslistener.cpp llassetstorage.cpp llavatarname.cpp llavatarnamecache.cpp @@ -38,19 +38,15 @@ set(llmessage_SOURCE_FILES llchainio.cpp llcircuit.cpp llclassifiedflags.cpp + llcoproceduremanager.cpp llcorehttputil.cpp - llcurl.cpp lldatapacker.cpp lldispatcher.cpp llexperiencecache.cpp llfiltersd2xmlrpc.cpp llhost.cpp - llhttpassetstorage.cpp - llhttpclient.cpp - llhttpclientadapter.cpp - llhttpconstants.cpp llhttpnode.cpp - llhttpsender.cpp + llhttpsdhandler.cpp llinstantmessage.cpp lliobuffer.cpp lliohttpserver.cpp @@ -74,11 +70,8 @@ set(llmessage_SOURCE_FILES llpumpio.cpp llsdappservices.cpp llsdhttpserver.cpp - llsdmessage.cpp llsdmessagebuilder.cpp llsdmessagereader.cpp - llsdrpcclient.cpp - llsdrpcserver.cpp llservicebuilder.cpp llservice.cpp llstoredmessage.cpp @@ -92,7 +85,6 @@ set(llmessage_SOURCE_FILES lltransfertargetfile.cpp lltransfertargetvfile.cpp lltrustedmessageservice.cpp - llurlrequest.cpp lluseroperation.cpp llxfer.cpp llxfer_file.cpp @@ -115,8 +107,6 @@ set(llmessage_SOURCE_FILES set(llmessage_HEADER_FILES CMakeLists.txt - llares.h - llareslistener.h llassetstorage.h llavatarname.h llavatarnamecache.h @@ -128,8 +118,8 @@ set(llmessage_HEADER_FILES llcipher.h llcircuit.h llclassifiedflags.h + llcoproceduremanager.h llcorehttputil.h - llcurl.h lldatapacker.h lldbstrings.h lldispatcher.h @@ -139,14 +129,9 @@ set(llmessage_HEADER_FILES llfiltersd2xmlrpc.h llfollowcamparams.h llhost.h - llhttpassetstorage.h - llhttpclient.h - llhttpclientinterface.h - llhttpclientadapter.h - llhttpconstants.h llhttpnode.h llhttpnodeadapter.h - llhttpsender.h + llhttpsdhandler.h llinstantmessage.h llinvite.h lliobuffer.h @@ -176,11 +161,8 @@ set(llmessage_HEADER_FILES llregionhandle.h llsdappservices.h llsdhttpserver.h - llsdmessage.h llsdmessagebuilder.h llsdmessagereader.h - llsdrpcclient.h - llsdrpcserver.h llservice.h llservicebuilder.h llstoredmessage.h @@ -196,7 +178,6 @@ set(llmessage_HEADER_FILES lltransfertargetfile.h lltransfertargetvfile.h lltrustedmessageservice.h - llurlrequest.h lluseroperation.h llvehicleparams.h llxfer.h @@ -222,17 +203,41 @@ set_source_files_properties(${llmessage_HEADER_FILES} list(APPEND llmessage_SOURCE_FILES ${llmessage_HEADER_FILES}) add_library (llmessage ${llmessage_SOURCE_FILES}) + +if (LINUX) target_link_libraries( llmessage ${CURL_LIBRARIES} ${LLCOMMON_LIBRARIES} - ${LLVFS_LIBRARES} + ${LLVFS_LIBRARIES} ${LLMATH_LIBRARIES} - ${CARES_LIBRARIES} + ${JSONCPP_LIBRARIES} ${OPENSSL_LIBRARIES} ${CRYPTO_LIBRARIES} ${XMLRPCEPI_LIBRARIES} + ${LLCOREHTTP_LIBRARIES} + ${BOOST_COROUTINE_LIBRARY} + ${BOOST_CONTEXT_LIBRARY} + ${BOOST_SYSTEM_LIBRARY} + rt ) +else (LINUX) +target_link_libraries( + llmessage + ${CURL_LIBRARIES} + ${LLCOMMON_LIBRARIES} + ${LLVFS_LIBRARIES} + ${LLMATH_LIBRARIES} + ${JSONCPP_LIBRARIES} + ${OPENSSL_LIBRARIES} + ${CRYPTO_LIBRARIES} + ${XMLRPCEPI_LIBRARIES} + ${LLCOREHTTP_LIBRARIES} + ${BOOST_COROUTINE_LIBRARY} + ${BOOST_CONTEXT_LIBRARY} + ${BOOST_SYSTEM_LIBRARY} + ) +endif(LINUX) # tests if (LL_TESTS) @@ -243,36 +248,42 @@ if (LL_TESTS) ) LL_ADD_PROJECT_UNIT_TESTS(llmessage "${llmessage_TEST_SOURCE_FILES}") + # set(TEST_DEBUG on) + +if (LINUX) set(test_libs + ${WINDOWS_LIBRARIES} + ${LLVFS_LIBRARIES} + ${LLMATH_LIBRARIES} ${CURL_LIBRARIES} + ${LLCOMMON_LIBRARIES} ${LLMESSAGE_LIBRARIES} + ${LLCOREHTTP_LIBRARIES} + ${JSONCPP_LIBRARIES} + ${BOOST_COROUTINE_LIBRARY} + ${BOOST_CONTEXT_LIBRARY} + rt + ${GOOGLEMOCK_LIBRARIES} + ) +else (LINUX) + set(test_libs ${WINDOWS_LIBRARIES} ${LLVFS_LIBRARIES} ${LLMATH_LIBRARIES} + ${CURL_LIBRARIES} ${LLCOMMON_LIBRARIES} - ${GOOGLEMOCK_LIBRARIES} - ) - - LL_ADD_INTEGRATION_TEST( - llsdmessage - "llsdmessage.cpp" - "${test_libs}" - ${PYTHON_EXECUTABLE} - "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_llsdmessage_peer.py" - ) - - LL_ADD_INTEGRATION_TEST( - llhttpclient - "llhttpclient.cpp" - "${test_libs}" - ${PYTHON_EXECUTABLE} - "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_llsdmessage_peer.py" + ${LLMESSAGE_LIBRARIES} + ${LLCOREHTTP_LIBRARIES} + ${JSONCPP_LIBRARIES} + ${BOOST_COROUTINE_LIBRARY} + ${BOOST_CONTEXT_LIBRARY} + ${GOOGLEMOCK_LIBRARIES} ) +endif(LINUX) - LL_ADD_INTEGRATION_TEST(llavatarnamecache "" "${test_libs}") + #LL_ADD_INTEGRATION_TEST(llavatarnamecache "" "${test_libs}") LL_ADD_INTEGRATION_TEST(llhost "" "${test_libs}") - LL_ADD_INTEGRATION_TEST(llhttpclientadapter "" "${test_libs}") LL_ADD_INTEGRATION_TEST(llpartdata "" "${test_libs}") LL_ADD_INTEGRATION_TEST(llxfer_file "" "${test_libs}") endif (LL_TESTS) diff --git a/indra/llmessage/llares.cpp b/indra/llmessage/llares.cpp deleted file mode 100644 index 9f90ae1544..0000000000 --- a/indra/llmessage/llares.cpp +++ /dev/null @@ -1,839 +0,0 @@ -/** - * @file llares.cpp - * @author Bryan O'Sullivan - * @date 2007-08-15 - * @brief Wrapper for asynchronous DNS lookups. - * - * $LicenseInfo:firstyear=2007&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 "llares.h" - -#include <ares_dns.h> -#include <ares_version.h> - -#include "apr_portable.h" -#include "apr_network_io.h" -#include "apr_poll.h" - -#include "llapr.h" -#include "llareslistener.h" - -#if defined(LL_WINDOWS) -#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally -# define ns_c_in 1 -# define NS_HFIXEDSZ 12 /* #/bytes of fixed data in header */ -# define NS_QFIXEDSZ 4 /* #/bytes of fixed data in query */ -# define NS_RRFIXEDSZ 10 /* #/bytes of fixed data in r record */ -#else -# include <arpa/nameser.h> -#endif - -LLAres::HostResponder::~HostResponder() -{ -} - -void LLAres::HostResponder::hostResult(const hostent *ent) -{ - LL_INFOS() << "LLAres::HostResponder::hostResult not implemented" << LL_ENDL; -} - -void LLAres::HostResponder::hostError(int code) -{ - LL_INFOS() << "LLAres::HostResponder::hostError " << code << ": " - << LLAres::strerror(code) << LL_ENDL; -} - -LLAres::NameInfoResponder::~NameInfoResponder() -{ -} - -void LLAres::NameInfoResponder::nameInfoResult(const char *node, - const char *service) -{ - LL_INFOS() << "LLAres::NameInfoResponder::nameInfoResult not implemented" - << LL_ENDL; -} - -void LLAres::NameInfoResponder::nameInfoError(int code) -{ - LL_INFOS() << "LLAres::NameInfoResponder::nameInfoError " << code << ": " - << LLAres::strerror(code) << LL_ENDL; -} - -LLAres::QueryResponder::~QueryResponder() -{ -} - -void LLAres::QueryResponder::queryResult(const char *buf, size_t len) -{ - LL_INFOS() << "LLAres::QueryResponder::queryResult not implemented" - << LL_ENDL; -} - -void LLAres::QueryResponder::queryError(int code) -{ - LL_INFOS() << "LLAres::QueryResponder::queryError " << code << ": " - << LLAres::strerror(code) << LL_ENDL; -} - -LLAres::LLAres() : - chan_(NULL), - mInitSuccess(false) -{ - if (ares_library_init( ARES_LIB_INIT_ALL ) != ARES_SUCCESS || - ares_init(&chan_) != ARES_SUCCESS) - { - LL_WARNS() << "Could not succesfully initialize ares!" << LL_ENDL; - return; - } - - mListener = boost::shared_ptr< LLAresListener >(new LLAresListener(this)); - - mInitSuccess = true; -} - -LLAres::~LLAres() -{ - ares_destroy(chan_); - ares_library_cleanup(); -} - -void LLAres::cancel() -{ - ares_cancel(chan_); -} - -static void host_callback_1_5(void *arg, int status, int timeouts, - struct hostent *ent) -{ - LLPointer<LLAres::HostResponder> *resp = - (LLPointer<LLAres::HostResponder> *) arg; - - if (status == ARES_SUCCESS) - { - (*resp)->hostResult(ent); - } else { - (*resp)->hostError(status); - } - - delete resp; -} - -#if ARES_VERSION_MAJOR == 1 && ARES_VERSION_MINOR == 4 -static void host_callback(void *arg, int status, struct hostent *ent) -{ - host_callback_1_5(arg, status, 0, ent); -} -#else -# define host_callback host_callback_1_5 -#endif - -void LLAres::getHostByName(const char *name, HostResponder *resp, - int family) -{ - ares_gethostbyname(chan_, name, family, host_callback, - new LLPointer<LLAres::HostResponder>(resp)); -} - -void LLAres::getSrvRecords(const std::string &name, SrvResponder *resp) -{ - search(name, RES_SRV, resp); -} - -void LLAres::rewriteURI(const std::string &uri, UriRewriteResponder *resp) -{ - if (resp && uri.size()) - { - LLURI* pURI = new LLURI(uri); - - resp->mUri = *pURI; - - delete pURI; - - if (!resp->mUri.scheme().size() || !resp->mUri.hostName().size()) - { - return; - } - - //LL_INFOS() << "LLAres::rewriteURI (" << uri << ") search: '" << "_" + resp->mUri.scheme() + "._tcp." + resp->mUri.hostName() << "'" << LL_ENDL; - - search("_" + resp->mUri.scheme() + "._tcp." + resp->mUri.hostName(), RES_SRV, resp); - - - } -} - -LLQueryResponder::LLQueryResponder() - : LLAres::QueryResponder(), - mResult(ARES_ENODATA), - mType(RES_INVALID) -{ -} - -int LLQueryResponder::parseRR(const char *buf, size_t len, const char *&pos, - LLPointer<LLDnsRecord> &r) -{ - std::string rrname; - size_t enclen; - int ret; - - // RR name. - - ret = LLAres::expandName(pos, buf, len, rrname, enclen); - if (ret != ARES_SUCCESS) - { - return ret; - } - - pos += enclen; - - if (pos + NS_RRFIXEDSZ > buf + len) - { - return ARES_EBADRESP; - } - - int rrtype = DNS_RR_TYPE(pos); - int rrclass = DNS_RR_CLASS(pos); - int rrttl = DNS_RR_TTL(pos); - int rrlen = DNS_RR_LEN(pos); - - if (rrclass != ns_c_in) - { - return ARES_EBADRESP; - } - - pos += NS_RRFIXEDSZ; - - if (pos + rrlen > buf + len) - { - return ARES_EBADRESP; - } - - switch (rrtype) - { - case RES_A: - r = new LLARecord(rrname, rrttl); - break; - case RES_NS: - r = new LLNsRecord(rrname, rrttl); - break; - case RES_CNAME: - r = new LLCnameRecord(rrname, rrttl); - break; - case RES_PTR: - r = new LLPtrRecord(rrname, rrttl); - break; - case RES_AAAA: - r = new LLAaaaRecord(rrname, rrttl); - break; - case RES_SRV: - r = new LLSrvRecord(rrname, rrttl); - break; - default: - LL_INFOS() << "LLQueryResponder::parseRR got unknown RR type " << rrtype - << LL_ENDL; - return ARES_EBADRESP; - } - - ret = r->parse(buf, len, pos, rrlen); - - if (ret == ARES_SUCCESS) - { - pos += rrlen; - } else { - r = NULL; - } - - return ret; -} - -int LLQueryResponder::parseSection(const char *buf, size_t len, - size_t count, const char *&pos, - dns_rrs_t &rrs) -{ - int ret = ARES_SUCCESS; - - for (size_t i = 0; i < count; i++) - { - LLPointer<LLDnsRecord> r; - ret = parseRR(buf, len, pos, r); - if (ret != ARES_SUCCESS) - { - break; - } - rrs.push_back(r); - } - - return ret; -} - -void LLQueryResponder::queryResult(const char *buf, size_t len) -{ - const char *pos = buf; - int qdcount = DNS_HEADER_QDCOUNT(pos); - int ancount = DNS_HEADER_ANCOUNT(pos); - int nscount = DNS_HEADER_NSCOUNT(pos); - int arcount = DNS_HEADER_ARCOUNT(pos); - int ret; - - if (qdcount == 0 || ancount + nscount + arcount == 0) - { - ret = ARES_ENODATA; - goto bail; - } - - pos += NS_HFIXEDSZ; - - for (int i = 0; i < qdcount; i++) - { - std::string ignore; - size_t enclen; - - ret = LLAres::expandName(pos, buf, len, i == 0 ? mQuery : ignore, - enclen); - if (ret != ARES_SUCCESS) - { - goto bail; - } - - pos += enclen; - - if (i == 0) - { - int t = DNS_QUESTION_TYPE(pos); - switch (t) - { - case RES_A: - case RES_NS: - case RES_CNAME: - case RES_PTR: - case RES_AAAA: - case RES_SRV: - mType = (LLResType) t; - break; - default: - LL_INFOS() << "Cannot grok query type " << t << LL_ENDL; - ret = ARES_EBADQUERY; - goto bail; - } - } - - pos += NS_QFIXEDSZ; - if (pos > buf + len) - { - ret = ARES_EBADRESP; - goto bail; - } - } - - ret = parseSection(buf, len, ancount, pos, mAnswers); - if (ret != ARES_SUCCESS) - { - goto bail; - } - - ret = parseSection(buf, len, nscount, pos, mAuthorities); - if (ret != ARES_SUCCESS) - { - goto bail; - } - - ret = parseSection(buf, len, arcount, pos, mAdditional); - -bail: - mResult = ret; - if (mResult == ARES_SUCCESS) - { - querySuccess(); - } else { - queryError(mResult); - } -} - -void LLQueryResponder::querySuccess() -{ - LL_INFOS() << "LLQueryResponder::queryResult not implemented" << LL_ENDL; -} - -void LLAres::SrvResponder::querySuccess() -{ - if (mType == RES_SRV) - { - srvResult(mAnswers); - } else { - srvError(ARES_EBADRESP); - } -} - -void LLAres::SrvResponder::queryError(int code) -{ - srvError(code); -} - -void LLAres::SrvResponder::srvResult(const dns_rrs_t &ents) -{ - LL_INFOS() << "LLAres::SrvResponder::srvResult not implemented" << LL_ENDL; - - for (size_t i = 0; i < ents.size(); i++) - { - const LLSrvRecord *s = (const LLSrvRecord *) ents[i].get(); - - LL_INFOS() << "[" << i << "] " << s->host() << ":" << s->port() - << " priority " << s->priority() - << " weight " << s->weight() - << LL_ENDL; - } -} - -void LLAres::SrvResponder::srvError(int code) -{ - LL_INFOS() << "LLAres::SrvResponder::srvError " << code << ": " - << LLAres::strerror(code) << LL_ENDL; -} - -static void nameinfo_callback_1_5(void *arg, int status, int timeouts, - char *node, char *service) -{ - LLPointer<LLAres::NameInfoResponder> *resp = - (LLPointer<LLAres::NameInfoResponder> *) arg; - - if (status == ARES_SUCCESS) - { - (*resp)->nameInfoResult(node, service); - } else { - (*resp)->nameInfoError(status); - } - - delete resp; -} - -#if ARES_VERSION_MAJOR == 1 && ARES_VERSION_MINOR == 4 -static void nameinfo_callback(void *arg, int status, char *node, char *service) -{ - nameinfo_callback_1_5(arg, status, 0, node, service); -} -#else -# define nameinfo_callback nameinfo_callback_1_5 -#endif - -void LLAres::getNameInfo(const struct sockaddr &sa, socklen_t salen, int flags, - NameInfoResponder *resp) -{ - ares_getnameinfo(chan_, &sa, salen, flags, nameinfo_callback, - new LLPointer<NameInfoResponder>(resp)); -} - -static void search_callback_1_5(void *arg, int status, int timeouts, - unsigned char *abuf, int alen) -{ - LLPointer<LLAres::QueryResponder> *resp = - (LLPointer<LLAres::QueryResponder> *) arg; - - if (status == ARES_SUCCESS) - { - (*resp)->queryResult((const char *) abuf, alen); - } else { - (*resp)->queryError(status); - } - - delete resp; -} - -#if ARES_VERSION_MAJOR == 1 && ARES_VERSION_MINOR == 4 -static void search_callback(void *arg, int status, unsigned char *abuf, - int alen) -{ - search_callback_1_5(arg, status, 0, abuf, alen); -} -#else -# define search_callback search_callback_1_5 -#endif - -void LLAres::search(const std::string &query, LLResType type, - QueryResponder *resp) -{ - ares_search(chan_, query.c_str(), ns_c_in, type, search_callback, - new LLPointer<QueryResponder>(resp)); -} - -bool LLAres::process(U64 timeout) -{ - if (!gAPRPoolp) - { - ll_init_apr(); - } - - ares_socket_t socks[ARES_GETSOCK_MAXNUM]; - apr_pollfd_t aprFds[ARES_GETSOCK_MAXNUM]; - apr_int32_t nsds = 0; - int nactive = 0; - int bitmask; - - bitmask = ares_getsock(chan_, socks, ARES_GETSOCK_MAXNUM); - - if (bitmask == 0) - { - return nsds > 0; - } - - apr_status_t status; - LLAPRPool pool; - status = pool.getStatus() ; - ll_apr_assert_status(status); - - for (int i = 0; i < ARES_GETSOCK_MAXNUM; i++) - { - if (ARES_GETSOCK_READABLE(bitmask, i)) - { - aprFds[nactive].reqevents = APR_POLLIN | APR_POLLERR; - } - else if (ARES_GETSOCK_WRITABLE(bitmask, i)) - { - aprFds[nactive].reqevents = APR_POLLOUT | APR_POLLERR; - } else { - continue; - } - - apr_socket_t *aprSock = NULL; - - status = apr_os_sock_put(&aprSock, (apr_os_sock_t *) &socks[i], pool.getAPRPool()); - if (status != APR_SUCCESS) - { - ll_apr_warn_status(status); - return nsds > 0; - } - - aprFds[nactive].desc.s = aprSock; - aprFds[nactive].desc_type = APR_POLL_SOCKET; - aprFds[nactive].p = pool.getAPRPool(); - aprFds[nactive].rtnevents = 0; - aprFds[nactive].client_data = &socks[i]; - - nactive++; - } - - if (nactive > 0) - { - status = apr_poll(aprFds, nactive, &nsds, timeout); - - if (status != APR_SUCCESS && status != APR_TIMEUP) - { - ll_apr_warn_status(status); - } - - for (int i = 0; i < nactive; i++) - { - int evts = aprFds[i].rtnevents; - int ifd = (evts & (APR_POLLIN | APR_POLLERR)) - ? *((int *) aprFds[i].client_data) : ARES_SOCKET_BAD; - int ofd = (evts & (APR_POLLOUT | APR_POLLERR)) - ? *((int *) aprFds[i].client_data) : ARES_SOCKET_BAD; - - ares_process_fd(chan_, ifd, ofd); - } - } - - return nsds > 0; -} - -bool LLAres::processAll() -{ - bool anyProcessed = false, ret; - - do { - timeval tv; - - ret = ares_timeout(chan_, NULL, &tv) != NULL; - - if (ret) - { - ret = process(tv.tv_sec * 1000000LL + tv.tv_usec); - anyProcessed |= ret; - } - } while (ret); - - return anyProcessed; -} - -int LLAres::expandName(const char *encoded, const char *abuf, size_t alen, - std::string &s, size_t &enclen) -{ - char *t; - int ret; - long e; - - ret = ares_expand_name((const unsigned char *) encoded, - (const unsigned char *) abuf, alen, &t, &e); - if (ret == ARES_SUCCESS) - { - s.assign(t); - enclen = e; - ares_free_string(t); - } - return ret; -} - -const char *LLAres::strerror(int code) -{ - return ares_strerror(code); -} - -LLAres *gAres; - -LLAres *ll_init_ares() -{ - if (gAres == NULL) - { - gAres = new LLAres(); - } - return gAres; -} - -void ll_cleanup_ares() -{ - if (gAres != NULL) - { - delete gAres; - gAres = NULL; - } -} - -LLDnsRecord::LLDnsRecord(LLResType type, const std::string &name, - unsigned ttl) - : LLRefCount(), - mType(type), - mName(name), - mTTL(ttl) -{ -} - -LLHostRecord::LLHostRecord(LLResType type, const std::string &name, - unsigned ttl) - : LLDnsRecord(type, name, ttl) -{ -} - -int LLHostRecord::parse(const char *buf, size_t len, const char *pos, - size_t rrlen) -{ - int ret; - - ret = LLAres::expandName(pos, buf, len, mHost); - if (ret != ARES_SUCCESS) - { - goto bail; - } - - ret = ARES_SUCCESS; - -bail: - return ret; -} - -LLCnameRecord::LLCnameRecord(const std::string &name, unsigned ttl) - : LLHostRecord(RES_CNAME, name, ttl) -{ -} - -LLPtrRecord::LLPtrRecord(const std::string &name, unsigned ttl) - : LLHostRecord(RES_PTR, name, ttl) -{ -} - -LLAddrRecord::LLAddrRecord(LLResType type, const std::string &name, - unsigned ttl) - : LLDnsRecord(type, name, ttl), - - mSize(0) -{ -} - -LLARecord::LLARecord(const std::string &name, unsigned ttl) - : LLAddrRecord(RES_A, name, ttl) -{ -} - -int LLARecord::parse(const char *buf, size_t len, const char *pos, - size_t rrlen) -{ - int ret; - - if (rrlen != sizeof(mSA.sin.sin_addr.s_addr)) - { - ret = ARES_EBADRESP; - goto bail; - } - - memset(&mSA, 0, sizeof(mSA)); - memcpy(&mSA.sin.sin_addr.s_addr, pos, rrlen); - mSA.sin.sin_family = AF_INET6; - mSize = sizeof(mSA.sin); - - ret = ARES_SUCCESS; - -bail: - return ret; -} - -LLAaaaRecord::LLAaaaRecord(const std::string &name, unsigned ttl) - : LLAddrRecord(RES_AAAA, name, ttl) -{ -} - -int LLAaaaRecord::parse(const char *buf, size_t len, const char *pos, - size_t rrlen) -{ - int ret; - - if (rrlen != sizeof(mSA.sin6.sin6_addr)) - { - ret = ARES_EBADRESP; - goto bail; - } - - memset(&mSA, 0, sizeof(mSA)); - memcpy(&mSA.sin6.sin6_addr.s6_addr, pos, rrlen); - mSA.sin6.sin6_family = AF_INET6; - mSize = sizeof(mSA.sin6); - - ret = ARES_SUCCESS; - -bail: - return ret; -} - -LLSrvRecord::LLSrvRecord(const std::string &name, unsigned ttl) - : LLHostRecord(RES_SRV, name, ttl), - - mPriority(0), - mWeight(0), - mPort(0) -{ -} - -int LLSrvRecord::parse(const char *buf, size_t len, const char *pos, - size_t rrlen) -{ - int ret; - - if (rrlen < 6) - { - ret = ARES_EBADRESP; - goto bail; - } - - memcpy(&mPriority, pos, 2); - memcpy(&mWeight, pos + 2, 2); - memcpy(&mPort, pos + 4, 2); - - mPriority = ntohs(mPriority); - mWeight = ntohs(mWeight); - mPort = ntohs(mPort); - - ret = LLHostRecord::parse(buf, len, pos + 6, rrlen - 6); - -bail: - return ret; -} - -LLNsRecord::LLNsRecord(const std::string &name, unsigned ttl) - : LLHostRecord(RES_NS, name, ttl) -{ -} - -void LLAres::UriRewriteResponder::queryError(int code) -{ - std::vector<std::string> uris; - uris.push_back(mUri.asString()); - rewriteResult(uris); -} - -void LLAres::UriRewriteResponder::querySuccess() -{ - std::vector<std::string> uris; - - if (mType != RES_SRV) - { - goto bail; - } - - for (size_t i = 0; i < mAnswers.size(); i++) - { - const LLSrvRecord *r = (const LLSrvRecord *) mAnswers[i].get(); - - if (r->type() == RES_SRV) - { - // Check the domain in the response to ensure that it's - // the same as the domain in the request, so that bad guys - // can't forge responses that point to their own login - // servers with their own certificates. - - // Hard-coding the domain to check here is a bit of a - // hack. Hoist it to an outer caller if anyone ever needs - // this functionality on other domains. - - static const std::string domain(".lindenlab.com"); - const std::string &host = r->host(); - - std::string::size_type s = host.find(domain) + domain.length(); - - if (s != host.length() && s != host.length() - 1) - { - continue; - } - - LLURI uri(mUri.scheme(), - mUri.userName(), - mUri.password(), - r->host(), - mUri.defaultPort() ? r->port() : mUri.hostPort(), - mUri.escapedPath(), - mUri.escapedQuery()); - uris.push_back(uri.asString()); - } - } - - if (!uris.empty()) - { - goto done; - } - -bail: - uris.push_back(mUri.asString()); - -done: - rewriteResult(uris); -} - -void LLAres::UriRewriteResponder::rewriteResult( - const std::vector<std::string> &uris) -{ - LL_INFOS() << "LLAres::UriRewriteResponder::rewriteResult not implemented" - << LL_ENDL; - - for (size_t i = 0; i < uris.size(); i++) - { - LL_INFOS() << "[" << i << "] " << uris[i] << LL_ENDL; - } -} diff --git a/indra/llmessage/llares.h b/indra/llmessage/llares.h deleted file mode 100644 index c727363b60..0000000000 --- a/indra/llmessage/llares.h +++ /dev/null @@ -1,583 +0,0 @@ -/** - * @file llares.h - * @author Bryan O'Sullivan - * @date 2007-08-15 - * @brief Wrapper for asynchronous DNS lookups. - * - * $LicenseInfo:firstyear=2007&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$ - */ - -#ifndef LL_LLARES_H -#define LL_LLARES_H - -#ifdef LL_WINDOWS -// ares.h is broken on windows in that it depends on types defined in ws2tcpip.h -// we need to include them first to work around it, but the headers issue warnings -# pragma warning(push) -# pragma warning(disable:4996) -# include <winsock2.h> -# include <ws2tcpip.h> -# pragma warning(pop) -#endif - -#ifdef LL_USESYSTEMLIBS -# include <ares.h> -#else -# include <ares/ares.h> -#endif - -#include "llpointer.h" -#include "llrefcount.h" -#include "lluri.h" - -#include <boost/shared_ptr.hpp> - -class LLQueryResponder; -class LLAresListener; - -/** - * @brief Supported DNS RR types. - */ -enum LLResType -{ - RES_INVALID = 0, /**< Cookie. */ - RES_A = 1, /**< "A" record. IPv4 address. */ - RES_NS = 2, /**< "NS" record. Authoritative server. */ - RES_CNAME = 5, /**< "CNAME" record. Canonical name. */ - RES_PTR = 12, /**< "PTR" record. Domain name pointer. */ - RES_AAAA = 28, /**< "AAAA" record. IPv6 Address. */ - RES_SRV = 33, /**< "SRV" record. Server Selection. */ - RES_MAX = 65536 /**< Sentinel; RR types are 16 bits wide. */ -}; - -/** - * @class LLDnsRecord - * @brief Base class for all DNS RR types. - */ -class LLDnsRecord : public LLRefCount -{ -protected: - friend class LLQueryResponder; - - LLResType mType; - std::string mName; - unsigned mTTL; - - virtual int parse(const char *buf, size_t len, const char *pos, - size_t rrlen) = 0; - - LLDnsRecord(LLResType type, const std::string &name, unsigned ttl); - -public: - /** - * @brief Record name. - */ - const std::string &name() const { return mName; } - - /** - * @brief Time-to-live value, in seconds. - */ - unsigned ttl() const { return mTTL; } - - /** - * @brief RR type. - */ - LLResType type() const { return mType; } -}; - -/** - * @class LLAddrRecord - * @brief Base class for address-related RRs. - */ -class LLAddrRecord : public LLDnsRecord -{ -protected: - friend class LLQueryResponder; - - LLAddrRecord(LLResType type, const std::string &name, unsigned ttl); - - union - { - sockaddr sa; - sockaddr_in sin; - sockaddr_in6 sin6; - } mSA; - - socklen_t mSize; - -public: - /** - * @brief Generic socket address. - */ - const sockaddr &addr() const { return mSA.sa; } - - /** - * @brief Size of the socket structure. - */ - socklen_t size() const { return mSize; } -}; - -/** - * @class LLARecord - * @brief A RR, for IPv4 addresses. - */ -class LLARecord : public LLAddrRecord -{ -protected: - friend class LLQueryResponder; - - LLARecord(const std::string &name, unsigned ttl); - - int parse(const char *buf, size_t len, const char *pos, size_t rrlen); - -public: - /** - * @brief Socket address. - */ - const sockaddr_in &addr_in() const { return mSA.sin; } -}; - -/** - * @class LLAaaaRecord - * @brief AAAA RR, for IPv6 addresses. - */ -class LLAaaaRecord : public LLAddrRecord -{ -protected: - friend class LLQueryResponder; - - LLAaaaRecord(const std::string &name, unsigned ttl); - - int parse(const char *buf, size_t len, const char *pos, size_t rrlen); - -public: - /** - * @brief Socket address. - */ - const sockaddr_in6 &addr_in6() const { return mSA.sin6; } -}; - -/** - * @class LLHostRecord - * @brief Base class for host-related RRs. - */ -class LLHostRecord : public LLDnsRecord -{ -protected: - LLHostRecord(LLResType type, const std::string &name, unsigned ttl); - - int parse(const char *buf, size_t len, const char *pos, size_t rrlen); - - std::string mHost; - -public: - /** - * @brief Host name. - */ - const std::string &host() const { return mHost; } -}; - -/** - * @class LLCnameRecord - * @brief CNAME RR. - */ -class LLCnameRecord : public LLHostRecord -{ -protected: - friend class LLQueryResponder; - - LLCnameRecord(const std::string &name, unsigned ttl); -}; - -/** - * @class LLPtrRecord - * @brief PTR RR. - */ -class LLPtrRecord : public LLHostRecord -{ -protected: - friend class LLQueryResponder; - - LLPtrRecord(const std::string &name, unsigned ttl); -}; - -/** - * @class LLSrvRecord - * @brief SRV RR. - */ -class LLSrvRecord : public LLHostRecord -{ -protected: - U16 mPriority; - U16 mWeight; - U16 mPort; - - int parse(const char *buf, size_t len, const char *pos, size_t rrlen); - -public: - LLSrvRecord(const std::string &name, unsigned ttl); - - /** - * @brief Service priority. - */ - U16 priority() const { return mPriority; } - - /** - * @brief Service weight. - */ - U16 weight() const { return mWeight; } - - /** - * @brief Port number of service. - */ - U16 port() const { return mPort; } - - /** - * @brief Functor for sorting SRV records by priority. - */ - struct ComparePriorityLowest - { - bool operator()(const LLSrvRecord& lhs, const LLSrvRecord& rhs) - { - return lhs.mPriority < rhs.mPriority; - } - }; -}; - -/** - * @class LLNsRecord - * @brief NS RR. - */ -class LLNsRecord : public LLHostRecord -{ -public: - LLNsRecord(const std::string &name, unsigned ttl); -}; - -class LLQueryResponder; - -/** - * @class LLAres - * @brief Asynchronous address resolver. - */ -class LLAres -{ -public: - /** - * @class HostResponder - * @brief Base class for responding to hostname lookups. - * @see LLAres::getHostByName - */ - class HostResponder : public LLRefCount - { - public: - virtual ~HostResponder(); - - virtual void hostResult(const hostent *ent); - virtual void hostError(int code); - }; - - /** - * @class NameInfoResponder - * @brief Base class for responding to address lookups. - * @see LLAres::getNameInfo - */ - class NameInfoResponder : public LLRefCount - { - public: - virtual ~NameInfoResponder(); - - virtual void nameInfoResult(const char *node, const char *service); - virtual void nameInfoError(int code); - }; - - /** - * @class QueryResponder - * @brief Base class for responding to custom searches. - * @see LLAres::search - */ - class QueryResponder : public LLRefCount - { - public: - virtual ~QueryResponder(); - - virtual void queryResult(const char *buf, size_t len); - virtual void queryError(int code); - }; - - class SrvResponder; - class UriRewriteResponder; - - LLAres(); - - ~LLAres(); - - /** - * Cancel all outstanding requests. The error methods of the - * corresponding responders will be called, with ARES_ETIMEOUT. - */ - void cancel(); - - /** - * Look up the address of a host. - * - * @param name name of host to look up - * @param resp responder to call with result - * @param family AF_INET for IPv4 addresses, AF_INET6 for IPv6 - */ - void getHostByName(const std::string &name, HostResponder *resp, - int family = AF_INET) { - getHostByName(name.c_str(), resp, family); - } - - /** - * Look up the address of a host. - * - * @param name name of host to look up - * @param resp responder to call with result - * @param family AF_INET for IPv4 addresses, AF_INET6 for IPv6 - */ - void getHostByName(const char *name, HostResponder *resp, - int family = PF_INET); - - /** - * Look up the name associated with a socket address. - * - * @param sa socket address to look up - * @param salen size of socket address - * @param flags flags to use - * @param resp responder to call with result - */ - void getNameInfo(const struct sockaddr &sa, socklen_t salen, int flags, - NameInfoResponder *resp); - - /** - * Look up SRV (service location) records for a service name. - * - * @param name service name (e.g. "_https._tcp.login.agni.lindenlab.com") - * @param resp responder to call with result - */ - void getSrvRecords(const std::string &name, SrvResponder *resp); - - /** - * Rewrite a URI, using SRV (service location) records for its - * protocol if available. If no SRV records are published, the - * existing URI is handed to the responder. - * - * @param uri URI to rewrite - * @param resp responder to call with result - */ - void rewriteURI(const std::string &uri, - UriRewriteResponder *resp); - - /** - * Start a custom search. - * - * @param query query to make - * @param type type of query to perform - * @param resp responder to call with result - */ - void search(const std::string &query, LLResType type, - QueryResponder *resp); - - /** - * Process any outstanding queries. This method takes an optional - * timeout parameter (specified in microseconds). If provided, it - * will block the calling thread for that length of time to await - * possible responses. A timeout of zero will return immediately - * if there are no responses or timeouts to process. - * - * @param timeoutUsecs number of microseconds to block before timing out - * @return whether any responses were processed - */ - bool process(U64 timeoutUsecs = 0); - - /** - * Process all outstanding queries, blocking the calling thread - * until all have either been responded to or timed out. - * - * @return whether any responses were processed - */ - bool processAll(); - - /** - * Expand a DNS-encoded compressed string into a normal string. - * - * @param encoded the encoded name (null-terminated) - * @param abuf the response buffer in which the string is embedded - * @param alen the length of the response buffer - * @param s the string into which to place the result - * @return ARES_SUCCESS on success, otherwise an error indicator - */ - static int expandName(const char *encoded, const char *abuf, size_t alen, - std::string &s) { - size_t ignore; - return expandName(encoded, abuf, alen, s, ignore); - } - - static int expandName(const char *encoded, const char *abuf, size_t alen, - std::string &s, size_t &enclen); - - /** - * Return a string describing an error code. - */ - static const char *strerror(int code); - - bool isInitialized(void) { return mInitSuccess; } - -protected: - ares_channel chan_; - bool mInitSuccess; - // boost::scoped_ptr would actually fit the requirement better, but it - // can't handle incomplete types as boost::shared_ptr can. - boost::shared_ptr<LLAresListener> mListener; -}; - -/** - * An ordered collection of DNS resource records. - */ -typedef std::vector<LLPointer<LLDnsRecord> > dns_rrs_t; - -/** - * @class LLQueryResponder - * @brief Base class for friendly handling of DNS query responses. - * - * This class parses a DNS response and represents it in a friendly - * manner. - * - * @see LLDnsRecord - * @see LLARecord - * @see LLNsRecord - * @see LLCnameRecord - * @see LLPtrRecord - * @see LLAaaaRecord - * @see LLSrvRecord - */ -class LLQueryResponder : public LLAres::QueryResponder -{ -protected: - int mResult; - std::string mQuery; - LLResType mType; - - dns_rrs_t mAnswers; - dns_rrs_t mAuthorities; - dns_rrs_t mAdditional; - - /** - * Parse a single RR. - */ - int parseRR(const char *buf, size_t len, const char *&pos, - LLPointer<LLDnsRecord> &r); - /** - * Parse one section of a response. - */ - int parseSection(const char *buf, size_t len, - size_t count, const char *& pos, dns_rrs_t &rrs); - - void queryResult(const char *buf, size_t len); - virtual void querySuccess(); - -public: - LLQueryResponder(); - - /** - * Indicate whether the response could be parsed successfully. - */ - bool valid() const { return mResult == ARES_SUCCESS; } - - /** - * The more detailed result of parsing the response. - */ - int result() const { return mResult; } - - /** - * Return the query embedded in the response. - */ - const std::string &query() const { return mQuery; } - - /** - * Return the contents of the "answers" section of the response. - */ - const dns_rrs_t &answers() const { return mAnswers; } - - /** - * Return the contents of the "authorities" section of the - * response. - */ - const dns_rrs_t &authorities() const { return mAuthorities; } - - /** - * Return the contents of the "additional records" section of the - * response. - */ - const dns_rrs_t &additional() const { return mAdditional; } -}; - -/** - * @class LLAres::SrvResponder - * @brief Class for handling SRV query responses. - */ -class LLAres::SrvResponder : public LLQueryResponder -{ -public: - friend void LLAres::getSrvRecords(const std::string &name, - SrvResponder *resp); - void querySuccess(); - void queryError(int code); - - virtual void srvResult(const dns_rrs_t &ents); - virtual void srvError(int code); -}; - -/** - * @class LLAres::UriRewriteResponder - * @brief Class for handling URI rewrites based on SRV records. - */ -class LLAres::UriRewriteResponder : public LLQueryResponder -{ -protected: - LLURI mUri; - -public: - friend void LLAres::rewriteURI(const std::string &uri, - UriRewriteResponder *resp); - void querySuccess(); - void queryError(int code); - - virtual void rewriteResult(const std::vector<std::string> &uris); -}; - -/** - * Singleton responder. - */ -extern LLAres *gAres; - -/** - * Set up the singleton responder. It's safe to call this more than - * once from within a single thread, but this function is not - * thread safe. - */ -extern LLAres *ll_init_ares(); -extern void ll_cleanup_ares(); - -#endif // LL_LLARES_H diff --git a/indra/llmessage/llareslistener.cpp b/indra/llmessage/llareslistener.cpp deleted file mode 100644 index 3d65906b98..0000000000 --- a/indra/llmessage/llareslistener.cpp +++ /dev/null @@ -1,104 +0,0 @@ -/** - * @file llareslistener.cpp - * @author Nat Goodspeed - * @date 2009-03-18 - * @brief Implementation for llareslistener. - * - * $LicenseInfo:firstyear=2009&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$ - */ - -// Precompiled header -#include "linden_common.h" -// associated header -#include "llareslistener.h" -// STL headers -// std headers -// external library headers -// other Linden headers -#include "llares.h" -#include "llerror.h" -#include "llevents.h" -#include "llsdutil.h" - -LLAresListener::LLAresListener(LLAres* llares): - LLEventAPI("LLAres", - "LLAres listener to request DNS operations"), - mAres(llares) -{ - // add() every method we want to be able to invoke via this event API. - // Optional last parameter validates expected LLSD request structure. - add("rewriteURI", - "Given [\"uri\"], return on [\"reply\"] an array of alternative URIs.\n" - "On failure, returns an array containing only the original URI, so\n" - "failure case can be processed like success case.", - &LLAresListener::rewriteURI, - LLSD().with("uri", LLSD()).with("reply", LLSD())); -} - -/// This UriRewriteResponder subclass packages returned URIs as an LLSD -/// array to send back to the requester. -class UriRewriteResponder: public LLAres::UriRewriteResponder -{ -public: - /** - * Specify the request, containing the event pump name on which to send - * the reply. - */ - UriRewriteResponder(const LLSD& request): - mReqID(request), - mPumpName(request["reply"]) - {} - - /// Called by base class with results. This is called in both the - /// success and error cases. On error, the calling logic passes the - /// original URI. - virtual void rewriteResult(const std::vector<std::string>& uris) - { - LLSD result; - for (std::vector<std::string>::const_iterator ui(uris.begin()), uend(uris.end()); - ui != uend; ++ui) - { - result.append(*ui); - } - // This call knows enough to avoid trying to insert a map key into an - // LLSD array. It's there so that if, for any reason, we ever decide - // to change the response from array to map, it will Just Start Working. - mReqID.stamp(result); - LLEventPumps::instance().obtain(mPumpName).post(result); - } - -private: - LLReqID mReqID; - const std::string mPumpName; -}; - -void LLAresListener::rewriteURI(const LLSD& data) -{ - if (mAres) - { - mAres->rewriteURI(data["uri"], new UriRewriteResponder(data)); - } - else - { - LL_INFOS() << "LLAresListener::rewriteURI requested without Ares present. Ignoring: " << data << LL_ENDL; - } -} diff --git a/indra/llmessage/llareslistener.h b/indra/llmessage/llareslistener.h deleted file mode 100644 index 780dcdd9c5..0000000000 --- a/indra/llmessage/llareslistener.h +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file llareslistener.h - * @author Nat Goodspeed - * @date 2009-03-18 - * @brief LLEventPump API for LLAres. This header doesn't actually define the - * API; the API is defined by the pump name on which this class - * listens, and by the expected content of LLSD it receives. - * - * $LicenseInfo:firstyear=2009&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$ - */ - -#if ! defined(LL_LLARESLISTENER_H) -#define LL_LLARESLISTENER_H - -#include "lleventapi.h" - -class LLAres; -class LLSD; - -/// Listen on an LLEventPump with specified name for LLAres request events. -class LLAresListener: public LLEventAPI -{ -public: - /// Bind the LLAres instance to use (e.g. gAres) - LLAresListener(LLAres* llares); - -private: - /// command["op"] == "rewriteURI" - void rewriteURI(const LLSD& data); - - LLAres* mAres; -}; - -#endif /* ! defined(LL_LLARESLISTENER_H) */ diff --git a/indra/llmessage/llassetstorage.cpp b/indra/llmessage/llassetstorage.cpp index cab3073eca..bcf4e52b8f 100644 --- a/indra/llmessage/llassetstorage.cpp +++ b/indra/llmessage/llassetstorage.cpp @@ -306,7 +306,7 @@ LLAssetStorage::LLAssetStorage(LLMessageSystem *msg, LLXferManager *xfer, LLVFS LLAssetStorage::LLAssetStorage(LLMessageSystem *msg, LLXferManager *xfer, LLVFS *vfs, LLVFS *static_vfs) { - _init(msg, xfer, vfs, static_vfs, LLHost::invalid); + _init(msg, xfer, vfs, static_vfs, LLHost()); } @@ -1629,3 +1629,4 @@ void LLAssetStorage::markAssetToxic( const LLUUID& uuid ) } } + diff --git a/indra/llmessage/llassetstorage.h b/indra/llmessage/llassetstorage.h index 8a4d41565f..4be677a4b0 100644 --- a/indra/llmessage/llassetstorage.h +++ b/indra/llmessage/llassetstorage.h @@ -27,7 +27,6 @@ #ifndef LL_LLASSETSTORAGE_H #define LL_LLASSETSTORAGE_H - #include <string> #include "lluuid.h" diff --git a/indra/llmessage/llavatarnamecache.cpp b/indra/llmessage/llavatarnamecache.cpp index 360d239e61..1ca5f58ae2 100644 --- a/indra/llmessage/llavatarnamecache.cpp +++ b/indra/llmessage/llavatarnamecache.cpp @@ -30,12 +30,20 @@ #include "llcachename.h" // we wrap this system #include "llframetimer.h" -#include "llhttpclient.h" #include "llsd.h" #include "llsdserialize.h" - +#include "httpresponse.h" +#include "llhttpsdhandler.h" #include <boost/tokenizer.hpp> +#include "httpcommon.h" +#include "httprequest.h" +#include "httpheaders.h" +#include "httpoptions.h" +#include "llcoros.h" +#include "lleventcoro.h" +#include "llcorehttputil.h" + #include <map> #include <set> @@ -90,6 +98,12 @@ namespace LLAvatarNameCache // Time-to-live for a temp cache entry. const F64 TEMP_CACHE_ENTRY_LIFETIME = 60.0; + LLCore::HttpRequest::ptr_t sHttpRequest; + LLCore::HttpHeaders::ptr_t sHttpHeaders; + LLCore::HttpOptions::ptr_t sHttpOptions; + LLCore::HttpRequest::policy_t sHttpPolicy; + LLCore::HttpRequest::priority_t sHttpPriority; + //----------------------------------------------------------------------- // Internal methods //----------------------------------------------------------------------- @@ -121,7 +135,12 @@ namespace LLAvatarNameCache // Erase expired names from cache void eraseUnrefreshed(); - bool expirationFromCacheControl(const LLSD& headers, F64 *expires); + bool expirationFromCacheControl(const LLSD& headers, F64 *expires); + + // This is a coroutine. + void requestAvatarNameCache_(std::string url, std::vector<LLUUID> agentIds); + + void handleAvNameCacheSuccess(const LLSD &data, const LLSD &httpResult); } /* Sample response: @@ -163,94 +182,117 @@ namespace LLAvatarNameCache </llsd> */ -class LLAvatarNameResponder : public LLHTTPClient::Responder +// Coroutine for sending and processing avatar name cache requests. +// Do not call directly. See documentation in lleventcoro.h and llcoro.h for +// further explanation. +void LLAvatarNameCache::requestAvatarNameCache_(std::string url, std::vector<LLUUID> agentIds) { - LOG_CLASS(LLAvatarNameResponder); -private: - // need to store agent ids that are part of this request in case of - // an error, so we can flag them as unavailable - std::vector<LLUUID> mAgentIDs; - -public: - LLAvatarNameResponder(const std::vector<LLUUID>& agent_ids) - : mAgentIDs(agent_ids) - { } - -protected: - /*virtual*/ void httpSuccess() - { - const LLSD& content = getContent(); - if (!content.isMap()) - { - failureResult(HTTP_INTERNAL_ERROR, "Malformed response contents", content); - return; - } - // Pull expiration out of headers if available - F64 expires = LLAvatarNameCache::nameExpirationFromHeaders(getResponseHeaders()); - F64 now = LLFrameTimer::getTotalSeconds(); + LL_DEBUGS("AvNameCache") << "Entering coroutine " << LLCoros::instance().getName() + << " with url '" << url << "', requesting " << agentIds.size() << " Agent Ids" << LL_ENDL; - const LLSD& agents = content["agents"]; - LLSD::array_const_iterator it = agents.beginArray(); - for ( ; it != agents.endArray(); ++it) - { - const LLSD& row = *it; - LLUUID agent_id = row["id"].asUUID(); + try + { + bool success = true; - LLAvatarName av_name; - av_name.fromLLSD(row); + LLCoreHttpUtil::HttpCoroutineAdapter httpAdapter("NameCache", LLAvatarNameCache::sHttpPolicy); + LLSD results = httpAdapter.getAndSuspend(sHttpRequest, url); + LLSD httpResults; - // Use expiration time from header - av_name.mExpires = expires; + LL_DEBUGS() << results << LL_ENDL; - LL_DEBUGS("AvNameCache") << "LLAvatarNameResponder::result for " << agent_id << LL_ENDL; - av_name.dump(); - - // cache it and fire signals - LLAvatarNameCache::processName(agent_id, av_name); - } + if (!results.isMap()) + { + LL_WARNS("AvNameCache") << " Invalid result returned from LLCoreHttpUtil::HttpCoroHandler." << LL_ENDL; + success = false; + } + else + { + httpResults = results["http_result"]; + success = httpResults["success"].asBoolean(); + if (!success) + { + LL_WARNS("AvNameCache") << "Error result from LLCoreHttpUtil::HttpCoroHandler. Code " + << httpResults["status"] << ": '" << httpResults["message"] << "'" << LL_ENDL; + } + } - // Same logic as error response case - const LLSD& unresolved_agents = content["bad_ids"]; - S32 num_unresolved = unresolved_agents.size(); - if (num_unresolved > 0) - { - LL_WARNS("AvNameCache") << "LLAvatarNameResponder::result " << num_unresolved << " unresolved ids; " - << "expires in " << expires - now << " seconds" - << LL_ENDL; - it = unresolved_agents.beginArray(); - for ( ; it != unresolved_agents.endArray(); ++it) - { - const LLUUID& agent_id = *it; + if (!success) + { // on any sort of failure add dummy records for any agent IDs + // in this request that we do not have cached already + std::vector<LLUUID>::const_iterator it = agentIds.begin(); + for ( ; it != agentIds.end(); ++it) + { + const LLUUID& agent_id = *it; + LLAvatarNameCache::handleAgentError(agent_id); + } + return; + } - LL_WARNS("AvNameCache") << "LLAvatarNameResponder::result " - << "failed id " << agent_id - << LL_ENDL; + LLAvatarNameCache::handleAvNameCacheSuccess(results, httpResults); - LLAvatarNameCache::handleAgentError(agent_id); - } - } - LL_DEBUGS("AvNameCache") << "LLAvatarNameResponder::result " - << LLAvatarNameCache::sCache.size() << " cached names" - << LL_ENDL; } + catch (std::exception e) + { + LL_WARNS() << "Caught exception '" << e.what() << "'" << LL_ENDL; + } + catch (...) + { + LL_WARNS() << "Caught unknown exception." << LL_ENDL; + } +} - /*virtual*/ void httpFailure() - { - // If there's an error, it might be caused by PeopleApi, - // or when loading textures on startup and using a very slow - // network, this query may time out. - // What we should do depends on whether or not we have a cached name - LL_WARNS("AvNameCache") << dumpResponse() << LL_ENDL; - - // Add dummy records for any agent IDs in this request that we do not have cached already - std::vector<LLUUID>::const_iterator it = mAgentIDs.begin(); - for ( ; it != mAgentIDs.end(); ++it) - { - const LLUUID& agent_id = *it; - LLAvatarNameCache::handleAgentError(agent_id); - } - } -}; +void LLAvatarNameCache::handleAvNameCacheSuccess(const LLSD &data, const LLSD &httpResult) +{ + + LLSD headers = httpResult["headers"]; + // Pull expiration out of headers if available + F64 expires = LLAvatarNameCache::nameExpirationFromHeaders(headers); + F64 now = LLFrameTimer::getTotalSeconds(); + + const LLSD& agents = data["agents"]; + LLSD::array_const_iterator it = agents.beginArray(); + for (; it != agents.endArray(); ++it) + { + const LLSD& row = *it; + LLUUID agent_id = row["id"].asUUID(); + + LLAvatarName av_name; + av_name.fromLLSD(row); + + // Use expiration time from header + av_name.mExpires = expires; + + LL_DEBUGS("AvNameCache") << "LLAvatarNameResponder::result for " << agent_id << LL_ENDL; + av_name.dump(); + + // cache it and fire signals + LLAvatarNameCache::processName(agent_id, av_name); + } + + // Same logic as error response case + const LLSD& unresolved_agents = data["bad_ids"]; + S32 num_unresolved = unresolved_agents.size(); + if (num_unresolved > 0) + { + LL_WARNS("AvNameCache") << "LLAvatarNameResponder::result " << num_unresolved << " unresolved ids; " + << "expires in " << expires - now << " seconds" + << LL_ENDL; + it = unresolved_agents.beginArray(); + for (; it != unresolved_agents.endArray(); ++it) + { + const LLUUID& agent_id = *it; + + LL_WARNS("AvNameCache") << "LLAvatarNameResponder::result " + << "failed id " << agent_id + << LL_ENDL; + + LLAvatarNameCache::handleAgentError(agent_id); + } + } + LL_DEBUGS("AvNameCache") << "LLAvatarNameResponder::result " + << LLAvatarNameCache::sCache.size() << " cached names" + << LL_ENDL; +} // Provide some fallback for agents that return errors void LLAvatarNameCache::handleAgentError(const LLUUID& agent_id) @@ -353,10 +395,15 @@ void LLAvatarNameCache::requestNamesViaCapability() } } - if (!url.empty()) - { - LL_DEBUGS("AvNameCache") << " getting " << ids << " ids" << LL_ENDL; - LLHTTPClient::get(url, new LLAvatarNameResponder(agent_ids)); + if (!url.empty()) + { + LL_DEBUGS("AvNameCache") << "requested " << ids << " ids" << LL_ENDL; + + std::string coroname = + LLCoros::instance().launch("LLAvatarNameCache::requestAvatarNameCache_", + boost::bind(&LLAvatarNameCache::requestAvatarNameCache_, url, agent_ids)); + LL_DEBUGS("AvNameCache") << coroname << " with url '" << url << "', agent_ids.size()=" << agent_ids.size() << LL_ENDL; + } } @@ -419,11 +466,20 @@ void LLAvatarNameCache::initClass(bool running, bool usePeopleAPI) { sRunning = running; sUsePeopleAPI = usePeopleAPI; + + sHttpRequest = LLCore::HttpRequest::ptr_t(new LLCore::HttpRequest()); + sHttpHeaders = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders()); + sHttpOptions = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()); + sHttpPolicy = LLCore::HttpRequest::DEFAULT_POLICY_ID; + sHttpPriority = 0; } void LLAvatarNameCache::cleanupClass() { - sCache.clear(); + sHttpRequest.reset(); + sHttpHeaders.reset(); + sHttpOptions.reset(); + sCache.clear(); } bool LLAvatarNameCache::importFile(std::istream& istr) @@ -698,6 +754,50 @@ void LLAvatarNameCache::insert(const LLUUID& agent_id, const LLAvatarName& av_na sCache[agent_id] = av_name; } +#if 0 +F64 LLAvatarNameCache::nameExpirationFromHeaders(LLCore::HttpHeaders *headers) +{ + F64 expires = 0.0; + if (expirationFromCacheControl(headers, &expires)) + { + return expires; + } + else + { + // With no expiration info, default to an hour + const F64 DEFAULT_EXPIRES = 60.0 * 60.0; + F64 now = LLFrameTimer::getTotalSeconds(); + return now + DEFAULT_EXPIRES; + } +} + +bool LLAvatarNameCache::expirationFromCacheControl(LLCore::HttpHeaders *headers, F64 *expires) +{ + bool fromCacheControl = false; + F64 now = LLFrameTimer::getTotalSeconds(); + + // Allow the header to override the default + const std::string *cache_control; + + cache_control = headers->find(HTTP_IN_HEADER_CACHE_CONTROL); + + if (cache_control && !cache_control->empty()) + { + S32 max_age = 0; + if (max_age_from_cache_control(*cache_control, &max_age)) + { + *expires = now + (F64)max_age; + fromCacheControl = true; + } + } + LL_DEBUGS("AvNameCache") + << ( fromCacheControl ? "expires based on cache control " : "default expiration " ) + << "in " << *expires - now << " seconds" + << LL_ENDL; + + return fromCacheControl; +} +#else F64 LLAvatarNameCache::nameExpirationFromHeaders(const LLSD& headers) { F64 expires = 0.0; @@ -742,7 +842,7 @@ bool LLAvatarNameCache::expirationFromCacheControl(const LLSD& headers, F64 *exp return fromCacheControl; } - +#endif void LLAvatarNameCache::addUseDisplayNamesCallback(const use_display_name_signal_t::slot_type& cb) { diff --git a/indra/llmessage/llavatarnamecache.h b/indra/llmessage/llavatarnamecache.h index 5a10053a69..bd2715e956 100644 --- a/indra/llmessage/llavatarnamecache.h +++ b/indra/llmessage/llavatarnamecache.h @@ -29,7 +29,6 @@ #define LLAVATARNAMECACHE_H #include "llavatarname.h" // for convenience - #include <boost/signals2.hpp> class LLSD; @@ -49,7 +48,7 @@ namespace LLAvatarNameCache bool importFile(std::istream& istr); void exportFile(std::ostream& ostr); - // On the viewer, usually a simulator capabilitity. + // On the viewer, usually a simulator capabilities. // If empty, name cache will fall back to using legacy name lookup system. void setNameLookupURL(const std::string& name_lookup_url); @@ -90,7 +89,7 @@ namespace LLAvatarNameCache // Compute name expiration time from HTTP Cache-Control header, // or return default value, in seconds from epoch. - F64 nameExpirationFromHeaders(const LLSD& headers); + F64 nameExpirationFromHeaders(const LLSD& headers); void addUseDisplayNamesCallback(const use_display_name_signal_t::slot_type& cb); } diff --git a/indra/llmessage/llcachename.cpp b/indra/llmessage/llcachename.cpp index daf3e0b4de..66bd85f4e6 100644 --- a/indra/llmessage/llcachename.cpp +++ b/indra/llmessage/llcachename.cpp @@ -259,7 +259,7 @@ LLCacheName::~LLCacheName() } LLCacheName::Impl::Impl(LLMessageSystem* msg) - : mMsg(msg), mUpstreamHost(LLHost::invalid) + : mMsg(msg), mUpstreamHost(LLHost()) { mMsg->setHandlerFuncFast( _PREHASH_UUIDNameRequest, handleUUIDNameRequest, (void**)this); diff --git a/indra/llmessage/llcoproceduremanager.cpp b/indra/llmessage/llcoproceduremanager.cpp new file mode 100644 index 0000000000..f0fe1ab01b --- /dev/null +++ b/indra/llmessage/llcoproceduremanager.cpp @@ -0,0 +1,405 @@ +/** +* @file LLCoprocedurePool.cpp +* @author Rider Linden +* @brief Singleton class for managing asset uploads to the sim. +* +* $LicenseInfo:firstyear=2015&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2015, 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 "llcoproceduremanager.h" +#include <boost/assign.hpp> + +//========================================================================= +// Map of pool sizes for known pools +// *TODO$: When C++11 this can be initialized here as follows: +// = {{"AIS", 25}, {"Upload", 1}} +static std::map<std::string, U32> DefaultPoolSizes = + boost::assign::map_list_of + (std::string("Upload"), 1) + (std::string("AIS"), 25); + +#define DEFAULT_POOL_SIZE 5 + +//========================================================================= +class LLCoprocedurePool: private boost::noncopyable +{ +public: + typedef LLCoprocedureManager::CoProcedure_t CoProcedure_t; + + LLCoprocedurePool(const std::string &name, size_t size); + virtual ~LLCoprocedurePool(); + + /// Places the coprocedure on the queue for processing. + /// + /// @param name Is used for debugging and should identify this coroutine. + /// @param proc Is a bound function to be executed + /// + /// @return This method returns a UUID that can be used later to cancel execution. + LLUUID enqueueCoprocedure(const std::string &name, CoProcedure_t proc); + + /// Cancel a coprocedure. If the coprocedure is already being actively executed + /// this method calls cancelSuspendedOperation() on the associated HttpAdapter + /// If it has not yet been dequeued it is simply removed from the queue. + bool cancelCoprocedure(const LLUUID &id); + + /// Requests a shutdown of the upload manager. Passing 'true' will perform + /// an immediate kill on the upload coroutine. + void shutdown(bool hardShutdown = false); + + /// Returns the number of coprocedures in the queue awaiting processing. + /// + inline size_t countPending() const + { + return mPendingCoprocs.size(); + } + + /// Returns the number of coprocedures actively being processed. + /// + inline size_t countActive() const + { + return mActiveCoprocs.size(); + } + + /// Returns the total number of coprocedures either queued or in active processing. + /// + inline size_t count() const + { + return countPending() + countActive(); + } + +private: + struct QueuedCoproc + { + typedef boost::shared_ptr<QueuedCoproc> ptr_t; + + QueuedCoproc(const std::string &name, const LLUUID &id, CoProcedure_t proc) : + mName(name), + mId(id), + mProc(proc) + {} + + std::string mName; + LLUUID mId; + CoProcedure_t mProc; + }; + + // we use a deque here rather than std::queue since we want to be able to + // iterate through the queue and potentially erase an entry from the middle. + typedef std::deque<QueuedCoproc::ptr_t> CoprocQueue_t; + typedef std::map<LLUUID, LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t> ActiveCoproc_t; + + std::string mPoolName; + size_t mPoolSize; + CoprocQueue_t mPendingCoprocs; + ActiveCoproc_t mActiveCoprocs; + bool mShutdown; + LLEventStream mWakeupTrigger; + + typedef std::map<std::string, LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t> CoroAdapterMap_t; + LLCore::HttpRequest::policy_t mHTTPPolicy; + + CoroAdapterMap_t mCoroMapping; + + void coprocedureInvokerCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter); + +}; + +//========================================================================= +LLCoprocedureManager::LLCoprocedureManager() +{ +} + +LLCoprocedureManager::~LLCoprocedureManager() +{ + +} + +LLCoprocedureManager::poolPtr_t LLCoprocedureManager::initializePool(const std::string &poolName) +{ + // Attempt to look up a pool size in the configuration. If found use that + std::string keyName = "PoolSize" + poolName; + int size = 0; + + if (poolName.empty()) + LL_ERRS("CoprocedureManager") << "Poolname must not be empty" << LL_ENDL; + + if (mPropertyQueryFn && !mPropertyQueryFn.empty()) + { + size = mPropertyQueryFn(keyName); + } + + if (size == 0) + { // if not found grab the know default... if there is no known + // default use a reasonable number like 5. + std::map<std::string, U32>::iterator it = DefaultPoolSizes.find(poolName); + if (it == DefaultPoolSizes.end()) + size = DEFAULT_POOL_SIZE; + else + size = (*it).second; + + if (mPropertyDefineFn && !mPropertyDefineFn.empty()) + mPropertyDefineFn(keyName, size, "Coroutine Pool size for " + poolName); + LL_WARNS() << "LLCoprocedureManager: No setting for \"" << keyName << "\" setting pool size to default of " << size << LL_ENDL; + } + + poolPtr_t pool(new LLCoprocedurePool(poolName, size)); + mPoolMap.insert(poolMap_t::value_type(poolName, pool)); + + if (!pool) + LL_ERRS("CoprocedureManager") << "Unable to create pool named \"" << poolName << "\" FATAL!" << LL_ENDL; + return pool; +} + +//------------------------------------------------------------------------- +LLUUID LLCoprocedureManager::enqueueCoprocedure(const std::string &pool, const std::string &name, CoProcedure_t proc) +{ + // Attempt to find the pool and enqueue the procedure. If the pool does + // not exist, create it. + poolPtr_t targetPool; + poolMap_t::iterator it = mPoolMap.find(pool); + + if (it == mPoolMap.end()) + { + targetPool = initializePool(pool); + } + else + { + targetPool = (*it).second; + } + + return targetPool->enqueueCoprocedure(name, proc); +} + +void LLCoprocedureManager::cancelCoprocedure(const LLUUID &id) +{ + for (poolMap_t::const_iterator it = mPoolMap.begin(); it != mPoolMap.end(); ++it) + { + if ((*it).second->cancelCoprocedure(id)) + return; + } + LL_INFOS() << "Coprocedure not found." << LL_ENDL; +} + +void LLCoprocedureManager::shutdown(bool hardShutdown) +{ + for (poolMap_t::const_iterator it = mPoolMap.begin(); it != mPoolMap.end(); ++it) + { + (*it).second->shutdown(hardShutdown); + } + mPoolMap.clear(); +} + +void LLCoprocedureManager::setPropertyMethods(SettingQuery_t queryfn, SettingUpdate_t updatefn) +{ + mPropertyQueryFn = queryfn; + mPropertyDefineFn = updatefn; +} + +//------------------------------------------------------------------------- +size_t LLCoprocedureManager::countPending() const +{ + size_t count = 0; + for (poolMap_t::const_iterator it = mPoolMap.begin(); it != mPoolMap.end(); ++it) + { + count += (*it).second->countPending(); + } + return count; +} + +size_t LLCoprocedureManager::countPending(const std::string &pool) const +{ + poolMap_t::const_iterator it = mPoolMap.find(pool); + + if (it == mPoolMap.end()) + return 0; + return (*it).second->countPending(); +} + +size_t LLCoprocedureManager::countActive() const +{ + size_t count = 0; + for (poolMap_t::const_iterator it = mPoolMap.begin(); it != mPoolMap.end(); ++it) + { + count += (*it).second->countActive(); + } + return count; +} + +size_t LLCoprocedureManager::countActive(const std::string &pool) const +{ + poolMap_t::const_iterator it = mPoolMap.find(pool); + + if (it == mPoolMap.end()) + return 0; + return (*it).second->countActive(); +} + +size_t LLCoprocedureManager::count() const +{ + size_t count = 0; + for (poolMap_t::const_iterator it = mPoolMap.begin(); it != mPoolMap.end(); ++it) + { + count += (*it).second->count(); + } + return count; +} + +size_t LLCoprocedureManager::count(const std::string &pool) const +{ + poolMap_t::const_iterator it = mPoolMap.find(pool); + + if (it == mPoolMap.end()) + return 0; + return (*it).second->count(); +} + +//========================================================================= +LLCoprocedurePool::LLCoprocedurePool(const std::string &poolName, size_t size): + mPoolName(poolName), + mPoolSize(size), + mPendingCoprocs(), + mShutdown(false), + mWakeupTrigger("CoprocedurePool" + poolName, true), + mCoroMapping(), + mHTTPPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID) +{ + for (size_t count = 0; count < mPoolSize; ++count) + { + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter( mPoolName + "Adapter", mHTTPPolicy)); + + std::string pooledCoro = LLCoros::instance().launch("LLCoprocedurePool("+mPoolName+")::coprocedureInvokerCoro", + boost::bind(&LLCoprocedurePool::coprocedureInvokerCoro, this, httpAdapter)); + + mCoroMapping.insert(CoroAdapterMap_t::value_type(pooledCoro, httpAdapter)); + } + + LL_INFOS() << "Created coprocedure pool named \"" << mPoolName << "\" with " << size << " items." << LL_ENDL; + + mWakeupTrigger.post(LLSD()); +} + +LLCoprocedurePool::~LLCoprocedurePool() +{ + shutdown(); +} + +//------------------------------------------------------------------------- +void LLCoprocedurePool::shutdown(bool hardShutdown) +{ + CoroAdapterMap_t::iterator it; + + for (it = mCoroMapping.begin(); it != mCoroMapping.end(); ++it) + { + if (hardShutdown) + { + LLCoros::instance().kill((*it).first); + } + if ((*it).second) + { + (*it).second->cancelSuspendedOperation(); + } + } + + mShutdown = true; + mCoroMapping.clear(); + mPendingCoprocs.clear(); +} + +//------------------------------------------------------------------------- +LLUUID LLCoprocedurePool::enqueueCoprocedure(const std::string &name, LLCoprocedurePool::CoProcedure_t proc) +{ + LLUUID id(LLUUID::generateNewID()); + + mPendingCoprocs.push_back(QueuedCoproc::ptr_t(new QueuedCoproc(name, id, proc))); + LL_INFOS() << "Coprocedure(" << name << ") enqueued with id=" << id.asString() << " in pool \"" << mPoolName << "\"" << LL_ENDL; + + mWakeupTrigger.post(LLSD()); + + return id; +} + +bool LLCoprocedurePool::cancelCoprocedure(const LLUUID &id) +{ + // first check the active coroutines. If there, remove it and return. + ActiveCoproc_t::iterator itActive = mActiveCoprocs.find(id); + if (itActive != mActiveCoprocs.end()) + { + LL_INFOS() << "Found and canceling active coprocedure with id=" << id.asString() << " in pool \"" << mPoolName << "\"" << LL_ENDL; + (*itActive).second->cancelSuspendedOperation(); + mActiveCoprocs.erase(itActive); + return true; + } + + for (CoprocQueue_t::iterator it = mPendingCoprocs.begin(); it != mPendingCoprocs.end(); ++it) + { + if ((*it)->mId == id) + { + LL_INFOS() << "Found and removing queued coroutine(" << (*it)->mName << ") with Id=" << id.asString() << " in pool \"" << mPoolName << "\"" << LL_ENDL; + mPendingCoprocs.erase(it); + return true; + } + } + + LL_INFOS() << "Coprocedure with Id=" << id.asString() << " was not found in pool \"" << mPoolName << "\"" << LL_ENDL; + return false; +} + +//------------------------------------------------------------------------- +void LLCoprocedurePool::coprocedureInvokerCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter) +{ + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + + while (!mShutdown) + { + llcoro::suspendUntilEventOn(mWakeupTrigger); + if (mShutdown) + break; + + while (!mPendingCoprocs.empty()) + { + QueuedCoproc::ptr_t coproc = mPendingCoprocs.front(); + mPendingCoprocs.pop_front(); + ActiveCoproc_t::iterator itActive = mActiveCoprocs.insert(ActiveCoproc_t::value_type(coproc->mId, httpAdapter)).first; + + LL_INFOS() << "Dequeued and invoking coprocedure(" << coproc->mName << ") with id=" << coproc->mId.asString() << " in pool \"" << mPoolName << "\"" << LL_ENDL; + + try + { + coproc->mProc(httpAdapter, coproc->mId); + } + catch (std::exception &e) + { + LL_WARNS() << "Coprocedure(" << coproc->mName << ") id=" << coproc->mId.asString() << + " threw an exception! Message=\"" << e.what() << "\"" << LL_ENDL; + } + catch (...) + { + LL_WARNS() << "A non std::exception was thrown from " << coproc->mName << " with id=" << coproc->mId << "." << " in pool \"" << mPoolName << "\"" << LL_ENDL; + } + + LL_INFOS() << "Finished coprocedure(" << coproc->mName << ")" << " in pool \"" << mPoolName << "\"" << LL_ENDL; + + mActiveCoprocs.erase(itActive); + } + } +} diff --git a/indra/llmessage/llcoproceduremanager.h b/indra/llmessage/llcoproceduremanager.h new file mode 100644 index 0000000000..497367b80c --- /dev/null +++ b/indra/llmessage/llcoproceduremanager.h @@ -0,0 +1,98 @@ +/** +* @file llcoproceduremanager.h +* @author Rider Linden +* @brief Singleton class for managing asset uploads to the sim. +* +* $LicenseInfo:firstyear=2015&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2015, 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$ +*/ + +#ifndef LL_COPROCEDURE_MANAGER_H +#define LL_COPROCEDURE_MANAGER_H + +#include "lleventcoro.h" +#include "llcoros.h" +#include "llcorehttputil.h" +#include "lluuid.h" + +class LLCoprocedurePool; + +class LLCoprocedureManager : public LLSingleton < LLCoprocedureManager > +{ + friend class LLSingleton < LLCoprocedureManager > ; + +public: + typedef boost::function<U32(const std::string &)> SettingQuery_t; + typedef boost::function<void(const std::string &, U32, const std::string &)> SettingUpdate_t; + + typedef boost::function<void(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &, const LLUUID &id)> CoProcedure_t; + + LLCoprocedureManager(); + virtual ~LLCoprocedureManager(); + + /// Places the coprocedure on the queue for processing. + /// + /// @param name Is used for debugging and should identify this coroutine. + /// @param proc Is a bound function to be executed + /// + /// @return This method returns a UUID that can be used later to cancel execution. + LLUUID enqueueCoprocedure(const std::string &pool, const std::string &name, CoProcedure_t proc); + + /// Cancel a coprocedure. If the coprocedure is already being actively executed + /// this method calls cancelYieldingOperation() on the associated HttpAdapter + /// If it has not yet been dequeued it is simply removed from the queue. + void cancelCoprocedure(const LLUUID &id); + + /// Requests a shutdown of the upload manager. Passing 'true' will perform + /// an immediate kill on the upload coroutine. + void shutdown(bool hardShutdown = false); + + void setPropertyMethods(SettingQuery_t queryfn, SettingUpdate_t updatefn); + + /// Returns the number of coprocedures in the queue awaiting processing. + /// + size_t countPending() const; + size_t countPending(const std::string &pool) const; + + /// Returns the number of coprocedures actively being processed. + /// + size_t countActive() const; + size_t countActive(const std::string &pool) const; + + /// Returns the total number of coprocedures either queued or in active processing. + /// + size_t count() const; + size_t count(const std::string &pool) const; + +private: + + typedef boost::shared_ptr<LLCoprocedurePool> poolPtr_t; + typedef std::map<std::string, poolPtr_t> poolMap_t; + + poolMap_t mPoolMap; + + poolPtr_t initializePool(const std::string &poolName); + + SettingQuery_t mPropertyQueryFn; + SettingUpdate_t mPropertyDefineFn; +}; + +#endif diff --git a/indra/llmessage/llcorehttputil.cpp b/indra/llmessage/llcorehttputil.cpp index ee80b0fd94..9a23ede81b 100644 --- a/indra/llmessage/llcorehttputil.cpp +++ b/indra/llmessage/llcorehttputil.cpp @@ -28,10 +28,18 @@ #include "linden_common.h" #include <sstream> - +#include <algorithm> +#include <iterator> #include "llcorehttputil.h" +#include "llhttpconstants.h" +#include "llsd.h" +#include "llsdjson.h" #include "llsdserialize.h" +#include "reader.h" // JSON +#include "writer.h" // JSON +#include "llvfile.h" +#include "message.h" // for getting the port using namespace LLCore; @@ -39,101 +47,1269 @@ using namespace LLCore; namespace LLCoreHttpUtil { +const F32 HTTP_REQUEST_EXPIRY_SECS = 60.0f; + + +void logMessageSuccess(std::string logAuth, std::string url, std::string message) +{ + LL_INFOS() << logAuth << " Success '" << message << "' for " << url << LL_ENDL; +} + +void logMessageFail(std::string logAuth, std::string url, std::string message) +{ + LL_WARNS() << logAuth << " Failure '" << message << "' for " << url << LL_ENDL; +} + +//========================================================================= +/// The HttpRequestPumper is a utility class. When constructed it will poll the +/// supplied HttpRequest once per frame until it is destroyed. +/// +class HttpRequestPumper +{ +public: + HttpRequestPumper(const LLCore::HttpRequest::ptr_t &request); + ~HttpRequestPumper(); + +private: + bool pollRequest(const LLSD&); + + LLTempBoundListener mBoundListener; + LLCore::HttpRequest::ptr_t mHttpRequest; +}; + + +//========================================================================= // *TODO: Currently converts only from XML content. A mode // to convert using fromBinary() might be useful as well. Mesh // headers could use it. bool responseToLLSD(HttpResponse * response, bool log, LLSD & out_llsd) { - // Convert response to LLSD - BufferArray * body(response->getBody()); - if (! body || ! body->size()) - { - return false; - } + // Convert response to LLSD + BufferArray * body(response->getBody()); + if (!body || !body->size()) + { + return false; + } - LLCore::BufferArrayStream bas(body); - LLSD body_llsd; - S32 parse_status(LLSDSerialize::fromXML(body_llsd, bas, log)); - if (LLSDParser::PARSE_FAILURE == parse_status){ - return false; - } - out_llsd = body_llsd; - return true; + LLCore::BufferArrayStream bas(body); + LLSD body_llsd; + S32 parse_status(LLSDSerialize::fromXML(body_llsd, bas, log)); + if (LLSDParser::PARSE_FAILURE == parse_status){ + return false; + } + out_llsd = body_llsd; + return true; } HttpHandle requestPostWithLLSD(HttpRequest * request, - HttpRequest::policy_t policy_id, - HttpRequest::priority_t priority, - const std::string & url, - const LLSD & body, - HttpOptions * options, - HttpHeaders * headers, - HttpHandler * handler) + HttpRequest::policy_t policy_id, + HttpRequest::priority_t priority, + const std::string & url, + const LLSD & body, + const HttpOptions::ptr_t &options, + const HttpHeaders::ptr_t &headers, + const HttpHandler::ptr_t &handler) { - HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); + HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); - BufferArray * ba = new BufferArray(); - BufferArrayStream bas(ba); - LLSDSerialize::toXML(body, bas); + BufferArray * ba = new BufferArray(); + BufferArrayStream bas(ba); + LLSDSerialize::toXML(body, bas); - handle = request->requestPost(policy_id, - priority, - url, - ba, - options, - headers, - handler); - ba->release(); - return handle; + handle = request->requestPost(policy_id, + priority, + url, + ba, + options, + headers, + handler); + ba->release(); + return handle; +} + + +HttpHandle requestPutWithLLSD(HttpRequest * request, + HttpRequest::policy_t policy_id, + HttpRequest::priority_t priority, + const std::string & url, + const LLSD & body, + const HttpOptions::ptr_t &options, + const HttpHeaders::ptr_t &headers, + const HttpHandler::ptr_t &handler) +{ + HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); + + BufferArray * ba = new BufferArray(); + BufferArrayStream bas(ba); + LLSDSerialize::toXML(body, bas); + + handle = request->requestPut(policy_id, + priority, + url, + ba, + options, + headers, + handler); + ba->release(); + return handle; +} + +HttpHandle requestPatchWithLLSD(HttpRequest * request, + HttpRequest::policy_t policy_id, + HttpRequest::priority_t priority, + const std::string & url, + const LLSD & body, + const HttpOptions::ptr_t &options, + const HttpHeaders::ptr_t &headers, + const HttpHandler::ptr_t &handler) +{ + HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); + + BufferArray * ba = new BufferArray(); + BufferArrayStream bas(ba); + LLSDSerialize::toXML(body, bas); + + handle = request->requestPatch(policy_id, + priority, + url, + ba, + options, + headers, + handler); + ba->release(); + return handle; } std::string responseToString(LLCore::HttpResponse * response) { - static const std::string empty("[Empty]"); - - if (! response) - { - return empty; - } - - BufferArray * body(response->getBody()); - if (! body || ! body->size()) - { - return empty; - } - - // Attempt to parse as LLSD regardless of content-type - LLSD body_llsd; - if (responseToLLSD(response, false, body_llsd)) - { - std::ostringstream tmp; - - LLSDSerialize::toPrettyNotation(body_llsd, tmp); - std::size_t temp_len(tmp.tellp()); - - if (temp_len) - { - return tmp.str().substr(0, std::min(temp_len, std::size_t(1024))); - } - } - else - { - // *TODO: More elaborate forms based on Content-Type as needed. - char content[1024]; - - size_t len(body->read(0, content, sizeof(content))); - if (len) - { - return std::string(content, 0, len); - } - } - - // Default - return empty; + static const std::string empty("[Empty]"); + + if (!response) + { + return empty; + } + + BufferArray * body(response->getBody()); + if (!body || !body->size()) + { + return empty; + } + + // Attempt to parse as LLSD regardless of content-type + LLSD body_llsd; + if (responseToLLSD(response, false, body_llsd)) + { + std::ostringstream tmp; + + LLSDSerialize::toPrettyNotation(body_llsd, tmp); + std::size_t temp_len(tmp.tellp()); + + if (temp_len) + { + return tmp.str().substr(0, std::min(temp_len, std::size_t(1024))); + } + } + else + { + // *TODO: More elaborate forms based on Content-Type as needed. + char content[1024]; + + size_t len(body->read(0, content, sizeof(content))); + if (len) + { + return std::string(content, 0, len); + } + } + + // Default + return empty; +} + +//======================================================================== + +HttpCoroHandler::HttpCoroHandler(LLEventStream &reply) : + mReplyPump(reply) +{ +} + +void HttpCoroHandler::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response) +{ + LLSD result; + + LLCore::HttpStatus status = response->getStatus(); + + if (status == LLCore::HttpStatus(LLCore::HttpStatus::LLCORE, LLCore::HE_HANDLE_NOT_FOUND)) + { // A response came in for a canceled request and we have not processed the + // cancel yet. Patience! + return; + } + + if (!status) + { + bool parseSuccess(false); + result = LLSD::emptyMap(); + LLCore::HttpStatus::type_enum_t errType = status.getType(); + + LL_WARNS() + << "\n--------------------------------------------------------------------------\n" + << " Error[" << status.toTerseString() << "] cannot access url '" << response->getRequestURL() + << "' because " << status.toString() + << "\n--------------------------------------------------------------------------" + << LL_ENDL; + if ((errType >= 400) && (errType < 500)) + { + LLSD body = this->parseBody(response, parseSuccess); + if (!body.isUndefined()) + { + if (!body.isMap()) + { + result[HttpCoroutineAdapter::HTTP_RESULTS_CONTENT] = body; + } + else + { + result = body; + } + } + + } + } + else + { + result = this->handleSuccess(response, status); + } + + buildStatusEntry(response, status, result); + + if (!status) + { + LLSD &httpStatus = result[HttpCoroutineAdapter::HTTP_RESULTS]; + + LLCore::BufferArray *body = response->getBody(); + LLCore::BufferArrayStream bas(body); + LLSD::String bodyData; + bodyData.reserve(response->getBodySize()); + bas >> std::noskipws; + bodyData.assign(std::istream_iterator<U8>(bas), std::istream_iterator<U8>()); + httpStatus["error_body"] = LLSD(bodyData); +#if 1 + // commenting out, but keeping since this can be useful for debugging + LL_WARNS() << "Returned body=" << std::endl << httpStatus["error_body"].asString() << LL_ENDL; +#endif + } + + mReplyPump.post(result); +} + +void HttpCoroHandler::buildStatusEntry(LLCore::HttpResponse *response, LLCore::HttpStatus status, LLSD &result) +{ + LLSD httpresults = LLSD::emptyMap(); + + writeStatusCodes(status, response->getRequestURL(), httpresults); + + LLSD httpHeaders = LLSD::emptyMap(); + LLCore::HttpHeaders::ptr_t hdrs = response->getHeaders(); + + if (hdrs) + { + for (LLCore::HttpHeaders::iterator it = hdrs->begin(); it != hdrs->end(); ++it) + { + if (!(*it).second.empty()) + { + httpHeaders[(*it).first] = (*it).second; + } + else + { + httpHeaders[(*it).first] = static_cast<LLSD::Boolean>(true); + } + } + } + + httpresults[HttpCoroutineAdapter::HTTP_RESULTS_HEADERS] = httpHeaders; + result[HttpCoroutineAdapter::HTTP_RESULTS] = httpresults; +} + +void HttpCoroHandler::writeStatusCodes(LLCore::HttpStatus status, const std::string &url, LLSD &result) +{ + result[HttpCoroutineAdapter::HTTP_RESULTS_SUCCESS] = static_cast<LLSD::Boolean>(status); + result[HttpCoroutineAdapter::HTTP_RESULTS_TYPE] = static_cast<LLSD::Integer>(status.getType()); + result[HttpCoroutineAdapter::HTTP_RESULTS_STATUS] = static_cast<LLSD::Integer>(status.getStatus()); + result[HttpCoroutineAdapter::HTTP_RESULTS_MESSAGE] = static_cast<LLSD::String>(status.getMessage()); + result[HttpCoroutineAdapter::HTTP_RESULTS_URL] = static_cast<LLSD::String>(url); + +} + +//========================================================================= +/// The HttpCoroLLSDHandler is a specialization of the LLCore::HttpHandler for +/// interacting with coroutines. When the request is completed the response +/// will be posted onto the supplied Event Pump. +/// +/// If the LLSD retrieved from through the HTTP connection is not in the form +/// of a LLSD::map it will be returned as in an llsd["content"] element. +/// +/// The LLSD posted back to the coroutine will have the following additions: +/// llsd["http_result"] -+- ["message"] - An error message returned from the HTTP status +/// +- ["status"] - The status code associated with the HTTP call +/// +- ["success"] - Success of failure of the HTTP call and LLSD parsing. +/// +- ["type"] - The LLCore::HttpStatus type associted with the HTTP call +/// +- ["url"] - The URL used to make the call. +/// +- ["headers"] - A map of name name value pairs with the HTTP headers. +/// +class HttpCoroLLSDHandler : public HttpCoroHandler +{ +public: + HttpCoroLLSDHandler(LLEventStream &reply); + +protected: + virtual LLSD handleSuccess(LLCore::HttpResponse * response, LLCore::HttpStatus &status); + virtual LLSD parseBody(LLCore::HttpResponse *response, bool &success); +}; + +//------------------------------------------------------------------------- +HttpCoroLLSDHandler::HttpCoroLLSDHandler(LLEventStream &reply): + HttpCoroHandler(reply) +{ +} + + +LLSD HttpCoroLLSDHandler::handleSuccess(LLCore::HttpResponse * response, LLCore::HttpStatus &status) +{ + LLSD result; + +// const bool emit_parse_errors = false; + bool success(false); + + result = parseBody(response, success); + +#if 0 + bool parsed = !((response->getBodySize() == 0) || + !LLCoreHttpUtil::responseToLLSD(response, emit_parse_errors, result)); + + if (!parsed) + { + // Only emit a warning if we failed to parse when 'content-type' == 'application/llsd+xml' + LLCore::HttpHeaders::ptr_t headers(response->getHeaders()); + const std::string *contentType = (headers) ? headers->find(HTTP_IN_HEADER_CONTENT_TYPE) : NULL; + + if (contentType && (HTTP_CONTENT_LLSD_XML == *contentType)) + { + std::string thebody = LLCoreHttpUtil::responseToString(response); + LL_WARNS() << "Failed to deserialize . " << response->getRequestURL() << " [status:" << response->getStatus().toString() << "] " + << " body: " << thebody << LL_ENDL; + + // Replace the status with a new one indicating the failure. + status = LLCore::HttpStatus(499, "Failed to deserialize LLSD."); + } + } +#endif + + if (!success) + { +#if 1 + // Only emit a warning if we failed to parse when 'content-type' == 'application/llsd+xml' + LLCore::HttpHeaders::ptr_t headers(response->getHeaders()); + const std::string *contentType = (headers) ? headers->find(HTTP_IN_HEADER_CONTENT_TYPE) : NULL; + + if (contentType && (HTTP_CONTENT_LLSD_XML == *contentType)) + { + std::string thebody = LLCoreHttpUtil::responseToString(response); + LL_WARNS() << "Failed to deserialize . " << response->getRequestURL() << " [status:" << response->getStatus().toString() << "] " + << " body: " << thebody << LL_ENDL; + + // Replace the status with a new one indicating the failure. + status = LLCore::HttpStatus(499, "Failed to deserialize LLSD."); + } +#endif + // If we've gotten to this point and the result LLSD is still undefined + // either there was an issue deserializing the body or the response was + // blank. Create an empty map to hold the result either way. + result = LLSD::emptyMap(); + } + else if (!result.isMap()) + { // The results are not themselves a map. Move them down so that + // this method can return a map to the caller. + // *TODO: Should it always do this? + LLSD newResult = LLSD::emptyMap(); + newResult[HttpCoroutineAdapter::HTTP_RESULTS_CONTENT] = result; + result = newResult; + } + + return result; +} + +LLSD HttpCoroLLSDHandler::parseBody(LLCore::HttpResponse *response, bool &success) +{ + success = true; + if (response->getBodySize() == 0) + return LLSD(); + + LLSD result; + + if (!LLCoreHttpUtil::responseToLLSD(response, true, result)) + { + success = false; + return LLSD(); + } + + return result; +} + + +//======================================================================== +/// The HttpCoroRawHandler is a specialization of the LLCore::HttpHandler for +/// interacting with coroutines. +/// +/// In addition to the normal "http_results" the returned LLSD will contain +/// an entry keyed with "raw" containing the unprocessed results of the HTTP +/// call. +/// +class HttpCoroRawHandler : public HttpCoroHandler +{ +public: + HttpCoroRawHandler(LLEventStream &reply); + + virtual LLSD handleSuccess(LLCore::HttpResponse * response, LLCore::HttpStatus &status); + virtual LLSD parseBody(LLCore::HttpResponse *response, bool &success); +}; + +//------------------------------------------------------------------------- +HttpCoroRawHandler::HttpCoroRawHandler(LLEventStream &reply): + HttpCoroHandler(reply) +{ +} + +LLSD HttpCoroRawHandler::handleSuccess(LLCore::HttpResponse * response, LLCore::HttpStatus &status) +{ + LLSD result = LLSD::emptyMap(); + + BufferArray * body(response->getBody()); + if (!body || !body->size()) + { + return result; + } + + size_t size = body->size(); + + LLCore::BufferArrayStream bas(body); + +#if 1 + // This is the slower implementation. It is safe vis-a-vi the const_cast<> and modification + // of a LLSD managed array but contains an extra (potentially large) copy. + // + // *TODO: https://jira.secondlife.com/browse/MAINT-5221 + + LLSD::Binary data; + data.reserve(size); + bas >> std::noskipws; + data.assign(std::istream_iterator<U8>(bas), std::istream_iterator<U8>()); + + result[HttpCoroutineAdapter::HTTP_RESULTS_RAW] = data; + +#else + // This is disabled because it's dangerous. See the other case for an + // alternate implementation. + // We create a new LLSD::Binary object and assign it to the result map. + // The LLSD has created it's own copy so we retrieve it asBinary and const cast + // the reference so that we can modify it. + // *TODO: This is potentially dangerous... but I am trying to avoid a potentially + // large copy. + result[HttpCoroutineAdapter::HTTP_RESULTS_RAW] = LLSD::Binary(); + LLSD::Binary &data = const_cast<LLSD::Binary &>( result[HttpCoroutineAdapter::HTTP_RESULTS_RAW].asBinary() ); + + data.reserve(size); + bas >> std::noskipws; + data.assign(std::istream_iterator<U8>(bas), std::istream_iterator<U8>()); +#endif + + return result; +} + +LLSD HttpCoroRawHandler::parseBody(LLCore::HttpResponse *response, bool &success) +{ + success = true; + return LLSD(); +} + +//======================================================================== +/// The HttpCoroJSONHandler is a specialization of the LLCore::HttpHandler for +/// interacting with coroutines. +/// +/// In addition to the normal "http_results" the returned LLSD will contain +/// JSON entries will be converted into an LLSD map. All results are considered +/// strings +/// +class HttpCoroJSONHandler : public HttpCoroHandler +{ +public: + HttpCoroJSONHandler(LLEventStream &reply); + + virtual LLSD handleSuccess(LLCore::HttpResponse * response, LLCore::HttpStatus &status); + virtual LLSD parseBody(LLCore::HttpResponse *response, bool &success); +}; + +//------------------------------------------------------------------------- +HttpCoroJSONHandler::HttpCoroJSONHandler(LLEventStream &reply) : + HttpCoroHandler(reply) +{ +} + +LLSD HttpCoroJSONHandler::handleSuccess(LLCore::HttpResponse * response, LLCore::HttpStatus &status) +{ + LLSD result = LLSD::emptyMap(); + + BufferArray * body(response->getBody()); + if (!body || !body->size()) + { + return result; + } + + LLCore::BufferArrayStream bas(body); + Json::Value jsonRoot; + + try + { + bas >> jsonRoot; + } + catch (std::runtime_error e) + { // deserialization failed. Record the reason and pass back an empty map for markup. + status = LLCore::HttpStatus(499, std::string(e.what())); + return result; + } + + // Convert the JSON structure to LLSD + result = LlsdFromJson(jsonRoot); + + return result; +} + +LLSD HttpCoroJSONHandler::parseBody(LLCore::HttpResponse *response, bool &success) +{ + success = true; + BufferArray * body(response->getBody()); + if (!body || !body->size()) + { + return LLSD(); + } + + LLCore::BufferArrayStream bas(body); + Json::Value jsonRoot; + + try + { + bas >> jsonRoot; + } + catch (std::runtime_error e) + { + success = false; + return LLSD(); + } + + // Convert the JSON structure to LLSD + return LlsdFromJson(jsonRoot); +} + +//======================================================================== +HttpRequestPumper::HttpRequestPumper(const LLCore::HttpRequest::ptr_t &request) : + mHttpRequest(request) +{ + mBoundListener = LLEventPumps::instance().obtain("mainloop"). + listen(LLEventPump::inventName(), boost::bind(&HttpRequestPumper::pollRequest, this, _1)); +} + +HttpRequestPumper::~HttpRequestPumper() +{ + if (mBoundListener.connected()) + { + mBoundListener.disconnect(); + } +} + +bool HttpRequestPumper::pollRequest(const LLSD&) +{ + if (mHttpRequest->getStatus() != HttpStatus(HttpStatus::LLCORE, HE_OP_CANCELED)) + { + mHttpRequest->update(0L); + } + return false; } +//======================================================================== +const std::string HttpCoroutineAdapter::HTTP_RESULTS("http_result"); +const std::string HttpCoroutineAdapter::HTTP_RESULTS_SUCCESS("success"); +const std::string HttpCoroutineAdapter::HTTP_RESULTS_TYPE("type"); +const std::string HttpCoroutineAdapter::HTTP_RESULTS_STATUS("status"); +const std::string HttpCoroutineAdapter::HTTP_RESULTS_MESSAGE("message"); +const std::string HttpCoroutineAdapter::HTTP_RESULTS_URL("url"); +const std::string HttpCoroutineAdapter::HTTP_RESULTS_HEADERS("headers"); +const std::string HttpCoroutineAdapter::HTTP_RESULTS_CONTENT("content"); +const std::string HttpCoroutineAdapter::HTTP_RESULTS_RAW("raw"); + +HttpCoroutineAdapter::HttpCoroutineAdapter(const std::string &name, + LLCore::HttpRequest::policy_t policyId, LLCore::HttpRequest::priority_t priority) : + mAdapterName(name), + mPolicyId(policyId), + mPriority(priority), + mYieldingHandle(LLCORE_HTTP_HANDLE_INVALID), + mWeakRequest(), + mWeakHandler() +{ +} + +HttpCoroutineAdapter::~HttpCoroutineAdapter() +{ + cancelSuspendedOperation(); +} + +LLSD HttpCoroutineAdapter::postAndSuspend(LLCore::HttpRequest::ptr_t request, + const std::string & url, const LLSD & body, + LLCore::HttpOptions::ptr_t options, LLCore::HttpHeaders::ptr_t headers) +{ + LLEventStream replyPump(mAdapterName, true); + HttpCoroHandler::ptr_t httpHandler(new HttpCoroLLSDHandler(replyPump)); + + return postAndSuspend_(request, url, body, options, headers, httpHandler); +} + +LLSD HttpCoroutineAdapter::postAndSuspend_(LLCore::HttpRequest::ptr_t &request, + const std::string & url, const LLSD & body, + LLCore::HttpOptions::ptr_t &options, LLCore::HttpHeaders::ptr_t &headers, + HttpCoroHandler::ptr_t &handler) +{ + HttpRequestPumper pumper(request); + + checkDefaultHeaders(headers); + + // The HTTPCoroHandler does not self delete, so retrieval of a the contained + // pointer from the smart pointer is safe in this case. + LLCore::HttpHandle hhandle = requestPostWithLLSD(request, + mPolicyId, mPriority, url, body, options, headers, + handler); + + if (hhandle == LLCORE_HTTP_HANDLE_INVALID) + { + return HttpCoroutineAdapter::buildImmediateErrorResult(request, url); + } + + saveState(hhandle, request, handler); + LLSD results = llcoro::suspendUntilEventOn(handler->getReplyPump()); + cleanState(); + + return results; +} + +LLSD HttpCoroutineAdapter::postAndSuspend(LLCore::HttpRequest::ptr_t request, + const std::string & url, LLCore::BufferArray::ptr_t rawbody, + LLCore::HttpOptions::ptr_t options, LLCore::HttpHeaders::ptr_t headers) +{ + LLEventStream replyPump(mAdapterName, true); + HttpCoroHandler::ptr_t httpHandler(new HttpCoroLLSDHandler(replyPump)); + + return postAndSuspend_(request, url, rawbody, options, headers, httpHandler); +} + +LLSD HttpCoroutineAdapter::postRawAndSuspend(LLCore::HttpRequest::ptr_t request, + const std::string & url, LLCore::BufferArray::ptr_t rawbody, + LLCore::HttpOptions::ptr_t options, LLCore::HttpHeaders::ptr_t headers) +{ + LLEventStream replyPump(mAdapterName, true); + HttpCoroHandler::ptr_t httpHandler(new HttpCoroRawHandler(replyPump)); + + return postAndSuspend_(request, url, rawbody, options, headers, httpHandler); +} + +// *TODO: This functionality could be moved into the LLCore::Http library itself +// by having the CURL layer read the file directly. +LLSD HttpCoroutineAdapter::postFileAndSuspend(LLCore::HttpRequest::ptr_t request, + const std::string & url, std::string fileName, + LLCore::HttpOptions::ptr_t options, LLCore::HttpHeaders::ptr_t headers) +{ + LLCore::BufferArray::ptr_t fileData(new LLCore::BufferArray); + + // scoping for our streams so that they go away when we no longer need them. + { + LLCore::BufferArrayStream outs(fileData.get()); + llifstream ins(fileName.c_str(), std::iostream::binary | std::iostream::out); + + if (ins.is_open()) + { + + ins.seekg(0, std::ios::beg); + ins >> std::noskipws; + + std::copy(std::istream_iterator<U8>(ins), std::istream_iterator<U8>(), + std::ostream_iterator<U8>(outs)); + + ins.close(); + } + } + + return postAndSuspend(request, url, fileData, options, headers); +} + +// *TODO: This functionality could be moved into the LLCore::Http library itself +// by having the CURL layer read the file directly. +LLSD HttpCoroutineAdapter::postFileAndSuspend(LLCore::HttpRequest::ptr_t request, + const std::string & url, LLUUID assetId, LLAssetType::EType assetType, + LLCore::HttpOptions::ptr_t options, LLCore::HttpHeaders::ptr_t headers) +{ + LLCore::BufferArray::ptr_t fileData(new LLCore::BufferArray); + + // scoping for our streams so that they go away when we no longer need them. + { + LLCore::BufferArrayStream outs(fileData.get()); + LLVFile vfile(gVFS, assetId, assetType, LLVFile::READ); + + S32 fileSize = vfile.getSize(); + U8* fileBuffer; + fileBuffer = new U8[fileSize]; + vfile.read(fileBuffer, fileSize); + + outs.write((char*)fileBuffer, fileSize); + delete[] fileBuffer; + } + + return postAndSuspend(request, url, fileData, options, headers); +} + +LLSD HttpCoroutineAdapter::postJsonAndSuspend(LLCore::HttpRequest::ptr_t request, + const std::string & url, const LLSD & body, + LLCore::HttpOptions::ptr_t options, LLCore::HttpHeaders::ptr_t headers) +{ + LLEventStream replyPump(mAdapterName, true); + HttpCoroHandler::ptr_t httpHandler(new HttpCoroJSONHandler(replyPump)); + + LLCore::BufferArray::ptr_t rawbody(new LLCore::BufferArray); + + { + LLCore::BufferArrayStream outs(rawbody.get()); + Json::Value root = LlsdToJson(body); + Json::FastWriter writer; + + LL_WARNS("Http::post") << "JSON Generates: \"" << writer.write(root) << "\"" << LL_ENDL; + + outs << writer.write(root); + } + + return postAndSuspend_(request, url, rawbody, options, headers, httpHandler); +} + + +LLSD HttpCoroutineAdapter::postAndSuspend_(LLCore::HttpRequest::ptr_t &request, + const std::string & url, LLCore::BufferArray::ptr_t &rawbody, + LLCore::HttpOptions::ptr_t &options, LLCore::HttpHeaders::ptr_t &headers, + HttpCoroHandler::ptr_t &handler) +{ + HttpRequestPumper pumper(request); + + checkDefaultHeaders(headers); + + // The HTTPCoroHandler does not self delete, so retrieval of a the contained + // pointer from the smart pointer is safe in this case. + LLCore::HttpHandle hhandle = request->requestPost(mPolicyId, mPriority, url, rawbody.get(), + options, headers, handler); + + if (hhandle == LLCORE_HTTP_HANDLE_INVALID) + { + return HttpCoroutineAdapter::buildImmediateErrorResult(request, url); + } + + saveState(hhandle, request, handler); + LLSD results = llcoro::suspendUntilEventOn(handler->getReplyPump()); + cleanState(); + + return results; +} + +LLSD HttpCoroutineAdapter::putAndSuspend(LLCore::HttpRequest::ptr_t request, + const std::string & url, const LLSD & body, + LLCore::HttpOptions::ptr_t options, LLCore::HttpHeaders::ptr_t headers) +{ + LLEventStream replyPump(mAdapterName + "Reply", true); + HttpCoroHandler::ptr_t httpHandler(new HttpCoroLLSDHandler(replyPump)); + + return putAndSuspend_(request, url, body, options, headers, httpHandler); +} + +LLSD HttpCoroutineAdapter::putJsonAndSuspend(LLCore::HttpRequest::ptr_t request, + const std::string & url, const LLSD & body, + LLCore::HttpOptions::ptr_t options, LLCore::HttpHeaders::ptr_t headers) +{ + LLEventStream replyPump(mAdapterName, true); + HttpCoroHandler::ptr_t httpHandler(new HttpCoroJSONHandler(replyPump)); + + LLCore::BufferArray::ptr_t rawbody(new LLCore::BufferArray); + + { + LLCore::BufferArrayStream outs(rawbody.get()); + Json::Value root = LlsdToJson(body); + Json::FastWriter writer; + + LL_WARNS("Http::put") << "JSON Generates: \"" << writer.write(root) << "\"" << LL_ENDL; + outs << writer.write(root); + } + + return putAndSuspend_(request, url, rawbody, options, headers, httpHandler); +} + +LLSD HttpCoroutineAdapter::putAndSuspend_(LLCore::HttpRequest::ptr_t &request, + const std::string & url, const LLSD & body, + LLCore::HttpOptions::ptr_t &options, LLCore::HttpHeaders::ptr_t &headers, + HttpCoroHandler::ptr_t &handler) +{ + HttpRequestPumper pumper(request); + + checkDefaultHeaders(headers); + + // The HTTPCoroHandler does not self delete, so retrieval of a the contained + // pointer from the smart pointer is safe in this case. + LLCore::HttpHandle hhandle = requestPutWithLLSD(request, + mPolicyId, mPriority, url, body, options, headers, + handler); + + if (hhandle == LLCORE_HTTP_HANDLE_INVALID) + { + return HttpCoroutineAdapter::buildImmediateErrorResult(request, url); + } + + saveState(hhandle, request, handler); + LLSD results = llcoro::suspendUntilEventOn(handler->getReplyPump()); + cleanState(); + + return results; +} + +LLSD HttpCoroutineAdapter::putAndSuspend_(LLCore::HttpRequest::ptr_t &request, + const std::string & url, const LLCore::BufferArray::ptr_t & rawbody, + LLCore::HttpOptions::ptr_t &options, LLCore::HttpHeaders::ptr_t &headers, + HttpCoroHandler::ptr_t &handler) +{ + HttpRequestPumper pumper(request); + + checkDefaultHeaders(headers); + + // The HTTPCoroHandler does not self delete, so retrieval of a the contained + // pointer from the smart pointer is safe in this case. + LLCore::HttpHandle hhandle = request->requestPut(mPolicyId, mPriority, + url, rawbody.get(), options, headers, handler); + + if (hhandle == LLCORE_HTTP_HANDLE_INVALID) + { + return HttpCoroutineAdapter::buildImmediateErrorResult(request, url); + } + + saveState(hhandle, request, handler); + LLSD results = llcoro::suspendUntilEventOn(handler->getReplyPump()); + cleanState(); + + return results; +} + + +LLSD HttpCoroutineAdapter::getAndSuspend(LLCore::HttpRequest::ptr_t request, + const std::string & url, + LLCore::HttpOptions::ptr_t options, LLCore::HttpHeaders::ptr_t headers) +{ + LLEventStream replyPump(mAdapterName + "Reply", true); + HttpCoroHandler::ptr_t httpHandler(new HttpCoroLLSDHandler(replyPump)); + + return getAndSuspend_(request, url, options, headers, httpHandler); +} + +LLSD HttpCoroutineAdapter::getRawAndSuspend(LLCore::HttpRequest::ptr_t request, + const std::string & url, + LLCore::HttpOptions::ptr_t options, LLCore::HttpHeaders::ptr_t headers) +{ + LLEventStream replyPump(mAdapterName + "Reply", true); + HttpCoroHandler::ptr_t httpHandler(new HttpCoroRawHandler(replyPump)); + + return getAndSuspend_(request, url, options, headers, httpHandler); +} + +LLSD HttpCoroutineAdapter::getJsonAndSuspend(LLCore::HttpRequest::ptr_t request, + const std::string & url, LLCore::HttpOptions::ptr_t options, LLCore::HttpHeaders::ptr_t headers) +{ + LLEventStream replyPump(mAdapterName + "Reply", true); + HttpCoroHandler::ptr_t httpHandler(new HttpCoroJSONHandler(replyPump)); + + return getAndSuspend_(request, url, options, headers, httpHandler); +} + + +LLSD HttpCoroutineAdapter::getAndSuspend_(LLCore::HttpRequest::ptr_t &request, + const std::string & url, + LLCore::HttpOptions::ptr_t &options, LLCore::HttpHeaders::ptr_t &headers, + HttpCoroHandler::ptr_t &handler) +{ + HttpRequestPumper pumper(request); + checkDefaultHeaders(headers); + + // The HTTPCoroHandler does not self delete, so retrieval of a the contained + // pointer from the smart pointer is safe in this case. + LLCore::HttpHandle hhandle = request->requestGet(mPolicyId, mPriority, + url, options, headers, handler); + + if (hhandle == LLCORE_HTTP_HANDLE_INVALID) + { + return HttpCoroutineAdapter::buildImmediateErrorResult(request, url); + } + + saveState(hhandle, request, handler); + LLSD results = llcoro::suspendUntilEventOn(handler->getReplyPump()); + cleanState(); + + return results; +} + + +LLSD HttpCoroutineAdapter::deleteAndSuspend(LLCore::HttpRequest::ptr_t request, + const std::string & url, + LLCore::HttpOptions::ptr_t options, LLCore::HttpHeaders::ptr_t headers) +{ + LLEventStream replyPump(mAdapterName + "Reply", true); + HttpCoroHandler::ptr_t httpHandler(new HttpCoroLLSDHandler(replyPump)); + + return deleteAndSuspend_(request, url, options, headers, httpHandler); +} + +LLSD HttpCoroutineAdapter::deleteJsonAndSuspend(LLCore::HttpRequest::ptr_t request, + const std::string & url, + LLCore::HttpOptions::ptr_t options, LLCore::HttpHeaders::ptr_t headers) +{ + LLEventStream replyPump(mAdapterName + "Reply", true); + HttpCoroHandler::ptr_t httpHandler(new HttpCoroJSONHandler(replyPump)); + + return deleteAndSuspend_(request, url, options, headers, httpHandler); +} + + +LLSD HttpCoroutineAdapter::deleteAndSuspend_(LLCore::HttpRequest::ptr_t &request, + const std::string & url, LLCore::HttpOptions::ptr_t &options, + LLCore::HttpHeaders::ptr_t &headers, HttpCoroHandler::ptr_t &handler) +{ + HttpRequestPumper pumper(request); + + checkDefaultHeaders(headers); + // The HTTPCoroHandler does not self delete, so retrieval of a the contained + // pointer from the smart pointer is safe in this case. + LLCore::HttpHandle hhandle = request->requestDelete(mPolicyId, mPriority, + url, options, headers, handler); + + if (hhandle == LLCORE_HTTP_HANDLE_INVALID) + { + return HttpCoroutineAdapter::buildImmediateErrorResult(request, url); + } + + saveState(hhandle, request, handler); + LLSD results = llcoro::suspendUntilEventOn(handler->getReplyPump()); + cleanState(); + + return results; +} + +LLSD HttpCoroutineAdapter::patchAndSuspend(LLCore::HttpRequest::ptr_t request, + const std::string & url, const LLSD & body, + LLCore::HttpOptions::ptr_t options, LLCore::HttpHeaders::ptr_t headers) +{ + LLEventStream replyPump(mAdapterName + "Reply", true); + HttpCoroHandler::ptr_t httpHandler(new HttpCoroLLSDHandler(replyPump)); + + return patchAndSuspend_(request, url, body, options, headers, httpHandler); +} + + +LLSD HttpCoroutineAdapter::patchAndSuspend_(LLCore::HttpRequest::ptr_t &request, + const std::string & url, const LLSD & body, + LLCore::HttpOptions::ptr_t &options, LLCore::HttpHeaders::ptr_t &headers, + HttpCoroHandler::ptr_t &handler) +{ + HttpRequestPumper pumper(request); + + checkDefaultHeaders(headers); + + // The HTTPCoroHandler does not self delete, so retrieval of a the contained + // pointer from the smart pointer is safe in this case. + LLCore::HttpHandle hhandle = requestPatchWithLLSD(request, + mPolicyId, mPriority, url, body, options, headers, + handler); + + if (hhandle == LLCORE_HTTP_HANDLE_INVALID) + { + return HttpCoroutineAdapter::buildImmediateErrorResult(request, url); + } + + saveState(hhandle, request, handler); + LLSD results = llcoro::suspendUntilEventOn(handler->getReplyPump()); + cleanState(); + + return results; +} + +LLSD HttpCoroutineAdapter::copyAndSuspend(LLCore::HttpRequest::ptr_t request, + const std::string & url, const std::string dest, + LLCore::HttpOptions::ptr_t options, LLCore::HttpHeaders::ptr_t headers) +{ + LLEventStream replyPump(mAdapterName + "Reply", true); + HttpCoroHandler::ptr_t httpHandler(new HttpCoroLLSDHandler(replyPump)); + + if (!headers) + headers.reset(new LLCore::HttpHeaders); + headers->append(HTTP_OUT_HEADER_DESTINATION, dest); + + return copyAndSuspend_(request, url, options, headers, httpHandler); +} + + +LLSD HttpCoroutineAdapter::copyAndSuspend_(LLCore::HttpRequest::ptr_t &request, + const std::string & url, + LLCore::HttpOptions::ptr_t &options, LLCore::HttpHeaders::ptr_t &headers, + HttpCoroHandler::ptr_t &handler) +{ + HttpRequestPumper pumper(request); + + checkDefaultHeaders(headers); + + // The HTTPCoroHandler does not self delete, so retrieval of a the contained + // pointer from the smart pointer is safe in this case. + // + LLCore::HttpHandle hhandle = request->requestCopy(mPolicyId, mPriority, url, + options, headers, handler); + + if (hhandle == LLCORE_HTTP_HANDLE_INVALID) + { + return HttpCoroutineAdapter::buildImmediateErrorResult(request, url); + } + + saveState(hhandle, request, handler); + LLSD results = llcoro::suspendUntilEventOn(handler->getReplyPump()); + cleanState(); + + return results; +} + +LLSD HttpCoroutineAdapter::moveAndSuspend(LLCore::HttpRequest::ptr_t request, + const std::string & url, const std::string dest, + LLCore::HttpOptions::ptr_t options, LLCore::HttpHeaders::ptr_t headers) +{ + LLEventStream replyPump(mAdapterName + "Reply", true); + HttpCoroHandler::ptr_t httpHandler(new HttpCoroLLSDHandler(replyPump)); + + if (!headers) + headers.reset(new LLCore::HttpHeaders); + headers->append(HTTP_OUT_HEADER_DESTINATION, dest); + + return moveAndSuspend_(request, url, options, headers, httpHandler); +} + + +LLSD HttpCoroutineAdapter::moveAndSuspend_(LLCore::HttpRequest::ptr_t &request, + const std::string & url, + LLCore::HttpOptions::ptr_t &options, LLCore::HttpHeaders::ptr_t &headers, + HttpCoroHandler::ptr_t &handler) +{ + HttpRequestPumper pumper(request); + + checkDefaultHeaders(headers); + + // The HTTPCoroHandler does not self delete, so retrieval of a the contained + // pointer from the smart pointer is safe in this case. + // + LLCore::HttpHandle hhandle = request->requestMove(mPolicyId, mPriority, url, + options, headers, handler); + + if (hhandle == LLCORE_HTTP_HANDLE_INVALID) + { + return HttpCoroutineAdapter::buildImmediateErrorResult(request, url); + } + + saveState(hhandle, request, handler); + LLSD results = llcoro::suspendUntilEventOn(handler->getReplyPump()); + cleanState(); + + return results; +} + + +void HttpCoroutineAdapter::checkDefaultHeaders(LLCore::HttpHeaders::ptr_t &headers) +{ + if (!headers) + headers.reset(new LLCore::HttpHeaders); + if (!headers->find(HTTP_OUT_HEADER_ACCEPT)) + { + headers->append(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_LLSD_XML); + } + if (!headers->find(HTTP_OUT_HEADER_CONTENT_TYPE)) + { + headers->append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_LLSD_XML); + } + + if (!headers->find("X-SecondLife-UDP-Listen-Port") && gMessageSystem) + { + headers->append("X-SecondLife-UDP-Listen-Port", llformat("%d", gMessageSystem->mPort)); + } +} + + +void HttpCoroutineAdapter::cancelSuspendedOperation() +{ + LLCore::HttpRequest::ptr_t request = mWeakRequest.lock(); + HttpCoroHandler::ptr_t handler = mWeakHandler.lock(); + if ((request) && (handler) && (mYieldingHandle != LLCORE_HTTP_HANDLE_INVALID)) + { + cleanState(); + LL_INFOS() << "Canceling yielding request!" << LL_ENDL; + request->requestCancel(mYieldingHandle, handler); + } +} + +void HttpCoroutineAdapter::saveState(LLCore::HttpHandle yieldingHandle, + LLCore::HttpRequest::ptr_t &request, HttpCoroHandler::ptr_t &handler) +{ + mWeakRequest = request; + mWeakHandler = handler; + mYieldingHandle = yieldingHandle; +} + +void HttpCoroutineAdapter::cleanState() +{ + mWeakRequest.reset(); + mWeakHandler.reset(); + mYieldingHandle = LLCORE_HTTP_HANDLE_INVALID; +} + +/*static*/ +LLSD HttpCoroutineAdapter::buildImmediateErrorResult(const LLCore::HttpRequest::ptr_t &request, + const std::string &url) +{ + LLCore::HttpStatus status = request->getStatus(); + LL_WARNS() << "Error posting to " << url << " Status=" << status.getStatus() << + " message = " << status.getMessage() << LL_ENDL; + + // Mimic the status results returned from an http error that we had + // to wait on + LLSD httpresults = LLSD::emptyMap(); + + HttpCoroHandler::writeStatusCodes(status, url, httpresults); + + LLSD errorres = LLSD::emptyMap(); + errorres["http_result"] = httpresults; + + return errorres; +} + +/*static*/ +LLCore::HttpStatus HttpCoroutineAdapter::getStatusFromLLSD(const LLSD &httpResults) +{ + LLCore::HttpStatus::type_enum_t type = static_cast<LLCore::HttpStatus::type_enum_t>(httpResults[HttpCoroutineAdapter::HTTP_RESULTS_TYPE].asInteger()); + short code = static_cast<short>(httpResults[HttpCoroutineAdapter::HTTP_RESULTS_STATUS].asInteger()); + + return LLCore::HttpStatus(type, code); +} + +/*static*/ +void HttpCoroutineAdapter::callbackHttpGet(const std::string &url, LLCore::HttpRequest::policy_t policyId, completionCallback_t success, completionCallback_t failure) +{ + LLCoros::instance().launch("HttpCoroutineAdapter::genericGetCoro", + boost::bind(&HttpCoroutineAdapter::trivialGetCoro, url, policyId, success, failure)); +} + +/*static*/ +void HttpCoroutineAdapter::messageHttpGet(const std::string &url, const std::string &success, const std::string &failure) +{ + completionCallback_t cbSuccess = (success.empty()) ? NULL : + static_cast<completionCallback_t>(boost::bind(&logMessageSuccess, "HttpCoroutineAdapter", url, success)); + completionCallback_t cbFailure = (failure.empty()) ? NULL : + static_cast<completionCallback_t>(boost::bind(&logMessageFail, "HttpCoroutineAdapter", url, failure)); + callbackHttpGet(url, cbSuccess, cbFailure); +} + +/*static*/ +void HttpCoroutineAdapter::trivialGetCoro(std::string url, LLCore::HttpRequest::policy_t policyId, completionCallback_t success, completionCallback_t failure) +{ + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("genericGetCoro", policyId)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); + + httpOpts->setWantHeaders(true); + + LL_INFOS("HttpCoroutineAdapter", "genericGetCoro") << "Generic GET for " << url << LL_ENDL; + + LLSD result = httpAdapter->getAndSuspend(httpRequest, url, httpOpts); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) + { + if (failure) + { + failure(httpResults); + } + } + else + { + if (success) + { + success(result); + } + } +} + +/*static*/ +void HttpCoroutineAdapter::callbackHttpPost(const std::string &url, LLCore::HttpRequest::policy_t policyId, const LLSD &postData, completionCallback_t success, completionCallback_t failure) +{ + LLCoros::instance().launch("HttpCoroutineAdapter::genericPostCoro", + boost::bind(&HttpCoroutineAdapter::trivialPostCoro, url, policyId, postData, success, failure)); +} + +/*static*/ +void HttpCoroutineAdapter::messageHttpPost(const std::string &url, const LLSD &postData, const std::string &success, const std::string &failure) +{ + completionCallback_t cbSuccess = (success.empty()) ? NULL : + static_cast<completionCallback_t>(boost::bind(&logMessageSuccess, "HttpCoroutineAdapter", url, success)); + completionCallback_t cbFailure = (failure.empty()) ? NULL : + static_cast<completionCallback_t>(boost::bind(&logMessageFail, "HttpCoroutineAdapter", url, failure)); + + callbackHttpPost(url, postData, cbSuccess, cbFailure); +} + +/*static*/ +void HttpCoroutineAdapter::trivialPostCoro(std::string url, LLCore::HttpRequest::policy_t policyId, LLSD postData, completionCallback_t success, completionCallback_t failure) +{ + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("genericPostCoro", policyId)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); + + httpOpts->setWantHeaders(true); + + LL_INFOS("HttpCoroutineAdapter", "genericPostCoro") << "Generic POST for " << url << LL_ENDL; + + LLSD result = httpAdapter->postAndSuspend(httpRequest, url, postData, httpOpts); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) + { + // If a failure routine is provided do it. + if (failure) + { + failure(httpResults); + } + } + else + { + // If a success routine is provided do it. + if (success) + { + success(result); + } + } +} + + } // end namespace LLCoreHttpUtil diff --git a/indra/llmessage/llcorehttputil.h b/indra/llmessage/llcorehttputil.h index d40172bc7a..d21f5ff45c 100644 --- a/indra/llmessage/llcorehttputil.h +++ b/indra/llmessage/llcorehttputil.h @@ -36,9 +36,15 @@ #include "httpheaders.h" #include "httpoptions.h" #include "httphandler.h" +#include "llhttpconstants.h" // *TODO: move to llcorehttp #include "bufferarray.h" #include "bufferstream.h" #include "llsd.h" +#include "llevents.h" +#include "llcoros.h" +#include "lleventcoro.h" +#include "llassettype.h" +#include "lluuid.h" /// /// The base llcorehttp library implements many HTTP idioms @@ -51,6 +57,7 @@ /// namespace LLCoreHttpUtil { + extern const F32 HTTP_REQUEST_EXPIRY_SECS; /// Attempt to convert a response object's contents to LLSD. /// It is expected that the response body will be of non-zero @@ -101,15 +108,573 @@ std::string responseToString(LLCore::HttpResponse * response); /// a now-useless HttpHandler object. /// LLCore::HttpHandle requestPostWithLLSD(LLCore::HttpRequest * request, - LLCore::HttpRequest::policy_t policy_id, - LLCore::HttpRequest::priority_t priority, - const std::string & url, - const LLSD & body, - LLCore::HttpOptions * options, - LLCore::HttpHeaders * headers, - LLCore::HttpHandler * handler); + LLCore::HttpRequest::policy_t policy_id, + LLCore::HttpRequest::priority_t priority, + const std::string & url, + const LLSD & body, + const LLCore::HttpOptions::ptr_t &options, + const LLCore::HttpHeaders::ptr_t &headers, + const LLCore::HttpHandler::ptr_t &handler); -} // end namespace LLCoreHttpUtil +inline LLCore::HttpHandle requestPostWithLLSD(LLCore::HttpRequest::ptr_t & request, + LLCore::HttpRequest::policy_t policy_id, + LLCore::HttpRequest::priority_t priority, + const std::string & url, + const LLSD & body, + const LLCore::HttpOptions::ptr_t & options, + const LLCore::HttpHeaders::ptr_t & headers, + const LLCore::HttpHandler::ptr_t & handler) +{ + return requestPostWithLLSD(request.get(), policy_id, priority, + url, body, options, headers, handler); +} + +inline LLCore::HttpHandle requestPostWithLLSD(LLCore::HttpRequest::ptr_t & request, + LLCore::HttpRequest::policy_t policy_id, + LLCore::HttpRequest::priority_t priority, + const std::string & url, + const LLSD & body, + const LLCore::HttpHandler::ptr_t &handler) +{ + LLCore::HttpOptions::ptr_t options; + LLCore::HttpHeaders::ptr_t headers; + + return requestPostWithLLSD(request.get(), policy_id, priority, + url, body, options, headers, handler); +} + + +/// Issue a standard HttpRequest::requestPut() call but using +/// and LLSD object as the request body. Conventions are the +/// same as with that method. Caller is expected to provide +/// an HttpHeaders object with a correct 'Content-Type:' header. +/// One will not be provided by this call. +/// +/// @return If request is successfully issued, the +/// HttpHandle representing the request. +/// On error, LLCORE_HTTP_HANDLE_INVALID +/// is returned and caller can fetch detailed +/// status with the getStatus() method on the +/// request object. In case of error, no +/// request is queued and caller may need to +/// perform additional cleanup such as freeing +/// a now-useless HttpHandler object. +/// +LLCore::HttpHandle requestPutWithLLSD(LLCore::HttpRequest * request, + LLCore::HttpRequest::policy_t policy_id, + LLCore::HttpRequest::priority_t priority, + const std::string & url, + const LLSD & body, + const LLCore::HttpOptions::ptr_t &options, + const LLCore::HttpHeaders::ptr_t &headers, + const LLCore::HttpHandler::ptr_t &handler); + +inline LLCore::HttpHandle requestPutWithLLSD(LLCore::HttpRequest::ptr_t & request, + LLCore::HttpRequest::policy_t policy_id, + LLCore::HttpRequest::priority_t priority, + const std::string & url, + const LLSD & body, + const LLCore::HttpOptions::ptr_t & options, + const LLCore::HttpHeaders::ptr_t & headers, + LLCore::HttpHandler::ptr_t handler) +{ + return requestPutWithLLSD(request.get(), policy_id, priority, + url, body, options, headers, handler); +} + +inline LLCore::HttpHandle requestPutWithLLSD(LLCore::HttpRequest::ptr_t & request, + LLCore::HttpRequest::policy_t policy_id, + LLCore::HttpRequest::priority_t priority, + const std::string & url, + const LLSD & body, + LLCore::HttpHandler::ptr_t handler) +{ + LLCore::HttpOptions::ptr_t options; + LLCore::HttpHeaders::ptr_t headers; + + return requestPutWithLLSD(request.get(), policy_id, priority, + url, body, options, headers, handler); +} + +/// Issue a standard HttpRequest::requestPatch() call but using +/// and LLSD object as the request body. Conventions are the +/// same as with that method. Caller is expected to provide +/// an HttpHeaders object with a correct 'Content-Type:' header. +/// One will not be provided by this call. +/// +/// @return If request is successfully issued, the +/// HttpHandle representing the request. +/// On error, LLCORE_HTTP_HANDLE_INVALID +/// is returned and caller can fetch detailed +/// status with the getStatus() method on the +/// request object. In case of error, no +/// request is queued and caller may need to +/// perform additional cleanup such as freeing +/// a now-useless HttpHandler object. +/// +LLCore::HttpHandle requestPatchWithLLSD(LLCore::HttpRequest * request, + LLCore::HttpRequest::policy_t policy_id, + LLCore::HttpRequest::priority_t priority, + const std::string & url, + const LLSD & body, + const LLCore::HttpOptions::ptr_t &options, + const LLCore::HttpHeaders::ptr_t &headers, + const LLCore::HttpHandler::ptr_t &handler); + +inline LLCore::HttpHandle requestPatchWithLLSD(LLCore::HttpRequest::ptr_t & request, + LLCore::HttpRequest::policy_t policy_id, + LLCore::HttpRequest::priority_t priority, + const std::string & url, + const LLSD & body, + const LLCore::HttpOptions::ptr_t & options, + const LLCore::HttpHeaders::ptr_t & headers, + const LLCore::HttpHandler::ptr_t & handler) +{ + return requestPatchWithLLSD(request.get(), policy_id, priority, + url, body, options, headers, handler); +} + +inline LLCore::HttpHandle requestPatchWithLLSD(LLCore::HttpRequest::ptr_t & request, + LLCore::HttpRequest::policy_t policy_id, + LLCore::HttpRequest::priority_t priority, + const std::string & url, + const LLSD & body, + const LLCore::HttpHandler::ptr_t &handler) +{ + LLCore::HttpOptions::ptr_t options; + LLCore::HttpHeaders::ptr_t headers; + + return requestPatchWithLLSD(request.get(), policy_id, priority, + url, body, options, headers, handler); +} + +//========================================================================= +/// The HttpCoroHandler is a specialization of the LLCore::HttpHandler for +/// interacting with coroutines. When the request is completed the response +/// will be posted onto the supplied Event Pump. +/// +/// The LLSD posted back to the coroutine will have the following additions: +/// llsd["http_result"] -+- ["message"] - An error message returned from the HTTP status +/// +- ["status"] - The status code associated with the HTTP call +/// +- ["success"] - Success of failure of the HTTP call and LLSD parsing. +/// +- ["type"] - The LLCore::HttpStatus type associted with the HTTP call +/// +- ["url"] - The URL used to make the call. +/// +- ["headers"] - A map of name name value pairs with the HTTP headers. +/// +class HttpCoroHandler : public LLCore::HttpHandler +{ +public: + + typedef boost::shared_ptr<HttpCoroHandler> ptr_t; + typedef boost::weak_ptr<HttpCoroHandler> wptr_t; + + HttpCoroHandler(LLEventStream &reply); + + static void writeStatusCodes(LLCore::HttpStatus status, const std::string &url, LLSD &result); + + virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response); + + inline LLEventStream &getReplyPump() + { + return mReplyPump; + } + +protected: + /// this method may modify the status value + virtual LLSD handleSuccess(LLCore::HttpResponse * response, LLCore::HttpStatus &status) = 0; + virtual LLSD parseBody(LLCore::HttpResponse *response, bool &success) = 0; + +private: + void buildStatusEntry(LLCore::HttpResponse *response, LLCore::HttpStatus status, LLSD &result); + LLEventStream &mReplyPump; +}; + +//========================================================================= +/// An adapter to handle some of the boilerplate code surrounding HTTP and coroutine +/// interaction. +/// +/// Construct an HttpCoroutineAdapter giving it a name and policy Id. After +/// any application specific setup call the post, put or get method. The request +/// will be automatically pumped and the method will return with an LLSD describing +/// the result of the operation. See HttpCoroHandler for a description of the +/// decoration done to the returned LLSD. +/// +/// Posting through the adapter will automatically add the following headers to +/// the request if they have not been previously specified in a supplied +/// HttpHeaders object: +/// "Accept=application/llsd+xml" +/// "X-SecondLife-UDP-Listen-Port=###" +/// +class HttpCoroutineAdapter +{ +public: + static const std::string HTTP_RESULTS; + static const std::string HTTP_RESULTS_SUCCESS; + static const std::string HTTP_RESULTS_TYPE; + static const std::string HTTP_RESULTS_STATUS; + static const std::string HTTP_RESULTS_MESSAGE; + static const std::string HTTP_RESULTS_URL; + static const std::string HTTP_RESULTS_HEADERS; + static const std::string HTTP_RESULTS_CONTENT; + static const std::string HTTP_RESULTS_RAW; + + typedef boost::shared_ptr<HttpCoroutineAdapter> ptr_t; + typedef boost::weak_ptr<HttpCoroutineAdapter> wptr_t; + + HttpCoroutineAdapter(const std::string &name, LLCore::HttpRequest::policy_t policyId, + LLCore::HttpRequest::priority_t priority = 0L); + ~HttpCoroutineAdapter(); + + /// Execute a Post transaction on the supplied URL and yield execution of + /// the coroutine until a result is available. + /// + /// @Note: the request's smart pointer is passed by value so that it will + /// not be deallocated during the yield. + LLSD postAndSuspend(LLCore::HttpRequest::ptr_t request, + const std::string & url, const LLSD & body, + LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), + LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders())); + LLSD postAndSuspend(LLCore::HttpRequest::ptr_t request, + const std::string & url, LLCore::BufferArray::ptr_t rawbody, + LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), + LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders())); + + LLSD postAndSuspend(LLCore::HttpRequest::ptr_t &request, + const std::string & url, const LLSD & body, + LLCore::HttpHeaders::ptr_t &headers) + { + return postAndSuspend(request, url, body, + LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), headers); + } + + LLSD postAndSuspend(LLCore::HttpRequest::ptr_t &request, + const std::string & url, LLCore::BufferArray::ptr_t &rawbody, + LLCore::HttpHeaders::ptr_t &headers) + { + return postAndSuspend(request, url, rawbody, + LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), headers); + } + + LLSD postRawAndSuspend(LLCore::HttpRequest::ptr_t request, + const std::string & url, LLCore::BufferArray::ptr_t rawbody, + LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), + LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders())); + + LLSD postRawAndSuspend(LLCore::HttpRequest::ptr_t &request, + const std::string & url, LLCore::BufferArray::ptr_t &rawbody, + LLCore::HttpHeaders::ptr_t &headers) + { + return postRawAndSuspend(request, url, rawbody, + LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), headers); + } + + LLSD postFileAndSuspend(LLCore::HttpRequest::ptr_t request, + const std::string & url, std::string fileName, + LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), + LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders())); + + LLSD postFileAndSuspend(LLCore::HttpRequest::ptr_t &request, + const std::string & url, std::string fileName, + LLCore::HttpHeaders::ptr_t &headers) + { + return postFileAndSuspend(request, url, fileName, + LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), headers); + } + + + LLSD postFileAndSuspend(LLCore::HttpRequest::ptr_t request, + const std::string & url, LLUUID assetId, LLAssetType::EType assetType, + LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), + LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders())); + + LLSD postFileAndSuspend(LLCore::HttpRequest::ptr_t request, + const std::string & url, LLUUID assetId, LLAssetType::EType assetType, + LLCore::HttpHeaders::ptr_t &headers) + { + return postFileAndSuspend(request, url, assetId, assetType, + LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), headers); + } + + LLSD postJsonAndSuspend(LLCore::HttpRequest::ptr_t request, + const std::string & url, const LLSD & body, + LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), + LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders())); + LLSD postJsonAndSuspend(LLCore::HttpRequest::ptr_t &request, + const std::string & url, const LLSD & body, + LLCore::HttpHeaders::ptr_t &headers) + { + return postJsonAndSuspend(request, url, body, + LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), headers); + } + + + + /// Execute a Put transaction on the supplied URL and yield execution of + /// the coroutine until a result is available. + /// + /// @Note: the request's smart pointer is passed by value so that it will + /// not be deallocated during the yield. + LLSD putAndSuspend(LLCore::HttpRequest::ptr_t request, + const std::string & url, const LLSD & body, + LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), + LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders())); + + LLSD putAndSuspend(LLCore::HttpRequest::ptr_t request, + const std::string & url, const LLSD & body, + LLCore::HttpHeaders::ptr_t headers) + { + return putAndSuspend(request, url, body, + LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), headers); + } + + LLSD putJsonAndSuspend(LLCore::HttpRequest::ptr_t request, + const std::string & url, const LLSD & body, + LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), + LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders())); + LLSD putJsonAndSuspend(LLCore::HttpRequest::ptr_t &request, + const std::string & url, const LLSD & body, + LLCore::HttpHeaders::ptr_t &headers) + { + return putJsonAndSuspend(request, url, body, + LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), headers); + } + + /// Execute a Get transaction on the supplied URL and yield execution of + /// the coroutine until a result is available. + /// + /// @Note: the request's smart pointer is passed by value so that it will + /// not be deallocated during the yield. + /// + LLSD getAndSuspend(LLCore::HttpRequest::ptr_t request, + const std::string & url, + LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), + LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders())); + LLSD getAndSuspend(LLCore::HttpRequest::ptr_t &request, + const std::string & url, LLCore::HttpHeaders::ptr_t &headers) + { + return getAndSuspend(request, url, + LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), + headers); + } + + LLSD getRawAndSuspend(LLCore::HttpRequest::ptr_t request, + const std::string & url, + LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), + LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders())); + LLSD getRawAndSuspend(LLCore::HttpRequest::ptr_t &request, + const std::string & url, LLCore::HttpHeaders::ptr_t &headers) + { + return getRawAndSuspend(request, url, + LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), + headers); + } + + /// These methods have the same behavior as @getAndSuspend() however they are + /// expecting the server to return the results formatted in a JSON string. + /// On a successful GET call the JSON results will be converted into LLSD + /// before being returned to the caller. + LLSD getJsonAndSuspend(LLCore::HttpRequest::ptr_t request, + const std::string & url, + LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), + LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders())); + LLSD getJsonAndSuspend(LLCore::HttpRequest::ptr_t &request, + const std::string & url, LLCore::HttpHeaders::ptr_t &headers) + { + return getJsonAndSuspend(request, url, + LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), + headers); + } + + + /// Execute a DELETE transaction on the supplied URL and yield execution of + /// the coroutine until a result is available. + /// + /// @Note: the request's smart pointer is passed by value so that it will + /// not be deallocated during the yield. + LLSD deleteAndSuspend(LLCore::HttpRequest::ptr_t request, + const std::string & url, + LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), + LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders())); + LLSD deleteAndSuspend(LLCore::HttpRequest::ptr_t request, + const std::string & url, LLCore::HttpHeaders::ptr_t headers) + { + return deleteAndSuspend(request, url, + LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), + headers); + } + + /// These methods have the same behavior as @deleteAndSuspend() however they are + /// expecting the server to return any results formatted in a JSON string. + /// On a successful DELETE call the JSON results will be converted into LLSD + /// before being returned to the caller. + LLSD deleteJsonAndSuspend(LLCore::HttpRequest::ptr_t request, + const std::string & url, + LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), + LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders())); + LLSD deleteJsonAndSuspend(LLCore::HttpRequest::ptr_t request, + const std::string & url, LLCore::HttpHeaders::ptr_t headers) + { + return deleteJsonAndSuspend(request, url, + LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), + headers); + } + + + /// Execute a PATCH transaction on the supplied URL and yield execution of + /// the coroutine until a result is available. + /// + /// @Note: the request's smart pointer is passed by value so that it will + /// not be deallocated during the yield. + LLSD patchAndSuspend(LLCore::HttpRequest::ptr_t request, + const std::string & url, const LLSD & body, + LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), + LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders())); + LLSD patchAndSuspend(LLCore::HttpRequest::ptr_t &request, + const std::string & url, const LLSD & body, + LLCore::HttpHeaders::ptr_t &headers) + { + return patchAndSuspend(request, url, body, + LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), headers); + } + + /// Execute a COPY transaction on the supplied URL and yield execution of + /// the coroutine until a result is available. + /// + /// @Note: The destination is passed through the HTTP pipe as a header + /// The header used is defined as: HTTP_OUT_HEADER_DESTINATION("Destination"); + /// + /// @Note: the request's smart pointer is passed by value so that it will + /// not be deallocated during the yield. + LLSD copyAndSuspend(LLCore::HttpRequest::ptr_t request, + const std::string & url, const std::string dest, + LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), + LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders())); + LLSD copyAndSuspend(LLCore::HttpRequest::ptr_t &request, + const std::string & url, const std::string & dest, + LLCore::HttpHeaders::ptr_t &headers) + { + return copyAndSuspend(request, url, dest, + LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), headers); + } + + /// Execute a MOVE transaction on the supplied URL and yield execution of + /// the coroutine until a result is available. + /// + /// @Note: The destination is passed through the HTTP pipe in the headers. + /// The header used is defined as: HTTP_OUT_HEADER_DESTINATION("Destination"); + /// + /// @Note: the request's smart pointer is passed by value so that it will + /// not be deallocated during the yield. + LLSD moveAndSuspend(LLCore::HttpRequest::ptr_t request, + const std::string & url, const std::string dest, + LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), + LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders())); + LLSD moveAndSuspend(LLCore::HttpRequest::ptr_t &request, + const std::string & url, const std::string & dest, + LLCore::HttpHeaders::ptr_t &headers) + { + return moveAndSuspend(request, url, dest, + LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()), headers); + } + + /// + void cancelSuspendedOperation(); + + static LLCore::HttpStatus getStatusFromLLSD(const LLSD &httpResults); + + /// The convenience routines below can be provided with callback functors + /// which will be invoked in the case of success or failure. These callbacks + /// should match this form. + /// @sa callbackHttpGet + /// @sa callbackHttpPost + typedef boost::function<void(const LLSD &)> completionCallback_t; + + static void callbackHttpGet(const std::string &url, LLCore::HttpRequest::policy_t policyId, completionCallback_t success = NULL, completionCallback_t failure = NULL); + static void callbackHttpGet(const std::string &url, completionCallback_t success = NULL, completionCallback_t failure = NULL) + { + callbackHttpGet(url, LLCore::HttpRequest::DEFAULT_POLICY_ID, success, failure); + } + static void callbackHttpPost(const std::string &url, LLCore::HttpRequest::policy_t policyId, const LLSD &postData, completionCallback_t success = NULL, completionCallback_t failure = NULL); + static void callbackHttpPost(const std::string &url, const LLSD &postData, completionCallback_t success = NULL, completionCallback_t failure = NULL) + { + callbackHttpPost(url, LLCore::HttpRequest::DEFAULT_POLICY_ID, postData, success, failure); + } + + /// Generic Get and post routines for HTTP via coroutines. + /// These static methods do all required setup for the GET or POST operation. + /// When the operation completes successfully they will put the success message in the log at INFO level, + /// If the operation fails the failure message is written to the log at WARN level. + /// + static void messageHttpGet(const std::string &url, const std::string &success = std::string(), const std::string &failure = std::string()); + static void messageHttpPost(const std::string &url, const LLSD &postData, const std::string &success, const std::string &failure); + + +private: + static LLSD buildImmediateErrorResult(const LLCore::HttpRequest::ptr_t &request, const std::string &url); + + void saveState(LLCore::HttpHandle yieldingHandle, LLCore::HttpRequest::ptr_t &request, + HttpCoroHandler::ptr_t &handler); + void cleanState(); + + LLSD postAndSuspend_(LLCore::HttpRequest::ptr_t &request, + const std::string & url, const LLSD & body, + LLCore::HttpOptions::ptr_t &options, LLCore::HttpHeaders::ptr_t &headers, + HttpCoroHandler::ptr_t &handler); + + LLSD postAndSuspend_(LLCore::HttpRequest::ptr_t &request, + const std::string & url, LLCore::BufferArray::ptr_t &rawbody, + LLCore::HttpOptions::ptr_t &options, LLCore::HttpHeaders::ptr_t &headers, + HttpCoroHandler::ptr_t &handler); + + LLSD putAndSuspend_(LLCore::HttpRequest::ptr_t &request, + const std::string & url, const LLSD & body, + LLCore::HttpOptions::ptr_t &options, LLCore::HttpHeaders::ptr_t &headers, + HttpCoroHandler::ptr_t &handler); + + LLSD putAndSuspend_(LLCore::HttpRequest::ptr_t &request, + const std::string & url, const LLCore::BufferArray::ptr_t & rawbody, + LLCore::HttpOptions::ptr_t &options, LLCore::HttpHeaders::ptr_t &headers, + HttpCoroHandler::ptr_t &handler); + + LLSD getAndSuspend_(LLCore::HttpRequest::ptr_t &request, + const std::string & url, LLCore::HttpOptions::ptr_t &options, + LLCore::HttpHeaders::ptr_t &headers, HttpCoroHandler::ptr_t &handler); + + LLSD deleteAndSuspend_(LLCore::HttpRequest::ptr_t &request, + const std::string & url, LLCore::HttpOptions::ptr_t &options, + LLCore::HttpHeaders::ptr_t &headers, HttpCoroHandler::ptr_t &handler); + + LLSD patchAndSuspend_(LLCore::HttpRequest::ptr_t &request, + const std::string & url, const LLSD & body, + LLCore::HttpOptions::ptr_t &options, LLCore::HttpHeaders::ptr_t &headers, + HttpCoroHandler::ptr_t &handler); + + LLSD copyAndSuspend_(LLCore::HttpRequest::ptr_t &request, + const std::string & url, + LLCore::HttpOptions::ptr_t &options, LLCore::HttpHeaders::ptr_t &headers, + HttpCoroHandler::ptr_t &handler); + + LLSD moveAndSuspend_(LLCore::HttpRequest::ptr_t &request, + const std::string & url, + LLCore::HttpOptions::ptr_t &options, LLCore::HttpHeaders::ptr_t &headers, + HttpCoroHandler::ptr_t &handler); + + static void trivialGetCoro(std::string url, LLCore::HttpRequest::policy_t policyId, completionCallback_t success, completionCallback_t failure); + static void trivialPostCoro(std::string url, LLCore::HttpRequest::policy_t policyId, LLSD postData, completionCallback_t success, completionCallback_t failure); + + void checkDefaultHeaders(LLCore::HttpHeaders::ptr_t &headers); + + std::string mAdapterName; + LLCore::HttpRequest::priority_t mPriority; + LLCore::HttpRequest::policy_t mPolicyId; + + LLCore::HttpHandle mYieldingHandle; + LLCore::HttpRequest::wptr_t mWeakRequest; + HttpCoroHandler::wptr_t mWeakHandler; +}; + + +} // end namespace LLCoreHttpUtil #endif // LL_LLCOREHTTPUTIL_H diff --git a/indra/llmessage/llcurl.cpp b/indra/llmessage/llcurl.cpp deleted file mode 100644 index 7e9ae8d108..0000000000 --- a/indra/llmessage/llcurl.cpp +++ /dev/null @@ -1,2043 +0,0 @@ -/** - * @file llcurl.cpp - * @author Zero / Donovan - * @date 2006-10-15 - * @brief Implementation of wrapper around libcurl. - * - * $LicenseInfo:firstyear=2006&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010-2013, 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$ - */ - -#if LL_WINDOWS -#define SAFE_SSL 1 -#elif LL_DARWIN -#define SAFE_SSL 1 -#else -#define SAFE_SSL 1 -#endif - -#include "linden_common.h" - -#include "llcurl.h" - -#include <algorithm> -#include <iomanip> -#include <curl/curl.h> -#if SAFE_SSL -#include <openssl/crypto.h> -#endif - -#include "llbufferstream.h" -#include "llproxy.h" -#include "llsdserialize.h" -#include "llstl.h" -#include "llstring.h" -#include "llthread.h" -#include "lltimer.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. - */ - -////////////////////////////////////////////////////////////////////////////// - -static const U32 EASY_HANDLE_POOL_SIZE = 5; -static const S32 MULTI_PERFORM_CALL_REPEAT = 5; -static const S32 CURL_REQUEST_TIMEOUT = 120; // seconds per operation -static const S32 CURL_CONNECT_TIMEOUT = 30; //seconds to wait for a connection -static const S32 MAX_ACTIVE_REQUEST_COUNT = 100; - -// DEBUG // -S32 gCurlEasyCount = 0; -S32 gCurlMultiCount = 0; - -////////////////////////////////////////////////////////////////////////////// - -//static -std::vector<LLMutex*> LLCurl::sSSLMutex; -std::string LLCurl::sCAPath; -std::string LLCurl::sCAFile; -LLCurlThread* LLCurl::sCurlThread = NULL ; -LLMutex* LLCurl::sHandleMutexp = NULL ; -S32 LLCurl::sTotalHandles = 0 ; -bool LLCurl::sNotQuitting = true; -F32 LLCurl::sCurlRequestTimeOut = 120.f; //seonds -S32 LLCurl::sMaxHandles = 256; //max number of handles, (multi handles and easy handles combined). -CURL* LLCurl::sCurlTemplateStandardHandle = NULL; - -void check_curl_code(CURLcode code) -{ - if (code != CURLE_OK) - { - // linux appears to throw a curl error once per session for a bad initialization - // at a pretty random time (when enabling cookies). - LL_WARNS("curl") << "curl error detected: " << curl_easy_strerror(code) << LL_ENDL; - } -} - -void check_curl_multi_code(CURLMcode code) -{ - if (code != CURLM_OK) - { - // linux appears to throw a curl error once per session for a bad initialization - // at a pretty random time (when enabling cookies). - LL_WARNS("curl") << "curl multi error detected: " << curl_multi_strerror(code) << LL_ENDL; - } -} - -//static -void LLCurl::setCAPath(const std::string& path) -{ - sCAPath = path; -} - -//static -void LLCurl::setCAFile(const std::string& file) -{ - sCAFile = file; -} - -//static -std::string LLCurl::getVersionString() -{ - return std::string(curl_version()); -} - -////////////////////////////////////////////////////////////////////////////// - -LLCurl::Responder::Responder() - : mHTTPMethod(HTTP_INVALID), mStatus(HTTP_INTERNAL_ERROR) -{ -} - -LLCurl::Responder::~Responder() -{ - LL_CHECK_MEMORY -} - -// virtual -void LLCurl::Responder::httpFailure() -{ - LL_WARNS("curl") << dumpResponse() << LL_ENDL; -} - -std::string LLCurl::Responder::dumpResponse() const -{ - std::ostringstream s; - s << "[" << httpMethodAsVerb(mHTTPMethod) << ":" << mURL << "] " - << "[status:" << mStatus << "] " - << "[reason:" << mReason << "] "; - - if (mResponseHeaders.has(HTTP_IN_HEADER_CONTENT_TYPE)) - { - s << "[content-type:" << mResponseHeaders[HTTP_IN_HEADER_CONTENT_TYPE] << "] "; - } - - s << "[content:" << mContent << "]"; - - return s.str(); -} - -// virtual -void LLCurl::Responder::httpSuccess() -{ -} - -void LLCurl::Responder::setURL(const std::string& url) -{ - mURL = url; -} - -const std::string& LLCurl::Responder::getURL() -{ - return mURL; -} - -void LLCurl::Responder::successResult(const LLSD& content) -{ - setResult(HTTP_OK, "", content); - httpSuccess(); -} - -void LLCurl::Responder::failureResult(S32 status, const std::string& reason, const LLSD& content /* = LLSD() */) -{ - setResult(status, reason, content); - httpFailure(); -} - -void LLCurl::Responder::completeResult(S32 status, const std::string& reason, const LLSD& content /* = LLSD() */) -{ - setResult(status, reason, content); - httpCompleted(); -} - -void LLCurl::Responder::setResult(S32 status, const std::string& reason, const LLSD& content /* = LLSD() */) -{ - mStatus = status; - mReason = reason; - mContent = content; -} - -void LLCurl::Responder::setHTTPMethod(EHTTPMethod method) -{ - mHTTPMethod = method; -} - -void LLCurl::Responder::setResponseHeader(const std::string& header, const std::string& value) -{ - mResponseHeaders[header] = value; -} - -const std::string& LLCurl::Responder::getResponseHeader(const std::string& header) const -{ - if (mResponseHeaders.has(header)) - { - return mResponseHeaders[header].asStringRef(); - } - static const std::string empty; - return empty; -} - -bool LLCurl::Responder::hasResponseHeader(const std::string& header) const -{ - if (mResponseHeaders.has(header)) return true; - return false; -} - -// virtual -void LLCurl::Responder::completedRaw( - const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer) -{ - LLBufferStream istr(channels, buffer.get()); - const bool emit_parse_errors = false; - - std::string debug_body("(empty)"); - bool parsed=true; - if (EOF == istr.peek()) - { - parsed=false; - } - // Try to parse body as llsd, no matter what 'content-type' says. - else if (LLSDParser::PARSE_FAILURE == LLSDSerialize::fromXML(mContent, istr, emit_parse_errors)) - { - parsed=false; - char body[1025]; - body[1024] = '\0'; - istr.seekg(0, std::ios::beg); - istr.get(body,1024); - if (strlen(body) > 0) - { - mContent = body; - debug_body = body; - } - } - - // Only emit a warning if we failed to parse when 'content-type' == 'application/llsd+xml' - if (!parsed && (HTTP_CONTENT_LLSD_XML == getResponseHeader(HTTP_IN_HEADER_CONTENT_TYPE))) - { - LL_WARNS() << "Failed to deserialize . " << mURL << " [status:" << mStatus << "] " - << "(" << mReason << ") body: " << debug_body << LL_ENDL; - } - - httpCompleted(); -} - -// virtual -void LLCurl::Responder::httpCompleted() -{ - if (isGoodStatus()) - { - httpSuccess(); - } - else - { - httpFailure(); - } -} - -////////////////////////////////////////////////////////////////////////////// - -std::set<CURL*> LLCurl::Easy::sFreeHandles; -std::set<CURL*> LLCurl::Easy::sActiveHandles; -LLMutex* LLCurl::Easy::sHandleMutexp = NULL ; - -//static -CURL* LLCurl::Easy::allocEasyHandle() -{ - llassert(LLCurl::getCurlThread()) ; - - CURL* ret = NULL; - - LLMutexLock lock(sHandleMutexp) ; - - if (sFreeHandles.empty()) - { - ret = LLCurl::newEasyHandle(); - } - else - { - ret = *(sFreeHandles.begin()); - sFreeHandles.erase(ret); - curl_easy_reset(ret); - } - - if (ret) - { - sActiveHandles.insert(ret); - } - - return ret; -} - -//static -void LLCurl::Easy::releaseEasyHandle(CURL* handle) -{ - static const S32 MAX_NUM_FREE_HANDLES = 32 ; - - if (!handle) - { - return ; //handle allocation failed. - //LL_ERRS() << "handle cannot be NULL!" << LL_ENDL; - } - - LLMutexLock lock(sHandleMutexp) ; - if (sActiveHandles.find(handle) != sActiveHandles.end()) - { - LL_CHECK_MEMORY - sActiveHandles.erase(handle); - LL_CHECK_MEMORY - if(sFreeHandles.size() < MAX_NUM_FREE_HANDLES) - { - sFreeHandles.insert(handle); - LL_CHECK_MEMORY - } - else - { - LLCurl::deleteEasyHandle(handle) ; - LL_CHECK_MEMORY - } - } - else - { - LL_ERRS() << "Invalid handle." << LL_ENDL; - } -} - -//static -void LLCurl::Easy::deleteAllActiveHandles() -{ - LLMutexLock lock(sHandleMutexp) ; - LL_CHECK_MEMORY - for (std::set<CURL*>::iterator activeHandle = sActiveHandles.begin(); activeHandle != sActiveHandles.end(); ++activeHandle) - { - CURL* curlHandle = *activeHandle; - LLCurl::deleteEasyHandle(curlHandle); - LL_CHECK_MEMORY - } - - sFreeHandles.clear(); -} - -//static -void LLCurl::Easy::deleteAllFreeHandles() -{ - LLMutexLock lock(sHandleMutexp) ; - LL_CHECK_MEMORY - for (std::set<CURL*>::iterator freeHandle = sFreeHandles.begin(); freeHandle != sFreeHandles.end(); ++freeHandle) - { - CURL* curlHandle = *freeHandle; - LLCurl::deleteEasyHandle(curlHandle); - LL_CHECK_MEMORY - } - - sFreeHandles.clear(); -} - -LLCurl::Easy::Easy() - : mHeaders(NULL), - mCurlEasyHandle(NULL) -{ - mErrorBuffer[0] = 0; -} - -LLCurl::Easy* LLCurl::Easy::getEasy() -{ - Easy* easy = new Easy(); - easy->mCurlEasyHandle = allocEasyHandle(); - - if (!easy->mCurlEasyHandle) - { - // this can happen if we have too many open files (fails in c-ares/ares_init.c) - LL_WARNS("curl") << "allocEasyHandle() returned NULL! Easy handles: " - << gCurlEasyCount << " Multi handles: " << gCurlMultiCount << LL_ENDL; - delete easy; - return NULL; - } - - // Enable a brief cache period for now. This was zero for the longest time - // which caused some routers grief and generated unneeded traffic. For the - // threaded resolver, we're using system resolution libraries and non-zero values - // are preferred. The c-ares resolver is another matter and it might not - // track server changes as well. - CURLcode result = curl_easy_setopt(easy->mCurlEasyHandle, CURLOPT_DNS_CACHE_TIMEOUT, 15); - check_curl_code(result); - result = curl_easy_setopt(easy->mCurlEasyHandle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); - check_curl_code(result); - - ++gCurlEasyCount; - return easy; -} - -LLCurl::Easy::~Easy() -{ - releaseEasyHandle(mCurlEasyHandle); - --gCurlEasyCount; - curl_slist_free_all(mHeaders); - LL_CHECK_MEMORY - for_each(mStrings.begin(), mStrings.end(), DeletePointerArray()); - LL_CHECK_MEMORY - if (mResponder && LLCurl::sNotQuitting) //aborted - { - // HTTP_REQUEST_TIME_OUT, timeout, abort - // *TODO: This looks like improper use of the 408 status code. - // See: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.9 - // This status code should be returned by the *server* when: - // "The client did not produce a request within the time that the server was prepared to wait." - mResponder->setResult(HTTP_REQUEST_TIME_OUT, "Request timeout, aborted."); - mResponder->completedRaw(mChannels, mOutput); - LL_CHECK_MEMORY - } - mResponder = NULL; -} - -void LLCurl::Easy::resetState() -{ - curl_easy_reset(mCurlEasyHandle); - - if (mHeaders) - { - curl_slist_free_all(mHeaders); - mHeaders = NULL; - } - - mRequest.str(""); - mRequest.clear(); - - mOutput.reset(); - - mInput.str(""); - mInput.clear(); - - mErrorBuffer[0] = 0; - - mHeaderOutput.str(""); - mHeaderOutput.clear(); -} - -void LLCurl::Easy::setErrorBuffer() -{ - setopt(CURLOPT_ERRORBUFFER, &mErrorBuffer); -} - -const char* LLCurl::Easy::getErrorBuffer() -{ - return mErrorBuffer; -} - -void LLCurl::Easy::setCA() -{ - if (!sCAPath.empty()) - { - setoptString(CURLOPT_CAPATH, sCAPath); - } - if (!sCAFile.empty()) - { - setoptString(CURLOPT_CAINFO, sCAFile); - } -} - -void LLCurl::Easy::setHeaders() -{ - setopt(CURLOPT_HTTPHEADER, mHeaders); -} - -void LLCurl::Easy::getTransferInfo(LLCurl::TransferInfo* info) -{ - check_curl_code(curl_easy_getinfo(mCurlEasyHandle, CURLINFO_SIZE_DOWNLOAD, &info->mSizeDownload)); - check_curl_code(curl_easy_getinfo(mCurlEasyHandle, CURLINFO_TOTAL_TIME, &info->mTotalTime)); - check_curl_code(curl_easy_getinfo(mCurlEasyHandle, CURLINFO_SPEED_DOWNLOAD, &info->mSpeedDownload)); -} - -S32 LLCurl::Easy::report(CURLcode code) -{ - S32 responseCode = 0; - std::string responseReason; - - if (code == CURLE_OK) - { - check_curl_code(curl_easy_getinfo(mCurlEasyHandle, CURLINFO_RESPONSE_CODE, &responseCode)); - //*TODO: get reason from first line of mHeaderOutput - } - else - { - responseCode = HTTP_INTERNAL_ERROR; - responseReason = strerror(code) + " : " + mErrorBuffer; - setopt(CURLOPT_FRESH_CONNECT, TRUE); - } - - if (mResponder) - { - mResponder->setResult(responseCode, responseReason); - mResponder->completedRaw(mChannels, mOutput); - mResponder = NULL; - } - - resetState(); - return responseCode; -} - -// Note: these all assume the caller tracks the value (i.e. keeps it persistant) -void LLCurl::Easy::setopt(CURLoption option, S32 value) -{ - CURLcode result = curl_easy_setopt(mCurlEasyHandle, option, value); - check_curl_code(result); -} - -void LLCurl::Easy::setopt(CURLoption option, void* value) -{ - CURLcode result = curl_easy_setopt(mCurlEasyHandle, option, value); - check_curl_code(result); -} - -void LLCurl::Easy::setopt(CURLoption option, char* value) -{ - CURLcode result = curl_easy_setopt(mCurlEasyHandle, option, value); - check_curl_code(result); -} - -// Note: this copies the string so that the caller does not have to keep it around -void LLCurl::Easy::setoptString(CURLoption option, const std::string& value) -{ - char* tstring = new char[value.length()+1]; - strcpy(tstring, value.c_str()); - mStrings.push_back(tstring); - CURLcode result = curl_easy_setopt(mCurlEasyHandle, option, tstring); - check_curl_code(result); -} - -void LLCurl::Easy::slist_append(const std::string& header, const std::string& value) -{ - std::string pair(header); - if (value.empty()) - { - pair += ":"; - } - else - { - pair += ": "; - pair += value; - } - slist_append(pair.c_str()); -} - -void LLCurl::Easy::slist_append(const char* str) -{ - if (str) - { - mHeaders = curl_slist_append(mHeaders, str); - if (!mHeaders) - { - LL_WARNS() << "curl_slist_append() call returned NULL appending " << str << LL_ENDL; - } - } -} - -size_t curlReadCallback(char* data, size_t size, size_t nmemb, void* user_data) -{ - LLCurl::Easy* easy = (LLCurl::Easy*)user_data; - - S32 n = size * nmemb; - S32 startpos = (S32)easy->getInput().tellg(); - easy->getInput().seekg(0, std::ios::end); - S32 endpos = (S32)easy->getInput().tellg(); - easy->getInput().seekg(startpos, std::ios::beg); - S32 maxn = endpos - startpos; - n = llmin(n, maxn); - easy->getInput().read((char*)data, n); - - return n; -} - -size_t curlWriteCallback(char* data, size_t size, size_t nmemb, void* user_data) -{ - LLCurl::Easy* easy = (LLCurl::Easy*)user_data; - - S32 n = size * nmemb; - easy->getOutput()->append(easy->getChannels().in(), (const U8*)data, n); - - return n; -} - -size_t curlHeaderCallback(void* data, size_t size, size_t nmemb, void* user_data) -{ - LLCurl::Easy* easy = (LLCurl::Easy*)user_data; - - size_t n = size * nmemb; - easy->getHeaderOutput().write((const char*)data, n); - - return n; -} - -void LLCurl::Easy::prepRequest(const std::string& url, - const std::vector<std::string>& headers, - ResponderPtr responder, S32 time_out, bool post) -{ - resetState(); - - if (post) setoptString(CURLOPT_ENCODING, ""); - - //setopt(CURLOPT_VERBOSE, 1); // useful for debugging - setopt(CURLOPT_NOSIGNAL, 1); - setopt(CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); - - // Set the CURL options for either Socks or HTTP proxy - LLProxy::getInstance()->applyProxySettings(this); - - mOutput.reset(new LLBufferArray); - mOutput->setThreaded(true); - setopt(CURLOPT_WRITEFUNCTION, (void*)&curlWriteCallback); - setopt(CURLOPT_WRITEDATA, (void*)this); - - setopt(CURLOPT_READFUNCTION, (void*)&curlReadCallback); - setopt(CURLOPT_READDATA, (void*)this); - - setopt(CURLOPT_HEADERFUNCTION, (void*)&curlHeaderCallback); - setopt(CURLOPT_HEADERDATA, (void*)this); - - // Allow up to five redirects - if (responder && responder->followRedir()) - { - setopt(CURLOPT_FOLLOWLOCATION, 1); - setopt(CURLOPT_MAXREDIRS, MAX_REDIRECTS); - } - - setErrorBuffer(); - setCA(); - - setopt(CURLOPT_SSL_VERIFYPEER, true); - - //don't verify host name so urls with scrubbed host names will work (improves DNS performance) - setopt(CURLOPT_SSL_VERIFYHOST, 0); - setopt(CURLOPT_TIMEOUT, llmax(time_out, CURL_REQUEST_TIMEOUT)); - setopt(CURLOPT_CONNECTTIMEOUT, CURL_CONNECT_TIMEOUT); - - setoptString(CURLOPT_URL, url); - - mResponder = responder; - - if (!post) - { - // *TODO: Should this be set to 'Keep-Alive' ? - slist_append(HTTP_OUT_HEADER_CONNECTION, "keep-alive"); - slist_append(HTTP_OUT_HEADER_KEEP_ALIVE, "300"); - // Accept and other headers - for (std::vector<std::string>::const_iterator iter = headers.begin(); - iter != headers.end(); ++iter) - { - slist_append((*iter).c_str()); - } - } -} - -//////////////////////////////////////////////////////////////////////////// -LLCurl::Multi::Multi(F32 idle_time_out) - : mQueued(0), - mErrorCount(0), - mState(STATE_READY), - mDead(FALSE), - mValid(TRUE), - mMutexp(NULL), - mDeletionMutexp(NULL), - mEasyMutexp(NULL) -{ - mCurlMultiHandle = LLCurl::newMultiHandle(); - if (!mCurlMultiHandle) - { - LL_WARNS() << "curl_multi_init() returned NULL! Easy handles: " << gCurlEasyCount << " Multi handles: " << gCurlMultiCount << LL_ENDL; - mCurlMultiHandle = LLCurl::newMultiHandle(); - } - - //llassert_always(mCurlMultiHandle); - - if(mCurlMultiHandle) - { - if(LLCurl::getCurlThread()->getThreaded()) - { - mMutexp = new LLMutex(NULL) ; - mDeletionMutexp = new LLMutex(NULL) ; - mEasyMutexp = new LLMutex(NULL) ; - } - LLCurl::getCurlThread()->addMulti(this) ; - - mIdleTimeOut = idle_time_out ; - if(mIdleTimeOut < LLCurl::sCurlRequestTimeOut) - { - mIdleTimeOut = LLCurl::sCurlRequestTimeOut ; - } - - ++gCurlMultiCount; -} -} - -LLCurl::Multi::~Multi() -{ - cleanup(true) ; - - delete mDeletionMutexp ; - mDeletionMutexp = NULL ; -} - -void LLCurl::Multi::cleanup(bool deleted) -{ - if(!mCurlMultiHandle) - { - return ; //nothing to clean. - } - llassert_always(deleted || !mValid) ; - - LLMutexLock lock(mDeletionMutexp); - - - // Clean up active - for(easy_active_list_t::iterator iter = mEasyActiveList.begin(); - iter != mEasyActiveList.end(); ++iter) - { - Easy* easy = *iter; - LL_CHECK_MEMORY - check_curl_multi_code(curl_multi_remove_handle(mCurlMultiHandle, easy->getCurlHandle())); - LL_CHECK_MEMORY - if(deleted) - { - easy->mResponder = NULL ; //avoid triggering mResponder. - LL_CHECK_MEMORY - } - delete easy; - LL_CHECK_MEMORY - } - mEasyActiveList.clear(); - mEasyActiveMap.clear(); - - LL_CHECK_MEMORY - - // Clean up freed - for_each(mEasyFreeList.begin(), mEasyFreeList.end(), DeletePointer()); - mEasyFreeList.clear(); - - LL_CHECK_MEMORY - - check_curl_multi_code(LLCurl::deleteMultiHandle(mCurlMultiHandle)); - mCurlMultiHandle = NULL ; - - LL_CHECK_MEMORY - - delete mMutexp ; - mMutexp = NULL ; - - LL_CHECK_MEMORY - - delete mEasyMutexp ; - mEasyMutexp = NULL ; - - LL_CHECK_MEMORY - - mQueued = 0 ; - mState = STATE_COMPLETED; - - --gCurlMultiCount; - - return ; -} - -void LLCurl::Multi::lock() -{ - if(mMutexp) - { - mMutexp->lock() ; - } -} - -void LLCurl::Multi::unlock() -{ - if(mMutexp) - { - mMutexp->unlock() ; - } -} - -void LLCurl::Multi::markDead() -{ - { - LLMutexLock lock(mDeletionMutexp) ; - - if(mCurlMultiHandle != NULL) - { - mDead = TRUE ; - LLCurl::getCurlThread()->setPriority(mHandle, LLQueuedThread::PRIORITY_URGENT) ; - - return; - } - } - - //not valid, delete it. - delete this; -} - -void LLCurl::Multi::setState(LLCurl::Multi::ePerformState state) -{ - lock() ; - mState = state ; - unlock() ; - - if(mState == STATE_READY) - { - LLCurl::getCurlThread()->setPriority(mHandle, LLQueuedThread::PRIORITY_NORMAL) ; - } -} - -LLCurl::Multi::ePerformState LLCurl::Multi::getState() -{ - return mState; -} - -bool LLCurl::Multi::isCompleted() -{ - return STATE_COMPLETED == getState() ; -} - -bool LLCurl::Multi::waitToComplete() -{ - if(!isValid()) - { - return true ; - } - - if(!mMutexp) //not threaded - { - doPerform() ; - return true ; - } - - bool completed = (STATE_COMPLETED == mState) ; - if(!completed) - { - LLCurl::getCurlThread()->setPriority(mHandle, LLQueuedThread::PRIORITY_HIGH) ; - } - - return completed; -} - -CURLMsg* LLCurl::Multi::info_read(S32* msgs_in_queue) -{ - LLMutexLock lock(mMutexp) ; - - CURLMsg* curlmsg = curl_multi_info_read(mCurlMultiHandle, msgs_in_queue); - return curlmsg; -} - -//return true if dead -bool LLCurl::Multi::doPerform() -{ - LLMutexLock lock(mDeletionMutexp) ; - - bool dead = mDead ; - - if(mDead) - { - setState(STATE_COMPLETED); - mQueued = 0 ; - } - else if(getState() != STATE_COMPLETED) - { - setState(STATE_PERFORMING); - - S32 q = 0; - for (S32 call_count = 0; - call_count < MULTI_PERFORM_CALL_REPEAT; - call_count++) - { - LLMutexLock lock(mMutexp) ; - - //WARNING: curl_multi_perform will block for many hundreds of milliseconds - // NEVER call this from the main thread, and NEVER allow the main thread to - // wait on a mutex held by this thread while curl_multi_perform is executing - CURLMcode code = curl_multi_perform(mCurlMultiHandle, &q); - if (CURLM_CALL_MULTI_PERFORM != code || q == 0) - { - check_curl_multi_code(code); - - break; - } - } - - mQueued = q; - setState(STATE_COMPLETED) ; - mIdleTimer.reset() ; - } - else if(!mValid && mIdleTimer.getElapsedTimeF32() > mIdleTimeOut) //idle for too long, remove it. - { - dead = true ; - } - else if(mValid && mIdleTimer.getElapsedTimeF32() > mIdleTimeOut - 1.f) //idle for too long, mark it invalid. - { - mValid = FALSE ; - } - - return dead ; -} - -S32 LLCurl::Multi::process() -{ - if(!isValid()) - { - return 0 ; - } - - waitToComplete() ; - - if (getState() != STATE_COMPLETED) - { - return 0; - } - - CURLMsg* msg; - int msgs_in_queue; - - S32 processed = 0; - while ((msg = info_read(&msgs_in_queue))) - { - ++processed; - if (msg->msg == CURLMSG_DONE) - { - S32 response = 0; - Easy* easy = NULL ; - - { - LLMutexLock lock(mEasyMutexp) ; - easy_active_map_t::iterator iter = mEasyActiveMap.find(msg->easy_handle); - if (iter != mEasyActiveMap.end()) - { - easy = iter->second; - } - } - - if(easy) - { - response = easy->report(msg->data.result); - removeEasy(easy); - } - else - { - response = HTTP_INTERNAL_ERROR; - //*TODO: change to LL_WARNS() - LL_ERRS() << "cleaned up curl request completed!" << LL_ENDL; - } - if (response >= 400) - { - // failure of some sort, inc mErrorCount for debugging and flagging multi for destruction - ++mErrorCount; - } - } - } - - setState(STATE_READY); - - return processed; -} - -LLCurl::Easy* LLCurl::Multi::allocEasy() -{ - Easy* easy = 0; - - if (mEasyFreeList.empty()) - { - easy = Easy::getEasy(); - } - else - { - LLMutexLock lock(mEasyMutexp) ; - easy = *(mEasyFreeList.begin()); - mEasyFreeList.erase(easy); - } - if (easy) - { - LLMutexLock lock(mEasyMutexp) ; - mEasyActiveList.insert(easy); - mEasyActiveMap[easy->getCurlHandle()] = easy; - } - return easy; -} - -bool LLCurl::Multi::addEasy(Easy* easy) -{ - LLMutexLock lock(mMutexp) ; - CURLMcode mcode = curl_multi_add_handle(mCurlMultiHandle, easy->getCurlHandle()); - check_curl_multi_code(mcode); - //if (mcode != CURLM_OK) - //{ - // LL_WARNS() << "Curl Error: " << curl_multi_strerror(mcode) << LL_ENDL; - // return false; - //} - return true; -} - -void LLCurl::Multi::easyFree(Easy* easy) -{ - if(mEasyMutexp) - { - mEasyMutexp->lock() ; - } - - mEasyActiveList.erase(easy); - mEasyActiveMap.erase(easy->getCurlHandle()); - - if (mEasyFreeList.size() < EASY_HANDLE_POOL_SIZE) - { - mEasyFreeList.insert(easy); - - if(mEasyMutexp) - { - mEasyMutexp->unlock() ; - } - - easy->resetState(); - } - else - { - if(mEasyMutexp) - { - mEasyMutexp->unlock() ; - } - delete easy; - } -} - -void LLCurl::Multi::removeEasy(Easy* easy) -{ - { - LLMutexLock lock(mMutexp) ; - check_curl_multi_code(curl_multi_remove_handle(mCurlMultiHandle, easy->getCurlHandle())); - } - easyFree(easy); -} - -//------------------------------------------------------------ -//LLCurlThread -LLCurlThread::CurlRequest::CurlRequest(handle_t handle, LLCurl::Multi* multi, LLCurlThread* curl_thread) : - LLQueuedThread::QueuedRequest(handle, LLQueuedThread::PRIORITY_NORMAL, FLAG_AUTO_COMPLETE), - mMulti(multi), - mCurlThread(curl_thread) -{ -} - -LLCurlThread::CurlRequest::~CurlRequest() -{ - if(mMulti) - { - mCurlThread->deleteMulti(mMulti) ; - mMulti = NULL ; - } -} - -bool LLCurlThread::CurlRequest::processRequest() -{ - bool completed = true ; - if(mMulti) - { - completed = mCurlThread->doMultiPerform(mMulti) ; - - if(!completed) - { - setPriority(LLQueuedThread::PRIORITY_LOW) ; - } - } - - return completed ; -} - -void LLCurlThread::CurlRequest::finishRequest(bool completed) -{ - if(mMulti->isDead()) - { - mCurlThread->deleteMulti(mMulti) ; - } - else - { - mCurlThread->cleanupMulti(mMulti) ; //being idle too long, remove the request. - } - - mMulti = NULL ; -} - -LLCurlThread::LLCurlThread(bool threaded) : - LLQueuedThread("curlthread", threaded) -{ -} - -//virtual -LLCurlThread::~LLCurlThread() -{ -} - -S32 LLCurlThread::update(F32 max_time_ms) -{ - return LLQueuedThread::update(max_time_ms); -} - -void LLCurlThread::addMulti(LLCurl::Multi* multi) -{ - multi->mHandle = generateHandle() ; - - CurlRequest* req = new CurlRequest(multi->mHandle, multi, this) ; - - if (!addRequest(req)) - { - LL_WARNS() << "curl request added when the thread is quitted" << LL_ENDL; - } -} - -void LLCurlThread::killMulti(LLCurl::Multi* multi) -{ - if(!multi) - { - return ; - } - - - multi->markDead() ; -} - -//private -bool LLCurlThread::doMultiPerform(LLCurl::Multi* multi) -{ - return multi->doPerform() ; -} - -//private -void LLCurlThread::deleteMulti(LLCurl::Multi* multi) -{ - delete multi ; -} - -//private -void LLCurlThread::cleanupMulti(LLCurl::Multi* multi) -{ - multi->cleanup() ; - if(multi->isDead()) //check if marked dead during cleaning up. - { - deleteMulti(multi) ; - } -} - -//------------------------------------------------------------ - -//static -std::string LLCurl::strerror(CURLcode errorcode) -{ - return std::string(curl_easy_strerror(errorcode)); -} - -//////////////////////////////////////////////////////////////////////////// -// For generating a simple request for data -// using one multi and one easy per request - -LLCurlRequest::LLCurlRequest() : - mActiveMulti(NULL), - mActiveRequestCount(0) -{ - mProcessing = FALSE; -} - -LLCurlRequest::~LLCurlRequest() -{ - //stop all Multi handle background threads - for (curlmulti_set_t::iterator iter = mMultiSet.begin(); iter != mMultiSet.end(); ++iter) - { - LLCurl::getCurlThread()->killMulti(*iter) ; - } - mMultiSet.clear() ; -} - -void LLCurlRequest::addMulti() -{ - LLCurl::Multi* multi = new LLCurl::Multi(); - if(!multi->isValid()) - { - LLCurl::getCurlThread()->killMulti(multi) ; - mActiveMulti = NULL ; - mActiveRequestCount = 0 ; - return; - } - - mMultiSet.insert(multi); - mActiveMulti = multi; - mActiveRequestCount = 0; -} - -LLCurl::Easy* LLCurlRequest::allocEasy() -{ - if (!mActiveMulti || - mActiveRequestCount >= MAX_ACTIVE_REQUEST_COUNT || - mActiveMulti->mErrorCount > 0) - { - addMulti(); - } - if(!mActiveMulti) - { - return NULL ; - } - - //llassert_always(mActiveMulti); - ++mActiveRequestCount; - LLCurl::Easy* easy = mActiveMulti->allocEasy(); - return easy; -} - -bool LLCurlRequest::addEasy(LLCurl::Easy* easy) -{ - llassert_always(mActiveMulti); - - if (mProcessing) - { - LL_ERRS() << "Posting to a LLCurlRequest instance from within a responder is not allowed (causes DNS timeouts)." << LL_ENDL; - } - bool res = mActiveMulti->addEasy(easy); - return res; -} - -void LLCurlRequest::get(const std::string& url, LLCurl::ResponderPtr responder) -{ - getByteRange(url, headers_t(), 0, -1, responder); -} - -// Note: (length==0) is interpreted as "the rest of the file", i.e. the whole file if (offset==0) or -// the remainder of the file if not. -bool LLCurlRequest::getByteRange(const std::string& url, - const headers_t& headers, - S32 offset, S32 length, - LLCurl::ResponderPtr responder) -{ - llassert(LLCurl::sNotQuitting); - LLCurl::Easy* easy = allocEasy(); - if (!easy) - { - return false; - } - easy->prepRequest(url, headers, responder); - easy->setopt(CURLOPT_HTTPGET, 1); - if (length > 0) - { - std::string range = llformat("bytes=%d-%d", offset,offset+length-1); - easy->slist_append(HTTP_OUT_HEADER_RANGE, range); - } - else if (offset > 0) - { - std::string range = llformat("bytes=%d-", offset); - easy->slist_append(HTTP_OUT_HEADER_RANGE, range); - } - easy->setHeaders(); - bool res = addEasy(easy); - return res; -} - -bool LLCurlRequest::post(const std::string& url, - const headers_t& headers, - const LLSD& data, - LLCurl::ResponderPtr responder, S32 time_out) -{ - llassert(LLCurl::sNotQuitting); - LLCurl::Easy* easy = allocEasy(); - if (!easy) - { - return false; - } - easy->prepRequest(url, headers, responder, time_out); - - LLSDSerialize::toXML(data, easy->getInput()); - S32 bytes = easy->getInput().str().length(); - - easy->setopt(CURLOPT_POST, 1); - easy->setopt(CURLOPT_POSTFIELDS, (void*)NULL); - easy->setopt(CURLOPT_POSTFIELDSIZE, bytes); - - easy->slist_append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_LLSD_XML); - easy->setHeaders(); - - LL_DEBUGS() << "POSTING: " << bytes << " bytes." << LL_ENDL; - bool res = addEasy(easy); - return res; -} - -bool LLCurlRequest::post(const std::string& url, - const headers_t& headers, - const std::string& data, - LLCurl::ResponderPtr responder, S32 time_out) -{ - llassert(LLCurl::sNotQuitting); - LLCurl::Easy* easy = allocEasy(); - if (!easy) - { - return false; - } - easy->prepRequest(url, headers, responder, time_out); - - easy->getInput().write(data.data(), data.size()); - S32 bytes = easy->getInput().str().length(); - - easy->setopt(CURLOPT_POST, 1); - easy->setopt(CURLOPT_POSTFIELDS, (void*)NULL); - easy->setopt(CURLOPT_POSTFIELDSIZE, bytes); - - easy->slist_append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_OCTET_STREAM); - easy->setHeaders(); - - LL_DEBUGS() << "POSTING: " << bytes << " bytes." << LL_ENDL; - bool res = addEasy(easy); - return res; -} - -// Note: call once per frame -S32 LLCurlRequest::process() -{ - S32 res = 0; - - mProcessing = TRUE; - for (curlmulti_set_t::iterator iter = mMultiSet.begin(); - iter != mMultiSet.end(); ) - { - curlmulti_set_t::iterator curiter = iter++; - LLCurl::Multi* multi = *curiter; - - if(!multi->isValid()) - { - if(multi == mActiveMulti) - { - mActiveMulti = NULL ; - mActiveRequestCount = 0 ; - } - mMultiSet.erase(curiter) ; - LLCurl::getCurlThread()->killMulti(multi) ; - continue ; - } - - S32 tres = multi->process(); - res += tres; - if (multi != mActiveMulti && tres == 0 && multi->mQueued == 0) - { - mMultiSet.erase(curiter); - LLCurl::getCurlThread()->killMulti(multi); - } - } - mProcessing = FALSE; - return res; -} - -S32 LLCurlRequest::getQueued() -{ - S32 queued = 0; - for (curlmulti_set_t::iterator iter = mMultiSet.begin(); - iter != mMultiSet.end(); ) - { - curlmulti_set_t::iterator curiter = iter++; - LLCurl::Multi* multi = *curiter; - - if(!multi->isValid()) - { - if(multi == mActiveMulti) - { - mActiveMulti = NULL ; - mActiveRequestCount = 0 ; - } - LLCurl::getCurlThread()->killMulti(multi); - mMultiSet.erase(curiter) ; - continue ; - } - - queued += multi->mQueued; - if (multi->getState() != LLCurl::Multi::STATE_READY) - { - ++queued; - } - } - return queued; -} - -LLCurlTextureRequest::LLCurlTextureRequest(S32 concurrency) : - LLCurlRequest(), - mConcurrency(concurrency), - mInQueue(0), - mMutex(NULL), - mHandleCounter(1), - mTotalIssuedRequests(0), - mTotalReceivedBits(0) -{ - mGlobalTimer.reset(); -} - -LLCurlTextureRequest::~LLCurlTextureRequest() -{ - mRequestMap.clear(); - - for(req_queue_t::iterator iter = mCachedRequests.begin(); iter != mCachedRequests.end(); ++iter) - { - delete *iter; - } - mCachedRequests.clear(); -} - -//return 0: success -// > 0: cached handle -U32 LLCurlTextureRequest::getByteRange(const std::string& url, - const headers_t& headers, - S32 offset, S32 length, U32 pri, - LLCurl::ResponderPtr responder, F32 delay_time) -{ - U32 ret_val = 0; - bool success = false; - - if(mInQueue < mConcurrency && delay_time < 0.f) - { - success = LLCurlRequest::getByteRange(url, headers, offset, length, responder); - } - - LLMutexLock lock(&mMutex); - - if(success) - { - mInQueue++; - mTotalIssuedRequests++; - } - else - { - request_t* request = new request_t(mHandleCounter, url, headers, offset, length, pri, responder); - if(delay_time > 0.f) - { - request->mStartTime = mGlobalTimer.getElapsedTimeF32() + delay_time; - } - - mCachedRequests.insert(request); - mRequestMap[mHandleCounter] = request; - ret_val = mHandleCounter; - mHandleCounter++; - - if(!mHandleCounter) - { - mHandleCounter = 1; - } - } - - return ret_val; -} - -void LLCurlTextureRequest::completeRequest(S32 received_bytes) -{ - LLMutexLock lock(&mMutex); - - llassert_always(mInQueue > 0); - - mInQueue--; - mTotalReceivedBits += received_bytes * 8; -} - -void LLCurlTextureRequest::nextRequests() -{ - if(mCachedRequests.empty() || mInQueue >= mConcurrency) - { - return; - } - - F32 cur_time = mGlobalTimer.getElapsedTimeF32(); - - req_queue_t::iterator iter; - { - LLMutexLock lock(&mMutex); - iter = mCachedRequests.begin(); - } - while(1) - { - request_t* request = *iter; - if(request->mStartTime < cur_time) - { - if(!LLCurlRequest::getByteRange(request->mUrl, request->mHeaders, request->mOffset, request->mLength, request->mResponder)) - { - break; - } - - LLMutexLock lock(&mMutex); - ++iter; - mInQueue++; - mTotalIssuedRequests++; - mCachedRequests.erase(request); - mRequestMap.erase(request->mHandle); - delete request; - - if(iter == mCachedRequests.end() || mInQueue >= mConcurrency) - { - break; - } - } - else - { - LLMutexLock lock(&mMutex); - ++iter; - if(iter == mCachedRequests.end() || mInQueue >= mConcurrency) - { - break; - } - } - } - - return; -} - -void LLCurlTextureRequest::updatePriority(U32 handle, U32 pri) -{ - if(!handle) - { - return; - } - - LLMutexLock lock(&mMutex); - - std::map<S32, request_t*>::iterator iter = mRequestMap.find(handle); - if(iter != mRequestMap.end()) - { - request_t* req = iter->second; - - if(req->mPriority != pri) - { - mCachedRequests.erase(req); - req->mPriority = pri; - mCachedRequests.insert(req); - } - } -} - -void LLCurlTextureRequest::removeRequest(U32 handle) -{ - if(!handle) - { - return; - } - - LLMutexLock lock(&mMutex); - - std::map<S32, request_t*>::iterator iter = mRequestMap.find(handle); - if(iter != mRequestMap.end()) - { - request_t* req = iter->second; - mRequestMap.erase(iter); - mCachedRequests.erase(req); - delete req; - } -} - -bool LLCurlTextureRequest::isWaiting(U32 handle) -{ - if(!handle) - { - return false; - } - - LLMutexLock lock(&mMutex); - return mRequestMap.find(handle) != mRequestMap.end(); -} - -U32 LLCurlTextureRequest::getTotalReceivedBits() -{ - LLMutexLock lock(&mMutex); - - U32 bits = mTotalReceivedBits; - mTotalReceivedBits = 0; - return bits; -} - -U32 LLCurlTextureRequest::getTotalIssuedRequests() -{ - LLMutexLock lock(&mMutex); - return mTotalIssuedRequests; -} - -S32 LLCurlTextureRequest::getNumRequests() -{ - LLMutexLock lock(&mMutex); - return mInQueue; -} - -//////////////////////////////////////////////////////////////////////////// -// For generating one easy request -// associated with a single multi request - -LLCurlEasyRequest::LLCurlEasyRequest() - : mRequestSent(false), - mResultReturned(false) -{ - mMulti = new LLCurl::Multi(); - - if(mMulti->isValid()) - { - mEasy = mMulti->allocEasy(); - if (mEasy) - { - mEasy->setErrorBuffer(); - mEasy->setCA(); - // Set proxy settings if configured to do so. - LLProxy::getInstance()->applyProxySettings(mEasy); - } -} - else - { - LLCurl::getCurlThread()->killMulti(mMulti) ; - mEasy = NULL ; - mMulti = NULL ; - } -} - -LLCurlEasyRequest::~LLCurlEasyRequest() -{ - LLCurl::getCurlThread()->killMulti(mMulti) ; -} - -void LLCurlEasyRequest::setopt(CURLoption option, S32 value) -{ - if (isValid() && mEasy) - { - mEasy->setopt(option, value); - } -} - -void LLCurlEasyRequest::setoptString(CURLoption option, const std::string& value) -{ - if (isValid() && mEasy) - { - mEasy->setoptString(option, value); - } -} - -void LLCurlEasyRequest::setPost(char* postdata, S32 size) -{ - if (isValid() && mEasy) - { - mEasy->setopt(CURLOPT_POST, 1); - mEasy->setopt(CURLOPT_POSTFIELDS, postdata); - mEasy->setopt(CURLOPT_POSTFIELDSIZE, size); - } -} - -void LLCurlEasyRequest::setHeaderCallback(curl_header_callback callback, void* userdata) -{ - if (isValid() && mEasy) - { - mEasy->setopt(CURLOPT_HEADERFUNCTION, (void*)callback); - mEasy->setopt(CURLOPT_HEADERDATA, userdata); // aka CURLOPT_WRITEHEADER - } -} - -void LLCurlEasyRequest::setWriteCallback(curl_write_callback callback, void* userdata) -{ - if (isValid() && mEasy) - { - mEasy->setopt(CURLOPT_WRITEFUNCTION, (void*)callback); - mEasy->setopt(CURLOPT_WRITEDATA, userdata); - } -} - -void LLCurlEasyRequest::setReadCallback(curl_read_callback callback, void* userdata) -{ - if (isValid() && mEasy) - { - mEasy->setopt(CURLOPT_READFUNCTION, (void*)callback); - mEasy->setopt(CURLOPT_READDATA, userdata); - } -} - -void LLCurlEasyRequest::setSSLCtxCallback(curl_ssl_ctx_callback callback, void* userdata) -{ - if (isValid() && mEasy) - { - mEasy->setopt(CURLOPT_SSL_CTX_FUNCTION, (void*)callback); - mEasy->setopt(CURLOPT_SSL_CTX_DATA, userdata); - } -} - -void LLCurlEasyRequest::slist_append(const std::string& header, const std::string& value) -{ - if (isValid() && mEasy) - { - mEasy->slist_append(header, value); - } -} - -void LLCurlEasyRequest::slist_append(const char* str) -{ - if (isValid() && mEasy) - { - mEasy->slist_append(str); - } -} - -void LLCurlEasyRequest::sendRequest(const std::string& url) -{ - llassert_always(!mRequestSent); - mRequestSent = true; - LL_DEBUGS() << url << LL_ENDL; - if (isValid() && mEasy) - { - mEasy->setHeaders(); - mEasy->setoptString(CURLOPT_URL, url); - mMulti->addEasy(mEasy); - } -} - -void LLCurlEasyRequest::requestComplete() -{ - llassert_always(mRequestSent); - mRequestSent = false; - if (isValid() && mEasy) - { - mMulti->removeEasy(mEasy); - } -} - -// Usage: Call getRestult until it returns false (no more messages) -bool LLCurlEasyRequest::getResult(CURLcode* result, LLCurl::TransferInfo* info) -{ - if(!isValid()) - { - return false ; - } - if (!mMulti->isCompleted()) - { //we're busy, try again later - return false; - } - mMulti->setState(LLCurl::Multi::STATE_READY) ; - - if (!mEasy) - { - // Special case - we failed to initialize a curl_easy (can happen if too many open files) - // Act as though the request failed to connect - if (mResultReturned) - { - return false; - } - else - { - *result = CURLE_FAILED_INIT; - mResultReturned = true; - return true; - } - } - // In theory, info_read might return a message with a status other than CURLMSG_DONE - // In practice for all messages returned, msg == CURLMSG_DONE - // Ignore other messages just in case - while(1) - { - S32 q; - CURLMsg* curlmsg = info_read(&q, info); - if (curlmsg) - { - if (curlmsg->msg == CURLMSG_DONE) - { - *result = curlmsg->data.result; - return true; - } - // else continue - } - else - { - return false; - } - } -} - -// private -CURLMsg* LLCurlEasyRequest::info_read(S32* q, LLCurl::TransferInfo* info) -{ - if (mEasy) - { - CURLMsg* curlmsg = mMulti->info_read(q); - if (curlmsg && curlmsg->msg == CURLMSG_DONE) - { - if (info) - { - mEasy->getTransferInfo(info); - } - } - return curlmsg; - } - return NULL; -} - -std::string LLCurlEasyRequest::getErrorString() -{ - return isValid() && mEasy ? std::string(mEasy->getErrorBuffer()) : std::string(); -} - -//////////////////////////////////////////////////////////////////////////// - -#if SAFE_SSL -//static -void LLCurl::ssl_locking_callback(int mode, int type, const char *file, int line) -{ - if (mode & CRYPTO_LOCK) - { - LLCurl::sSSLMutex[type]->lock(); - } - else - { - LLCurl::sSSLMutex[type]->unlock(); - } -} - -//static -unsigned long LLCurl::ssl_thread_id(void) -{ - return LLThread::currentID(); -} -#endif - -void LLCurl::initClass(F32 curl_reuest_timeout, S32 max_number_handles, bool multi_threaded) -{ - sCurlRequestTimeOut = curl_reuest_timeout ; //seconds - sMaxHandles = max_number_handles ; //max number of handles, (multi handles and easy handles combined). - - // Do not change this "unless you are familiar with and mean to control - // internal operations of libcurl" - // - http://curl.haxx.se/libcurl/c/curl_global_init.html - CURLcode code = curl_global_init(CURL_GLOBAL_ALL); - - check_curl_code(code); - -#if SAFE_SSL - S32 mutex_count = CRYPTO_num_locks(); - for (S32 i=0; i<mutex_count; i++) - { - sSSLMutex.push_back(new LLMutex(NULL)); - } - CRYPTO_set_id_callback(&LLCurl::ssl_thread_id); - CRYPTO_set_locking_callback(&LLCurl::ssl_locking_callback); -#endif - - sCurlThread = new LLCurlThread(multi_threaded) ; - if(multi_threaded) - { - sHandleMutexp = new LLMutex(NULL) ; - Easy::sHandleMutexp = new LLMutex(NULL) ; - } -} - -void LLCurl::cleanupClass() -{ - sNotQuitting = false; //set quitting - - //shut down curl thread - while(1) - { - if(!sCurlThread->update(1)) //finish all tasks - { - break ; - } - } - LL_CHECK_MEMORY - sCurlThread->shutdown() ; - LL_CHECK_MEMORY - delete sCurlThread ; - sCurlThread = NULL ; - LL_CHECK_MEMORY - -#if SAFE_SSL - CRYPTO_set_locking_callback(NULL); - for_each(sSSLMutex.begin(), sSSLMutex.end(), DeletePointer()); - sSSLMutex.clear(); -#endif - - LL_CHECK_MEMORY - Easy::deleteAllFreeHandles(); - LL_CHECK_MEMORY - Easy::deleteAllActiveHandles(); - LL_CHECK_MEMORY - - // Free the template easy handle - curl_easy_cleanup(sCurlTemplateStandardHandle); - sCurlTemplateStandardHandle = NULL; - LL_CHECK_MEMORY - - delete Easy::sHandleMutexp ; - Easy::sHandleMutexp = NULL ; - - LL_CHECK_MEMORY - - delete sHandleMutexp ; - sHandleMutexp = NULL ; - - LL_CHECK_MEMORY - - // removed as per https://jira.secondlife.com/browse/SH-3115 - //llassert(Easy::sActiveHandles.empty()); -} - -//static -CURLM* LLCurl::newMultiHandle() -{ - llassert(sNotQuitting); - - LLMutexLock lock(sHandleMutexp) ; - - if(sTotalHandles + 1 > sMaxHandles) - { - LL_WARNS() << "no more handles available." << LL_ENDL ; - return NULL ; //failed - } - sTotalHandles++; - - CURLM* ret = curl_multi_init() ; - if(!ret) - { - LL_WARNS() << "curl_multi_init failed." << LL_ENDL ; - } - - return ret ; -} - -//static -CURLMcode LLCurl::deleteMultiHandle(CURLM* handle) -{ - if(handle) - { - LLMutexLock lock(sHandleMutexp) ; - sTotalHandles-- ; - return curl_multi_cleanup(handle) ; - } - return CURLM_OK ; -} - -//static -CURL* LLCurl::newEasyHandle() -{ - llassert(sNotQuitting); - LLMutexLock lock(sHandleMutexp) ; - - if(sTotalHandles + 1 > sMaxHandles) - { - LL_WARNS() << "no more handles available." << LL_ENDL ; - return NULL ; //failed - } - sTotalHandles++; - - CURL* ret = createStandardCurlHandle(); - if(!ret) - { - LL_WARNS() << "failed to create curl handle." << LL_ENDL ; - } - - return ret ; -} - -//static -void LLCurl::deleteEasyHandle(CURL* handle) -{ - if(handle) - { - LLMutexLock lock(sHandleMutexp) ; - LL_CHECK_MEMORY - curl_easy_cleanup(handle) ; - LL_CHECK_MEMORY - sTotalHandles-- ; - } -} - -const unsigned int LLCurl::MAX_REDIRECTS = 5; - -// Provide access to LLCurl free functions outside of llcurl.cpp without polluting the global namespace. -void LLCurlFF::check_easy_code(CURLcode code) -{ - check_curl_code(code); -} -void LLCurlFF::check_multi_code(CURLMcode code) -{ - check_curl_multi_code(code); -} - - -// Static -CURL* LLCurl::createStandardCurlHandle() -{ - if (sCurlTemplateStandardHandle == NULL) - { // Late creation of the template curl handle - sCurlTemplateStandardHandle = curl_easy_init(); - if (sCurlTemplateStandardHandle == NULL) - { - LL_WARNS() << "curl error calling curl_easy_init()" << LL_ENDL; - } - else - { - CURLcode result = curl_easy_setopt(sCurlTemplateStandardHandle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); - check_curl_code(result); - result = curl_easy_setopt(sCurlTemplateStandardHandle, CURLOPT_NOSIGNAL, 1); - check_curl_code(result); - result = curl_easy_setopt(sCurlTemplateStandardHandle, CURLOPT_NOPROGRESS, 1); - check_curl_code(result); - result = curl_easy_setopt(sCurlTemplateStandardHandle, CURLOPT_ENCODING, ""); - check_curl_code(result); - result = curl_easy_setopt(sCurlTemplateStandardHandle, CURLOPT_AUTOREFERER, 1); - check_curl_code(result); - result = curl_easy_setopt(sCurlTemplateStandardHandle, CURLOPT_FOLLOWLOCATION, 1); - check_curl_code(result); - result = curl_easy_setopt(sCurlTemplateStandardHandle, CURLOPT_SSL_VERIFYPEER, 1); - check_curl_code(result); - result = curl_easy_setopt(sCurlTemplateStandardHandle, CURLOPT_SSL_VERIFYHOST, 0); - check_curl_code(result); - - // The Linksys WRT54G V5 router has an issue with frequent - // DNS lookups from LAN machines. If they happen too often, - // like for every HTTP request, the router gets annoyed after - // about 700 or so requests and starts issuing TCP RSTs to - // new connections. Reuse the DNS lookups for even a few - // seconds and no RSTs. - result = curl_easy_setopt(sCurlTemplateStandardHandle, CURLOPT_DNS_CACHE_TIMEOUT, 15); - check_curl_code(result); - } - } - - return curl_easy_duphandle(sCurlTemplateStandardHandle); -} diff --git a/indra/llmessage/llcurl.h b/indra/llmessage/llcurl.h deleted file mode 100644 index 34758433c8..0000000000 --- a/indra/llmessage/llcurl.h +++ /dev/null @@ -1,557 +0,0 @@ -/** - * @file llcurl.h - * @author Zero / Donovan - * @date 2006-10-15 - * @brief A wrapper around libcurl. - * - * $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$ - */ - -#ifndef LL_LLCURL_H -#define LL_LLCURL_H - -#include "linden_common.h" - -#include <sstream> -#include <string> -#include <vector> - -#include <boost/intrusive_ptr.hpp> -#include <curl/curl.h> // TODO: remove dependency - -#include "llbuffer.h" -#include "llhttpconstants.h" -#include "lliopipe.h" -#include "llsd.h" -#include "llqueuedthread.h" -#include "llframetimer.h" -#include "llpointer.h" -#include "llsingleton.h" - -class LLMutex; -class LLCurlThread; - -// For whatever reason, this is not typedef'd in curl.h -typedef size_t (*curl_header_callback)(void *ptr, size_t size, size_t nmemb, void *stream); - -class LLCurl -{ - LOG_CLASS(LLCurl); - -public: - class Easy; - class Multi; - - struct TransferInfo - { - TransferInfo() : mSizeDownload(0.0), mTotalTime(0.0), mSpeedDownload(0.0) {} - F64 mSizeDownload; - F64 mTotalTime; - F64 mSpeedDownload; - }; - - class Responder : public LLThreadSafeRefCount - { - //LOG_CLASS(Responder); - public: - - Responder(); - virtual ~Responder(); - - virtual bool followRedir() - { - return false; - } - - /** - * @brief return true if the status code indicates success. - */ - bool isGoodStatus() const { return isHttpGoodStatus(mStatus); } - - S32 getStatus() const { return mStatus; } - const std::string& getReason() const { return mReason; } - const LLSD& getContent() const { return mContent; } - bool hasResponseHeader(const std::string& header) const; - const std::string& getResponseHeader(const std::string& header) const; - const LLSD& getResponseHeaders() const { return mResponseHeaders; } - const std::string& getURL() const { return mURL; } - EHTTPMethod getHTTPMethod() const { return mHTTPMethod; } - - // This formats response information for use in log spam. Includes content spam. - std::string dumpResponse() const; - - // Allows direct triggering of success/error with different results. - void completeResult(S32 status, const std::string& reason, const LLSD& content = LLSD()); - void successResult(const LLSD& content); - void failureResult(S32 status, const std::string& reason, const LLSD& content = LLSD()); - - // The default implementation will try to parse body content as an LLSD, however - // it should not spam about parsing failures unless the server sent a - // Content-Type: application/llsd+xml header. - virtual void completedRaw( - const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer); - /**< Override point for clients that may want to use this - class when the response is some other format besides LLSD - */ - - - // The http* methods are not public since these should be triggered internally - // after status, reason, content, etc have been set. - // If you need to trigger a completion method, use the *Result methods, above. - protected: - // These methods are the preferred way to process final results. - // By default, when one of these is called the following information will be resolved: - // * HTTP status code - getStatus() - // * Reason string - getReason() - // * Content - getContent() - // * Response Headers - getResponseHeaders() - - // By default, httpSuccess is triggered whenever httpCompleted is called with a 2xx status code. - virtual void httpSuccess(); - //< called by completed for good status codes. - - // By default, httpFailure is triggered whenever httpCompleted is called with a non-2xx status code. - virtual void httpFailure(); - //< called by httpCompleted() on bad status - - // httpCompleted does not generally need to be overridden, unless - // you don't care about the status code (which determine httpFailure or httpSuccess) - // or if you want to re-interpret what a 'good' vs' bad' status code is. - virtual void httpCompleted(); - /**< The default implementation calls - either: - * httpSuccess(), or - * httpFailure() - */ - - public: - void setHTTPMethod(EHTTPMethod method); - void setURL(const std::string& url); - const std::string& getURL(); - void setResult(S32 status, const std::string& reason, const LLSD& content = LLSD()); - void setResponseHeader(const std::string& header, const std::string& value); - - private: - // These can be accessed by the get* methods. Treated as 'read-only' during completion handlers. - EHTTPMethod mHTTPMethod; - std::string mURL; - LLSD mResponseHeaders; - - protected: - // These should also generally be treated as 'read-only' during completion handlers - // and should be accessed by the get* methods. The exception to this rule would - // be when overriding the completedRaw method in preparation for calling httpCompleted(). - S32 mStatus; - std::string mReason; - LLSD mContent; - }; - typedef LLPointer<Responder> ResponderPtr; - - - /** - * @ brief Set certificate authority file used to verify HTTPS certs. - */ - static void setCAFile(const std::string& file); - - /** - * @ brief Set certificate authority path used to verify HTTPS certs. - */ - static void setCAPath(const std::string& path); - - /** - * @ brief Return human-readable string describing libcurl version. - */ - static std::string getVersionString(); - - /** - * @ brief Get certificate authority file used to verify HTTPS certs. - */ - static const std::string& getCAFile() { return sCAFile; } - - /** - * @ brief Get certificate authority path used to verify HTTPS certs. - */ - static const std::string& getCAPath() { return sCAPath; } - - /** - * @ brief Initialize LLCurl class - */ - static void initClass(F32 curl_reuest_timeout = 120.f, S32 max_number_handles = 256, bool multi_threaded = false); - - /** - * @ brief Cleanup LLCurl class - */ - static void cleanupClass(); - - /** - * @ brief curl error code -> string - */ - static std::string strerror(CURLcode errorcode); - - // For OpenSSL callbacks - static std::vector<LLMutex*> sSSLMutex; - - // OpenSSL callbacks - static void ssl_locking_callback(int mode, int type, const char *file, int line); - static unsigned long ssl_thread_id(void); - - static LLCurlThread* getCurlThread() { return sCurlThread ;} - - static CURLM* newMultiHandle() ; - static CURLMcode deleteMultiHandle(CURLM* handle) ; - static CURL* newEasyHandle() ; - static void deleteEasyHandle(CURL* handle) ; - - static CURL* createStandardCurlHandle(); - -private: - static std::string sCAPath; - static std::string sCAFile; - static const unsigned int MAX_REDIRECTS; - static LLCurlThread* sCurlThread; - - static LLMutex* sHandleMutexp ; - static S32 sTotalHandles ; - static S32 sMaxHandles; - static CURL* sCurlTemplateStandardHandle; -public: - static bool sNotQuitting; - static F32 sCurlRequestTimeOut; -}; - -class LLCurl::Easy -{ - LOG_CLASS(Easy); - -private: - Easy(); - -public: - static Easy* getEasy(); - ~Easy(); - - CURL* getCurlHandle() const { return mCurlEasyHandle; } - - void setErrorBuffer(); - void setCA(); - - void setopt(CURLoption option, S32 value); - // These assume the setter does not free value! - void setopt(CURLoption option, void* value); - void setopt(CURLoption option, char* value); - // Copies the string so that it is guaranteed to stick around - void setoptString(CURLoption option, const std::string& value); - - void slist_append(const std::string& header, const std::string& value); - void slist_append(const char* str); - void setHeaders(); - - S32 report(CURLcode); - void getTransferInfo(LLCurl::TransferInfo* info); - - void prepRequest(const std::string& url, const std::vector<std::string>& headers, LLCurl::ResponderPtr, S32 time_out = 0, bool post = false); - - const char* getErrorBuffer(); - - std::stringstream& getInput() { return mInput; } - std::stringstream& getHeaderOutput() { return mHeaderOutput; } - LLIOPipe::buffer_ptr_t& getOutput() { return mOutput; } - const LLChannelDescriptors& getChannels() { return mChannels; } - - void resetState(); - - static CURL* allocEasyHandle(); - static void releaseEasyHandle(CURL* handle); - -private: - friend class LLCurl; - friend class LLCurl::Multi; - - CURL* mCurlEasyHandle; - struct curl_slist* mHeaders; - - std::stringstream mRequest; - LLChannelDescriptors mChannels; - LLIOPipe::buffer_ptr_t mOutput; - std::stringstream mInput; - std::stringstream mHeaderOutput; - char mErrorBuffer[CURL_ERROR_SIZE]; - - // Note: char*'s not strings since we pass pointers to curl - std::vector<char*> mStrings; - - LLCurl::ResponderPtr mResponder; - - static std::set<CURL*> sFreeHandles; - static std::set<CURL*> sActiveHandles; - static LLMutex* sHandleMutexp ; - - static void deleteAllActiveHandles(); - static void deleteAllFreeHandles(); -}; - -class LLCurl::Multi -{ - LOG_CLASS(Multi); - - friend class LLCurlThread ; - -private: - ~Multi(); - - void markDead() ; - bool doPerform(); - -public: - - typedef enum - { - STATE_READY=0, - STATE_PERFORMING=1, - STATE_COMPLETED=2 - } ePerformState; - - Multi(F32 idle_time_out = 0.f); - - LLCurl::Easy* allocEasy(); - bool addEasy(LLCurl::Easy* easy); - void removeEasy(LLCurl::Easy* easy); - - void lock() ; - void unlock() ; - - void setState(ePerformState state) ; - ePerformState getState() ; - - bool isCompleted() ; - bool isValid() {return mCurlMultiHandle != NULL && mValid;} - bool isDead() {return mDead;} - - bool waitToComplete() ; - - S32 process(); - - CURLMsg* info_read(S32* msgs_in_queue); - - S32 mQueued; - S32 mErrorCount; - -private: - void easyFree(LLCurl::Easy*); - void cleanup(bool deleted = false) ; - - CURLM* mCurlMultiHandle; - - typedef std::set<LLCurl::Easy*> easy_active_list_t; - easy_active_list_t mEasyActiveList; - typedef std::map<CURL*, LLCurl::Easy*> easy_active_map_t; - easy_active_map_t mEasyActiveMap; - typedef std::set<LLCurl::Easy*> easy_free_list_t; - easy_free_list_t mEasyFreeList; - - LLQueuedThread::handle_t mHandle ; - ePerformState mState; - - BOOL mDead ; - BOOL mValid ; - LLMutex* mMutexp ; - LLMutex* mDeletionMutexp ; - LLMutex* mEasyMutexp ; - LLFrameTimer mIdleTimer ; - F32 mIdleTimeOut; -}; - -class LLCurlThread : public LLQueuedThread -{ -public: - - class CurlRequest : public LLQueuedThread::QueuedRequest - { - protected: - virtual ~CurlRequest(); // use deleteRequest() - - public: - CurlRequest(handle_t handle, LLCurl::Multi* multi, LLCurlThread* curl_thread); - - /*virtual*/ bool processRequest(); - /*virtual*/ void finishRequest(bool completed); - - private: - // input - LLCurl::Multi* mMulti; - LLCurlThread* mCurlThread; - }; - friend class CurlRequest; - -public: - LLCurlThread(bool threaded = true) ; - virtual ~LLCurlThread() ; - - S32 update(F32 max_time_ms); - - void addMulti(LLCurl::Multi* multi) ; - void killMulti(LLCurl::Multi* multi) ; - -private: - bool doMultiPerform(LLCurl::Multi* multi) ; - void deleteMulti(LLCurl::Multi* multi) ; - void cleanupMulti(LLCurl::Multi* multi) ; -} ; - - -class LLCurlRequest -{ -public: - typedef std::vector<std::string> headers_t; - - LLCurlRequest(); - ~LLCurlRequest(); - - void get(const std::string& url, LLCurl::ResponderPtr responder); - bool getByteRange(const std::string& url, const headers_t& headers, S32 offset, S32 length, LLCurl::ResponderPtr responder); - bool post(const std::string& url, const headers_t& headers, const LLSD& data, LLCurl::ResponderPtr responder, S32 time_out = 0); - bool post(const std::string& url, const headers_t& headers, const std::string& data, LLCurl::ResponderPtr responder, S32 time_out = 0); - - S32 process(); - S32 getQueued(); - -private: - void addMulti(); - LLCurl::Easy* allocEasy(); - bool addEasy(LLCurl::Easy* easy); - -private: - typedef std::set<LLCurl::Multi*> curlmulti_set_t; - curlmulti_set_t mMultiSet; - LLCurl::Multi* mActiveMulti; - S32 mActiveRequestCount; - BOOL mProcessing; -}; - -//for texture fetch only -class LLCurlTextureRequest : public LLCurlRequest -{ -public: - LLCurlTextureRequest(S32 concurrency); - ~LLCurlTextureRequest(); - - U32 getByteRange(const std::string& url, const headers_t& headers, S32 offset, S32 length, U32 pri, LLCurl::ResponderPtr responder, F32 delay_time = -1.f); - void nextRequests(); - void completeRequest(S32 received_bytes); - - void updatePriority(U32 handle, U32 pri); - void removeRequest(U32 handle); - - U32 getTotalReceivedBits(); - U32 getTotalIssuedRequests(); - S32 getNumRequests(); - bool isWaiting(U32 handle); - -private: - LLMutex mMutex; - S32 mConcurrency; - S32 mInQueue; //request currently in queue. - U32 mHandleCounter; - U32 mTotalIssuedRequests; - U32 mTotalReceivedBits; - - typedef struct _request_t - { - _request_t(U32 handle, const std::string& url, const headers_t& headers, S32 offset, S32 length, U32 pri, LLCurl::ResponderPtr responder) : - mHandle(handle), mUrl(url), mHeaders(headers), mOffset(offset), mLength(length), mPriority(pri), mResponder(responder), mStartTime(0.f) - {} - - U32 mHandle; - std::string mUrl; - LLCurlRequest::headers_t mHeaders; - S32 mOffset; - S32 mLength; - LLCurl::ResponderPtr mResponder; - U32 mPriority; - F32 mStartTime; //start time to issue this request - } request_t; - - struct request_compare - { - bool operator()(const request_t* lhs, const request_t* rhs) const - { - if(lhs->mPriority != rhs->mPriority) - { - return lhs->mPriority > rhs->mPriority; // higher priority in front of queue (set) - } - else - { - return (U32)lhs < (U32)rhs; - } - } - }; - - typedef std::set<request_t*, request_compare> req_queue_t; - req_queue_t mCachedRequests; - std::map<S32, request_t*> mRequestMap; - - LLFrameTimer mGlobalTimer; -}; - -class LLCurlEasyRequest -{ -public: - LLCurlEasyRequest(); - ~LLCurlEasyRequest(); - void setopt(CURLoption option, S32 value); - void setoptString(CURLoption option, const std::string& value); - void setPost(char* postdata, S32 size); - void setHeaderCallback(curl_header_callback callback, void* userdata); - void setWriteCallback(curl_write_callback callback, void* userdata); - void setReadCallback(curl_read_callback callback, void* userdata); - void setSSLCtxCallback(curl_ssl_ctx_callback callback, void* userdata); - void slist_append(const std::string& header, const std::string& value); - void slist_append(const char* str); - void sendRequest(const std::string& url); - void requestComplete(); - bool getResult(CURLcode* result, LLCurl::TransferInfo* info = NULL); - std::string getErrorString(); - bool isCompleted() {return mMulti->isCompleted() ;} - bool wait() { return mMulti->waitToComplete(); } - bool isValid() {return mMulti && mMulti->isValid(); } - - LLCurl::Easy* getEasy() const { return mEasy; } - -private: - CURLMsg* info_read(S32* queue, LLCurl::TransferInfo* info); - -private: - LLCurl::Multi* mMulti; - LLCurl::Easy* mEasy; - bool mRequestSent; - bool mResultReturned; -}; - -// Provide access to LLCurl free functions outside of llcurl.cpp without polluting the global namespace. -namespace LLCurlFF -{ - void check_easy_code(CURLcode code); - void check_multi_code(CURLMcode code); -} - -#endif // LL_LLCURL_H diff --git a/indra/llmessage/llexperiencecache.cpp b/indra/llmessage/llexperiencecache.cpp index 52b60a176e..779d1d9d99 100644 --- a/indra/llmessage/llexperiencecache.cpp +++ b/indra/llmessage/llexperiencecache.cpp @@ -26,616 +26,986 @@ #include "llexperiencecache.h" #include "llavatarname.h" -#include "llframetimer.h" -#include "llhttpclient.h" #include "llsdserialize.h" +#include "llcoros.h" +#include "lleventcoro.h" +#include "lleventfilter.h" +#include "llcoproceduremanager.h" +#include "lldir.h" #include <set> #include <map> -#include "boost/tokenizer.hpp" +#include <boost/tokenizer.hpp> +#include <boost/concept_check.hpp> - -namespace LLExperienceCache +//========================================================================= +namespace LLExperienceCacheImpl { + void mapKeys(const LLSD& legacyKeys); + F64 getErrorRetryDeltaTime(S32 status, LLSD headers); + bool maxAgeFromCacheControl(const std::string& cache_control, S32 *max_age); + + static const std::string PRIVATE_KEY = "private_id"; + static const std::string EXPERIENCE_ID = "public_id"; + + static const std::string MAX_AGE("max-age"); + static const boost::char_separator<char> EQUALS_SEPARATOR("="); + static const boost::char_separator<char> COMMA_SEPARATOR(","); + // *TODO$: this seems to be tied to mapKeys which is used by bootstrap.... but I don't think that bootstrap is used. typedef std::map<LLUUID, LLUUID> KeyMap; KeyMap privateToPublicKeyMap; +} - void mapKeys(const LLSD& legacyKeys); +//========================================================================= +const std::string LLExperienceCache::PRIVATE_KEY = "private_id"; +const std::string LLExperienceCache::MISSING = "DoesNotExist"; + +const std::string LLExperienceCache::AGENT_ID = "agent_id"; +const std::string LLExperienceCache::GROUP_ID = "group_id"; +const std::string LLExperienceCache::EXPERIENCE_ID = "public_id"; +const std::string LLExperienceCache::NAME = "name"; +const std::string LLExperienceCache::PROPERTIES = "properties"; +const std::string LLExperienceCache::EXPIRES = "expiration"; +const std::string LLExperienceCache::DESCRIPTION = "description"; +const std::string LLExperienceCache::QUOTA = "quota"; +const std::string LLExperienceCache::MATURITY = "maturity"; +const std::string LLExperienceCache::METADATA = "extended_metadata"; +const std::string LLExperienceCache::SLURL = "slurl"; + +// should be in sync with experience-api/experiences/models.py +const int LLExperienceCache::PROPERTY_INVALID = 1 << 0; +const int LLExperienceCache::PROPERTY_PRIVILEGED = 1 << 3; +const int LLExperienceCache::PROPERTY_GRID = 1 << 4; +const int LLExperienceCache::PROPERTY_PRIVATE = 1 << 5; +const int LLExperienceCache::PROPERTY_DISABLED = 1 << 6; +const int LLExperienceCache::PROPERTY_SUSPENDED = 1 << 7; + +// default values +const F64 LLExperienceCache::DEFAULT_EXPIRATION = 600.0; +const S32 LLExperienceCache::DEFAULT_QUOTA = 128; // this is megabytes +const int LLExperienceCache::SEARCH_PAGE_SIZE = 30; + +//========================================================================= +LLExperienceCache::LLExperienceCache(): + mShutdown(false) +{ +} - std::string sLookupURL; +LLExperienceCache::~LLExperienceCache() +{ - typedef std::map<LLUUID, std::string> ask_queue_t; - ask_queue_t sAskQueue; +} - typedef std::map<LLUUID, F64> pending_queue_t; - pending_queue_t sPendingQueue; +void LLExperienceCache::initSingleton() +{ + mCacheFileName = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "experience_cache.xml"); - cache_t sCache; - int sMaximumLookups = 10; + LL_INFOS("ExperienceCache") << "Loading " << mCacheFileName << LL_ENDL; + llifstream cache_stream(mCacheFileName.c_str()); - LLFrameTimer sRequestTimer; + if (cache_stream.is_open()) + { + cache_stream >> (*this); + } - // Periodically clean out expired entries from the cache - LLFrameTimer sEraseExpiredTimer; + LLCoros::instance().launch("LLExperienceCache::idleCoro", + boost::bind(&LLExperienceCache::idleCoro, this)); - // May have multiple callbacks for a single ID, which are - // represented as multiple slots bound to the signal. - // Avoid copying signals via pointers. - typedef std::map<LLUUID, callback_signal_t*> signal_map_t; - signal_map_t sSignalMap; +} +void LLExperienceCache::cleanup() +{ + LL_INFOS("ExperienceCache") << "Saving " << mCacheFileName << LL_ENDL; + + llofstream cache_stream(mCacheFileName.c_str()); + if (cache_stream.is_open()) + { + cache_stream << (*this); + } + mShutdown = true; +} +//------------------------------------------------------------------------- +void LLExperienceCache::importFile(std::istream& istr) +{ + LLSD data; + S32 parse_count = LLSDSerialize::fromXMLDocument(data, istr); + if (parse_count < 1) return; - bool max_age_from_cache_control(const std::string& cache_control, S32 *max_age); - void eraseExpired(); + LLSD experiences = data["experiences"]; - void processExperience( const LLUUID& public_key, const LLSD& experience ) - { - sCache[public_key]=experience; - LLSD & row = sCache[public_key]; + LLUUID public_key; + LLSD::map_const_iterator it = experiences.beginMap(); + for (; it != experiences.endMap(); ++it) + { + public_key.set(it->first); + mCache[public_key] = it->second; + } - if(row.has(EXPIRES)) - { - row[EXPIRES] = row[EXPIRES].asReal() + LLFrameTimer::getTotalSeconds(); - } + LL_DEBUGS("ExperienceCache") << "importFile() loaded " << mCache.size() << LL_ENDL; +} - if(row.has(EXPERIENCE_ID)) - { - sPendingQueue.erase(row[EXPERIENCE_ID].asUUID()); - } +void LLExperienceCache::exportFile(std::ostream& ostr) const +{ + LLSD experiences; - //signal - signal_map_t::iterator sig_it = sSignalMap.find(public_key); - if (sig_it != sSignalMap.end()) - { - callback_signal_t* signal = sig_it->second; - (*signal)(experience); + cache_t::const_iterator it = mCache.begin(); + for (; it != mCache.end(); ++it) + { + if (!it->second.has(EXPERIENCE_ID) || it->second[EXPERIENCE_ID].asUUID().isNull() || + it->second.has("DoesNotExist") || (it->second.has(PROPERTIES) && it->second[PROPERTIES].asInteger() & PROPERTY_INVALID)) + continue; - sSignalMap.erase(public_key); + experiences[it->first.asString()] = it->second; + } - delete signal; - } - } + LLSD data; + data["experiences"] = experiences; - void initClass( ) - { - } + LLSDSerialize::toPrettyXML(data, ostr); +} + +// *TODO$: Rider: This method does not seem to be used... it may be useful in testing. +void LLExperienceCache::bootstrap(const LLSD& legacyKeys, int initialExpiration) +{ + LLExperienceCacheImpl::mapKeys(legacyKeys); + LLSD::array_const_iterator it = legacyKeys.beginArray(); + for (/**/; it != legacyKeys.endArray(); ++it) + { + LLSD experience = *it; + if (experience.has(EXPERIENCE_ID)) + { + if (!experience.has(EXPIRES)) + { + experience[EXPIRES] = initialExpiration; + } + processExperience(experience[EXPERIENCE_ID].asUUID(), experience); + } + else + { + LL_WARNS("ExperienceCache") + << "Skipping bootstrap entry which is missing " << EXPERIENCE_ID + << LL_ENDL; + } + } +} + +LLUUID LLExperienceCache::getExperienceId(const LLUUID& private_key, bool null_if_not_found) +{ + if (private_key.isNull()) + return LLUUID::null; + + LLExperienceCacheImpl::KeyMap::const_iterator it = LLExperienceCacheImpl::privateToPublicKeyMap.find(private_key); + if (it == LLExperienceCacheImpl::privateToPublicKeyMap.end()) + { + if (null_if_not_found) + { + return LLUUID::null; + } + return private_key; + } + LL_WARNS("LLExperience") << "converted private key " << private_key << " to experience_id " << it->second << LL_ENDL; + return it->second; +} - const cache_t& getCached() +//========================================================================= +void LLExperienceCache::processExperience(const LLUUID& public_key, const LLSD& experience) +{ + LL_INFOS("ExperienceCache") << "Processing experience \"" << experience[NAME] << "\" with key " << public_key.asString() << LL_ENDL; + + mCache[public_key]=experience; + LLSD & row = mCache[public_key]; + + if(row.has(EXPIRES)) { - return sCache; + row[EXPIRES] = row[EXPIRES].asReal() + LLFrameTimer::getTotalSeconds(); } - void setMaximumLookups( int maximumLookups) + if(row.has(EXPERIENCE_ID)) { - sMaximumLookups = maximumLookups; + mPendingQueue.erase(row[EXPERIENCE_ID].asUUID()); } - void bootstrap(const LLSD& legacyKeys, int initialExpiration) + //signal + signal_map_t::iterator sig_it = mSignalMap.find(public_key); + if (sig_it != mSignalMap.end()) { - mapKeys(legacyKeys); - LLSD::array_const_iterator it = legacyKeys.beginArray(); - for(/**/; it != legacyKeys.endArray(); ++it) - { - LLSD experience = *it; - if(experience.has(EXPERIENCE_ID)) - { - if(!experience.has(EXPIRES)) - { - experience[EXPIRES] = initialExpiration; - } - processExperience(experience[EXPERIENCE_ID].asUUID(), experience); - } - else - { - LL_WARNS("ExperienceCache") - << "Skipping bootstrap entry which is missing " << EXPERIENCE_ID - << LL_ENDL; - } - } + signal_ptr signal = sig_it->second; + (*signal)(experience); + + mSignalMap.erase(public_key); } +} + +const LLExperienceCache::cache_t& LLExperienceCache::getCached() +{ + return mCache; +} +void LLExperienceCache::requestExperiencesCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &httpAdapter, std::string url, RequestQueue_t requests) +{ + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest()); + + //LL_INFOS("requestExperiencesCoro") << "url: " << url << LL_ENDL; + + LLSD result = httpAdapter->getAndSuspend(httpRequest, url); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) + { + F64 now = LLFrameTimer::getTotalSeconds(); + + LLSD headers = httpResults[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_HEADERS]; + // build dummy entries for the failed requests + for (RequestQueue_t::const_iterator it = requests.begin(); it != requests.end(); ++it) + { + LLSD exp = get(*it); + //leave the properties alone if we already have a cache entry for this xp + if (exp.isUndefined()) + { + exp[PROPERTIES] = PROPERTY_INVALID; + } + exp[EXPIRES] = now + LLExperienceCacheImpl::getErrorRetryDeltaTime(status, headers); + exp[EXPERIENCE_ID] = *it; + exp["key_type"] = EXPERIENCE_ID; + exp["uuid"] = *it; + exp["error"] = (LLSD::Integer)status.getType(); + exp[QUOTA] = DEFAULT_QUOTA; + + processExperience(*it, exp); + } + return; + } + + LLSD experiences = result["experience_keys"]; + + for (LLSD::array_const_iterator it = experiences.beginArray(); + it != experiences.endArray(); ++it) + { + const LLSD& row = *it; + LLUUID public_key = row[EXPERIENCE_ID].asUUID(); + + LL_DEBUGS("ExperienceCache") << "Received result for " << public_key + << " display '" << row[LLExperienceCache::NAME].asString() << "'" << LL_ENDL; + + processExperience(public_key, row); + } + + LLSD error_ids = result["error_ids"]; + + for (LLSD::array_const_iterator errIt = error_ids.beginArray(); + errIt != error_ids.endArray(); ++errIt) + { + LLUUID id = errIt->asUUID(); + LLSD exp; + exp[EXPIRES] = DEFAULT_EXPIRATION; + exp[EXPERIENCE_ID] = id; + exp[PROPERTIES] = PROPERTY_INVALID; + exp[MISSING] = true; + exp[QUOTA] = DEFAULT_QUOTA; + + processExperience(id, exp); + LL_WARNS("ExperienceCache") << "LLExperienceResponder::result() error result for " << id << LL_ENDL; + } +} - bool expirationFromCacheControl(LLSD headers, F64 *expires) + +void LLExperienceCache::requestExperiences() +{ + if (mCapability.empty()) + { + LL_WARNS("ExperienceCache") << "Capability query method not set." << LL_ENDL; + return; + } + + std::string urlBase = mCapability("GetExperienceInfo"); + if (urlBase.empty()) + { + LL_WARNS("ExperienceCache") << "No Experience capability." << LL_ENDL; + return; + } + + if (*urlBase.rbegin() != '/') + { + urlBase += "/"; + } + urlBase += "id/"; + + + F64 now = LLFrameTimer::getTotalSeconds(); + + const U32 EXP_URL_SEND_THRESHOLD = 3000; + const U32 PAGE_SIZE = EXP_URL_SEND_THRESHOLD / UUID_STR_LENGTH; + + std::ostringstream ostr; + ostr << urlBase << "?page_size=" << PAGE_SIZE; + RequestQueue_t requests; + + while (!mRequestQueue.empty()) + { + RequestQueue_t::iterator it = mRequestQueue.begin(); + LLUUID key = (*it); + mRequestQueue.erase(it); + requests.insert(key); + + ostr << "&" << EXPERIENCE_ID << "=" << key.asString(); + mPendingQueue[key] = now; + + if (mRequestQueue.empty() || (ostr.tellp() > EXP_URL_SEND_THRESHOLD)) + { // request is placed in the coprocedure pool for the ExpCache cache. Throttling is done by the pool itself. + LLCoprocedureManager::instance().enqueueCoprocedure("ExpCache", "RequestExperiences", + boost::bind(&LLExperienceCache::requestExperiencesCoro, this, _1, ostr.str(), requests) ); + + ostr.str(std::string()); + ostr << urlBase << "?page_size=" << PAGE_SIZE; + requests.clear(); + } + } + +} + + +bool LLExperienceCache::isRequestPending(const LLUUID& public_key) +{ + bool isPending = false; + const F64 PENDING_TIMEOUT_SECS = 5.0 * 60.0; + + PendingQueue_t::const_iterator it = mPendingQueue.find(public_key); + + if(it != mPendingQueue.end()) { - // Allow the header to override the default - LLSD cache_control_header = headers["cache-control"]; - if (cache_control_header.isDefined()) - { - S32 max_age = 0; - std::string cache_control = cache_control_header.asString(); - if (max_age_from_cache_control(cache_control, &max_age)) - { - LL_WARNS("ExperienceCache") - << "got EXPIRES from headers, max_age " << max_age - << LL_ENDL; - F64 now = LLFrameTimer::getTotalSeconds(); - *expires = now + (F64)max_age; - return true; - } - } - return false; + F64 expire_time = LLFrameTimer::getTotalSeconds() - PENDING_TIMEOUT_SECS; + isPending = (it->second > expire_time); } + return isPending; +} + +void LLExperienceCache::setCapabilityQuery(LLExperienceCache::CapabilityQuery_t queryfn) +{ + mCapability = queryfn; +} + + +void LLExperienceCache::idleCoro() +{ + const F32 SECS_BETWEEN_REQUESTS = 0.5f; + const F32 ERASE_EXPIRED_TIMEOUT = 60.f; // seconds - static const std::string MAX_AGE("max-age"); - static const boost::char_separator<char> EQUALS_SEPARATOR("="); - static const boost::char_separator<char> COMMA_SEPARATOR(","); + LL_INFOS("ExperienceCache") << "Launching Experience cache idle coro." << LL_ENDL; + do + { + llcoro::suspendUntilTimeout(SECS_BETWEEN_REQUESTS); - bool max_age_from_cache_control(const std::string& cache_control, S32 *max_age) + if (mEraseExpiredTimer.checkExpirationAndReset(ERASE_EXPIRED_TIMEOUT)) + { + eraseExpired(); + } + + if (!mRequestQueue.empty()) + { + requestExperiences(); + } + + } while (!mShutdown); + + // The coroutine system will likely be shut down by the time we get to this point + // (or at least no further cycling will occur on it since the user has decided to quit.) +} + +void LLExperienceCache::erase(const LLUUID& key) +{ + cache_t::iterator it = mCache.find(key); + + if(it != mCache.end()) { - // Split the string on "," to get a list of directives - typedef boost::tokenizer<boost::char_separator<char> > tokenizer; - tokenizer directives(cache_control, COMMA_SEPARATOR); + mCache.erase(it); + } +} - tokenizer::iterator token_it = directives.begin(); - for ( ; token_it != directives.end(); ++token_it) - { - // Tokens may have leading or trailing whitespace - std::string token = *token_it; - LLStringUtil::trim(token); +void LLExperienceCache::eraseExpired() +{ + F64 now = LLFrameTimer::getTotalSeconds(); + cache_t::iterator it = mCache.begin(); + while (it != mCache.end()) + { + cache_t::iterator cur = it; + LLSD& exp = cur->second; + ++it; - if (token.compare(0, MAX_AGE.size(), MAX_AGE) == 0) + //LL_INFOS("ExperienceCache") << "Testing experience \"" << exp[NAME] << "\" with exp time " << exp[EXPIRES].asReal() << "(now = " << now << ")" << LL_ENDL; + + if(exp.has(EXPIRES) && exp[EXPIRES].asReal() < now) + { + if(!exp.has(EXPERIENCE_ID)) { - // ...this token starts with max-age, so let's chop it up by "=" - tokenizer subtokens(token, EQUALS_SEPARATOR); - tokenizer::iterator subtoken_it = subtokens.begin(); - - // Must have a token - if (subtoken_it == subtokens.end()) return false; - std::string subtoken = *subtoken_it; - - // Must exactly equal "max-age" - LLStringUtil::trim(subtoken); - if (subtoken != MAX_AGE) return false; - - // Must have another token - ++subtoken_it; - if (subtoken_it == subtokens.end()) return false; - subtoken = *subtoken_it; - - // Must be a valid integer - // *NOTE: atoi() returns 0 for invalid values, so we have to - // check the string first. - // *TODO: Do servers ever send "0000" for zero? We don't handle it - LLStringUtil::trim(subtoken); - if (subtoken == "0") + LL_WARNS("ExperienceCache") << "Removing experience with no id " << LL_ENDL ; + mCache.erase(cur); + } + else + { + LLUUID id = exp[EXPERIENCE_ID].asUUID(); + LLUUID private_key = exp.has(LLExperienceCache::PRIVATE_KEY) ? exp[LLExperienceCache::PRIVATE_KEY].asUUID():LLUUID::null; + if(private_key.notNull() || !exp.has("DoesNotExist")) { - *max_age = 0; - return true; + fetch(id, true); } - S32 val = atoi( subtoken.c_str() ); - if (val > 0 && val < S32_MAX) + else { - *max_age = val; - return true; + LL_WARNS("ExperienceCache") << "Removing invalid experience " << id << LL_ENDL ; + mCache.erase(cur); } - return false; } } - return false; } - - - void importFile(std::istream& istr) +} + +bool LLExperienceCache::fetch(const LLUUID& key, bool refresh/* = true*/) +{ + if(!key.isNull() && !isRequestPending(key) && (refresh || mCache.find(key)==mCache.end())) { - LLSD data; - S32 parse_count = LLSDSerialize::fromXMLDocument(data, istr); - if(parse_count < 1) return; + LL_DEBUGS("ExperienceCache") << " queue request for " << EXPERIENCE_ID << " " << key << LL_ENDL; - LLSD experiences = data["experiences"]; - - LLUUID public_key; - LLSD::map_const_iterator it = experiences.beginMap(); - for(; it != experiences.endMap() ; ++it) - { - public_key.set(it->first); - sCache[public_key]=it->second; - } - - LL_DEBUGS("ExperienceCache") << "importFile() loaded " << sCache.size() << LL_ENDL; + mRequestQueue.insert(key); + return true; } + return false; +} - void exportFile(std::ostream& ostr) +void LLExperienceCache::insert(const LLSD& experience_data) +{ + if(experience_data.has(EXPERIENCE_ID)) { - LLSD experiences; - - cache_t::const_iterator it =sCache.begin(); - for( ; it != sCache.end() ; ++it) - { - if(!it->second.has(EXPERIENCE_ID) || it->second[EXPERIENCE_ID].asUUID().isNull() || - it->second.has("DoesNotExist") || (it->second.has(PROPERTIES) && it->second[PROPERTIES].asInteger() & PROPERTY_INVALID)) - continue; - - experiences[it->first.asString()] = it->second; - } + processExperience(experience_data[EXPERIENCE_ID].asUUID(), experience_data); + } + else + { + LL_WARNS("ExperienceCache") << ": Ignoring cache insert of experience which is missing " << EXPERIENCE_ID << LL_ENDL; + } +} - LLSD data; - data["experiences"] = experiences; +const LLSD& LLExperienceCache::get(const LLUUID& key) +{ + static const LLSD empty; + + if(key.isNull()) + return empty; + cache_t::const_iterator it = mCache.find(key); - LLSDSerialize::toPrettyXML(data, ostr); + if (it != mCache.end()) + { + return it->second; } + fetch(key); - class LLExperienceResponder : public LLHTTPClient::Responder - { - public: - LLExperienceResponder(const ask_queue_t& keys) - :mKeys(keys) - { + return empty; +} - } +void LLExperienceCache::get(const LLUUID& key, LLExperienceCache::ExperienceGetFn_t slot) +{ + if(key.isNull()) + return; - /*virtual*/ void httpCompleted() - { - LLSD experiences = getContent()["experience_keys"]; - LLSD::array_const_iterator it = experiences.beginArray(); - for( /**/ ; it != experiences.endArray(); ++it) - { - const LLSD& row = *it; - LLUUID public_key = row[EXPERIENCE_ID].asUUID(); + cache_t::const_iterator it = mCache.find(key); + if (it != mCache.end()) + { + // ...name already exists in cache, fire callback now + callback_signal_t signal; + signal.connect(slot); + + signal(it->second); + return; + } + fetch(key); - LL_DEBUGS("ExperienceCache") << "Received result for " << public_key - << " display '" << row[LLExperienceCache::NAME].asString() << "'" << LL_ENDL ; + signal_ptr signal = signal_ptr(new callback_signal_t()); + + std::pair<signal_map_t::iterator, bool> result = mSignalMap.insert(signal_map_t::value_type(key, signal)); + if (!result.second) + signal = (*result.first).second; + signal->connect(slot); +} - processExperience(public_key, row); - } +//========================================================================= +void LLExperienceCache::fetchAssociatedExperience(const LLUUID& objectId, const LLUUID& itemId, ExperienceGetFn_t fn) +{ + if (mCapability.empty()) + { + LL_WARNS("ExperienceCache") << "Capability query method not set." << LL_ENDL; + return; + } + + LLCoprocedureManager::instance().enqueueCoprocedure("ExpCache", "Fetch Associated", + boost::bind(&LLExperienceCache::fetchAssociatedExperienceCoro, this, _1, objectId, itemId, fn)); +} - LLSD error_ids = getContent()["error_ids"]; - LLSD::array_const_iterator errIt = error_ids.beginArray(); - for( /**/ ; errIt != error_ids.endArray() ; ++errIt ) - { - LLUUID id = errIt->asUUID(); - LLSD exp; - exp[EXPIRES]=DEFAULT_EXPIRATION; - exp[EXPERIENCE_ID] = id; - exp[PROPERTIES]=PROPERTY_INVALID; - exp[MISSING]=true; - exp[QUOTA] = DEFAULT_QUOTA; - - processExperience(id, exp); - LL_WARNS("ExperienceCache") << "LLExperienceResponder::result() error result for " << id << LL_ENDL ; - } +void LLExperienceCache::fetchAssociatedExperienceCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &httpAdapter, LLUUID objectId, LLUUID itemId, ExperienceGetFn_t fn) +{ + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest()); + std::string url = mCapability("GetMetadata"); + + if (url.empty()) + { + LL_WARNS("ExperienceCache") << "No Metadata capability." << LL_ENDL; + return; + } + + LLSD fields; + fields.append("experience"); + LLSD data; + data["object-id"] = objectId; + data["item-id"] = itemId; + data["fields"] = fields; + + LLSD result = httpAdapter->postAndSuspend(httpRequest, url, data); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if ((!status) || (!result.has("experience"))) + { + LLSD failure; + if (!status) + { + failure["error"] = (LLSD::Integer)status.getType(); + failure["message"] = status.getMessage(); + } + else + { + failure["error"] = -1; + failure["message"] = "no experience"; + } + if (fn && !fn.empty()) + fn(failure); + return; + } + + LLUUID expId = result["experience"].asUUID(); + get(expId, fn); +} - LL_DEBUGS("ExperienceCache") << sCache.size() << " cached experiences" << LL_ENDL; - } +//------------------------------------------------------------------------- +void LLExperienceCache::findExperienceByName(const std::string text, int page, ExperienceGetFn_t fn) +{ + if (mCapability.empty()) + { + LL_WARNS("ExperienceCache") << "Capability query method not set." << LL_ENDL; + return; + } + + LLCoprocedureManager::instance().enqueueCoprocedure("ExpCache", "Search Name", + boost::bind(&LLExperienceCache::findExperienceByNameCoro, this, _1, text, page, fn)); +} - /*virtual*/ void httpFailure() - { - LL_WARNS("ExperienceCache") << "Request failed "<<getStatus()<<" "<<getReason()<< LL_ENDL; - // We're going to construct a dummy record and cache it for a while, - // either briefly for a 503 Service Unavailable, or longer for other - // errors. - F64 retry_timestamp = errorRetryTimestamp(getStatus()); - - - // Add dummy records for all agent IDs in this request - ask_queue_t::const_iterator it = mKeys.begin(); - for ( ; it != mKeys.end(); ++it) - { +void LLExperienceCache::findExperienceByNameCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &httpAdapter, std::string text, int page, ExperienceGetFn_t fn) +{ + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest()); + std::ostringstream url; - LLSD exp = get(it->first); - //leave the properties alone if we already have a cache entry for this xp - if(exp.isUndefined()) - { - exp[PROPERTIES]=PROPERTY_INVALID; - } - exp[EXPIRES]=retry_timestamp; - exp[EXPERIENCE_ID] = it->first; - exp["key_type"] = it->second; - exp["uuid"] = it->first; - exp["error"] = (LLSD::Integer)getStatus(); - exp[QUOTA] = DEFAULT_QUOTA; - - LLExperienceCache::processExperience(it->first, exp); - } - } + url << mCapability("FindExperienceByName") << "?page=" << page << "&page_size=" << SEARCH_PAGE_SIZE << "&query=" << LLURI::escape(text); - // Return time to retry a request that generated an error, based on - // error type and headers. Return value is seconds-since-epoch. - F64 errorRetryTimestamp(S32 status) - { + LLSD result = httpAdapter->getAndSuspend(httpRequest, url.str()); - // Retry-After takes priority - LLSD retry_after = getResponseHeaders()["retry-after"]; - if (retry_after.isDefined()) - { - // We only support the delta-seconds type - S32 delta_seconds = retry_after.asInteger(); - if (delta_seconds > 0) - { - // ...valid delta-seconds - return F64(delta_seconds); - } - } + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); - // If no Retry-After, look for Cache-Control max-age - F64 expires = 0.0; - if (LLExperienceCache::expirationFromCacheControl(getResponseHeaders(), &expires)) - { - return expires; - } + if (!status) + { + fn(LLSD()); + return; + } - // No information in header, make a guess - if (status == 503) - { - // ...service unavailable, retry soon - const F64 SERVICE_UNAVAILABLE_DELAY = 600.0; // 10 min - return SERVICE_UNAVAILABLE_DELAY; - } - else if (status == 499) - { - // ...we were probably too busy, retry quickly - const F64 BUSY_DELAY = 10.0; // 10 seconds - return BUSY_DELAY; + result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS); - } - else - { - // ...other unexpected error - const F64 DEFAULT_DELAY = 3600.0; // 1 hour - return DEFAULT_DELAY; - } - } + const LLSD& experiences = result["experience_keys"]; + for (LLSD::array_const_iterator it = experiences.beginArray(); it != experiences.endArray(); ++it) + { + insert(*it); + } - private: - ask_queue_t mKeys; - }; + fn(result); +} - void requestExperiences() - { - if(sAskQueue.empty() || sLookupURL.empty()) - return; +//------------------------------------------------------------------------- +void LLExperienceCache::getGroupExperiences(const LLUUID &groupId, ExperienceGetFn_t fn) +{ + if (mCapability.empty()) + { + LL_WARNS("ExperienceCache") << "Capability query method not set." << LL_ENDL; + return; + } + + LLCoprocedureManager::instance().enqueueCoprocedure("ExpCache", "Group Experiences", + boost::bind(&LLExperienceCache::getGroupExperiencesCoro, this, _1, groupId, fn)); +} - F64 now = LLFrameTimer::getTotalSeconds(); +void LLExperienceCache::getGroupExperiencesCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &httpAdapter, LLUUID groupId, ExperienceGetFn_t fn) +{ + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest()); - const U32 EXP_URL_SEND_THRESHOLD = 3000; - const U32 PAGE_SIZE = EXP_URL_SEND_THRESHOLD/UUID_STR_LENGTH; + // search for experiences owned by the current group + std::string url = mCapability("GroupExperiences"); + if (url.empty()) + { + LL_WARNS("ExperienceCache") << "No Group Experiences capability" << LL_ENDL; + return; + } - std::ostringstream ostr; + url += "?" + groupId.asString(); - ask_queue_t keys; + LLSD result = httpAdapter->getAndSuspend(httpRequest, url); - ostr << sLookupURL << "?page_size=" << PAGE_SIZE; + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + if (!status) + { + fn(LLSD()); + return; + } - int request_count = 0; - while(!sAskQueue.empty() && request_count < sMaximumLookups) - { - ask_queue_t::iterator it = sAskQueue.begin(); - const LLUUID& key = it->first; - const std::string& key_type = it->second; + const LLSD& experienceIds = result["experience_ids"]; + fn(experienceIds); +} - ostr << '&' << key_type << '=' << key.asString() ; - - keys[key]=key_type; - request_count++; +//------------------------------------------------------------------------- +void LLExperienceCache::getRegionExperiences(CapabilityQuery_t regioncaps, ExperienceGetFn_t fn) +{ + LLCoprocedureManager::instance().enqueueCoprocedure("ExpCache", "Region Experiences", + boost::bind(&LLExperienceCache::regionExperiencesCoro, this, _1, regioncaps, false, LLSD(), fn)); +} - sPendingQueue[key] = now; - - if(ostr.tellp() > EXP_URL_SEND_THRESHOLD) - { - LL_DEBUGS("ExperienceCache") << "requestExperiences() query: " << ostr.str() << LL_ENDL; - LLHTTPClient::get(ostr.str(), new LLExperienceResponder(keys)); - ostr.clear(); - ostr.str(sLookupURL); - ostr << "?page_size=" << PAGE_SIZE; - keys.clear(); - } - sAskQueue.erase(it); - } +void LLExperienceCache::setRegionExperiences(CapabilityQuery_t regioncaps, const LLSD &experiences, ExperienceGetFn_t fn) +{ + LLCoprocedureManager::instance().enqueueCoprocedure("ExpCache", "Region Experiences", + boost::bind(&LLExperienceCache::regionExperiencesCoro, this, _1, regioncaps, true, experiences, fn)); +} - if(ostr.tellp() > sLookupURL.size()) - { - LL_DEBUGS("ExperienceCache") << "requestExperiences() query 2: " << ostr.str() << LL_ENDL; - LLHTTPClient::get(ostr.str(), new LLExperienceResponder(keys)); - } - } +void LLExperienceCache::regionExperiencesCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &httpAdapter, + CapabilityQuery_t regioncaps, bool update, LLSD experiences, ExperienceGetFn_t fn) +{ + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest()); + + // search for experiences owned by the current group + std::string url = regioncaps("RegionExperiences"); + if (url.empty()) + { + LL_WARNS("ExperienceCache") << "No Region Experiences capability" << LL_ENDL; + return; + } + + LLSD result; + if (update) + result = httpAdapter->postAndSuspend(httpRequest, url, experiences); + else + result = httpAdapter->getAndSuspend(httpRequest, url); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) + { +// fn(LLSD()); + return; + } + + result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS); + fn(result); - bool isRequestPending(const LLUUID& public_key) - { - bool isPending = false; - const F64 PENDING_TIMEOUT_SECS = 5.0 * 60.0; +} - pending_queue_t::const_iterator it = sPendingQueue.find(public_key); +//------------------------------------------------------------------------- +void LLExperienceCache::getExperiencePermission(const LLUUID &experienceId, ExperienceGetFn_t fn) +{ + if (mCapability.empty()) + { + LL_WARNS("ExperienceCache") << "Capability query method not set." << LL_ENDL; + return; + } + + std::string url = mCapability("ExperiencePreferences") + "?" + experienceId.asString(); + + permissionInvoker_fn invoker(boost::bind( + // Humans ignore next line. It is just a cast to specify which LLCoreHttpUtil::HttpCoroutineAdapter routine overload. + static_cast<LLSD(LLCoreHttpUtil::HttpCoroutineAdapter::*)(LLCore::HttpRequest::ptr_t, const std::string &, LLCore::HttpOptions::ptr_t, LLCore::HttpHeaders::ptr_t)> + //---- + // _1 -> httpAdapter + // _2 -> httpRequest + // _3 -> url + (&LLCoreHttpUtil::HttpCoroutineAdapter::getAndSuspend), _1, _2, _3, LLCore::HttpOptions::ptr_t(), LLCore::HttpHeaders::ptr_t())); + + + LLCoprocedureManager::instance().enqueueCoprocedure("ExpCache", "Preferences Set", + boost::bind(&LLExperienceCache::experiencePermissionCoro, this, _1, invoker, url, fn)); +} - if(it != sPendingQueue.end()) - { - F64 expire_time = LLFrameTimer::getTotalSeconds() - PENDING_TIMEOUT_SECS; - isPending = (it->second > expire_time); - } +void LLExperienceCache::setExperiencePermission(const LLUUID &experienceId, const std::string &permission, ExperienceGetFn_t fn) +{ + if (mCapability.empty()) + { + LL_WARNS("ExperienceCache") << "Capability query method not set." << LL_ENDL; + return; + } + + std::string url = mCapability("ExperiencePreferences"); + if (url.empty()) + return; + LLSD permData; + LLSD data; + permData["permission"] = permission; + data[experienceId.asString()] = permData; + + permissionInvoker_fn invoker(boost::bind( + // Humans ignore next line. It is just a cast to specify which LLCoreHttpUtil::HttpCoroutineAdapter routine overload. + static_cast<LLSD(LLCoreHttpUtil::HttpCoroutineAdapter::*)(LLCore::HttpRequest::ptr_t, const std::string &, const LLSD &, LLCore::HttpOptions::ptr_t, LLCore::HttpHeaders::ptr_t)> + //---- + // _1 -> httpAdapter + // _2 -> httpRequest + // _3 -> url + (&LLCoreHttpUtil::HttpCoroutineAdapter::putAndSuspend), _1, _2, _3, data, LLCore::HttpOptions::ptr_t(), LLCore::HttpHeaders::ptr_t())); + + + LLCoprocedureManager::instance().enqueueCoprocedure("ExpCache", "Preferences Set", + boost::bind(&LLExperienceCache::experiencePermissionCoro, this, _1, invoker, url, fn)); +} - return isPending; - } +void LLExperienceCache::forgetExperiencePermission(const LLUUID &experienceId, ExperienceGetFn_t fn) +{ + if (mCapability.empty()) + { + LL_WARNS("ExperienceCache") << "Capability query method not set." << LL_ENDL; + return; + } + std::string url = mCapability("ExperiencePreferences") + "?" + experienceId.asString(); - void setLookupURL( const std::string& lookup_url ) - { - sLookupURL = lookup_url; - if(!sLookupURL.empty()) - { - sLookupURL += "id/"; - } - } - bool hasLookupURL() - { - return !sLookupURL.empty(); - } + permissionInvoker_fn invoker(boost::bind( + // Humans ignore next line. It is just a cast to specify which LLCoreHttpUtil::HttpCoroutineAdapter routine overload. + static_cast<LLSD(LLCoreHttpUtil::HttpCoroutineAdapter::*)(LLCore::HttpRequest::ptr_t, const std::string &, LLCore::HttpOptions::ptr_t, LLCore::HttpHeaders::ptr_t)> + //---- + // _1 -> httpAdapter + // _2 -> httpRequest + // _3 -> url + (&LLCoreHttpUtil::HttpCoroutineAdapter::deleteAndSuspend), _1, _2, _3, LLCore::HttpOptions::ptr_t(), LLCore::HttpHeaders::ptr_t())); - void idle() - { - const F32 SECS_BETWEEN_REQUESTS = 0.1f; - if (!sRequestTimer.checkExpirationAndReset(SECS_BETWEEN_REQUESTS)) - { - return; - } + LLCoprocedureManager::instance().enqueueCoprocedure("ExpCache", "Preferences Set", + boost::bind(&LLExperienceCache::experiencePermissionCoro, this, _1, invoker, url, fn)); +} - // Must be large relative to above - const F32 ERASE_EXPIRED_TIMEOUT = 60.f; // seconds - if (sEraseExpiredTimer.checkExpirationAndReset(ERASE_EXPIRED_TIMEOUT)) - { - eraseExpired(); - } +void LLExperienceCache::experiencePermissionCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &httpAdapter, permissionInvoker_fn invokerfn, std::string url, ExperienceGetFn_t fn) +{ + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest()); + // search for experiences owned by the current group - if(!sAskQueue.empty()) - { - requestExperiences(); - } - } + LLSD result = invokerfn(httpAdapter, httpRequest, url); - void erase( const LLUUID& key ) - { - cache_t::iterator it = sCache.find(key); - - if(it != sCache.end()) - { - sCache.erase(it); - } - } + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS); - void eraseExpired() - { - F64 now = LLFrameTimer::getTotalSeconds(); - cache_t::iterator it = sCache.begin(); - while (it != sCache.end()) - { - cache_t::iterator cur = it; - LLSD& exp = cur->second; - ++it; + if (status) + { + fn(result); + } +} - if(exp.has(EXPIRES) && exp[EXPIRES].asReal() < now) - { - if(!exp.has(EXPERIENCE_ID)) - { - LL_WARNS("ExperienceCache") << "Removing experience with no id " << LL_ENDL ; - sCache.erase(cur); - } - else - { - LLUUID id = exp[EXPERIENCE_ID].asUUID(); - LLUUID private_key = exp.has(LLExperienceCache::PRIVATE_KEY) ? exp[LLExperienceCache::PRIVATE_KEY].asUUID():LLUUID::null; - if(private_key.notNull() || !exp.has("DoesNotExist")) - { - fetch(id, true); - } - else - { - LL_WARNS("ExperienceCache") << "Removing invalid experience " << id << LL_ENDL ; - sCache.erase(cur); - } - } - } - } - } +//------------------------------------------------------------------------- +void LLExperienceCache::getExperienceAdmin(const LLUUID &experienceId, ExperienceGetFn_t fn) +{ + if (mCapability.empty()) + { + LL_WARNS("ExperienceCache") << "Capability query method not set." << LL_ENDL; + return; + } + + LLCoprocedureManager::instance().enqueueCoprocedure("ExpCache", "IsAdmin", + boost::bind(&LLExperienceCache::getExperienceAdminCoro, this, _1, experienceId, fn)); +} - - bool fetch( const LLUUID& key, bool refresh/* = true*/ ) - { - if(!key.isNull() && !isRequestPending(key) && (refresh || sCache.find(key)==sCache.end())) - { - LL_DEBUGS("ExperienceCache") << " queue request for " << EXPERIENCE_ID << " " << key << LL_ENDL ; - sAskQueue[key]=EXPERIENCE_ID; +void LLExperienceCache::getExperienceAdminCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &httpAdapter, LLUUID experienceId, ExperienceGetFn_t fn) +{ + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest()); - return true; - } - return false; - } + std::string url = mCapability("IsExperienceAdmin"); + if (url.empty()) + { + LL_WARNS("ExperienceCache") << "No Region Experiences capability" << LL_ENDL; + return; + } + url += "?experience_id=" + experienceId.asString(); - void insert(const LLSD& experience_data ) - { - if(experience_data.has(EXPERIENCE_ID)) - { - processExperience(experience_data[EXPERIENCE_ID].asUUID(), experience_data); - } - else - { - LL_WARNS("ExperienceCache") << ": Ignoring cache insert of experience which is missing " << EXPERIENCE_ID << LL_ENDL; - } - } - static LLSD empty; - const LLSD& get(const LLUUID& key) - { - if(key.isNull()) return empty; - cache_t::const_iterator it = sCache.find(key); + LLSD result = httpAdapter->getAndSuspend(httpRequest, url); +// LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; +// LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); - if (it != sCache.end()) - { - return it->second; - } + fn(result); +} - fetch(key); +//------------------------------------------------------------------------- +void LLExperienceCache::updateExperience(LLSD updateData, ExperienceGetFn_t fn) +{ + if (mCapability.empty()) + { + LL_WARNS("ExperienceCache") << "Capability query method not set." << LL_ENDL; + return; + } + + LLCoprocedureManager::instance().enqueueCoprocedure("ExpCache", "IsAdmin", + boost::bind(&LLExperienceCache::updateExperienceCoro, this, _1, updateData, fn)); +} - return empty; - } - void get( const LLUUID& key, callback_slot_t slot ) - { - if(key.isNull()) return; +void LLExperienceCache::updateExperienceCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &httpAdapter, LLSD updateData, ExperienceGetFn_t fn) +{ + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest()); - cache_t::const_iterator it = sCache.find(key); - if (it != sCache.end()) - { - // ...name already exists in cache, fire callback now - callback_signal_t signal; - signal.connect(slot); - - signal(it->second); - return; - } + std::string url = mCapability("UpdateExperience"); + if (url.empty()) + { + LL_WARNS("ExperienceCache") << "No Region Experiences capability" << LL_ENDL; + return; + } - fetch(key); + updateData.erase(LLExperienceCache::QUOTA); + updateData.erase(LLExperienceCache::EXPIRES); + updateData.erase(LLExperienceCache::AGENT_ID); - // always store additional callback, even if request is pending - signal_map_t::iterator sig_it = sSignalMap.find(key); - if (sig_it == sSignalMap.end()) - { - // ...new callback for this id - callback_signal_t* signal = new callback_signal_t(); - signal->connect(slot); - sSignalMap[key] = signal; - } - else - { - // ...existing callback, bind additional slot - callback_signal_t* signal = sig_it->second; - signal->connect(slot); - } - } + LLSD result = httpAdapter->postAndSuspend(httpRequest, url, updateData); + fn(result); } - -void LLExperienceCache::mapKeys( const LLSD& legacyKeys ) +//========================================================================= +void LLExperienceCacheImpl::mapKeys(const LLSD& legacyKeys) { LLSD::array_const_iterator exp = legacyKeys.beginArray(); - for(/**/ ; exp != legacyKeys.endArray() ; ++exp) + for (/**/; exp != legacyKeys.endArray(); ++exp) { - if(exp->has(LLExperienceCache::EXPERIENCE_ID) && exp->has(LLExperienceCache::PRIVATE_KEY)) + if (exp->has(LLExperienceCacheImpl::EXPERIENCE_ID) && exp->has(LLExperienceCacheImpl::PRIVATE_KEY)) { - privateToPublicKeyMap[(*exp)[LLExperienceCache::PRIVATE_KEY].asUUID()]=(*exp)[LLExperienceCache::EXPERIENCE_ID].asUUID(); + LLExperienceCacheImpl::privateToPublicKeyMap[(*exp)[LLExperienceCacheImpl::PRIVATE_KEY].asUUID()] = + (*exp)[LLExperienceCacheImpl::EXPERIENCE_ID].asUUID(); } } } - -LLUUID LLExperienceCache::getExperienceId(const LLUUID& private_key, bool null_if_not_found) +// Return time to retry a request that generated an error, based on +// error type and headers. Return value is seconds-since-epoch. +F64 LLExperienceCacheImpl::getErrorRetryDeltaTime(S32 status, LLSD headers) { - if (private_key.isNull()) - return LLUUID::null; - KeyMap::const_iterator it=privateToPublicKeyMap.find(private_key); - if(it == privateToPublicKeyMap.end()) + // Retry-After takes priority + LLSD retry_after = headers["retry-after"]; + if (retry_after.isDefined()) + { + // We only support the delta-seconds type + S32 delta_seconds = retry_after.asInteger(); + if (delta_seconds > 0) + { + // ...valid delta-seconds + return F64(delta_seconds); + } + } + + // If no Retry-After, look for Cache-Control max-age + // Allow the header to override the default + LLSD cache_control_header = headers["cache-control"]; + if (cache_control_header.isDefined()) + { + S32 max_age = 0; + std::string cache_control = cache_control_header.asString(); + if (LLExperienceCacheImpl::maxAgeFromCacheControl(cache_control, &max_age)) + { + LL_WARNS("ExperienceCache") + << "got EXPIRES from headers, max_age " << max_age + << LL_ENDL; + return (F64)max_age; + } + } + + // No information in header, make a guess + if (status == 503) + { + // ...service unavailable, retry soon + const F64 SERVICE_UNAVAILABLE_DELAY = 600.0; // 10 min + return SERVICE_UNAVAILABLE_DELAY; + } + else if (status == 499) + { + // ...we were probably too busy, retry quickly + const F64 BUSY_DELAY = 10.0; // 10 seconds + return BUSY_DELAY; + + } + else + { + // ...other unexpected error + const F64 DEFAULT_DELAY = 3600.0; // 1 hour + return DEFAULT_DELAY; + } +} + +bool LLExperienceCacheImpl::maxAgeFromCacheControl(const std::string& cache_control, S32 *max_age) +{ + // Split the string on "," to get a list of directives + typedef boost::tokenizer<boost::char_separator<char> > tokenizer; + tokenizer directives(cache_control, COMMA_SEPARATOR); + + tokenizer::iterator token_it = directives.begin(); + for ( ; token_it != directives.end(); ++token_it) { - if(null_if_not_found) + // Tokens may have leading or trailing whitespace + std::string token = *token_it; + LLStringUtil::trim(token); + + if (token.compare(0, MAX_AGE.size(), MAX_AGE) == 0) { - return LLUUID::null; + // ...this token starts with max-age, so let's chop it up by "=" + tokenizer subtokens(token, EQUALS_SEPARATOR); + tokenizer::iterator subtoken_it = subtokens.begin(); + + // Must have a token + if (subtoken_it == subtokens.end()) return false; + std::string subtoken = *subtoken_it; + + // Must exactly equal "max-age" + LLStringUtil::trim(subtoken); + if (subtoken != MAX_AGE) return false; + + // Must have another token + ++subtoken_it; + if (subtoken_it == subtokens.end()) return false; + subtoken = *subtoken_it; + + // Must be a valid integer + // *NOTE: atoi() returns 0 for invalid values, so we have to + // check the string first. + // *TODO: Do servers ever send "0000" for zero? We don't handle it + LLStringUtil::trim(subtoken); + if (subtoken == "0") + { + *max_age = 0; + return true; + } + S32 val = atoi( subtoken.c_str() ); + if (val > 0 && val < S32_MAX) + { + *max_age = val; + return true; + } + return false; } - return private_key; } - LL_WARNS("LLExperience") << "converted private key " << private_key << " to experience_id " << it->second << LL_ENDL; - return it->second; + return false; } + + + + diff --git a/indra/llmessage/llexperiencecache.h b/indra/llmessage/llexperiencecache.h index e669ee888e..1002b33f80 100644 --- a/indra/llmessage/llexperiencecache.h +++ b/indra/llmessage/llexperiencecache.h @@ -30,75 +30,155 @@ #define LL_LLEXPERIENCECACHE_H #include "linden_common.h" +#include "llsingleton.h" +#include "llframetimer.h" +#include "llsd.h" +#include "llcorehttputil.h" #include <boost/signals2.hpp> +#include <boost/function.hpp> class LLSD; class LLUUID; - -namespace LLExperienceCache +class LLExperienceCache: public LLSingleton < LLExperienceCache > { - const std::string PRIVATE_KEY = "private_id"; - const std::string MISSING = "DoesNotExist"; - - const std::string AGENT_ID = "agent_id"; - const std::string GROUP_ID = "group_id"; - const std::string EXPERIENCE_ID = "public_id"; - const std::string NAME = "name"; - const std::string PROPERTIES = "properties"; - const std::string EXPIRES = "expiration"; - const std::string DESCRIPTION = "description"; - const std::string QUOTA = "quota"; - const std::string MATURITY = "maturity"; - const std::string METADATA = "extended_metadata"; - const std::string SLURL = "slurl"; - - - // should be in sync with experience-api/experiences/models.py - const int PROPERTY_INVALID = 1 << 0; - const int PROPERTY_PRIVILEGED = 1 << 3; - const int PROPERTY_GRID = 1 << 4; - const int PROPERTY_PRIVATE = 1 << 5; - const int PROPERTY_DISABLED = 1 << 6; - const int PROPERTY_SUSPENDED = 1 << 7; - - - // default values - const static F64 DEFAULT_EXPIRATION = 600.0; - const static S32 DEFAULT_QUOTA = 128; // this is megabytes - - // Callback types for get() below - typedef boost::signals2::signal<void (const LLSD& experience)> - callback_signal_t; - typedef callback_signal_t::slot_type callback_slot_t; + friend class LLSingleton < LLExperienceCache > ; + +public: + typedef boost::function<std::string(const std::string &)> CapabilityQuery_t; + typedef boost::function<void(const LLSD &)> ExperienceGetFn_t; + + void setCapabilityQuery(CapabilityQuery_t queryfn); + void cleanup(); + + //------------------------------------------- + // Cache methods + void erase(const LLUUID& key); + bool fetch(const LLUUID& key, bool refresh = false); + void insert(const LLSD& experience_data); + const LLSD& get(const LLUUID& key); + void get(const LLUUID& key, ExperienceGetFn_t slot); // If name information is in cache, callback will be called immediately. + + bool isRequestPending(const LLUUID& public_key); + + //------------------------------------------- + void fetchAssociatedExperience(const LLUUID& objectId, const LLUUID& itemId, ExperienceGetFn_t fn); + void findExperienceByName(const std::string text, int page, ExperienceGetFn_t fn); + void getGroupExperiences(const LLUUID &groupId, ExperienceGetFn_t fn); + + // the Get/Set Region Experiences take a CapabilityQuery to get the capability since + // the region being queried may not be the region that the agent is standing on. + void getRegionExperiences(CapabilityQuery_t regioncaps, ExperienceGetFn_t fn); + void setRegionExperiences(CapabilityQuery_t regioncaps, const LLSD &experiences, ExperienceGetFn_t fn); + + void getExperiencePermission(const LLUUID &experienceId, ExperienceGetFn_t fn); + void setExperiencePermission(const LLUUID &experienceId, const std::string &permission, ExperienceGetFn_t fn); + void forgetExperiencePermission(const LLUUID &experienceId, ExperienceGetFn_t fn); + + void getExperienceAdmin(const LLUUID &experienceId, ExperienceGetFn_t fn); + + void updateExperience(LLSD updateData, ExperienceGetFn_t fn); + //------------------------------------------- + static const std::string NAME; // "name" + static const std::string EXPERIENCE_ID; // "public_id" + static const std::string AGENT_ID; // "agent_id" + static const std::string GROUP_ID; // "group_id" + static const std::string PROPERTIES; // "properties" + static const std::string EXPIRES; // "expiration" + static const std::string DESCRIPTION; // "description" + static const std::string QUOTA; // "quota" + static const std::string MATURITY; // "maturity" + static const std::string METADATA; // "extended_metadata" + static const std::string SLURL; // "slurl" + + static const std::string MISSING; // "DoesNotExist" + + // should be in sync with experience-api/experiences/models.py + static const int PROPERTY_INVALID; // 1 << 0 + static const int PROPERTY_PRIVILEGED; // 1 << 3 + static const int PROPERTY_GRID; // 1 << 4 + static const int PROPERTY_PRIVATE; // 1 << 5 + static const int PROPERTY_DISABLED; // 1 << 6 + static const int PROPERTY_SUSPENDED; // 1 << 7 + +private: + LLExperienceCache(); + virtual ~LLExperienceCache(); + + virtual void initSingleton(); + + typedef boost::function<LLSD(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &, LLCore::HttpRequest::ptr_t, std::string)> permissionInvoker_fn; + + // Callback types for get() + typedef boost::signals2::signal < void(const LLSD &) > callback_signal_t; + typedef boost::shared_ptr<callback_signal_t> signal_ptr; + // May have multiple callbacks for a single ID, which are + // represented as multiple slots bound to the signal. + // Avoid copying signals via pointers. + typedef std::map<LLUUID, signal_ptr> signal_map_t; typedef std::map<LLUUID, LLSD> cache_t; - - - void setLookupURL(const std::string& lookup_url); - bool hasLookupURL(); - - void setMaximumLookups(int maximumLookups); - - void idle(); - void exportFile(std::ostream& ostr); - void importFile(std::istream& istr); - void initClass(); - void bootstrap(const LLSD& legacyKeys, int initialExpiration); - void erase(const LLUUID& key); - bool fetch(const LLUUID& key, bool refresh=false); - void insert(const LLSD& experience_data); - const LLSD& get(const LLUUID& key); - - // If name information is in cache, callback will be called immediately. - void get(const LLUUID& key, callback_slot_t slot); + typedef std::set<LLUUID> RequestQueue_t; + typedef std::map<LLUUID, F64> PendingQueue_t; + //-------------------------------------------- + static const std::string PRIVATE_KEY; // "private_id" + + // default values + static const F64 DEFAULT_EXPIRATION; // 600.0 + static const S32 DEFAULT_QUOTA; // 128 this is megabytes + static const int SEARCH_PAGE_SIZE; + +//-------------------------------------------- + void processExperience(const LLUUID& public_key, const LLSD& experience); + +//-------------------------------------------- + cache_t mCache; + signal_map_t mSignalMap; + RequestQueue_t mRequestQueue; + PendingQueue_t mPendingQueue; + + LLFrameTimer mEraseExpiredTimer; // Periodically clean out expired entries from the cache + CapabilityQuery_t mCapability; + std::string mCacheFileName; + bool mShutdown; + + void idleCoro(); + void eraseExpired(); + void requestExperiencesCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &, std::string, RequestQueue_t); + void requestExperiences(); + + void fetchAssociatedExperienceCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &, LLUUID, LLUUID, ExperienceGetFn_t); + void findExperienceByNameCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &, std::string, int, ExperienceGetFn_t); + void getGroupExperiencesCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &, LLUUID , ExperienceGetFn_t); + void regionExperiencesCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &httpAdapter, CapabilityQuery_t regioncaps, bool update, LLSD experiences, ExperienceGetFn_t fn); + void experiencePermissionCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &httpAdapter, permissionInvoker_fn invokerfn, std::string url, ExperienceGetFn_t fn); + void getExperienceAdminCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &httpAdapter, LLUUID experienceId, ExperienceGetFn_t fn); + void updateExperienceCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &httpAdapter, LLSD updateData, ExperienceGetFn_t fn); + + void bootstrap(const LLSD& legacyKeys, int initialExpiration); + void exportFile(std::ostream& ostr) const; + void importFile(std::istream& istr); + + // const cache_t& getCached(); // maps an experience private key to the experience id LLUUID getExperienceId(const LLUUID& private_key, bool null_if_not_found=false); + //===================================================================== + inline friend std::ostream &operator << (std::ostream &os, const LLExperienceCache &cache) + { + cache.exportFile(os); + return os; + } + + inline friend std::istream &operator >> (std::istream &is, LLExperienceCache &cache) + { + cache.importFile(is); + return is; + } }; #endif // LL_LLEXPERIENCECACHE_H diff --git a/indra/llmessage/llhost.cpp b/indra/llmessage/llhost.cpp index 63c15f0d5e..ae5c2ecf69 100644 --- a/indra/llmessage/llhost.cpp +++ b/indra/llmessage/llhost.cpp @@ -41,8 +41,6 @@ #include <arpa/inet.h> #endif -LLHost LLHost::invalid(INVALID_PORT,INVALID_HOST_IP_ADDRESS); - LLHost::LLHost(const std::string& ip_and_port) { std::string::size_type colon_index = ip_and_port.find(":"); diff --git a/indra/llmessage/llhost.h b/indra/llmessage/llhost.h index 0cf52a4151..79cad4b123 100644 --- a/indra/llmessage/llhost.h +++ b/indra/llmessage/llhost.h @@ -40,9 +40,8 @@ class LLHost { protected: U32 mPort; U32 mIP; + std::string mUntrustedSimCap; public: - - static LLHost invalid; // CREATORS LLHost() @@ -89,13 +88,17 @@ public: // READERS U32 getAddress() const { return mIP; } U32 getPort() const { return mPort; } - BOOL isOk() const { return (mIP != INVALID_HOST_IP_ADDRESS) && (mPort != INVALID_PORT); } + bool isOk() const { return (mIP != INVALID_HOST_IP_ADDRESS) && (mPort != INVALID_PORT); } + bool isInvalid() { return (mIP == INVALID_HOST_IP_ADDRESS) || (mPort == INVALID_PORT); } size_t hash() const { return (mIP << 16) | (mPort & 0xffff); } std::string getString() const; std::string getIPString() const; std::string getHostName() const; std::string getIPandPort() const; + std::string getUntrustedSimulatorCap() const { return mUntrustedSimCap; } + void setUntrustedSimulatorCap(const std::string &capurl) { mUntrustedSimCap = capurl; } + friend std::ostream& operator<< (std::ostream& os, const LLHost &hh); // This operator is not well defined. does it expect a diff --git a/indra/llmessage/llhttpassetstorage.cpp b/indra/llmessage/llhttpassetstorage.cpp deleted file mode 100644 index ace65760c3..0000000000 --- a/indra/llmessage/llhttpassetstorage.cpp +++ /dev/null @@ -1,1454 +0,0 @@ -/** - * @file llhttpassetstorage.cpp - * @brief Subclass capable of loading asset data to/from an external - * source. Currently, a web server accessed via curl - * - * $LicenseInfo:firstyear=2003&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 "llhttpassetstorage.h" - -#include <sys/stat.h> - -#include "indra_constants.h" -#include "message.h" -#include "llproxy.h" -#include "llvfile.h" -#include "llvfs.h" -#include "llxfer.h" - -#ifdef LL_USESYSTEMLIBS -# include <zlib.h> -#else -# include "zlib/zlib.h" -#endif - -const char* const LOCAL_ASSET_URL_FORMAT = "http://%s:12041/asset"; - -const U32 MAX_RUNNING_REQUESTS = 1; - -// Try for 30 minutes for now. -const F32 GET_URL_TO_FILE_TIMEOUT = 1800.0f; - -const S32 COMPRESSED_INPUT_BUFFER_SIZE = 4096; - -///////////////////////////////////////////////////////////////////////////////// -// LLTempAssetData -// An asset not stored on central asset store, but on a simulator node somewhere. -///////////////////////////////////////////////////////////////////////////////// -struct LLTempAssetData -{ - LLUUID mAssetID; - LLUUID mAgentID; - std::string mHostName; -}; - -///////////////////////////////////////////////////////////////////////////////// -// LLHTTPAssetRequest -///////////////////////////////////////////////////////////////////////////////// - -class LLHTTPAssetRequest : public LLAssetRequest -{ -public: - LLHTTPAssetRequest(LLHTTPAssetStorage *asp, const LLUUID &uuid, - LLAssetType::EType type, LLAssetStorage::ERequestType rt, - const std::string& url, CURLM *curl_multi); - virtual ~LLHTTPAssetRequest(); - - void setupCurlHandle(); - void cleanupCurlHandle(); - - void prepareCompressedUpload(); - void finishCompressedUpload(); - size_t readCompressedData(void* data, size_t size); - - static size_t curlCompressedUploadCallback( - void *data, size_t size, size_t nmemb, void *user_data); - - virtual LLSD getTerseDetails() const; - virtual LLSD getFullDetails() const; - -public: - LLHTTPAssetStorage *mAssetStoragep; - - CURL *mCurlHandle; - CURLM *mCurlMultiHandle; - std::string mURLBuffer; - struct curl_slist *mHTTPHeaders; - LLVFile *mVFile; - LLUUID mTmpUUID; - LLAssetStorage::ERequestType mRequestType; - - bool mZInitialized; - z_stream mZStream; - char* mZInputBuffer; - bool mZInputExhausted; - - FILE *mFP; -}; - - -LLHTTPAssetRequest::LLHTTPAssetRequest(LLHTTPAssetStorage *asp, - const LLUUID &uuid, - LLAssetType::EType type, - LLAssetStorage::ERequestType rt, - const std::string& url, - CURLM *curl_multi) - : LLAssetRequest(uuid, type), - mZInitialized(false) -{ - memset(&mZStream, 0, sizeof(mZStream)); // we'll initialize this later, but for now zero the whole C-style struct to avoid debug/coverity noise - mAssetStoragep = asp; - mCurlHandle = NULL; - mCurlMultiHandle = curl_multi; - mVFile = NULL; - mRequestType = rt; - mHTTPHeaders = NULL; - mFP = NULL; - mZInputBuffer = NULL; - mZInputExhausted = false; - - mURLBuffer = url; -} - -LLHTTPAssetRequest::~LLHTTPAssetRequest() -{ - // Cleanup/cancel the request - if (mCurlHandle) - { - curl_multi_remove_handle(mCurlMultiHandle, mCurlHandle); - cleanupCurlHandle(); - } - if (mHTTPHeaders) - { - curl_slist_free_all(mHTTPHeaders); - } - delete mVFile; - finishCompressedUpload(); -} - -// virtual -LLSD LLHTTPAssetRequest::getTerseDetails() const -{ - LLSD sd = LLAssetRequest::getTerseDetails(); - - sd["url"] = mURLBuffer; - - return sd; -} - -// virtual -LLSD LLHTTPAssetRequest::getFullDetails() const -{ - LLSD sd = LLAssetRequest::getFullDetails(); - - if (mCurlHandle) - { - long curl_response = -1; - long curl_connect = -1; - double curl_total_time = -1.0f; - double curl_size_upload = -1.0f; - double curl_size_download = -1.0f; - double curl_content_length_upload = -1.0f; - double curl_content_length_download = -1.0f; - long curl_request_size = -1; - const char* curl_content_type = NULL; - - curl_easy_getinfo(mCurlHandle, CURLINFO_HTTP_CODE, &curl_response); - curl_easy_getinfo(mCurlHandle, CURLINFO_HTTP_CONNECTCODE, &curl_connect); - curl_easy_getinfo(mCurlHandle, CURLINFO_TOTAL_TIME, &curl_total_time); - curl_easy_getinfo(mCurlHandle, CURLINFO_SIZE_UPLOAD, &curl_size_upload); - curl_easy_getinfo(mCurlHandle, CURLINFO_SIZE_DOWNLOAD, &curl_size_download); - curl_easy_getinfo(mCurlHandle, CURLINFO_CONTENT_LENGTH_UPLOAD, &curl_content_length_upload); - curl_easy_getinfo(mCurlHandle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &curl_content_length_download); - curl_easy_getinfo(mCurlHandle, CURLINFO_REQUEST_SIZE, &curl_request_size); - curl_easy_getinfo(mCurlHandle, CURLINFO_CONTENT_TYPE, &curl_content_type); - - sd["curl_response_code"] = (int) curl_response; - sd["curl_http_connect_code"] = (int) curl_connect; - sd["curl_total_time"] = curl_total_time; - sd["curl_size_upload"] = curl_size_upload; - sd["curl_size_download"] = curl_size_download; - sd["curl_content_length_upload"] = curl_content_length_upload; - sd["curl_content_length_download"] = curl_content_length_download; - sd["curl_request_size"] = (int) curl_request_size; - if (curl_content_type) - { - sd["curl_content_type"] = curl_content_type; - } - else - { - sd["curl_content_type"] = ""; - } - } - - sd["temp_id"] = mTmpUUID; - sd["request_type"] = LLAssetStorage::getRequestName(mRequestType); - sd["z_initialized"] = mZInitialized; - sd["z_input_exhausted"] = mZInputExhausted; - - S32 file_size = -1; - if (mFP) - { - struct stat file_stat; - int file_desc = fileno(mFP); - if ( fstat(file_desc, &file_stat) == 0) - { - file_size = file_stat.st_size; - } - } - sd["file_size"] = file_size; - - return sd; -} - - -void LLHTTPAssetRequest::setupCurlHandle() -{ - // *NOTE: Similar code exists in mapserver/llcurlutil.cpp JC - mCurlHandle = LLCurl::newEasyHandle(); - llassert_always(mCurlHandle != NULL) ; - - // Apply proxy settings if configured to do so - LLProxy::getInstance()->applyProxySettings(mCurlHandle); - - curl_easy_setopt(mCurlHandle, CURLOPT_NOSIGNAL, 1); - curl_easy_setopt(mCurlHandle, CURLOPT_NOPROGRESS, 1); - curl_easy_setopt(mCurlHandle, CURLOPT_URL, mURLBuffer.c_str()); - curl_easy_setopt(mCurlHandle, CURLOPT_PRIVATE, this); - if (LLAssetStorage::RT_DOWNLOAD == mRequestType) - { - curl_easy_setopt(mCurlHandle, CURLOPT_ENCODING, ""); - // only do this on downloads, as uploads - // to some apache configs (like our test grids) - // mistakenly claim the response is gzip'd if the resource - // name ends in .gz, even though in a PUT, the response is - // just plain HTML saying "created" - } - /* Remove the Pragma: no-cache header that libcurl inserts by default; - we want the cached version, if possible. */ - if (mZInitialized) - { - curl_easy_setopt(mCurlHandle, CURLOPT_PROXY, ""); - // disable use of proxy, which can't handle chunked transfers - } - mHTTPHeaders = curl_slist_append(mHTTPHeaders, "Pragma:"); - - // bug in curl causes DNS to be cached for too long a time, 0 sets it to never cache DNS results internally (to curl) - curl_easy_setopt(mCurlHandle, CURLOPT_DNS_CACHE_TIMEOUT, 0); - - // resist the temptation to explicitly add the Transfer-Encoding: chunked - // header here - invokes a libCURL bug - curl_easy_setopt(mCurlHandle, CURLOPT_HTTPHEADER, mHTTPHeaders); - if (mAssetStoragep) - { - // Set the appropriate pending upload or download flag - mAssetStoragep->addRunningRequest(mRequestType, this); - } - else - { - LL_ERRS() << "LLHTTPAssetRequest::setupCurlHandle - No asset storage associated with this request!" << LL_ENDL; - } -} - -void LLHTTPAssetRequest::cleanupCurlHandle() -{ - LLCurl::deleteEasyHandle(mCurlHandle); - if (mAssetStoragep) - { - // Terminating a request. Thus upload or download is no longer pending. - mAssetStoragep->removeRunningRequest(mRequestType, this); - } - else - { - LL_ERRS() << "LLHTTPAssetRequest::~LLHTTPAssetRequest - No asset storage associated with this request!" << LL_ENDL; - } - mCurlHandle = NULL; -} - -void LLHTTPAssetRequest::prepareCompressedUpload() -{ - mZStream.next_in = Z_NULL; - mZStream.avail_in = 0; - mZStream.zalloc = Z_NULL; - mZStream.zfree = Z_NULL; - mZStream.opaque = Z_NULL; - - int r = deflateInit2(&mZStream, - 1, // compression level - Z_DEFLATED, // the only method defined - 15 + 16, // the default windowBits + gzip header flag - 8, // the default memLevel - Z_DEFAULT_STRATEGY); - - if (r != Z_OK) - { - LL_ERRS() << "LLHTTPAssetRequest::prepareCompressedUpload defalateInit2() failed" << LL_ENDL; - } - - mZInitialized = true; - mZInputBuffer = new char[COMPRESSED_INPUT_BUFFER_SIZE]; - mZInputExhausted = false; - - mVFile = new LLVFile(gAssetStorage->mVFS, - getUUID(), getType(), LLVFile::READ); -} - -void LLHTTPAssetRequest::finishCompressedUpload() -{ - if (mZInitialized) - { - LL_INFOS() << "LLHTTPAssetRequest::finishCompressedUpload: " - << "read " << mZStream.total_in << " byte asset file, " - << "uploaded " << mZStream.total_out << " byte compressed asset" - << LL_ENDL; - - deflateEnd(&mZStream); - delete[] mZInputBuffer; - } -} - -size_t LLHTTPAssetRequest::readCompressedData(void* data, size_t size) -{ - llassert(mZInitialized); - - mZStream.next_out = (Bytef*)data; - mZStream.avail_out = size; - - while (mZStream.avail_out > 0) - { - if (mZStream.avail_in == 0 && !mZInputExhausted) - { - S32 to_read = llmin(COMPRESSED_INPUT_BUFFER_SIZE, - (S32)(mVFile->getSize() - mVFile->tell())); - - if ( to_read > 0 ) - { - mVFile->read((U8*)mZInputBuffer, to_read); /*Flawfinder: ignore*/ - mZStream.next_in = (Bytef*)mZInputBuffer; - mZStream.avail_in = mVFile->getLastBytesRead(); - } - - mZInputExhausted = mZStream.avail_in == 0; - } - - int r = deflate(&mZStream, - mZInputExhausted ? Z_FINISH : Z_NO_FLUSH); - - if (r == Z_STREAM_END || r < 0 || mZInputExhausted) - { - if (r < 0) - { - LL_WARNS() << "LLHTTPAssetRequest::readCompressedData: deflate returned error code " - << (S32) r << LL_ENDL; - } - break; - } - } - - return size - mZStream.avail_out; -} - -//static -size_t LLHTTPAssetRequest::curlCompressedUploadCallback( - void *data, size_t size, size_t nmemb, void *user_data) -{ - size_t num_read = 0; - - if (gAssetStorage) - { - CURL *curl_handle = (CURL *)user_data; - LLHTTPAssetRequest *req = NULL; - curl_easy_getinfo(curl_handle, CURLINFO_PRIVATE, &req); - if (req) - { - num_read = req->readCompressedData(data, size * nmemb); - } - } - - return num_read; -} - -///////////////////////////////////////////////////////////////////////////////// -// LLHTTPAssetStorage -///////////////////////////////////////////////////////////////////////////////// - - -LLHTTPAssetStorage::LLHTTPAssetStorage(LLMessageSystem *msg, LLXferManager *xfer, - LLVFS *vfs, LLVFS *static_vfs, - const LLHost &upstream_host, - const std::string& web_host, - const std::string& local_web_host, - const std::string& host_name) - : LLAssetStorage(msg, xfer, vfs, static_vfs, upstream_host) -{ - _init(web_host, local_web_host, host_name); -} - -LLHTTPAssetStorage::LLHTTPAssetStorage(LLMessageSystem *msg, LLXferManager *xfer, - LLVFS *vfs, - LLVFS *static_vfs, - const std::string& web_host, - const std::string& local_web_host, - const std::string& host_name) - : LLAssetStorage(msg, xfer, vfs, static_vfs) -{ - _init(web_host, local_web_host, host_name); -} - -void LLHTTPAssetStorage::_init(const std::string& web_host, const std::string& local_web_host, const std::string& host_name) -{ - mBaseURL = web_host; - mLocalBaseURL = local_web_host; - mHostName = host_name; - - // curl_global_init moved to LLCurl::initClass() - - mCurlMultiHandle = LLCurl::newMultiHandle() ; - llassert_always(mCurlMultiHandle != NULL) ; -} - -LLHTTPAssetStorage::~LLHTTPAssetStorage() -{ - LLCurl::deleteMultiHandle(mCurlMultiHandle); - mCurlMultiHandle = NULL; - - // curl_global_cleanup moved to LLCurl::initClass() -} - -// storing data is simpler than getting it, so we just overload the whole method -void LLHTTPAssetStorage::storeAssetData( - const LLUUID& uuid, - LLAssetType::EType type, - LLAssetStorage::LLStoreAssetCallback callback, - void* user_data, - bool temp_file, - bool is_priority, - bool store_local, - const LLUUID& requesting_agent_id, - bool user_waiting, - F64Seconds timeout) -{ - if (mVFS->getExists(uuid, type)) // VFS treats nonexistant and zero-length identically - { - LLAssetRequest *req = new LLAssetRequest(uuid, type); - req->mUpCallback = callback; - req->mUserData = user_data; - req->mRequestingAgentID = requesting_agent_id; - req->mIsUserWaiting = user_waiting; - req->mTimeout = timeout; - - // LLAssetStorage metric: Successful Request - S32 size = mVFS->getSize(uuid, type); - const char *message; - if( store_local ) - { - message = "Added to local upload queue"; - } - else - { - message = "Added to upload queue"; - } - reportMetric( uuid, type, LLStringUtil::null, requesting_agent_id, size, MR_OKAY, __FILE__, __LINE__, message ); - - // this will get picked up and transmitted in checkForTimeouts - if(store_local) - { - mPendingLocalUploads.push_back(req); - } - else if(is_priority) - { - mPendingUploads.push_front(req); - } - else - { - mPendingUploads.push_back(req); - } - } - else - { - LL_WARNS() << "AssetStorage: attempt to upload non-existent vfile " << uuid << ":" << LLAssetType::lookup(type) << LL_ENDL; - if (callback) - { - // LLAssetStorage metric: Zero size VFS - reportMetric( uuid, type, LLStringUtil::null, requesting_agent_id, 0, MR_ZERO_SIZE, __FILE__, __LINE__, "The file didn't exist or was zero length (VFS - can't tell which)" ); - callback(uuid, user_data, LL_ERR_ASSET_REQUEST_NONEXISTENT_FILE, LL_EXSTAT_NONEXISTENT_FILE); - } - } -} - -// virtual -void LLHTTPAssetStorage::storeAssetData( - const std::string& filename, - const LLUUID& asset_id, - LLAssetType::EType asset_type, - LLStoreAssetCallback callback, - void* user_data, - bool temp_file, - bool is_priority, - bool user_waiting, - F64Seconds timeout) -{ - LL_INFOS() << "LLAssetStorage::storeAssetData (legacy)" << asset_id << ":" << LLAssetType::lookup(asset_type) << LL_ENDL; - - LLLegacyAssetRequest *legacy = new LLLegacyAssetRequest; - - legacy->mUpCallback = callback; - legacy->mUserData = user_data; - - FILE *fp = LLFile::fopen(filename, "rb"); /*Flawfinder: ignore*/ - S32 size = 0; - if (fp) - { - fseek(fp, 0, SEEK_END); - size = ftell(fp); - fseek(fp, 0, SEEK_SET); - } - - if( size ) - { - LLVFile file(mVFS, asset_id, asset_type, LLVFile::WRITE); - - file.setMaxSize(size); - - const S32 buf_size = 65536; - U8 copy_buf[buf_size]; - while ((size = (S32)fread(copy_buf, 1, buf_size, fp))) - { - file.write(copy_buf, size); - } - fclose(fp); - - // if this upload fails, the caller needs to setup a new tempfile for us - if (temp_file) - { - LLFile::remove(filename); - } - - // LLAssetStorage metric: Success not needed; handled in the overloaded method here: - storeAssetData( - asset_id, - asset_type, - legacyStoreDataCallback, - (void**)legacy, - temp_file, - is_priority, - false, - LLUUID::null, - user_waiting, - timeout); - } - else // !size - { - if( fp ) - { - // LLAssetStorage metric: Zero size - reportMetric( asset_id, asset_type, filename, LLUUID::null, 0, MR_ZERO_SIZE, __FILE__, __LINE__, "The file was zero length" ); - fclose( fp ); - } - else - { - // LLAssetStorage metric: Missing File - reportMetric( asset_id, asset_type, filename, LLUUID::null, 0, MR_FILE_NONEXIST, __FILE__, __LINE__, "The file didn't exist" ); - } - if (callback) - { - callback(LLUUID::null, user_data, LL_ERR_CANNOT_OPEN_FILE, LL_EXSTAT_BLOCKED_FILE); - } - delete legacy; - } -} - -// virtual -LLSD LLHTTPAssetStorage::getPendingDetails(LLAssetStorage::ERequestType rt, - LLAssetType::EType asset_type, - const std::string& detail_prefix) const -{ - LLSD sd = LLAssetStorage::getPendingDetails(rt, asset_type, detail_prefix); - const request_list_t* running = getRunningList(rt); - if (running) - { - // Loop through the pending requests sd, and add extra info about its running status. - S32 num_pending = sd["requests"].size(); - S32 i; - for (i = 0; i < num_pending; ++i) - { - LLSD& pending = sd["requests"][i]; - // See if this pending request is running. - const LLAssetRequest* req = findRequest(running, - LLAssetType::lookup(pending["type"].asString()), - pending["asset_id"]); - if (req) - { - // Keep the detail_url so we don't have to rebuild it. - LLURI detail_url = pending["detail"]; - pending = req->getTerseDetails(); - pending["detail"] = detail_url; - pending["is_running"] = true; - } - else - { - pending["is_running"] = false; - } - } - } - return sd; -} - -// virtual -LLSD LLHTTPAssetStorage::getPendingRequest(LLAssetStorage::ERequestType rt, - LLAssetType::EType asset_type, - const LLUUID& asset_id) const -{ - // Look for this asset in the running list first. - const request_list_t* running = getRunningList(rt); - if (running) - { - LLSD sd = LLAssetStorage::getPendingRequestImpl(running, asset_type, asset_id); - if (sd) - { - sd["is_running"] = true; - return sd; - } - } - LLSD sd = LLAssetStorage::getPendingRequest(rt, asset_type, asset_id); - if (sd) - { - sd["is_running"] = false; - } - return sd; -} - -// virtual -bool LLHTTPAssetStorage::deletePendingRequest(LLAssetStorage::ERequestType rt, - LLAssetType::EType asset_type, - const LLUUID& asset_id) -{ - // Try removing this from the running list first. - request_list_t* running = getRunningList(rt); - if (running) - { - LLAssetRequest* req = findRequest(running, asset_type, asset_id); - if (req) - { - // Remove this request from the running list to get it out of curl. - running->remove(req); - - // Find this request in the pending list, so we can move it to the end of the line. - request_list_t* pending = getRequestList(rt); - if (pending) - { - request_list_t::iterator result = std::find_if(pending->begin(), pending->end(), - std::bind2nd(ll_asset_request_equal<LLAssetRequest*>(), req)); - if (pending->end() != result) - { - // This request was found in the pending list. Move it to the end! - LLAssetRequest* pending_req = *result; - pending->remove(pending_req); - - if (!pending_req->mIsUserWaiting) //A user is waiting on this request. Toss it. - { - pending->push_back(pending_req); - } - else - { - if (pending_req->mUpCallback) //Clean up here rather than _callUploadCallbacks because this request is already cleared the req. - { - pending_req->mUpCallback(pending_req->getUUID(), pending_req->mUserData, -1, LL_EXSTAT_REQUEST_DROPPED); - } - - } - - LL_INFOS() << "Asset " << getRequestName(rt) << " request for " - << asset_id << "." << LLAssetType::lookup(asset_type) - << " removed from curl and placed at the end of the pending queue." - << LL_ENDL; - } - else - { - LL_WARNS() << "Unable to find pending " << getRequestName(rt) << " request for " - << asset_id << "." << LLAssetType::lookup(asset_type) << LL_ENDL; - } - } - delete req; - - return true; - } - } - return LLAssetStorage::deletePendingRequest(rt, asset_type, asset_id); -} - -// internal requester, used by getAssetData in superclass -void LLHTTPAssetStorage::_queueDataRequest(const LLUUID& uuid, LLAssetType::EType type, - void (*callback)(LLVFS *vfs, const LLUUID&, LLAssetType::EType, void *, S32, LLExtStat), - void *user_data, BOOL duplicate, - BOOL is_priority) -{ - // stash the callback info so we can find it after we get the response message - LLAssetRequest *req = new LLAssetRequest(uuid, type); - req->mDownCallback = callback; - req->mUserData = user_data; - req->mIsPriority = is_priority; - - // this will get picked up and downloaded in checkForTimeouts - - // - // HAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACK! Asset requests were taking too long and timing out. - // Since texture requests are the LEAST sensitive (on the simulator) to being delayed, add - // non-texture requests to the front, and add texture requests to the back. The theory is - // that we always want them first, even if they're out of order. - // - - if (req->getType() == LLAssetType::AT_TEXTURE) - { - mPendingDownloads.push_back(req); - } - else - { - mPendingDownloads.push_front(req); - } -} - -LLAssetRequest* LLHTTPAssetStorage::findNextRequest(LLAssetStorage::request_list_t& pending, - LLAssetStorage::request_list_t& running) -{ - // Early exit if the running list is full, or we don't have more pending than running. - if (running.size() >= MAX_RUNNING_REQUESTS - || pending.size() <= running.size()) return NULL; - - // Look for the first pending request that is not already running. - request_list_t::iterator running_begin = running.begin(); - request_list_t::iterator running_end = running.end(); - - request_list_t::iterator pending_iter = pending.begin(); - - // Loop over all pending requests until we miss finding it in the running list. - for (; pending_iter != pending.end(); ++pending_iter) - { - LLAssetRequest* req = *pending_iter; - // Look for this pending request in the running list. - if (running_end == std::find_if(running_begin, running_end, - std::bind2nd(ll_asset_request_equal<LLAssetRequest*>(), req))) - { - // It isn't running! Return it. - return req; - } - } - return NULL; -} - -// overloaded to additionally move data to/from the webserver -void LLHTTPAssetStorage::checkForTimeouts() -{ - CURLMcode mcode; - LLAssetRequest *req; - while ( (req = findNextRequest(mPendingDownloads, mRunningDownloads)) ) - { - // Setup this curl download request - // We need to generate a new request here - // since the one in the list could go away - std::string tmp_url; - std::string uuid_str; - req->getUUID().toString(uuid_str); - std::string base_url = getBaseURL(req->getUUID(), req->getType()); - tmp_url = llformat("%s/%36s.%s", base_url.c_str() , uuid_str.c_str(), LLAssetType::lookup(req->getType())); - - LLHTTPAssetRequest *new_req = new LLHTTPAssetRequest(this, req->getUUID(), - req->getType(), RT_DOWNLOAD, tmp_url, mCurlMultiHandle); - new_req->mTmpUUID.generate(); - - // Sets pending download flag internally - new_req->setupCurlHandle(); - curl_easy_setopt(new_req->mCurlHandle, CURLOPT_FOLLOWLOCATION, TRUE); - curl_easy_setopt(new_req->mCurlHandle, CURLOPT_WRITEFUNCTION, &curlDownCallback); - curl_easy_setopt(new_req->mCurlHandle, CURLOPT_WRITEDATA, new_req->mCurlHandle); - - mcode = curl_multi_add_handle(mCurlMultiHandle, new_req->mCurlHandle); - if (mcode > CURLM_OK) - { - // Failure. Deleting the pending request will remove it from the running - // queue, and push it to the end of the pending queue. - new_req->cleanupCurlHandle(); - deletePendingRequest(RT_DOWNLOAD, new_req->getType(), new_req->getUUID()); - break; - } - else - { - LL_INFOS() << "Requesting " << new_req->mURLBuffer << LL_ENDL; - } - } - - while ( (req = findNextRequest(mPendingUploads, mRunningUploads)) ) - { - // setup this curl upload request - - bool do_compress = req->getType() == LLAssetType::AT_OBJECT; - - std::string tmp_url; - std::string uuid_str; - req->getUUID().toString(uuid_str); - tmp_url = mBaseURL + "/" + uuid_str + "." + LLAssetType::lookup(req->getType()); - if (do_compress) tmp_url += ".gz"; - - LLHTTPAssetRequest *new_req = new LLHTTPAssetRequest(this, req->getUUID(), - req->getType(), RT_UPLOAD, tmp_url, mCurlMultiHandle); - - if (req->mIsUserWaiting) //If a user is waiting on a realtime response, we want to perserve information across upload attempts. - { - new_req->mTime = req->mTime; - new_req->mTimeout = req->mTimeout; - new_req->mIsUserWaiting = req->mIsUserWaiting; - } - - if (do_compress) - { - new_req->prepareCompressedUpload(); - } - - // Sets pending upload flag internally - new_req->setupCurlHandle(); - curl_easy_setopt(new_req->mCurlHandle, CURLOPT_UPLOAD, 1); - curl_easy_setopt(new_req->mCurlHandle, CURLOPT_WRITEFUNCTION, &nullOutputCallback); - - if (do_compress) - { - curl_easy_setopt(new_req->mCurlHandle, CURLOPT_READFUNCTION, - &LLHTTPAssetRequest::curlCompressedUploadCallback); - } - else - { - LLVFile file(mVFS, req->getUUID(), req->getType()); - curl_easy_setopt(new_req->mCurlHandle, CURLOPT_INFILESIZE, file.getSize()); - curl_easy_setopt(new_req->mCurlHandle, CURLOPT_READFUNCTION, - &curlUpCallback); - } - curl_easy_setopt(new_req->mCurlHandle, CURLOPT_READDATA, new_req->mCurlHandle); - - mcode = curl_multi_add_handle(mCurlMultiHandle, new_req->mCurlHandle); - if (mcode > CURLM_OK) - { - // Failure. Deleting the pending request will remove it from the running - // queue, and push it to the end of the pending queue. - new_req->cleanupCurlHandle(); - deletePendingRequest(RT_UPLOAD, new_req->getType(), new_req->getUUID()); - break; - } - else - { - // Get the uncompressed file size. - LLVFile file(mVFS,new_req->getUUID(),new_req->getType()); - S32 size = file.getSize(); - LL_INFOS() << "Requesting PUT " << new_req->mURLBuffer << ", asset size: " << size << " bytes" << LL_ENDL; - if (size == 0) - { - LL_WARNS() << "Rejecting zero size PUT request!" << LL_ENDL; - new_req->cleanupCurlHandle(); - deletePendingRequest(RT_UPLOAD, new_req->getType(), new_req->getUUID()); - } - } - // Pending upload will have been flagged by the request - } - - while ( (req = findNextRequest(mPendingLocalUploads, mRunningLocalUploads)) ) - { - // setup this curl upload request - LLVFile file(mVFS, req->getUUID(), req->getType()); - - std::string tmp_url; - std::string uuid_str; - req->getUUID().toString(uuid_str); - - // KLW - All temporary uploads are saved locally "http://localhost:12041/asset" - tmp_url = llformat("%s/%36s.%s", mLocalBaseURL.c_str(), uuid_str.c_str(), LLAssetType::lookup(req->getType())); - - LLHTTPAssetRequest *new_req = new LLHTTPAssetRequest(this, req->getUUID(), - req->getType(), RT_LOCALUPLOAD, tmp_url, mCurlMultiHandle); - new_req->mRequestingAgentID = req->mRequestingAgentID; - - // Sets pending upload flag internally - new_req->setupCurlHandle(); - curl_easy_setopt(new_req->mCurlHandle, CURLOPT_PUT, 1); - curl_easy_setopt(new_req->mCurlHandle, CURLOPT_INFILESIZE, file.getSize()); - curl_easy_setopt(new_req->mCurlHandle, CURLOPT_WRITEFUNCTION, &nullOutputCallback); - curl_easy_setopt(new_req->mCurlHandle, CURLOPT_READFUNCTION, &curlUpCallback); - curl_easy_setopt(new_req->mCurlHandle, CURLOPT_READDATA, new_req->mCurlHandle); - - mcode = curl_multi_add_handle(mCurlMultiHandle, new_req->mCurlHandle); - if (mcode > CURLM_OK) - { - // Failure. Deleting the pending request will remove it from the running - // queue, and push it to the end of the pending queue. - new_req->cleanupCurlHandle(); - deletePendingRequest(RT_LOCALUPLOAD, new_req->getType(), new_req->getUUID()); - break; - } - else - { - // Get the uncompressed file size. - S32 size = file.getSize(); - - LL_INFOS() << "TAT: LLHTTPAssetStorage::checkForTimeouts() : pending local!" - << " Requesting PUT " << new_req->mURLBuffer << ", asset size: " << size << " bytes" << LL_ENDL; - if (size == 0) - { - - LL_WARNS() << "Rejecting zero size PUT request!" << LL_ENDL; - new_req->cleanupCurlHandle(); - deletePendingRequest(RT_UPLOAD, new_req->getType(), new_req->getUUID()); - } - - } - // Pending upload will have been flagged by the request - } - S32 count = 0; - int queue_length; - do - { - mcode = curl_multi_perform(mCurlMultiHandle, &queue_length); - count++; - } while (mcode == CURLM_CALL_MULTI_PERFORM && (count < 5)); - - CURLMsg *curl_msg; - do - { - curl_msg = curl_multi_info_read(mCurlMultiHandle, &queue_length); - if (curl_msg && curl_msg->msg == CURLMSG_DONE) - { - long curl_result = 0; - S32 xfer_result = LL_ERR_NOERR; - - LLHTTPAssetRequest *http_req = NULL; - curl_easy_getinfo(curl_msg->easy_handle, CURLINFO_PRIVATE, &http_req); - - // TODO: Throw curl_result at all callbacks. - curl_easy_getinfo(curl_msg->easy_handle, CURLINFO_HTTP_CODE, &curl_result); - if (RT_UPLOAD == http_req->mRequestType || RT_LOCALUPLOAD == http_req->mRequestType) - { - if (curl_msg->data.result == CURLE_OK && - ( curl_result == HTTP_OK - || curl_result == HTTP_CREATED - || curl_result == HTTP_NO_CONTENT)) - { - LL_INFOS() << "Success uploading " << http_req->getUUID() << " to " << http_req->mURLBuffer << LL_ENDL; - if (RT_LOCALUPLOAD == http_req->mRequestType) - { - addTempAssetData(http_req->getUUID(), http_req->mRequestingAgentID, mHostName); - } - } - else if (curl_msg->data.result == CURLE_COULDNT_CONNECT || - curl_msg->data.result == CURLE_OPERATION_TIMEOUTED || - curl_result == HTTP_BAD_GATEWAY || - curl_result == HTTP_SERVICE_UNAVAILABLE) - { - LL_WARNS() << "Re-requesting upload for " << http_req->getUUID() << ". Received upload error to " << http_req->mURLBuffer << - " with result " << curl_easy_strerror(curl_msg->data.result) << ", http result " << curl_result << LL_ENDL; - - ////HACK (probably) I am sick of this getting requeued and driving me mad. - //if (http_req->mIsUserWaiting) - //{ - // deletePendingRequest(RT_UPLOAD, http_req->getType(), http_req->getUUID()); - //} - } - else - { - LL_WARNS() << "Failure uploading " << http_req->getUUID() << " to " << http_req->mURLBuffer << - " with result " << curl_easy_strerror(curl_msg->data.result) << ", http result " << curl_result << LL_ENDL; - - xfer_result = LL_ERR_ASSET_REQUEST_FAILED; - } - - if (!(curl_msg->data.result == CURLE_COULDNT_CONNECT || - curl_msg->data.result == CURLE_OPERATION_TIMEOUTED || - curl_result == HTTP_BAD_GATEWAY || - curl_result == HTTP_SERVICE_UNAVAILABLE)) - { - // shared upload finished callback - // in the base class, this is called from processUploadComplete - _callUploadCallbacks(http_req->getUUID(), http_req->getType(), (xfer_result == 0), LL_EXSTAT_CURL_RESULT | curl_result); - // Pending upload flag will get cleared when the request is deleted - } - } - else if (RT_DOWNLOAD == http_req->mRequestType) - { - if (curl_result == HTTP_OK && curl_msg->data.result == CURLE_OK) - { - if (http_req->mVFile && http_req->mVFile->getSize() > 0) - { - LL_INFOS() << "Success downloading " << http_req->mURLBuffer << ", size " << http_req->mVFile->getSize() << LL_ENDL; - - http_req->mVFile->rename(http_req->getUUID(), http_req->getType()); - } - else - { - // *TODO: if this actually indicates a bad asset on the server - // (not certain at this point), then delete it - LL_WARNS() << "Found " << http_req->mURLBuffer << " to be zero size" << LL_ENDL; - xfer_result = LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE; - } - } - else - { - // KLW - TAT See if an avatar owns this texture, and if so request re-upload. - LL_WARNS() << "Failure downloading " << http_req->mURLBuffer << - " with result " << curl_easy_strerror(curl_msg->data.result) << ", http result " << curl_result << LL_ENDL; - - xfer_result = (curl_result == HTTP_NOT_FOUND) ? LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE : LL_ERR_ASSET_REQUEST_FAILED; - - if (http_req->mVFile) - { - http_req->mVFile->remove(); - } - } - - // call the static callback for transfer completion - // this will cleanup all requests for this asset, including ours - downloadCompleteCallback( - xfer_result, - http_req->getUUID(), - http_req->getType(), - http_req, - LL_EXSTAT_CURL_RESULT | curl_result); - // Pending download flag will get cleared when the request is deleted - } - else - { - // nothing, just axe this request - // currently this can only mean an asset delete - } - - // Deleting clears the pending upload/download flag if it's set and the request is transferring - delete http_req; - http_req = NULL; - } - - } while (curl_msg && queue_length > 0); - - - // Cleanup - // We want to bump to the back of the line any running uploads that have timed out. - bumpTimedOutUploads(); - - LLAssetStorage::checkForTimeouts(); -} - -void LLHTTPAssetStorage::bumpTimedOutUploads() -{ - bool user_waiting=FALSE; - - F64Seconds mt_secs = LLMessageSystem::getMessageTimeSeconds(); - - if (mPendingUploads.size()) - { - request_list_t::iterator it = mPendingUploads.begin(); - LLAssetRequest* req = *it; - user_waiting=req->mIsUserWaiting; - } - - // No point bumping currently running uploads if there are no others in line. - if (!(mPendingUploads.size() > mRunningUploads.size()) && !user_waiting) - { - return; - } - - // deletePendingRequest will modify the mRunningUploads list so we don't want to iterate over it. - request_list_t temp_running = mRunningUploads; - - request_list_t::iterator it = temp_running.begin(); - request_list_t::iterator end = temp_running.end(); - for ( ; it != end; ++it) - { - //request_list_t::iterator curiter = iter++; - LLAssetRequest* req = *it; - - if ( req->mTimeout < (mt_secs - req->mTime) ) - { - LL_WARNS() << "Asset upload request timed out for " - << req->getUUID() << "." - << LLAssetType::lookup(req->getType()) - << ", bumping to the back of the line!" << LL_ENDL; - - deletePendingRequest(RT_UPLOAD, req->getType(), req->getUUID()); - } - } -} - -// static -size_t LLHTTPAssetStorage::curlDownCallback(void *data, size_t size, size_t nmemb, void *user_data) -{ - if (!gAssetStorage) - { - LL_WARNS() << "Missing gAssetStorage, aborting curl download callback!" << LL_ENDL; - return 0; - } - S32 bytes = (S32)(size * nmemb); - CURL *curl_handle = (CURL *)user_data; - LLHTTPAssetRequest *req = NULL; - curl_easy_getinfo(curl_handle, CURLINFO_PRIVATE, &req); - - if (! req->mVFile) - { - req->mVFile = new LLVFile(gAssetStorage->mVFS, req->mTmpUUID, LLAssetType::AT_NONE, LLVFile::APPEND); - } - - double content_length = 0.0; - curl_easy_getinfo(curl_handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &content_length); - - // sanitize content_length, reconcile w/ actual data - S32 file_length = llmax(0, (S32)llmin(content_length, 20000000.0), bytes + req->mVFile->getSize()); - - req->mVFile->setMaxSize(file_length); - req->mVFile->write((U8*)data, bytes); - - return nmemb; -} - -// static -size_t LLHTTPAssetStorage::curlUpCallback(void *data, size_t size, size_t nmemb, void *user_data) -{ - if (!gAssetStorage) - { - LL_WARNS() << "Missing gAssetStorage, aborting curl download callback!" << LL_ENDL; - return 0; - } - CURL *curl_handle = (CURL *)user_data; - LLHTTPAssetRequest *req = NULL; - curl_easy_getinfo(curl_handle, CURLINFO_PRIVATE, &req); - - if (! req->mVFile) - { - req->mVFile = new LLVFile(gAssetStorage->mVFS, req->getUUID(), req->getType(), LLVFile::READ); - } - - S32 bytes = llmin((S32)(size * nmemb), (S32)(req->mVFile->getSize() - req->mVFile->tell())); - - req->mVFile->read((U8*)data, bytes);/*Flawfinder: ignore*/ - - return req->mVFile->getLastBytesRead(); -} - -// static -size_t LLHTTPAssetStorage::nullOutputCallback(void *data, size_t size, size_t nmemb, void *user_data) -{ - // do nothing, this is here to soak up script output so it doesn't end up on stdout - - return nmemb; -} - - - -// blocking asset fetch which bypasses the VFS -// this is a very limited function for use by the simstate loader and other one-offs -S32 LLHTTPAssetStorage::getURLToFile(const LLUUID& uuid, LLAssetType::EType asset_type, const std::string &url, const std::string& filename, progress_callback callback, void *userdata) -{ - // *NOTE: There is no guarantee that the uuid and the asset_type match - // - not that it matters. - Doug - LL_DEBUGS() << "LLHTTPAssetStorage::getURLToFile() - " << url << LL_ENDL; - - FILE *fp = LLFile::fopen(filename, "wb"); /*Flawfinder: ignore*/ - if (! fp) - { - LL_WARNS() << "Failed to open " << filename << " for writing" << LL_ENDL; - return LL_ERR_ASSET_REQUEST_FAILED; - } - - // make sure we use the normal curl setup, even though we don't really need a request object - LLHTTPAssetRequest req(this, uuid, asset_type, RT_DOWNLOAD, url, mCurlMultiHandle); - req.mFP = fp; - - req.setupCurlHandle(); - curl_easy_setopt(req.mCurlHandle, CURLOPT_FOLLOWLOCATION, TRUE); - curl_easy_setopt(req.mCurlHandle, CURLOPT_WRITEFUNCTION, &curlFileDownCallback); - curl_easy_setopt(req.mCurlHandle, CURLOPT_WRITEDATA, req.mCurlHandle); - - curl_multi_add_handle(mCurlMultiHandle, req.mCurlHandle); - LL_INFOS() << "Requesting as file " << req.mURLBuffer << LL_ENDL; - - // braindead curl loop - int queue_length; - CURLMsg *curl_msg; - LLTimer timeout; - timeout.setTimerExpirySec(GET_URL_TO_FILE_TIMEOUT); - bool success = false; - S32 xfer_result = 0; - do - { - curl_multi_perform(mCurlMultiHandle, &queue_length); - curl_msg = curl_multi_info_read(mCurlMultiHandle, &queue_length); - - if (callback) - { - callback(userdata); - } - - if ( curl_msg && (CURLMSG_DONE == curl_msg->msg) ) - { - success = true; - } - else if (timeout.hasExpired()) - { - LL_WARNS() << "Request for " << url << " has timed out." << LL_ENDL; - success = false; - xfer_result = LL_ERR_ASSET_REQUEST_FAILED; - break; - } - } while (!success); - - if (success) - { - long curl_result = 0; - curl_easy_getinfo(curl_msg->easy_handle, CURLINFO_HTTP_CODE, &curl_result); - - if (curl_result == HTTP_OK && curl_msg->data.result == CURLE_OK) - { - S32 size = ftell(req.mFP); - if (size > 0) - { - // everything seems to be in order - LL_INFOS() << "Success downloading " << req.mURLBuffer << " to file, size " << size << LL_ENDL; - } - else - { - LL_WARNS() << "Found " << req.mURLBuffer << " to be zero size" << LL_ENDL; - xfer_result = LL_ERR_ASSET_REQUEST_FAILED; - } - } - else - { - xfer_result = curl_result == HTTP_NOT_FOUND ? LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE : LL_ERR_ASSET_REQUEST_FAILED; - LL_INFOS() << "Failure downloading " << req.mURLBuffer << - " with result " << curl_easy_strerror(curl_msg->data.result) << ", http result " << curl_result << LL_ENDL; - } - } - - fclose(fp); - if (xfer_result) - { - LLFile::remove(filename); - } - return xfer_result; -} - - -// static -size_t LLHTTPAssetStorage::curlFileDownCallback(void *data, size_t size, size_t nmemb, void *user_data) -{ - CURL *curl_handle = (CURL *)user_data; - LLHTTPAssetRequest *req = NULL; - curl_easy_getinfo(curl_handle, CURLINFO_PRIVATE, &req); - - if (! req->mFP) - { - LL_WARNS() << "Missing mFP, aborting curl file download callback!" << LL_ENDL; - return 0; - } - - return fwrite(data, size, nmemb, req->mFP); -} - -LLAssetStorage::request_list_t* LLHTTPAssetStorage::getRunningList(LLAssetStorage::ERequestType rt) -{ - switch (rt) - { - case RT_DOWNLOAD: - return &mRunningDownloads; - case RT_UPLOAD: - return &mRunningUploads; - case RT_LOCALUPLOAD: - return &mRunningLocalUploads; - default: - return NULL; - } -} - -const LLAssetStorage::request_list_t* LLHTTPAssetStorage::getRunningList(LLAssetStorage::ERequestType rt) const -{ - switch (rt) - { - case RT_DOWNLOAD: - return &mRunningDownloads; - case RT_UPLOAD: - return &mRunningUploads; - case RT_LOCALUPLOAD: - return &mRunningLocalUploads; - default: - return NULL; - } -} - - -void LLHTTPAssetStorage::addRunningRequest(ERequestType rt, LLHTTPAssetRequest* request) -{ - request_list_t* requests = getRunningList(rt); - if (requests) - { - requests->push_back(request); - } - else - { - LL_ERRS() << "LLHTTPAssetStorage::addRunningRequest - Request is not an upload OR download, this is bad!" << LL_ENDL; - } -} - -void LLHTTPAssetStorage::removeRunningRequest(ERequestType rt, LLHTTPAssetRequest* request) -{ - request_list_t* requests = getRunningList(rt); - if (requests) - { - requests->remove(request); - } - else - { - LL_ERRS() << "LLHTTPAssetStorage::removeRunningRequest - Destroyed request is not an upload OR download, this is bad!" << LL_ENDL; - } -} - -// virtual -void LLHTTPAssetStorage::addTempAssetData(const LLUUID& asset_id, const LLUUID& agent_id, const std::string& host_name) -{ - if (agent_id.isNull() || asset_id.isNull()) - { - LL_WARNS() << "TAT: addTempAssetData bad id's asset_id: " << asset_id << " agent_id: " << agent_id << LL_ENDL; - return; - } - - LLTempAssetData temp_asset_data; - temp_asset_data.mAssetID = asset_id; - temp_asset_data.mAgentID = agent_id; - temp_asset_data.mHostName = host_name; - - mTempAssets[asset_id] = temp_asset_data; -} - -// virtual -BOOL LLHTTPAssetStorage::hasTempAssetData(const LLUUID& texture_id) const -{ - uuid_tempdata_map::const_iterator citer = mTempAssets.find(texture_id); - BOOL found = (citer != mTempAssets.end()); - return found; -} - -// virtual -std::string LLHTTPAssetStorage::getTempAssetHostName(const LLUUID& texture_id) const -{ - uuid_tempdata_map::const_iterator citer = mTempAssets.find(texture_id); - if (citer != mTempAssets.end()) - { - return citer->second.mHostName; - } - else - { - return std::string(); - } -} - -// virtual -LLUUID LLHTTPAssetStorage::getTempAssetAgentID(const LLUUID& texture_id) const -{ - uuid_tempdata_map::const_iterator citer = mTempAssets.find(texture_id); - if (citer != mTempAssets.end()) - { - return citer->second.mAgentID; - } - else - { - return LLUUID::null; - } -} - -// virtual -void LLHTTPAssetStorage::removeTempAssetData(const LLUUID& asset_id) -{ - mTempAssets.erase(asset_id); -} - -// virtual -void LLHTTPAssetStorage::removeTempAssetDataByAgentID(const LLUUID& agent_id) -{ - uuid_tempdata_map::iterator it = mTempAssets.begin(); - uuid_tempdata_map::iterator end = mTempAssets.end(); - - while (it != end) - { - const LLTempAssetData& asset_data = it->second; - if (asset_data.mAgentID == agent_id) - { - mTempAssets.erase(it++); - } - else - { - ++it; - } - } -} - -std::string LLHTTPAssetStorage::getBaseURL(const LLUUID& asset_id, LLAssetType::EType asset_type) -{ - if (LLAssetType::AT_TEXTURE == asset_type) - { - uuid_tempdata_map::const_iterator citer = mTempAssets.find(asset_id); - if (citer != mTempAssets.end()) - { - const std::string& host_name = citer->second.mHostName; - std::string url = llformat(LOCAL_ASSET_URL_FORMAT, host_name.c_str()); - return url; - } - } - - return mBaseURL; -} - -void LLHTTPAssetStorage::dumpTempAssetData(const LLUUID& avatar_id) const -{ - uuid_tempdata_map::const_iterator it = mTempAssets.begin(); - uuid_tempdata_map::const_iterator end = mTempAssets.end(); - S32 count = 0; - for ( ; it != end; ++it) - { - const LLTempAssetData& temp_asset_data = it->second; - if (avatar_id.isNull() - || avatar_id == temp_asset_data.mAgentID) - { - LL_INFOS() << "TAT: dump agent " << temp_asset_data.mAgentID - << " texture " << temp_asset_data.mAssetID - << " host " << temp_asset_data.mHostName - << LL_ENDL; - count++; - } - } - - if (avatar_id.isNull()) - { - LL_INFOS() << "TAT: dumped " << count << " entries for all avatars" << LL_ENDL; - } - else - { - LL_INFOS() << "TAT: dumped " << count << " entries for avatar " << avatar_id << LL_ENDL; - } -} - -void LLHTTPAssetStorage::clearTempAssetData() -{ - LL_INFOS() << "TAT: Clearing temp asset data map" << LL_ENDL; - mTempAssets.clear(); -} diff --git a/indra/llmessage/llhttpassetstorage.h b/indra/llmessage/llhttpassetstorage.h deleted file mode 100644 index 783e95cac6..0000000000 --- a/indra/llmessage/llhttpassetstorage.h +++ /dev/null @@ -1,159 +0,0 @@ -/** - * @file llhttpassetstorage.h - * @brief Class for loading asset data to/from an external source over http. - * - * $LicenseInfo:firstyear=2003&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$ - */ - -#ifndef LLHTTPASSETSTORAGE_H -#define LLHTTPASSETSTORAGE_H - -#include "llassetstorage.h" -#include "curl/curl.h" - -class LLVFile; -class LLHTTPAssetRequest; -typedef void (*progress_callback)(void* userdata); - -struct LLTempAssetData; - -typedef std::map<LLUUID,LLTempAssetData> uuid_tempdata_map; - -class LLHTTPAssetStorage : public LLAssetStorage -{ -public: - LLHTTPAssetStorage(LLMessageSystem *msg, LLXferManager *xfer, - LLVFS *vfs, LLVFS *static_vfs, - const LLHost &upstream_host, - const std::string& web_host, - const std::string& local_web_host, - const std::string& host_name); - - LLHTTPAssetStorage(LLMessageSystem *msg, LLXferManager *xfer, - LLVFS *vfs, LLVFS *static_vfs, - const std::string& web_host, - const std::string& local_web_host, - const std::string& host_name); - - - virtual ~LLHTTPAssetStorage(); - - using LLAssetStorage::storeAssetData; // Unhiding virtuals... - - virtual void storeAssetData( - const LLUUID& uuid, - LLAssetType::EType atype, - LLStoreAssetCallback callback, - void* user_data, - bool temp_file = false, - bool is_priority = false, - bool store_local = false, - const LLUUID& requesting_agent_id = LLUUID::null, - bool user_waiting=FALSE, - F64Seconds timeout=LL_ASSET_STORAGE_TIMEOUT); - - virtual void storeAssetData( - const std::string& filename, - const LLUUID& asset_id, - LLAssetType::EType atype, - LLStoreAssetCallback callback, - void* user_data, - bool temp_file, - bool is_priority, - bool user_waiting=FALSE, - F64Seconds timeout=LL_ASSET_STORAGE_TIMEOUT); - - virtual LLSD getPendingDetails(ERequestType rt, - LLAssetType::EType asset_type, - const std::string& detail_prefix) const; - - virtual LLSD getPendingRequest(ERequestType rt, - LLAssetType::EType asset_type, - const LLUUID& asset_id) const; - - virtual bool deletePendingRequest(ERequestType rt, - LLAssetType::EType asset_type, - const LLUUID& asset_id); - - // Hack. One off curl download an URL to a file. Probably should be elsewhere. - // Only used by lldynamicstate. The API is broken, and should be replaced with - // a generic HTTP file fetch - Doug 9/25/06 - S32 getURLToFile(const LLUUID& uuid, LLAssetType::EType asset_type, const std::string &url, const std::string& filename, progress_callback callback, void *userdata); - - LLAssetRequest* findNextRequest(request_list_t& pending, request_list_t& running); - - void checkForTimeouts(); - - static size_t curlDownCallback(void *data, size_t size, size_t nmemb, void *user_data); - static size_t curlFileDownCallback(void *data, size_t size, size_t nmemb, void *user_data); - static size_t curlUpCallback(void *data, size_t size, size_t nmemb, void *user_data); - static size_t nullOutputCallback(void *data, size_t size, size_t nmemb, void *user_data); - - // Should only be used by the LLHTTPAssetRequest - void addRunningRequest(ERequestType rt, LLHTTPAssetRequest* request); - void removeRunningRequest(ERequestType rt, LLHTTPAssetRequest* request); - - request_list_t* getRunningList(ERequestType rt); - const request_list_t* getRunningList(ERequestType rt) const; - - // Temp assets are stored on sim nodes, they have agent ID and location data associated with them. - virtual void addTempAssetData(const LLUUID& asset_id, const LLUUID& agent_id, const std::string& host_name); - virtual BOOL hasTempAssetData(const LLUUID& texture_id) const; - virtual std::string getTempAssetHostName(const LLUUID& texture_id) const; - virtual LLUUID getTempAssetAgentID(const LLUUID& texture_id) const; - virtual void removeTempAssetData(const LLUUID& asset_id); - virtual void removeTempAssetDataByAgentID(const LLUUID& agent_id); - - // Pass LLUUID::null for all - virtual void dumpTempAssetData(const LLUUID& avatar_id) const; - virtual void clearTempAssetData(); - -protected: - void _queueDataRequest(const LLUUID& uuid, LLAssetType::EType type, - void (*callback)(LLVFS *vfs, const LLUUID&, LLAssetType::EType, void *, S32, LLExtStat), - void *user_data, BOOL duplicate, BOOL is_priority); - -private: - void _init(const std::string& web_host, const std::string& local_web_host, const std::string& host_name); - - // This will return the correct base URI for any http asset request - std::string getBaseURL(const LLUUID& asset_id, LLAssetType::EType asset_type); - - // Check for running uploads that have timed out - // Bump these to the back of the line to let other uploads complete. - void bumpTimedOutUploads(); - -protected: - std::string mBaseURL; - std::string mLocalBaseURL; - std::string mHostName; - - CURLM *mCurlMultiHandle; - - request_list_t mRunningDownloads; - request_list_t mRunningUploads; - request_list_t mRunningLocalUploads; - - uuid_tempdata_map mTempAssets; -}; - -#endif 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; -} diff --git a/indra/llmessage/llhttpclient.h b/indra/llmessage/llhttpclient.h deleted file mode 100644 index fd48b4a743..0000000000 --- a/indra/llmessage/llhttpclient.h +++ /dev/null @@ -1,201 +0,0 @@ -/** - * @file llhttpclient.h - * @brief Declaration of classes for making HTTP client 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$ - */ - -#ifndef LL_LLHTTPCLIENT_H -#define LL_LLHTTPCLIENT_H - -/** - * These classes represent the HTTP client framework. - */ - -#include <string> - -#include <boost/intrusive_ptr.hpp> -#include <openssl/x509_vfy.h> -#include "llurlrequest.h" -#include "llassettype.h" -#include "llcurl.h" -#include "lliopipe.h" - -extern const F32 HTTP_REQUEST_EXPIRY_SECS; - -class LLUUID; -class LLPumpIO; -class LLSD; - - -class LLHTTPClient -{ -public: - // class Responder moved to LLCurl - - // For convenience - typedef LLCurl::Responder Responder; - typedef LLCurl::ResponderPtr ResponderPtr; - - - /** @name non-blocking API */ - //@{ - static void head( - const std::string& url, - ResponderPtr, - const LLSD& headers = LLSD(), - const F32 timeout=HTTP_REQUEST_EXPIRY_SECS, - bool follow_redirects = true); - static void getByteRange(const std::string& url, S32 offset, S32 bytes, ResponderPtr, - const LLSD& headers=LLSD(), const F32 timeout=HTTP_REQUEST_EXPIRY_SECS, - bool follow_redirects = true); - static void get(const std::string& url, ResponderPtr, const LLSD& headers = LLSD(), - const F32 timeout=HTTP_REQUEST_EXPIRY_SECS, bool follow_redirects = true); - static void get(const std::string& url, const LLSD& query, ResponderPtr, const LLSD& headers = LLSD(), - const F32 timeout=HTTP_REQUEST_EXPIRY_SECS, bool follow_redirects = true); - - static void put( - const std::string& url, - const LLSD& body, - ResponderPtr, - const LLSD& headers = LLSD(), - const F32 timeout=HTTP_REQUEST_EXPIRY_SECS); - static void putRaw( - const std::string& url, - const U8* data, - S32 size, - ResponderPtr responder, - const LLSD& headers = LLSD(), - const F32 timeout=HTTP_REQUEST_EXPIRY_SECS); - - - static void patch( - const std::string& url, - const LLSD& body, - ResponderPtr, - const LLSD& headers = LLSD(), - const F32 timeout=HTTP_REQUEST_EXPIRY_SECS); - - static void getHeaderOnly(const std::string& url, ResponderPtr, const F32 timeout=HTTP_REQUEST_EXPIRY_SECS, - bool follow_redirects = true); - static void getHeaderOnly(const std::string& url, ResponderPtr, const LLSD& headers, - const F32 timeout=HTTP_REQUEST_EXPIRY_SECS, bool follow_redirects = true); - - static void post( - const std::string& url, - const LLSD& body, - ResponderPtr, - const LLSD& headers = LLSD(), - const F32 timeout=HTTP_REQUEST_EXPIRY_SECS); - /** Takes ownership of data and deletes it when sent */ - static void postRaw( - const std::string& url, - const U8* data, - S32 size, - ResponderPtr responder, - const LLSD& headers = LLSD(), - const F32 timeout=HTTP_REQUEST_EXPIRY_SECS); - static void postFile( - const std::string& url, - const std::string& filename, - ResponderPtr, - const LLSD& headers = LLSD(), - const F32 timeout=HTTP_REQUEST_EXPIRY_SECS); - static void postFile( - const std::string& url, - const LLUUID& uuid, - LLAssetType::EType asset_type, - ResponderPtr responder, - const LLSD& headers = LLSD(), - const F32 timeout=HTTP_REQUEST_EXPIRY_SECS); - - static void del( - const std::string& url, - ResponderPtr responder, - const LLSD& headers = LLSD(), - const F32 timeout=HTTP_REQUEST_EXPIRY_SECS); - ///< sends a DELETE method, but we can't call it delete in c++ - - /** - * @brief Send a MOVE webdav method - * - * @param url The complete serialized (and escaped) url to get. - * @param destination The complete serialized destination url. - * @param responder The responder that will handle the result. - * @param headers A map of key:value headers to pass to the request - * @param timeout The number of seconds to give the server to respond. - */ - static void move( - const std::string& url, - const std::string& destination, - ResponderPtr responder, - const LLSD& headers = LLSD(), - const F32 timeout=HTTP_REQUEST_EXPIRY_SECS); - - /** - * @brief Send a COPY webdav method - * - * @param url The complete serialized (and escaped) url to get. - * @param destination The complete serialized destination url. - * @param responder The responder that will handle the result. - * @param headers A map of key:value headers to pass to the request - * @param timeout The number of seconds to give the server to respond. - */ - static void copy( - const std::string& url, - const std::string& destination, - ResponderPtr responder, - const LLSD& headers = LLSD(), - const F32 timeout=HTTP_REQUEST_EXPIRY_SECS); - - //@} - - /** - * @brief Blocking HTTP get that returns an LLSD map of status and body. - * - * @param url the complete serialized (and escaped) url to get - * @return An LLSD of { 'status':status, 'body':payload } - */ - static LLSD blockingGet(const std::string& url); - - /** - * @brief Blocking HTTP POST that returns an LLSD map of status and body. - * - * @param url the complete serialized (and escaped) url to get - * @param body the LLSD post body - * @return An LLSD of { 'status':status (an int), 'body':payload (an LLSD) } - */ - static LLSD blockingPost(const std::string& url, const LLSD& body); - - - static void setPump(LLPumpIO& pump); - ///< must be called before any of the above calls are made - static bool hasPump(); - - static void setCertVerifyCallback(LLURLRequest::SSLCertVerifyCallback callback); - static LLURLRequest::SSLCertVerifyCallback getCertVerifyCallback() { return mCertVerifyCallback; } - -protected: - static LLURLRequest::SSLCertVerifyCallback mCertVerifyCallback; -}; - -#endif // LL_LLHTTPCLIENT_H diff --git a/indra/llmessage/llhttpclientadapter.cpp b/indra/llmessage/llhttpclientadapter.cpp deleted file mode 100644 index b56a804f94..0000000000 --- a/indra/llmessage/llhttpclientadapter.cpp +++ /dev/null @@ -1,73 +0,0 @@ -/** - * @file llhttpclientadapter.cpp - * @brief - * - * $LicenseInfo:firstyear=2009&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 "llhttpclientadapter.h" -#include "llhttpclient.h" - -LLHTTPClientAdapter::~LLHTTPClientAdapter() -{ -} - -void LLHTTPClientAdapter::get(const std::string& url, LLCurl::ResponderPtr responder) -{ - LLSD empty_pragma_header; - // Pragma is required to stop curl adding "no-cache" - // Space is required to stop llurlrequest from turning off proxying - empty_pragma_header[HTTP_OUT_HEADER_PRAGMA] = " "; - LLHTTPClient::get(url, responder, empty_pragma_header); -} - -void LLHTTPClientAdapter::get(const std::string& url, LLCurl::ResponderPtr responder, const LLSD& headers) -{ - LLSD empty_pragma_header = headers; - if (!empty_pragma_header.has(HTTP_OUT_HEADER_PRAGMA)) - { - // as above - empty_pragma_header[HTTP_OUT_HEADER_PRAGMA] = " "; - } - LLHTTPClient::get(url, responder, empty_pragma_header); -} - -void LLHTTPClientAdapter::put(const std::string& url, const LLSD& body, LLCurl::ResponderPtr responder) -{ - LLHTTPClient::put(url, body, responder); -} - -void LLHTTPClientAdapter::put( - const std::string& url, - const LLSD& body, - LLCurl::ResponderPtr responder, - const LLSD& headers) -{ - LLHTTPClient::put(url, body, responder, headers); -} - -void LLHTTPClientAdapter::del( - const std::string& url, - LLCurl::ResponderPtr responder) -{ - LLHTTPClient::del(url, responder); -} diff --git a/indra/llmessage/llhttpclientadapter.h b/indra/llmessage/llhttpclientadapter.h deleted file mode 100644 index 270282c66f..0000000000 --- a/indra/llmessage/llhttpclientadapter.h +++ /dev/null @@ -1,51 +0,0 @@ -/** - * @file llhttpclientadepter.h - * @brief - * - * $LicenseInfo:firstyear=2008&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$ - */ - -#ifndef LL_HTTPCLIENTADAPTER_H -#define LL_HTTPCLIENTADAPTER_H - -#include "llhttpclientinterface.h" -#include "llsingleton.h" // LLSingleton<> - -class LLHTTPClientAdapter : public LLHTTPClientInterface, public LLSingleton<LLHTTPClientAdapter> -{ -public: - virtual ~LLHTTPClientAdapter(); - virtual void get(const std::string& url, LLCurl::ResponderPtr responder); - virtual void get(const std::string& url, LLCurl::ResponderPtr responder, const LLSD& headers); - virtual void put(const std::string& url, const LLSD& body, LLCurl::ResponderPtr responder); - virtual void put( - const std::string& url, - const LLSD& body, - LLCurl::ResponderPtr responder, - const LLSD& headers); - virtual void del( - const std::string& url, - LLCurl::ResponderPtr responder); -}; - -#endif - diff --git a/indra/llmessage/llhttpclientinterface.h b/indra/llmessage/llhttpclientinterface.h deleted file mode 100644 index 12a3857a61..0000000000 --- a/indra/llmessage/llhttpclientinterface.h +++ /dev/null @@ -1,45 +0,0 @@ -/** - * @file llhttpclientinterface.h - * @brief - * - * $LicenseInfo:firstyear=2008&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$ - */ - -#ifndef LL_LLHTTPCLIENTINTERFACE_H -#define LL_LLHTTPCLIENTINTERFACE_H - -#include "linden_common.h" -#include "llcurl.h" - -#include <string> - -class LLHTTPClientInterface -{ -public: - virtual ~LLHTTPClientInterface() {} - virtual void get(const std::string& url, LLCurl::ResponderPtr responder) = 0; - virtual void get(const std::string& url, LLCurl::ResponderPtr responder, const LLSD& headers) = 0; - virtual void put(const std::string& url, const LLSD& body, LLCurl::ResponderPtr responder) = 0; -}; - -#endif // LL_LLHTTPCLIENTINTERFACE_H - diff --git a/indra/llmessage/llhttpconstants.cpp b/indra/llmessage/llhttpconstants.cpp deleted file mode 100644 index 32f76f0d70..0000000000 --- a/indra/llmessage/llhttpconstants.cpp +++ /dev/null @@ -1,226 +0,0 @@ -/** - * @file llhttpconstants.cpp - * @brief Implementation of the HTTP request / response constant lookups - * - * $LicenseInfo:firstyear=2013&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2013-2014, 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 "llhttpconstants.h" -#include "lltimer.h" - -// for curl_getdate() (apparently parsing RFC 1123 dates is hard) -#include <curl/curl.h> - -// Outgoing headers. Do *not* use these to check incoming headers. -// For incoming headers, use the lower-case headers, below. -const std::string HTTP_OUT_HEADER_ACCEPT("Accept"); -const std::string HTTP_OUT_HEADER_ACCEPT_CHARSET("Accept-Charset"); -const std::string HTTP_OUT_HEADER_ACCEPT_ENCODING("Accept-Encoding"); -const std::string HTTP_OUT_HEADER_ACCEPT_LANGUAGE("Accept-Language"); -const std::string HTTP_OUT_HEADER_ACCEPT_RANGES("Accept-Ranges"); -const std::string HTTP_OUT_HEADER_AGE("Age"); -const std::string HTTP_OUT_HEADER_ALLOW("Allow"); -const std::string HTTP_OUT_HEADER_AUTHORIZATION("Authorization"); -const std::string HTTP_OUT_HEADER_CACHE_CONTROL("Cache-Control"); -const std::string HTTP_OUT_HEADER_CONNECTION("Connection"); -const std::string HTTP_OUT_HEADER_CONTENT_DESCRIPTION("Content-Description"); -const std::string HTTP_OUT_HEADER_CONTENT_ENCODING("Content-Encoding"); -const std::string HTTP_OUT_HEADER_CONTENT_ID("Content-ID"); -const std::string HTTP_OUT_HEADER_CONTENT_LANGUAGE("Content-Language"); -const std::string HTTP_OUT_HEADER_CONTENT_LENGTH("Content-Length"); -const std::string HTTP_OUT_HEADER_CONTENT_LOCATION("Content-Location"); -const std::string HTTP_OUT_HEADER_CONTENT_MD5("Content-MD5"); -const std::string HTTP_OUT_HEADER_CONTENT_RANGE("Content-Range"); -const std::string HTTP_OUT_HEADER_CONTENT_TRANSFER_ENCODING("Content-Transfer-Encoding"); -const std::string HTTP_OUT_HEADER_CONTENT_TYPE("Content-Type"); -const std::string HTTP_OUT_HEADER_COOKIE("Cookie"); -const std::string HTTP_OUT_HEADER_DATE("Date"); -const std::string HTTP_OUT_HEADER_DESTINATION("Destination"); -const std::string HTTP_OUT_HEADER_ETAG("ETag"); -const std::string HTTP_OUT_HEADER_EXPECT("Expect"); -const std::string HTTP_OUT_HEADER_EXPIRES("Expires"); -const std::string HTTP_OUT_HEADER_FROM("From"); -const std::string HTTP_OUT_HEADER_HOST("Host"); -const std::string HTTP_OUT_HEADER_IF_MATCH("If-Match"); -const std::string HTTP_OUT_HEADER_IF_MODIFIED_SINCE("If-Modified-Since"); -const std::string HTTP_OUT_HEADER_IF_NONE_MATCH("If-None-Match"); -const std::string HTTP_OUT_HEADER_IF_RANGE("If-Range"); -const std::string HTTP_OUT_HEADER_IF_UNMODIFIED_SINCE("If-Unmodified-Since"); -const std::string HTTP_OUT_HEADER_KEEP_ALIVE("Keep-Alive"); -const std::string HTTP_OUT_HEADER_LAST_MODIFIED("Last-Modified"); -const std::string HTTP_OUT_HEADER_LOCATION("Location"); -const std::string HTTP_OUT_HEADER_MAX_FORWARDS("Max-Forwards"); -const std::string HTTP_OUT_HEADER_MIME_VERSION("MIME-Version"); -const std::string HTTP_OUT_HEADER_PRAGMA("Pragma"); -const std::string HTTP_OUT_HEADER_PROXY_AUTHENTICATE("Proxy-Authenticate"); -const std::string HTTP_OUT_HEADER_PROXY_AUTHORIZATION("Proxy-Authorization"); -const std::string HTTP_OUT_HEADER_RANGE("Range"); -const std::string HTTP_OUT_HEADER_REFERER("Referer"); -const std::string HTTP_OUT_HEADER_RETRY_AFTER("Retry-After"); -const std::string HTTP_OUT_HEADER_SERVER("Server"); -const std::string HTTP_OUT_HEADER_SET_COOKIE("Set-Cookie"); -const std::string HTTP_OUT_HEADER_TE("TE"); -const std::string HTTP_OUT_HEADER_TRAILER("Trailer"); -const std::string HTTP_OUT_HEADER_TRANSFER_ENCODING("Transfer-Encoding"); -const std::string HTTP_OUT_HEADER_UPGRADE("Upgrade"); -const std::string HTTP_OUT_HEADER_USER_AGENT("User-Agent"); -const std::string HTTP_OUT_HEADER_VARY("Vary"); -const std::string HTTP_OUT_HEADER_VIA("Via"); -const std::string HTTP_OUT_HEADER_WARNING("Warning"); -const std::string HTTP_OUT_HEADER_WWW_AUTHENTICATE("WWW-Authenticate"); - -// Incoming headers are normalized to lower-case. -const std::string HTTP_IN_HEADER_ACCEPT_LANGUAGE("accept-language"); -const std::string HTTP_IN_HEADER_CACHE_CONTROL("cache-control"); -const std::string HTTP_IN_HEADER_CONTENT_LENGTH("content-length"); -const std::string HTTP_IN_HEADER_CONTENT_LOCATION("content-location"); -const std::string HTTP_IN_HEADER_CONTENT_TYPE("content-type"); -const std::string HTTP_IN_HEADER_HOST("host"); -const std::string HTTP_IN_HEADER_LOCATION("location"); -const std::string HTTP_IN_HEADER_RETRY_AFTER("retry-after"); -const std::string HTTP_IN_HEADER_SET_COOKIE("set-cookie"); -const std::string HTTP_IN_HEADER_USER_AGENT("user-agent"); -const std::string HTTP_IN_HEADER_X_FORWARDED_FOR("x-forwarded-for"); - -const std::string HTTP_CONTENT_LLSD_XML("application/llsd+xml"); -const std::string HTTP_CONTENT_OCTET_STREAM("application/octet-stream"); -const std::string HTTP_CONTENT_VND_LL_MESH("application/vnd.ll.mesh"); -const std::string HTTP_CONTENT_XML("application/xml"); -const std::string HTTP_CONTENT_JSON("application/json"); -const std::string HTTP_CONTENT_TEXT_HTML("text/html"); -const std::string HTTP_CONTENT_TEXT_HTML_UTF8("text/html; charset=utf-8"); -const std::string HTTP_CONTENT_TEXT_PLAIN_UTF8("text/plain; charset=utf-8"); -const std::string HTTP_CONTENT_TEXT_LLSD("text/llsd"); -const std::string HTTP_CONTENT_TEXT_XML("text/xml"); -const std::string HTTP_CONTENT_TEXT_LSL("text/lsl"); -const std::string HTTP_CONTENT_TEXT_PLAIN("text/plain"); -const std::string HTTP_CONTENT_IMAGE_X_J2C("image/x-j2c"); -const std::string HTTP_CONTENT_IMAGE_J2C("image/j2c"); -const std::string HTTP_CONTENT_IMAGE_JPEG("image/jpeg"); -const std::string HTTP_CONTENT_IMAGE_PNG("image/png"); -const std::string HTTP_CONTENT_IMAGE_BMP("image/bmp"); - -const std::string HTTP_NO_CACHE("no-cache"); -const std::string HTTP_NO_CACHE_CONTROL("no-cache, max-age=0"); - -const std::string HTTP_VERB_INVALID("(invalid)"); -const std::string HTTP_VERB_HEAD("HEAD"); -const std::string HTTP_VERB_GET("GET"); -const std::string HTTP_VERB_PUT("PUT"); -const std::string HTTP_VERB_POST("POST"); -const std::string HTTP_VERB_DELETE("DELETE"); -const std::string HTTP_VERB_MOVE("MOVE"); -const std::string HTTP_VERB_OPTIONS("OPTIONS"); -const std::string HTTP_VERB_PATCH("PATCH"); -const std::string HTTP_VERB_COPY("COPY"); - -const std::string& httpMethodAsVerb(EHTTPMethod method) -{ - static const std::string VERBS[] = - { - HTTP_VERB_INVALID, - HTTP_VERB_HEAD, - HTTP_VERB_GET, - HTTP_VERB_PUT, - HTTP_VERB_POST, - HTTP_VERB_DELETE, - HTTP_VERB_MOVE, - HTTP_VERB_OPTIONS, - HTTP_VERB_PATCH, - HTTP_VERB_COPY - }; - if(((S32)method <=0) || ((S32)method >= HTTP_METHOD_COUNT)) - { - return VERBS[0]; - } - return VERBS[method]; -} - -bool isHttpInformationalStatus(S32 status) -{ - // Check for status 1xx. - return((100 <= status) && (status < 200)); -} - -bool isHttpGoodStatus(S32 status) -{ - // Check for status 2xx. - return((200 <= status) && (status < 300)); -} - -bool isHttpRedirectStatus(S32 status) -{ - // Check for status 3xx. - return((300 <= status) && (status < 400)); -} - -bool isHttpClientErrorStatus(S32 status) -{ - // Status 499 is sometimes used for re-interpreted status 2xx errors - // based on body content. Treat these as potentially retryable 'server' status errors, - // since we do not have enough context to know if this will always fail. - if (HTTP_INTERNAL_ERROR == status) return false; - - // Check for status 5xx. - return((400 <= status) && (status < 500)); -} - -bool isHttpServerErrorStatus(S32 status) -{ - // Status 499 is sometimes used for re-interpreted status 2xx errors. - // Allow retry of these, since we don't have enough information in this - // context to know if this will always fail. - if (HTTP_INTERNAL_ERROR == status) return true; - - // Check for status 5xx. - return((500 <= status) && (status < 600)); -} - -// Parses 'Retry-After' header contents and returns seconds until retry should occur. -bool getSecondsUntilRetryAfter(const std::string& retry_after, F32& seconds_to_wait) -{ - // *TODO: This needs testing! Not in use yet. - // Examples of Retry-After headers: - // Retry-After: Fri, 31 Dec 1999 23:59:59 GMT - // Retry-After: 120 - - // Check for number of seconds version, first: - char* end = 0; - // Parse as double - double seconds = std::strtod(retry_after.c_str(), &end); - if ( end != 0 && *end == 0 ) - { - // Successful parse - seconds_to_wait = (F32) seconds; - return true; - } - - // Parse rfc1123 date. - time_t date = curl_getdate(retry_after.c_str(), NULL ); - if (-1 == date) return false; - - seconds_to_wait = (F64)date - LLTimer::getTotalSeconds(); - - return true; -} - diff --git a/indra/llmessage/llhttpconstants.h b/indra/llmessage/llhttpconstants.h deleted file mode 100644 index d6bcbd3c19..0000000000 --- a/indra/llmessage/llhttpconstants.h +++ /dev/null @@ -1,226 +0,0 @@ -/** - * @file llhttpconstants.h - * @brief Constants for HTTP requests and responses - * - * $LicenseInfo:firstyear=2001&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2001-2014, 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$ - */ - -#ifndef LL_HTTP_CONSTANTS_H -#define LL_HTTP_CONSTANTS_H - -#include "stdtypes.h" - -/////// HTTP STATUS CODES /////// - -// Standard errors from HTTP spec: -// http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6.1 -const S32 HTTP_CONTINUE = 100; -const S32 HTTP_SWITCHING_PROTOCOLS = 101; - -// Success -const S32 HTTP_OK = 200; -const S32 HTTP_CREATED = 201; -const S32 HTTP_ACCEPTED = 202; -const S32 HTTP_NON_AUTHORITATIVE_INFORMATION = 203; -const S32 HTTP_NO_CONTENT = 204; -const S32 HTTP_RESET_CONTENT = 205; -const S32 HTTP_PARTIAL_CONTENT = 206; - -// Redirection -const S32 HTTP_MULTIPLE_CHOICES = 300; -const S32 HTTP_MOVED_PERMANENTLY = 301; -const S32 HTTP_FOUND = 302; -const S32 HTTP_SEE_OTHER = 303; -const S32 HTTP_NOT_MODIFIED = 304; -const S32 HTTP_USE_PROXY = 305; -const S32 HTTP_TEMPORARY_REDIRECT = 307; - -// Client Error -const S32 HTTP_BAD_REQUEST = 400; -const S32 HTTP_UNAUTHORIZED = 401; -const S32 HTTP_PAYMENT_REQUIRED = 402; -const S32 HTTP_FORBIDDEN = 403; -const S32 HTTP_NOT_FOUND = 404; -const S32 HTTP_METHOD_NOT_ALLOWED = 405; -const S32 HTTP_NOT_ACCEPTABLE = 406; -const S32 HTTP_PROXY_AUTHENTICATION_REQUIRED = 407; -const S32 HTTP_REQUEST_TIME_OUT = 408; -const S32 HTTP_CONFLICT = 409; -const S32 HTTP_GONE = 410; -const S32 HTTP_LENGTH_REQUIRED = 411; -const S32 HTTP_PRECONDITION_FAILED = 412; -const S32 HTTP_REQUEST_ENTITY_TOO_LARGE = 413; -const S32 HTTP_REQUEST_URI_TOO_LARGE = 414; -const S32 HTTP_UNSUPPORTED_MEDIA_TYPE = 415; -const S32 HTTP_REQUESTED_RANGE_NOT_SATISFIABLE = 416; -const S32 HTTP_EXPECTATION_FAILED = 417; - -// Server Error -const S32 HTTP_INTERNAL_SERVER_ERROR = 500; -const S32 HTTP_NOT_IMPLEMENTED = 501; -const S32 HTTP_BAD_GATEWAY = 502; -const S32 HTTP_SERVICE_UNAVAILABLE = 503; -const S32 HTTP_GATEWAY_TIME_OUT = 504; -const S32 HTTP_VERSION_NOT_SUPPORTED = 505; - -// We combine internal process errors with status codes -// These status codes should not be sent over the wire -// and indicate something went wrong internally. -// If you get these they are not normal. -const S32 HTTP_INTERNAL_CURL_ERROR = 498; -const S32 HTTP_INTERNAL_ERROR = 499; - - -////// HTTP Methods ////// - -extern const std::string HTTP_VERB_INVALID; -extern const std::string HTTP_VERB_HEAD; -extern const std::string HTTP_VERB_GET; -extern const std::string HTTP_VERB_PUT; -extern const std::string HTTP_VERB_POST; -extern const std::string HTTP_VERB_DELETE; -extern const std::string HTTP_VERB_MOVE; -extern const std::string HTTP_VERB_OPTIONS; - -enum EHTTPMethod -{ - HTTP_INVALID = 0, - HTTP_HEAD, - HTTP_GET, - HTTP_PUT, - HTTP_POST, - HTTP_DELETE, - HTTP_MOVE, // Caller will need to set 'Destination' header - HTTP_OPTIONS, - HTTP_PATCH, - HTTP_COPY, - HTTP_METHOD_COUNT -}; - -const std::string& httpMethodAsVerb(EHTTPMethod method); -bool isHttpInformationalStatus(S32 status); -bool isHttpGoodStatus(S32 status); -bool isHttpRedirectStatus(S32 status); -bool isHttpClientErrorStatus(S32 status); -bool isHttpServerErrorStatus(S32 status); - -// Parses 'Retry-After' header contents and returns seconds until retry should occur. -bool getSecondsUntilRetryAfter(const std::string& retry_after, F32& seconds_to_wait); - -//// HTTP Headers ///// - -// Outgoing headers. Do *not* use these to check incoming headers. -// For incoming headers, use the lower-case headers, below. -extern const std::string HTTP_OUT_HEADER_ACCEPT; -extern const std::string HTTP_OUT_HEADER_ACCEPT_CHARSET; -extern const std::string HTTP_OUT_HEADER_ACCEPT_ENCODING; -extern const std::string HTTP_OUT_HEADER_ACCEPT_LANGUAGE; -extern const std::string HTTP_OUT_HEADER_ACCEPT_RANGES; -extern const std::string HTTP_OUT_HEADER_AGE; -extern const std::string HTTP_OUT_HEADER_ALLOW; -extern const std::string HTTP_OUT_HEADER_AUTHORIZATION; -extern const std::string HTTP_OUT_HEADER_CACHE_CONTROL; -extern const std::string HTTP_OUT_HEADER_CONNECTION; -extern const std::string HTTP_OUT_HEADER_CONTENT_DESCRIPTION; -extern const std::string HTTP_OUT_HEADER_CONTENT_ENCODING; -extern const std::string HTTP_OUT_HEADER_CONTENT_ID; -extern const std::string HTTP_OUT_HEADER_CONTENT_LANGUAGE; -extern const std::string HTTP_OUT_HEADER_CONTENT_LENGTH; -extern const std::string HTTP_OUT_HEADER_CONTENT_LOCATION; -extern const std::string HTTP_OUT_HEADER_CONTENT_MD5; -extern const std::string HTTP_OUT_HEADER_CONTENT_RANGE; -extern const std::string HTTP_OUT_HEADER_CONTENT_TRANSFER_ENCODING; -extern const std::string HTTP_OUT_HEADER_CONTENT_TYPE; -extern const std::string HTTP_OUT_HEADER_COOKIE; -extern const std::string HTTP_OUT_HEADER_DATE; -extern const std::string HTTP_OUT_HEADER_DESTINATION; -extern const std::string HTTP_OUT_HEADER_ETAG; -extern const std::string HTTP_OUT_HEADER_EXPECT; -extern const std::string HTTP_OUT_HEADER_EXPIRES; -extern const std::string HTTP_OUT_HEADER_FROM; -extern const std::string HTTP_OUT_HEADER_HOST; -extern const std::string HTTP_OUT_HEADER_IF_MATCH; -extern const std::string HTTP_OUT_HEADER_IF_MODIFIED_SINCE; -extern const std::string HTTP_OUT_HEADER_IF_NONE_MATCH; -extern const std::string HTTP_OUT_HEADER_IF_RANGE; -extern const std::string HTTP_OUT_HEADER_IF_UNMODIFIED_SINCE; -extern const std::string HTTP_OUT_HEADER_KEEP_ALIVE; -extern const std::string HTTP_OUT_HEADER_LAST_MODIFIED; -extern const std::string HTTP_OUT_HEADER_LOCATION; -extern const std::string HTTP_OUT_HEADER_MAX_FORWARDS; -extern const std::string HTTP_OUT_HEADER_MIME_VERSION; -extern const std::string HTTP_OUT_HEADER_PRAGMA; -extern const std::string HTTP_OUT_HEADER_PROXY_AUTHENTICATE; -extern const std::string HTTP_OUT_HEADER_PROXY_AUTHORIZATION; -extern const std::string HTTP_OUT_HEADER_RANGE; -extern const std::string HTTP_OUT_HEADER_REFERER; -extern const std::string HTTP_OUT_HEADER_RETRY_AFTER; -extern const std::string HTTP_OUT_HEADER_SERVER; -extern const std::string HTTP_OUT_HEADER_SET_COOKIE; -extern const std::string HTTP_OUT_HEADER_TE; -extern const std::string HTTP_OUT_HEADER_TRAILER; -extern const std::string HTTP_OUT_HEADER_TRANSFER_ENCODING; -extern const std::string HTTP_OUT_HEADER_UPGRADE; -extern const std::string HTTP_OUT_HEADER_USER_AGENT; -extern const std::string HTTP_OUT_HEADER_VARY; -extern const std::string HTTP_OUT_HEADER_VIA; -extern const std::string HTTP_OUT_HEADER_WARNING; -extern const std::string HTTP_OUT_HEADER_WWW_AUTHENTICATE; - -// Incoming headers are normalized to lower-case. -extern const std::string HTTP_IN_HEADER_ACCEPT_LANGUAGE; -extern const std::string HTTP_IN_HEADER_CACHE_CONTROL; -extern const std::string HTTP_IN_HEADER_CONTENT_LENGTH; -extern const std::string HTTP_IN_HEADER_CONTENT_LOCATION; -extern const std::string HTTP_IN_HEADER_CONTENT_TYPE; -extern const std::string HTTP_IN_HEADER_HOST; -extern const std::string HTTP_IN_HEADER_LOCATION; -extern const std::string HTTP_IN_HEADER_RETRY_AFTER; -extern const std::string HTTP_IN_HEADER_SET_COOKIE; -extern const std::string HTTP_IN_HEADER_USER_AGENT; -extern const std::string HTTP_IN_HEADER_X_FORWARDED_FOR; - -//// HTTP Content Types //// - -extern const std::string HTTP_CONTENT_LLSD_XML; -extern const std::string HTTP_CONTENT_OCTET_STREAM; -extern const std::string HTTP_CONTENT_VND_LL_MESH; -extern const std::string HTTP_CONTENT_XML; -extern const std::string HTTP_CONTENT_JSON; -extern const std::string HTTP_CONTENT_TEXT_HTML; -extern const std::string HTTP_CONTENT_TEXT_HTML_UTF8; -extern const std::string HTTP_CONTENT_TEXT_PLAIN_UTF8; -extern const std::string HTTP_CONTENT_TEXT_LLSD; -extern const std::string HTTP_CONTENT_TEXT_XML; -extern const std::string HTTP_CONTENT_TEXT_LSL; -extern const std::string HTTP_CONTENT_TEXT_PLAIN; -extern const std::string HTTP_CONTENT_IMAGE_X_J2C; -extern const std::string HTTP_CONTENT_IMAGE_J2C; -extern const std::string HTTP_CONTENT_IMAGE_JPEG; -extern const std::string HTTP_CONTENT_IMAGE_PNG; -extern const std::string HTTP_CONTENT_IMAGE_BMP; - -//// HTTP Cache Settings //// -extern const std::string HTTP_NO_CACHE; -extern const std::string HTTP_NO_CACHE_CONTROL; - -#endif diff --git a/indra/llmessage/llhttpsdhandler.cpp b/indra/llmessage/llhttpsdhandler.cpp new file mode 100644 index 0000000000..648bc5cfd8 --- /dev/null +++ b/indra/llmessage/llhttpsdhandler.cpp @@ -0,0 +1,77 @@ +/** +* @file llhttpsdhandler.h +* @brief Public-facing declarations for the HttpHandler class +* +* $LicenseInfo:firstyear=2012&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2012, 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 "llhttpconstants.h" + +#include "llhttpsdhandler.h" +#include "httpresponse.h" +#include "httpheaders.h" +#include "llsd.h" +#include "llsdserialize.h" +#include "bufferstream.h" +#include "llcorehttputil.h" + +//======================================================================== +LLHttpSDHandler::LLHttpSDHandler() +{ +} + +void LLHttpSDHandler::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response) +{ + LLCore::HttpStatus status = response->getStatus(); + + if (!status) + { + this->onFailure(response, status); + } + else + { + LLSD resplsd; + const bool emit_parse_errors = false; + + bool parsed = !((response->getBodySize() == 0) || + !LLCoreHttpUtil::responseToLLSD(response, emit_parse_errors, resplsd)); + + if (!parsed) + { + // Only emit a warning if we failed to parse when 'content-type' == 'application/llsd+xml' + LLCore::HttpHeaders::ptr_t headers(response->getHeaders()); + const std::string *contentType = (headers) ? headers->find(HTTP_IN_HEADER_CONTENT_TYPE) : NULL; + + if (contentType && (HTTP_CONTENT_LLSD_XML == *contentType)) + { + std::string thebody = LLCoreHttpUtil::responseToString(response); + + LL_WARNS() << "Failed to deserialize . " << response->getRequestURL() << " [status:" << response->getStatus().toString() << "] " + << " body: " << thebody << LL_ENDL; + } + } + + this->onSuccess(response, resplsd); + } + +} diff --git a/indra/llmessage/llhttpsdhandler.h b/indra/llmessage/llhttpsdhandler.h new file mode 100644 index 0000000000..ce40bdfc08 --- /dev/null +++ b/indra/llmessage/llhttpsdhandler.h @@ -0,0 +1,55 @@ +/** +* @file llhttpsdhandler.h +* @brief Public-facing declarations for the HttpHandler class +* +* $LicenseInfo:firstyear=2012&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2012, 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$ +*/ + +#ifndef _LLHTTPSDHANDLER_H_ +#define _LLHTTPSDHANDLER_H_ +#include "httpcommon.h" +#include "httphandler.h" +#include "lluri.h" + +/// Handler class LLCore's HTTP library. Splitting with separate success and +/// failure routines and parsing the result body into LLSD on success. It +/// is intended to be subclassed for specific capability handling. +/// +// *TODO: This class self deletes at the end of onCompleted method. This is +// less than ideal and should be revisited. +class LLHttpSDHandler : public LLCore::HttpHandler //, +// public std::enable_shared_from_this<LLHttpSDHandler> +{ +public: + + virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response); + +protected: + LLHttpSDHandler(); + + virtual void onSuccess(LLCore::HttpResponse * response, const LLSD &content) = 0; + virtual void onFailure(LLCore::HttpResponse * response, LLCore::HttpStatus status) = 0; + + +}; + +#endif diff --git a/indra/llmessage/llhttpsender.cpp b/indra/llmessage/llhttpsender.cpp deleted file mode 100644 index 5363088d79..0000000000 --- a/indra/llmessage/llhttpsender.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/** - * @file llhttpsender.cpp - * @brief Abstracts details of sending messages via HTTP. - * - * $LicenseInfo:firstyear=2007&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 "llhttpsender.h" - -#include <map> -#include <sstream> - -#include "llhost.h" -#include "llsd.h" - -namespace -{ - typedef std::map<LLHost, LLHTTPSender*> SenderMap; - static SenderMap senderMap; - static LLPointer<LLHTTPSender> defaultSender(new LLHTTPSender()); -} - -//virtual -LLHTTPSender::~LLHTTPSender() -{ -} - -//virtual -void LLHTTPSender::send(const LLHost& host, const std::string& name, - const LLSD& body, - LLHTTPClient::ResponderPtr response) const -{ - // Default implementation inserts sender, message and sends HTTP POST - std::ostringstream stream; - stream << "http://" << host << "/trusted-message/" << name; - LL_INFOS() << "LLHTTPSender::send: POST to " << stream.str() << LL_ENDL; - LLHTTPClient::post(stream.str(), body, response); -} - -//static -void LLHTTPSender::setSender(const LLHost& host, LLHTTPSender* sender) -{ - LL_INFOS() << "LLHTTPSender::setSender " << host << LL_ENDL; - senderMap[host] = sender; -} - -//static -const LLHTTPSender& LLHTTPSender::getSender(const LLHost& host) -{ - SenderMap::const_iterator iter = senderMap.find(host); - if(iter == senderMap.end()) - { - return *defaultSender; - } - return *(iter->second); -} - -//static -void LLHTTPSender::clearSender(const LLHost& host) -{ - SenderMap::iterator iter = senderMap.find(host); - if(iter != senderMap.end()) - { - delete iter->second; - senderMap.erase(iter); - } -} - -//static -void LLHTTPSender::setDefaultSender(LLHTTPSender* sender) -{ - defaultSender = sender; -} diff --git a/indra/llmessage/llhttpsender.h b/indra/llmessage/llhttpsender.h deleted file mode 100644 index ff8fa2f95b..0000000000 --- a/indra/llmessage/llhttpsender.h +++ /dev/null @@ -1,59 +0,0 @@ -/** - * @file llhttpsender.h - * @brief Abstracts details of sending messages via HTTP. - * - * $LicenseInfo:firstyear=2007&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$ - */ - -#ifndef LL_HTTP_SENDER_H -#define LL_HTTP_SENDER_H - -#include "llhttpclient.h" - -class LLHost; -class LLSD; - -class LLHTTPSender : public LLThreadSafeRefCount -{ - public: - - virtual ~LLHTTPSender(); - - /** @brief Send message to host with body, call response when done */ - virtual void send(const LLHost& host, - const std::string& message, const LLSD& body, - LLHTTPClient::ResponderPtr response) const; - - /** @brief Set sender for host, takes ownership of sender. */ - static void setSender(const LLHost& host, LLHTTPSender* sender); - - /** @brief Get sender for host, retains ownership of returned sender. */ - static const LLHTTPSender& getSender(const LLHost& host); - - /** @brief Clear sender for host. */ - static void clearSender(const LLHost& host); - - /** @brief Set default sender, takes ownership of sender. */ - static void setDefaultSender(LLHTTPSender* sender); -}; - -#endif // LL_HTTP_SENDER_H diff --git a/indra/llmessage/llproxy.cpp b/indra/llmessage/llproxy.cpp index 9b8d19cc3e..537efa69d8 100644 --- a/indra/llmessage/llproxy.cpp +++ b/indra/llmessage/llproxy.cpp @@ -30,9 +30,8 @@ #include <string> #include <curl/curl.h> - +#include "httpcommon.h" #include "llapr.h" -#include "llcurl.h" #include "llhost.h" // Static class variable instances @@ -408,16 +407,6 @@ void LLProxy::cleanupClass() deleteSingleton(); } -void LLProxy::applyProxySettings(LLCurlEasyRequest* handle) -{ - applyProxySettings(handle->getEasy()); -} - -void LLProxy::applyProxySettings(LLCurl::Easy* handle) -{ - applyProxySettings(handle->getCurlHandle()); -} - /** * @brief Apply proxy settings to a CuRL request if an HTTP proxy is enabled. * @@ -439,21 +428,21 @@ void LLProxy::applyProxySettings(CURL* handle) // Now test again to verify that the proxy wasn't disabled between the first check and the lock. if (mHTTPProxyEnabled) { - LLCurlFF::check_easy_code(curl_easy_setopt(handle, CURLOPT_PROXY, mHTTPProxy.getIPString().c_str())); - LLCurlFF::check_easy_code(curl_easy_setopt(handle, CURLOPT_PROXYPORT, mHTTPProxy.getPort())); + LLCore::LLHttp::check_curl_code(curl_easy_setopt(handle, CURLOPT_PROXY, mHTTPProxy.getIPString().c_str()), CURLOPT_PROXY); + LLCore::LLHttp::check_curl_code(curl_easy_setopt(handle, CURLOPT_PROXYPORT, mHTTPProxy.getPort()), CURLOPT_PROXYPORT); if (mProxyType == LLPROXY_SOCKS) { - LLCurlFF::check_easy_code(curl_easy_setopt(handle, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5)); + LLCore::LLHttp::check_curl_code(curl_easy_setopt(handle, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5), CURLOPT_PROXYTYPE); if (mAuthMethodSelected == METHOD_PASSWORD) { std::string auth_string = mSocksUsername + ":" + mSocksPassword; - LLCurlFF::check_easy_code(curl_easy_setopt(handle, CURLOPT_PROXYUSERPWD, auth_string.c_str())); + LLCore::LLHttp::check_curl_code(curl_easy_setopt(handle, CURLOPT_PROXYUSERPWD, auth_string.c_str()), CURLOPT_PROXYUSERPWD); } } else { - LLCurlFF::check_easy_code(curl_easy_setopt(handle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP)); + LLCore::LLHttp::check_curl_code(curl_easy_setopt(handle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP), CURLOPT_PROXYTYPE); } } } diff --git a/indra/llmessage/llproxy.h b/indra/llmessage/llproxy.h index a919370540..bd23dd39de 100644 --- a/indra/llmessage/llproxy.h +++ b/indra/llmessage/llproxy.h @@ -27,12 +27,12 @@ #ifndef LL_PROXY_H #define LL_PROXY_H -#include "llcurl.h" #include "llhost.h" #include "lliosocket.h" #include "llmemory.h" #include "llsingleton.h" #include "llthread.h" +#include <curl/curl.h> #include <string> // SOCKS error codes returned from the StartProxy method @@ -208,16 +208,13 @@ enum LLSocks5AuthType * thread-safe method to apply those options to a curl request * (LLProxy::applyProxySettings()). This method is overloaded * to accommodate the various abstraction libcurl layers that exist - * throughout the viewer (LLCurlEasyRequest, LLCurl::Easy, and CURL). - * - * If you are working with LLCurl or LLCurlEasyRequest objects, - * the configured proxy settings will be applied in the constructors - * of those request handles. If you are working with CURL objects - * directly, you will need to pass the handle of the request to - * applyProxySettings() before issuing the request. + * throughout the viewer (CURL). * * To ensure thread safety, all LLProxy members that relate to the HTTP * proxy require the LLProxyMutex to be locked before accessing. + * + * *TODO$: This should be moved into the LLCore::Http space. + * */ class LLProxy: public LLSingleton<LLProxy> { @@ -252,9 +249,6 @@ public: // Apply the current proxy settings to a curl request. Doesn't do anything if mHTTPProxyEnabled is false. // Safe to call from any thread. void applyProxySettings(CURL* handle); - void applyProxySettings(LLCurl::Easy* handle); - void applyProxySettings(LLCurlEasyRequest* handle); - // Start a connection to the SOCKS 5 proxy. Call from main thread only. S32 startSOCKSProxy(LLHost host); diff --git a/indra/llmessage/llsdmessage.cpp b/indra/llmessage/llsdmessage.cpp deleted file mode 100644 index 61fcc5dd2f..0000000000 --- a/indra/llmessage/llsdmessage.cpp +++ /dev/null @@ -1,168 +0,0 @@ -/** - * @file llsdmessage.cpp - * @author Nat Goodspeed - * @date 2008-10-31 - * @brief Implementation for llsdmessage. - * - * $LicenseInfo:firstyear=2008&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$ - */ - -#if LL_WINDOWS -#pragma warning (disable : 4675) // "resolved by ADL" -- just as I want! -#endif - -// Precompiled header -#include "linden_common.h" -// associated header -#include "llsdmessage.h" -// STL headers -// std headers -// external library headers -// other Linden headers -#include "llevents.h" -#include "llsdserialize.h" -#include "llhttpclient.h" -#include "llmessageconfig.h" -#include "llhost.h" -#include "message.h" -#include "llsdutil.h" - -// Declare a static LLSDMessage instance to ensure that we have a listener as -// soon as someone tries to post on our canonical LLEventPump name. -static LLSDMessage httpListener; - -LLSDMessage::LLSDMessage(): - // Instantiating our own local LLEventPump with a string name the - // constructor is NOT allowed to tweak is a way of ensuring Singleton - // semantics: attempting to instantiate a second LLSDMessage object would - // throw LLEventPump::DupPumpName. - mEventPump("LLHTTPClient") -{ - mEventPump.listen("self", boost::bind(&LLSDMessage::httpListener, this, _1)); -} - -bool LLSDMessage::httpListener(const LLSD& request) -{ - // Extract what we want from the request object. We do it all up front - // partly to document what we expect. - LLSD::String url(request["url"]); - LLSD payload(request["payload"]); - LLSD::String reply(request["reply"]); - LLSD::String error(request["error"]); - LLSD::Real timeout(request["timeout"]); - // If the LLSD doesn't even have a "url" key, we doubt it was intended for - // this listener. - if (url.empty()) - { - std::ostringstream out; - out << "request event without 'url' key to '" << mEventPump.getName() << "'"; - throw ArgError(out.str()); - } - // Establish default timeout. This test relies on LLSD::asReal() returning - // exactly 0.0 for an undef value. - if (! timeout) - { - timeout = HTTP_REQUEST_EXPIRY_SECS; - } - LLHTTPClient::post(url, payload, - new LLSDMessage::EventResponder(LLEventPumps::instance(), - request, - url, "POST", reply, error), - LLSD(), // headers - (F32)timeout); - return false; -} - -void LLSDMessage::EventResponder::httpSuccess() -{ - // If our caller passed an empty replyPump name, they're not - // listening: this is a fire-and-forget message. Don't bother posting - // to the pump whose name is "". - if (! mReplyPump.empty()) - { - LLSD response(getContent()); - mReqID.stamp(response); - mPumps.obtain(mReplyPump).post(response); - } - else // default success handling - { - LL_INFOS("LLSDMessage::EventResponder") - << "'" << mMessage << "' to '" << mTarget << "' succeeded" - << LL_ENDL; - } -} - -void LLSDMessage::EventResponder::httpFailure() -{ - // If our caller passed an empty errorPump name, they're not - // listening: "default error handling is acceptable." Only post to an - // explicit pump name. - if (! mErrorPump.empty()) - { - LLSD info(mReqID.makeResponse()); - info["target"] = mTarget; - info["message"] = mMessage; - info["status"] = getStatus(); - info["reason"] = getReason(); - info["content"] = getContent(); - mPumps.obtain(mErrorPump).post(info); - } - else // default error handling - { - // convention seems to be to use LL_INFOS(), but that seems a bit casual? - LL_WARNS("LLSDMessage::EventResponder") - << "'" << mMessage << "' to '" << mTarget - << "' failed " << dumpResponse() << LL_ENDL; - } -} - -LLSDMessage::ResponderAdapter::ResponderAdapter(LLHTTPClient::ResponderPtr responder, - const std::string& name): - mResponder(responder), - mReplyPump(name + ".reply", true), // tweak name for uniqueness - mErrorPump(name + ".error", true) -{ - mReplyPump.listen("self", boost::bind(&ResponderAdapter::listener, this, _1, true)); - mErrorPump.listen("self", boost::bind(&ResponderAdapter::listener, this, _1, false)); -} - -bool LLSDMessage::ResponderAdapter::listener(const LLSD& payload, bool success) -{ - if (success) - { - mResponder->successResult(payload); - } - else - { - mResponder->failureResult(payload["status"].asInteger(), payload["reason"], payload["content"]); - } - - /*---------------- MUST BE LAST STATEMENT BEFORE RETURN ----------------*/ - delete this; - // Destruction of mResponder will usually implicitly free its referent as well - /*------------------------- NOTHING AFTER THIS -------------------------*/ - return false; -} - -void LLSDMessage::link() -{ -} diff --git a/indra/llmessage/llsdmessage.h b/indra/llmessage/llsdmessage.h deleted file mode 100644 index e5d532d6a4..0000000000 --- a/indra/llmessage/llsdmessage.h +++ /dev/null @@ -1,168 +0,0 @@ -/** - * @file llsdmessage.h - * @author Nat Goodspeed - * @date 2008-10-30 - * @brief API intended to unify sending capability, UDP and TCP messages: - * https://wiki.lindenlab.com/wiki/Viewer:Messaging/Messaging_Notes - * - * $LicenseInfo:firstyear=2008&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$ - */ - -#if ! defined(LL_LLSDMESSAGE_H) -#define LL_LLSDMESSAGE_H - -#include "llerror.h" // LOG_CLASS() -#include "llevents.h" // LLEventPumps -#include "llhttpclient.h" -#include <string> -#include <stdexcept> - -class LLSD; - -/** - * Class managing the messaging API described in - * https://wiki.lindenlab.com/wiki/Viewer:Messaging/Messaging_Notes - */ -class LLSDMessage -{ - LOG_CLASS(LLSDMessage); - -public: - LLSDMessage(); - - /// Exception if you specify arguments badly - struct ArgError: public std::runtime_error - { - ArgError(const std::string& what): - std::runtime_error(std::string("ArgError: ") + what) {} - }; - - /** - * The response idiom used by LLSDMessage -- LLEventPump names on which to - * post reply or error -- is designed for the case in which your - * reply/error handlers are methods on the same class as the method - * sending the message. Any state available to the sending method that - * must be visible to the reply/error methods can conveniently be stored - * on that class itself, if it's not already. - * - * The LLHTTPClient::Responder idiom requires a separate instance of a - * separate class so that it can dispatch to the code of interest by - * calling canonical virtual methods. Interesting state must be copied - * into that new object. - * - * With some trepidation, because existing response code is packaged in - * LLHTTPClient::Responder subclasses, we provide this adapter class - * <i>for transitional purposes only.</i> Instantiate a new heap - * ResponderAdapter with your new LLHTTPClient::ResponderPtr. Pass - * ResponderAdapter::getReplyName() and/or getErrorName() in your - * LLSDMessage (or LLViewerRegion::getCapAPI()) request event. The - * ResponderAdapter will call the appropriate Responder method, then - * @c delete itself. - */ - class ResponderAdapter - { - public: - /** - * Bind the new LLHTTPClient::Responder subclass instance. - * - * Passing the constructor a name other than the default is only - * interesting if you suspect some usage will lead to an exception or - * log message. - */ - ResponderAdapter(LLHTTPClient::ResponderPtr responder, - const std::string& name="ResponderAdapter"); - - /// EventPump name on which LLSDMessage should post reply event - std::string getReplyName() const { return mReplyPump.getName(); } - /// EventPump name on which LLSDMessage should post error event - std::string getErrorName() const { return mErrorPump.getName(); } - - private: - // We have two different LLEventStreams, though we route them both to - // the same listener, so that we can bind an extra flag identifying - // which case (reply or error) reached that listener. - bool listener(const LLSD&, bool success); - - LLHTTPClient::ResponderPtr mResponder; - LLEventStream mReplyPump, mErrorPump; - }; - - /** - * Force our implementation file to be linked with caller. The .cpp file - * contains a static instance of this class, which must be linked into the - * executable to support the canonical listener. But since the primary - * interface to that static instance is via a named LLEventPump rather - * than by direct reference, the linker doesn't necessarily perceive the - * necessity to bring in the translation unit. Referencing this dummy - * method forces the issue. - */ - static void link(); - -private: - friend class LLCapabilityListener; - /// Responder used for internal purposes by LLSDMessage and - /// LLCapabilityListener. Others should use higher-level APIs. - class EventResponder: public LLHTTPClient::Responder - { - LOG_CLASS(EventResponder); - public: - /** - * LLHTTPClient::Responder that dispatches via named LLEventPump instances. - * We bind LLEventPumps, even though it's an LLSingleton, for testability. - * We bind the string names of the desired LLEventPump instances rather - * than actually obtain()ing them so we only obtain() the one we're going - * to use. If the caller doesn't bother to listen() on it, the other pump - * may never materialize at all. - * @a target and @a message are only to clarify error processing. - * For a capability message, @a target should be the region description, - * @a message should be the capability name. - * For a service with a visible URL, pass the URL as @a target and the HTTP verb - * (e.g. "POST") as @a message. - */ - EventResponder(LLEventPumps& pumps, - const LLSD& request, - const std::string& target, const std::string& message, - const std::string& replyPump, const std::string& errorPump): - mPumps(pumps), - mReqID(request), - mTarget(target), - mMessage(message), - mReplyPump(replyPump), - mErrorPump(errorPump) - {} - - protected: - virtual void httpSuccess(); - virtual void httpFailure(); - - private: - LLEventPumps& mPumps; - LLReqID mReqID; - const std::string mTarget, mMessage, mReplyPump, mErrorPump; - }; - -private: - bool httpListener(const LLSD&); - LLEventStream mEventPump; -}; - -#endif /* ! defined(LL_LLSDMESSAGE_H) */ diff --git a/indra/llmessage/llsdrpcclient.cpp b/indra/llmessage/llsdrpcclient.cpp deleted file mode 100644 index eb773ceb3a..0000000000 --- a/indra/llmessage/llsdrpcclient.cpp +++ /dev/null @@ -1,245 +0,0 @@ -/** - * @file llsdrpcclient.cpp - * @author Phoenix - * @date 2005-11-05 - * @brief Implementation of the llsd client classes. - * - * $LicenseInfo:firstyear=2005&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 "llsdrpcclient.h" - -#include "llbufferstream.h" -#include "llfasttimer.h" -#include "llfiltersd2xmlrpc.h" -#include "llpumpio.h" -#include "llsd.h" -#include "llsdserialize.h" -#include "llurlrequest.h" - -/** - * String constants - */ -static std::string LLSDRPC_RESPONSE_NAME("response"); -static std::string LLSDRPC_FAULT_NAME("fault"); - -/** - * LLSDRPCResponse - */ -LLSDRPCResponse::LLSDRPCResponse() : - mIsError(false), - mIsFault(false) -{ -} - -// virtual -LLSDRPCResponse::~LLSDRPCResponse() -{ -} - -bool LLSDRPCResponse::extractResponse(const LLSD& sd) -{ - bool rv = true; - if(sd.has(LLSDRPC_RESPONSE_NAME)) - { - mReturnValue = sd[LLSDRPC_RESPONSE_NAME]; - mIsFault = false; - } - else if(sd.has(LLSDRPC_FAULT_NAME)) - { - mReturnValue = sd[LLSDRPC_FAULT_NAME]; - mIsFault = true; - } - else - { - mReturnValue.clear(); - mIsError = true; - rv = false; - } - return rv; -} - -static LLTrace::BlockTimerStatHandle FTM_SDRPC_RESPONSE("SDRPC Response"); - -// virtual -LLIOPipe::EStatus LLSDRPCResponse::process_impl( - const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, - bool& eos, - LLSD& context, - LLPumpIO* pump) -{ - LL_RECORD_BLOCK_TIME(FTM_SDRPC_RESPONSE); - PUMP_DEBUG; - if(mIsError) - { - error(pump); - } - else if(mIsFault) - { - fault(pump); - } - else - { - response(pump); - } - PUMP_DEBUG; - return STATUS_DONE; -} - -/** - * LLSDRPCClient - */ - -LLSDRPCClient::LLSDRPCClient() : - mState(STATE_NONE), - mQueue(EPBQ_PROCESS) -{ -} - -// virtual -LLSDRPCClient::~LLSDRPCClient() -{ -} - -bool LLSDRPCClient::call( - const std::string& uri, - const std::string& method, - const LLSD& parameter, - LLSDRPCResponse* response, - EPassBackQueue queue) -{ - //LL_INFOS() << "RPC: " << uri << "." << method << "(" << *parameter << ")" - // << LL_ENDL; - if(method.empty() || !response) - { - return false; - } - mState = STATE_READY; - mURI.assign(uri); - std::stringstream req; - req << LLSDRPC_REQUEST_HEADER_1 << method - << LLSDRPC_REQUEST_HEADER_2; - LLSDSerialize::toNotation(parameter, req); - req << LLSDRPC_REQUEST_FOOTER; - mRequest = req.str(); - mQueue = queue; - mResponse = response; - return true; -} - -bool LLSDRPCClient::call( - const std::string& uri, - const std::string& method, - const std::string& parameter, - LLSDRPCResponse* response, - EPassBackQueue queue) -{ - //LL_INFOS() << "RPC: " << uri << "." << method << "(" << parameter << ")" - // << LL_ENDL; - if(method.empty() || parameter.empty() || !response) - { - return false; - } - mState = STATE_READY; - mURI.assign(uri); - std::stringstream req; - req << LLSDRPC_REQUEST_HEADER_1 << method - << LLSDRPC_REQUEST_HEADER_2 << parameter - << LLSDRPC_REQUEST_FOOTER; - mRequest = req.str(); - mQueue = queue; - mResponse = response; - return true; -} - -static LLTrace::BlockTimerStatHandle FTM_PROCESS_SDRPC_CLIENT("SDRPC Client"); - -// virtual -LLIOPipe::EStatus LLSDRPCClient::process_impl( - const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, - bool& eos, - LLSD& context, - LLPumpIO* pump) -{ - LL_RECORD_BLOCK_TIME(FTM_PROCESS_SDRPC_CLIENT); - PUMP_DEBUG; - if((STATE_NONE == mState) || (!pump)) - { - // You should have called the call() method already. - return STATUS_PRECONDITION_NOT_MET; - } - EStatus rv = STATUS_DONE; - switch(mState) - { - case STATE_READY: - { - PUMP_DEBUG; -// LL_DEBUGS() << "LLSDRPCClient::process_impl STATE_READY" << LL_ENDL; - buffer->append( - channels.out(), - (U8*)mRequest.c_str(), - mRequest.length()); - context[CONTEXT_DEST_URI_SD_LABEL] = mURI; - mState = STATE_WAITING_FOR_RESPONSE; - break; - } - case STATE_WAITING_FOR_RESPONSE: - { - PUMP_DEBUG; - // The input channel has the sd response in it. - //LL_DEBUGS() << "LLSDRPCClient::process_impl STATE_WAITING_FOR_RESPONSE" - // << LL_ENDL; - LLBufferStream resp(channels, buffer.get()); - LLSD sd; - LLSDSerialize::fromNotation(sd, resp, buffer->count(channels.in())); - LLSDRPCResponse* response = (LLSDRPCResponse*)mResponse.get(); - if (!response) - { - mState = STATE_DONE; - break; - } - response->extractResponse(sd); - if(EPBQ_PROCESS == mQueue) - { - LLPumpIO::chain_t chain; - chain.push_back(mResponse); - pump->addChain(chain, DEFAULT_CHAIN_EXPIRY_SECS); - } - else - { - pump->respond(mResponse.get()); - } - mState = STATE_DONE; - break; - } - case STATE_DONE: - default: - PUMP_DEBUG; - LL_INFOS() << "invalid state to process" << LL_ENDL; - rv = STATUS_ERROR; - break; - } - return rv; -} diff --git a/indra/llmessage/llsdrpcclient.h b/indra/llmessage/llsdrpcclient.h deleted file mode 100644 index c4e0333ca3..0000000000 --- a/indra/llmessage/llsdrpcclient.h +++ /dev/null @@ -1,323 +0,0 @@ -/** - * @file llsdrpcclient.h - * @author Phoenix - * @date 2005-11-05 - * @brief Implementation and helpers for structure data RPC clients. - * - * $LicenseInfo:firstyear=2005&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$ - */ - -#ifndef LL_LLSDRPCCLIENT_H -#define LL_LLSDRPCCLIENT_H - -/** - * This file declares classes to encapsulate a basic structured data - * remote procedure client. - */ - -#include "llchainio.h" -#include "llfiltersd2xmlrpc.h" -#include "lliopipe.h" -#include "llurlrequest.h" - -/** - * @class LLSDRPCClientResponse - * @brief Abstract base class to represent a response from an SD server. - * - * This is used as a base class for callbacks generated from an - * structured data remote procedure call. The - * <code>extractResponse</code> method will deal with the llsdrpc method - * call overhead, and keep track of what to call during the next call - * into <code>process</code>. If you use this as a base class, you - * need to implement <code>response</code>, <code>fault</code>, and - * <code>error</code> to do something useful. When in those methods, - * you can parse and utilize the mReturnValue member data. - */ -class LLSDRPCResponse : public LLIOPipe -{ -public: - LLSDRPCResponse(); - virtual ~LLSDRPCResponse(); - - /** - * @brief This method extracts the response out of the sd passed in - * - * Any appropriate data found in the sd passed in will be - * extracted and managed by this object - not copied or cloned. It - * will still be up to the caller to delete the pointer passed in. - * @param sd The raw structured data response from the remote server. - * @return Returns true if this was able to parse the structured data. - */ - bool extractResponse(const LLSD& sd); - -protected: - /** - * @brief Method called when the response is ready. - */ - virtual bool response(LLPumpIO* pump) = 0; - - /** - * @brief Method called when a fault is generated by the remote server. - */ - virtual bool fault(LLPumpIO* pump) = 0; - - /** - * @brief Method called when there was an error - */ - virtual bool error(LLPumpIO* pump) = 0; - -protected: - /* @name LLIOPipe virtual implementations - */ - //@{ - /** - * @brief Process the data in buffer - */ - virtual EStatus process_impl( - const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, - bool& eos, - LLSD& context, - LLPumpIO* pump); - //@} - -protected: - LLSD mReturnValue; - bool mIsError; - bool mIsFault; -}; - -/** - * @class LLSDRPCClient - * @brief Client class for a structured data remote procedure call. - * - * This class helps deal with making structured data calls to a remote - * server. You can visualize the calls as: - * <code> - * response = uri.method(parameter) - * </code> - * where you pass in everything to <code>call</code> and this class - * takes care of the rest of the details. - * In typical usage, you will derive a class from this class and - * provide an API more useful for the specific application at - * hand. For example, if you were writing a service to send an instant - * message, you could create an API for it to send the messsage, and - * that class would do the work of translating it into the method and - * parameter, find the destination, and invoke <code>call</call> with - * a useful implementation of LLSDRPCResponse passed in to handle the - * response from the network. - */ -class LLSDRPCClient : public LLIOPipe -{ -public: - LLSDRPCClient(); - virtual ~LLSDRPCClient(); - - /** - * @brief Enumeration for tracking which queue to process the - * response. - */ - enum EPassBackQueue - { - EPBQ_PROCESS, - EPBQ_CALLBACK, - }; - - /** - * @brief Call a method on a remote LLSDRPCServer - * - * @param uri The remote object to call, eg, - * http://localhost/usher. If you are using a factory with a fixed - * url, the uri passed in will probably be ignored. - * @param method The method to call on the remote object - * @param parameter The parameter to pass into the remote - * object. It is up to the caller to delete the value passed in. - * @param response The object which gets the response. - * @param queue Specifies to call the response on the process or - * callback queue. - * @return Returns true if this object will be able to make the RPC call. - */ - bool call( - const std::string& uri, - const std::string& method, - const LLSD& parameter, - LLSDRPCResponse* response, - EPassBackQueue queue); - - /** - * @brief Call a method on a remote LLSDRPCServer - * - * @param uri The remote object to call, eg, - * http://localhost/usher. If you are using a factory with a fixed - * url, the uri passed in will probably be ignored. - * @param method The method to call on the remote object - * @param parameter The seriailized parameter to pass into the - * remote object. - * @param response The object which gets the response. - * @param queue Specifies to call the response on the process or - * callback queue. - * @return Returns true if this object will be able to make the RPC call. - */ - bool call( - const std::string& uri, - const std::string& method, - const std::string& parameter, - LLSDRPCResponse* response, - EPassBackQueue queue); - -protected: - /** - * @brief Enumeration for tracking client state. - */ - enum EState - { - STATE_NONE, - STATE_READY, - STATE_WAITING_FOR_RESPONSE, - STATE_DONE - }; - - /* @name LLIOPipe virtual implementations - */ - //@{ - /** - * @brief Process the data in buffer - */ - virtual EStatus process_impl( - const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, - bool& eos, - LLSD& context, - LLPumpIO* pump); - //@} - -protected: - EState mState; - std::string mURI; - std::string mRequest; - EPassBackQueue mQueue; - LLIOPipe::ptr_t mResponse; -}; - -/** - * @class LLSDRPCClientFactory - * @brief Basic implementation for making an SD RPC client factory - * - * This class eases construction of a basic sd rpc client. Here is an - * example of it's use: - * <code> - * class LLUsefulService : public LLService { ... } - * LLService::registerCreator( - * "useful", - * LLService::creator_t(new LLSDRPCClientFactory<LLUsefulService>)) - * </code> - */ -template<class Client> -class LLSDRPCClientFactory : public LLChainIOFactory -{ -public: - LLSDRPCClientFactory() {} - LLSDRPCClientFactory(const std::string& fixed_url) : mURL(fixed_url) {} - virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const - { - LL_DEBUGS() << "LLSDRPCClientFactory::build" << LL_ENDL; - LLURLRequest* http(new LLURLRequest(HTTP_POST)); - if(!http->isValid()) - { - LL_WARNS() << "Creating LLURLRequest failed." << LL_ENDL ; - delete http; - return false; - } - - LLIOPipe::ptr_t service(new Client); - chain.push_back(service); - LLIOPipe::ptr_t http_pipe(http); - http->addHeader(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_TEXT_LLSD); - if(mURL.empty()) - { - chain.push_back(LLIOPipe::ptr_t(new LLContextURLExtractor(http))); - } - else - { - http->setURL(mURL); - } - chain.push_back(http_pipe); - chain.push_back(service); - return true; - } -protected: - std::string mURL; -}; - -/** - * @class LLXMLSDRPCClientFactory - * @brief Basic implementation for making an XMLRPC to SD RPC client factory - * - * This class eases construction of a basic sd rpc client which uses - * xmlrpc as a serialization grammar. Here is an example of it's use: - * <code> - * class LLUsefulService : public LLService { ... } - * LLService::registerCreator( - * "useful", - * LLService::creator_t(new LLXMLSDRPCClientFactory<LLUsefulService>)) - * </code> - */ -template<class Client> -class LLXMLSDRPCClientFactory : public LLChainIOFactory -{ -public: - LLXMLSDRPCClientFactory() {} - LLXMLSDRPCClientFactory(const std::string& fixed_url) : mURL(fixed_url) {} - virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const - { - LL_DEBUGS() << "LLXMLSDRPCClientFactory::build" << LL_ENDL; - - LLURLRequest* http(new LLURLRequest(HTTP_POST)); - if(!http->isValid()) - { - LL_WARNS() << "Creating LLURLRequest failed." << LL_ENDL ; - delete http; - return false ; - } - LLIOPipe::ptr_t service(new Client); - chain.push_back(service); - LLIOPipe::ptr_t http_pipe(http); - http->addHeader(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_TEXT_XML); - if(mURL.empty()) - { - chain.push_back(LLIOPipe::ptr_t(new LLContextURLExtractor(http))); - } - else - { - http->setURL(mURL); - } - chain.push_back(LLIOPipe::ptr_t(new LLFilterSD2XMLRPCRequest(NULL))); - chain.push_back(http_pipe); - chain.push_back(LLIOPipe::ptr_t(new LLFilterXMLRPCResponse2LLSD)); - chain.push_back(service); - return true; - } -protected: - std::string mURL; -}; - -#endif // LL_LLSDRPCCLIENT_H diff --git a/indra/llmessage/llsdrpcserver.cpp b/indra/llmessage/llsdrpcserver.cpp deleted file mode 100644 index c3ed19889e..0000000000 --- a/indra/llmessage/llsdrpcserver.cpp +++ /dev/null @@ -1,339 +0,0 @@ -/** - * @file llsdrpcserver.cpp - * @author Phoenix - * @date 2005-10-11 - * @brief Implementation of the LLSDRPCServer and related classes. - * - * $LicenseInfo:firstyear=2005&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 "llsdrpcserver.h" - -#include "llbuffer.h" -#include "llbufferstream.h" -#include "llfasttimer.h" -#include "llpumpio.h" -#include "llsdserialize.h" -#include "llstl.h" - -static const char FAULT_PART_1[] = "{'fault':{'code':i"; -static const char FAULT_PART_2[] = ", 'description':'"; -static const char FAULT_PART_3[] = "'}}"; - -static const char RESPONSE_PART_1[] = "{'response':"; -static const char RESPONSE_PART_2[] = "}"; - -static const S32 FAULT_GENERIC = 1000; -static const S32 FAULT_METHOD_NOT_FOUND = 1001; - -static const std::string LLSDRPC_METHOD_SD_NAME("method"); -static const std::string LLSDRPC_PARAMETER_SD_NAME("parameter"); - - -/** - * LLSDRPCServer - */ -LLSDRPCServer::LLSDRPCServer() : - mState(LLSDRPCServer::STATE_NONE), - mPump(NULL), - mLock(0) -{ -} - -LLSDRPCServer::~LLSDRPCServer() -{ - std::for_each( - mMethods.begin(), - mMethods.end(), - llcompose1( - DeletePointerFunctor<LLSDRPCMethodCallBase>(), - llselect2nd<method_map_t::value_type>())); - std::for_each( - mCallbackMethods.begin(), - mCallbackMethods.end(), - llcompose1( - DeletePointerFunctor<LLSDRPCMethodCallBase>(), - llselect2nd<method_map_t::value_type>())); -} - - -// virtual -ESDRPCSStatus LLSDRPCServer::deferredResponse( - const LLChannelDescriptors& channels, - LLBufferArray* data) { - // subclass should provide a sane implementation - return ESDRPCS_DONE; -} - -void LLSDRPCServer::clearLock() -{ - if(mLock && mPump) - { - mPump->clearLock(mLock); - mPump = NULL; - mLock = 0; - } -} - -static LLTrace::BlockTimerStatHandle FTM_PROCESS_SDRPC_SERVER("SDRPC Server"); - -// virtual -LLIOPipe::EStatus LLSDRPCServer::process_impl( - const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, - bool& eos, - LLSD& context, - LLPumpIO* pump) -{ - LL_RECORD_BLOCK_TIME(FTM_PROCESS_SDRPC_SERVER); - PUMP_DEBUG; -// LL_DEBUGS() << "LLSDRPCServer::process_impl" << LL_ENDL; - // Once we have all the data, We need to read the sd on - // the the in channel, and respond on the out channel - if(!eos) return STATUS_BREAK; - if(!pump || !buffer) return STATUS_PRECONDITION_NOT_MET; - - std::string method_name; - LLIOPipe::EStatus status = STATUS_DONE; - - switch(mState) - { - case STATE_DEFERRED: - PUMP_DEBUG; - if(ESDRPCS_DONE != deferredResponse(channels, buffer.get())) - { - buildFault( - channels, - buffer.get(), - FAULT_GENERIC, - "deferred response failed."); - } - mState = STATE_DONE; - return STATUS_DONE; - - case STATE_DONE: -// LL_DEBUGS() << "STATE_DONE" << LL_ENDL; - break; - case STATE_CALLBACK: -// LL_DEBUGS() << "STATE_CALLBACK" << LL_ENDL; - PUMP_DEBUG; - method_name = mRequest[LLSDRPC_METHOD_SD_NAME].asString(); - if(!method_name.empty() && mRequest.has(LLSDRPC_PARAMETER_SD_NAME)) - { - if(ESDRPCS_DONE != callbackMethod( - method_name, - mRequest[LLSDRPC_PARAMETER_SD_NAME], - channels, - buffer.get())) - { - buildFault( - channels, - buffer.get(), - FAULT_GENERIC, - "Callback method call failed."); - } - } - else - { - // this should never happen, since we should not be in - // this state unless we originally found a method and - // params during the first call to process. - buildFault( - channels, - buffer.get(), - FAULT_GENERIC, - "Invalid LLSDRPC sever state - callback without method."); - } - pump->clearLock(mLock); - mLock = 0; - mState = STATE_DONE; - break; - case STATE_NONE: -// LL_DEBUGS() << "STATE_NONE" << LL_ENDL; - default: - { - // First time we got here - process the SD request, and call - // the method. - PUMP_DEBUG; - LLBufferStream istr(channels, buffer.get()); - mRequest.clear(); - LLSDSerialize::fromNotation( - mRequest, - istr, - buffer->count(channels.in())); - - // { 'method':'...', 'parameter': ... } - method_name = mRequest[LLSDRPC_METHOD_SD_NAME].asString(); - if(!method_name.empty() && mRequest.has(LLSDRPC_PARAMETER_SD_NAME)) - { - ESDRPCSStatus rv = callMethod( - method_name, - mRequest[LLSDRPC_PARAMETER_SD_NAME], - channels, - buffer.get()); - switch(rv) - { - case ESDRPCS_DEFERRED: - mPump = pump; - mLock = pump->setLock(); - mState = STATE_DEFERRED; - status = STATUS_BREAK; - break; - - case ESDRPCS_CALLBACK: - { - mState = STATE_CALLBACK; - LLPumpIO::LLLinkInfo link; - link.mPipe = LLIOPipe::ptr_t(this); - link.mChannels = channels; - LLPumpIO::links_t links; - links.push_back(link); - pump->respond(links, buffer, context); - mLock = pump->setLock(); - status = STATUS_BREAK; - break; - } - case ESDRPCS_DONE: - mState = STATE_DONE; - break; - case ESDRPCS_ERROR: - default: - buildFault( - channels, - buffer.get(), - FAULT_GENERIC, - "Method call failed."); - break; - } - } - else - { - // send a fault - buildFault( - channels, - buffer.get(), - FAULT_GENERIC, - "Unable to find method and parameter in request."); - } - break; - } - } - - PUMP_DEBUG; - return status; -} - -// virtual -ESDRPCSStatus LLSDRPCServer::callMethod( - const std::string& method, - const LLSD& params, - const LLChannelDescriptors& channels, - LLBufferArray* response) -{ - // Try to find the method in the method table. - ESDRPCSStatus rv = ESDRPCS_DONE; - method_map_t::iterator it = mMethods.find(method); - if(it != mMethods.end()) - { - rv = (*it).second->call(params, channels, response); - } - else - { - it = mCallbackMethods.find(method); - if(it == mCallbackMethods.end()) - { - // method not found. - std::ostringstream message; - message << "rpc server unable to find method: " << method; - buildFault( - channels, - response, - FAULT_METHOD_NOT_FOUND, - message.str()); - } - else - { - // we found it in the callback methods - tell the process - // to coordinate calling on the pump callback. - return ESDRPCS_CALLBACK; - } - } - return rv; -} - -// virtual -ESDRPCSStatus LLSDRPCServer::callbackMethod( - const std::string& method, - const LLSD& params, - const LLChannelDescriptors& channels, - LLBufferArray* response) -{ - // Try to find the method in the callback method table. - ESDRPCSStatus rv = ESDRPCS_DONE; - method_map_t::iterator it = mCallbackMethods.find(method); - if(it != mCallbackMethods.end()) - { - rv = (*it).second->call(params, channels, response); - } - else - { - std::ostringstream message; - message << "pcserver unable to find callback method: " << method; - buildFault( - channels, - response, - FAULT_METHOD_NOT_FOUND, - message.str()); - } - return rv; -} - -// static -void LLSDRPCServer::buildFault( - const LLChannelDescriptors& channels, - LLBufferArray* data, - S32 code, - const std::string& msg) -{ - LLBufferStream ostr(channels, data); - ostr << FAULT_PART_1 << code << FAULT_PART_2 << msg << FAULT_PART_3; - LL_INFOS() << "LLSDRPCServer::buildFault: " << code << ", " << msg << LL_ENDL; -} - -// static -void LLSDRPCServer::buildResponse( - const LLChannelDescriptors& channels, - LLBufferArray* data, - const LLSD& response) -{ - LLBufferStream ostr(channels, data); - ostr << RESPONSE_PART_1; - LLSDSerialize::toNotation(response, ostr); - ostr << RESPONSE_PART_2; -#if LL_DEBUG - std::ostringstream debug_ostr; - debug_ostr << "LLSDRPCServer::buildResponse: "; - LLSDSerialize::toNotation(response, debug_ostr); - LL_INFOS() << debug_ostr.str() << LL_ENDL; -#endif -} diff --git a/indra/llmessage/llsdrpcserver.h b/indra/llmessage/llsdrpcserver.h deleted file mode 100644 index 415bd31c26..0000000000 --- a/indra/llmessage/llsdrpcserver.h +++ /dev/null @@ -1,360 +0,0 @@ -/** - * @file llsdrpcserver.h - * @author Phoenix - * @date 2005-10-11 - * @brief Declaration of the structured data remote procedure call server. - * - * $LicenseInfo:firstyear=2005&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$ - */ - -#ifndef LL_LLSDRPCSERVER_H -#define LL_LLSDRPCSERVER_H - -/** - * I've set this up to be pretty easy to use when you want to make a - * structured data rpc server which responds to methods by - * name. Derive a class from the LLSDRPCServer, and during - * construction (or initialization if you have the luxury) map method - * names to pointers to member functions. This will look a lot like: - * - * <code> - * class LLMessageAgents : public LLSDRPCServer {<br> - * public:<br> - * typedef LLSDRPCServer<LLUsher> mem_fn_t;<br> - * LLMessageAgents() {<br> - * mMethods["message"] = new mem_fn_t(this, &LLMessageAgents::rpc_IM);<br> - * mMethods["alert"] = new mem_fn_t(this, &LLMessageAgents::rpc_Alrt);<br> - * }<br> - * protected:<br> - * rpc_IM(const LLSD& params, - * const LLChannelDescriptors& channels, - * LLBufferArray* data) - * {...}<br> - * rpc_Alert(const LLSD& params, - * const LLChannelDescriptors& channels, - * LLBufferArray* data) - * {...}<br> - * };<br> - * </code> - * - * The params are an array where each element in the array is a single - * parameter in the call. - * - * It is up to you to pack a valid serialized llsd response into the - * data object passed into the method, but you can use the helper - * methods below to help. - */ - -#include <map> -#include "lliopipe.h" -#include "lliohttpserver.h" -#include "llfiltersd2xmlrpc.h" - -class LLSD; - -/** - * @brief Enumeration for specifying server method call status. This - * enumeration controls how the server class will manage the pump - * process/callback mechanism. - */ -enum ESDRPCSStatus -{ - // The call went ok, but the response is not yet ready. The - // method will arrange for the clearLock() call to be made at - // a later date, after which, once the chain is being pumped - // again, deferredResponse() will be called to gather the result - ESDRPCS_DEFERRED, - - // The LLSDRPCServer would like to handle the method on the - // callback queue of the pump. - ESDRPCS_CALLBACK, - - // The method call finished and generated output. - ESDRPCS_DONE, - - // Method failed for some unspecified reason - you should avoid - // this. A generic fault will be sent to the output. - ESDRPCS_ERROR, - - ESDRPCS_COUNT, -}; - -/** - * @class LLSDRPCMethodCallBase - * @brief Base class for calling a member function in an sd rpcserver - * implementation. - */ -class LLSDRPCMethodCallBase -{ -public: - LLSDRPCMethodCallBase() {} - virtual ~LLSDRPCMethodCallBase() {} - - virtual ESDRPCSStatus call( - const LLSD& params, - const LLChannelDescriptors& channels, - LLBufferArray* response) = 0; -protected: -}; - -/** - * @class LLSDRPCMethodCall - * @brief Class which implements member function calls. - */ -template<class Server> -class LLSDRPCMethodCall : public LLSDRPCMethodCallBase -{ -public: - typedef ESDRPCSStatus (Server::*mem_fn)( - const LLSD& params, - const LLChannelDescriptors& channels, - LLBufferArray* data); - LLSDRPCMethodCall(Server* s, mem_fn fn) : - mServer(s), - mMemFn(fn) - { - } - virtual ~LLSDRPCMethodCall() {} - virtual ESDRPCSStatus call( - const LLSD& params, - const LLChannelDescriptors& channels, - LLBufferArray* data) - { - return (*mServer.*mMemFn)(params, channels, data); - } - -protected: - Server* mServer; - mem_fn mMemFn; - //bool (Server::*mMemFn)(const LLSD& params, LLBufferArray& data); -}; - - -/** - * @class LLSDRPCServer - * @brief Basic implementation of a structure data rpc server - * - * The rpc server is also designed to appropriately straddle the pump - * <code>process()</code> and <code>callback()</code> to specify which - * thread you want to work on when handling a method call. The - * <code>mMethods</code> methods are called from - * <code>process()</code>, while the <code>mCallbackMethods</code> are - * called when a pump is in a <code>callback()</code> cycle. - */ -class LLSDRPCServer : public LLIOPipe -{ -public: - LLSDRPCServer(); - virtual ~LLSDRPCServer(); - - /** - * enumeration for generic fault codes - */ - enum - { - FAULT_BAD_REQUEST = 2000, - FAULT_NO_RESPONSE = 2001, - }; - - /** - * @brief Call this method to return an rpc fault. - * - * @param channel The channel for output on the data buffer - * @param data buffer which will recieve the final output - * @param code The fault code - * @param msg The fault message - */ - static void buildFault( - const LLChannelDescriptors& channels, - LLBufferArray* data, - S32 code, - const std::string& msg); - - /** - * @brief Call this method to build an rpc response. - * - * @param channel The channel for output on the data buffer - * @param data buffer which will recieve the final output - * @param response The return value from the method call - */ - static void buildResponse( - const LLChannelDescriptors& channels, - LLBufferArray* data, - const LLSD& response); - -protected: - /* @name LLIOPipe virtual implementations - */ - //@{ - /** - * @brief Process the data in buffer - */ - virtual EStatus process_impl( - const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, - bool& eos, - LLSD& context, - LLPumpIO* pump); - //@} - -protected: - - /** - * @brief Enumeration to track the state of the rpc server instance - */ - enum EState - { - STATE_NONE, - STATE_CALLBACK, - STATE_DEFERRED, - STATE_DONE - }; - - /** - * @brief This method is called when an http post comes in. - * - * The default behavior is to look at the method name, look up the - * method in the method table, and call it. If the method is not - * found, this function will build a fault response. You can - * implement your own version of this function if you want to hard - * wire some behavior or optimize things a bit. - * @param method The method name being called - * @param params The parameters - * @param channel The channel for output on the data buffer - * @param data The http data - * @return Returns the status of the method call, done/deferred/etc - */ - virtual ESDRPCSStatus callMethod( - const std::string& method, - const LLSD& params, - const LLChannelDescriptors& channels, - LLBufferArray* data); - - /** - * @brief This method is called when a pump callback is processed. - * - * The default behavior is to look at the method name, look up the - * method in the callback method table, and call it. If the method - * is not found, this function will build a fault response. You - * can implement your own version of this function if you want to - * hard wire some behavior or optimize things a bit. - * @param method The method name being called - * @param params The parameters - * @param channel The channel for output on the data buffer - * @param data The http data - * @return Returns the status of the method call, done/deferred/etc - */ - virtual ESDRPCSStatus callbackMethod( - const std::string& method, - const LLSD& params, - const LLChannelDescriptors& channels, - LLBufferArray* data); - - /** - * @brief Called after a deferred service is unlocked - * - * If a method returns ESDRPCS_DEFERRED, then the service chain - * will be locked and not processed until some other system calls - * clearLock() on the service instance again. At that point, - * once the pump starts processing the chain again, this method - * will be called so the service can output the final result - * into the buffers. - */ - virtual ESDRPCSStatus deferredResponse( - const LLChannelDescriptors& channels, - LLBufferArray* data); - - // donovan put this public here 7/27/06 -public: - /** - * @brief unlock a service that as ESDRPCS_DEFERRED - */ - void clearLock(); - -protected: - EState mState; - LLSD mRequest; - LLPumpIO* mPump; - S32 mLock; - typedef std::map<std::string, LLSDRPCMethodCallBase*> method_map_t; - method_map_t mMethods; - method_map_t mCallbackMethods; -}; - -/** - * @name Helper Templates for making LLHTTPNodes - * - * These templates help in creating nodes for handing a service from - * either SDRPC or XMLRPC, given a single implementation of LLSDRPCServer. - * - * To use it: - * \code - * class LLUsefulServer : public LLSDRPCServer { ... } - * - * LLHTTPNode& root = LLCreateHTTPWireServer(...); - * root.addNode("llsdrpc/useful", new LLSDRPCNode<LLUsefulServer>); - * root.addNode("xmlrpc/useful", new LLXMLRPCNode<LLUsefulServer>); - * \endcode - */ -//@{ - -template<class Server> -class LLSDRPCServerFactory : public LLChainIOFactory -{ -public: - virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const - { - LL_DEBUGS() << "LLXMLSDRPCServerFactory::build" << LL_ENDL; - chain.push_back(LLIOPipe::ptr_t(new Server)); - return true; - } -}; - -template<class Server> -class LLSDRPCNode : public LLHTTPNodeForFactory< - LLSDRPCServerFactory<Server> > -{ -}; - -template<class Server> -class LLXMLRPCServerFactory : public LLChainIOFactory -{ -public: - virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const - { - LL_DEBUGS() << "LLXMLSDRPCServerFactory::build" << LL_ENDL; - chain.push_back(LLIOPipe::ptr_t(new LLFilterXMLRPCRequest2LLSD)); - chain.push_back(LLIOPipe::ptr_t(new Server)); - chain.push_back(LLIOPipe::ptr_t(new LLFilterSD2XMLRPCResponse)); - return true; - } -}; - -template<class Server> -class LLXMLRPCNode : public LLHTTPNodeForFactory< - LLXMLRPCServerFactory<Server> > -{ -}; - -//@} - -#endif // LL_LLSDRPCSERVER_H diff --git a/indra/llmessage/lltrustedmessageservice.cpp b/indra/llmessage/lltrustedmessageservice.cpp index 5bd1112cfe..33944f7883 100644 --- a/indra/llmessage/lltrustedmessageservice.cpp +++ b/indra/llmessage/lltrustedmessageservice.cpp @@ -30,6 +30,7 @@ #include "llhost.h" #include "llmessageconfig.h" #include "message.h" +#include "llhttpconstants.h" bool LLTrustedMessageService::validate(const std::string& name, LLSD& context) diff --git a/indra/llmessage/llurlrequest.cpp b/indra/llmessage/llurlrequest.cpp deleted file mode 100644 index 1294379eca..0000000000 --- a/indra/llmessage/llurlrequest.cpp +++ /dev/null @@ -1,783 +0,0 @@ -/** - * @file llurlrequest.cpp - * @author Phoenix - * @date 2005-04-28 - * @brief Implementation of the URLRequest class and related classes. - * - * $LicenseInfo:firstyear=2005&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 "llurlrequest.h" - -#include <algorithm> -#include <openssl/x509_vfy.h> -#include <openssl/ssl.h> -#include "llcurl.h" -#include "llfasttimer.h" -#include "llioutil.h" -#include "llproxy.h" -#include "llpumpio.h" -#include "llsd.h" -#include "llstring.h" -#include "apr_env.h" -#include "llapr.h" - -/** - * String constants - */ -const std::string CONTEXT_DEST_URI_SD_LABEL("dest_uri"); -const std::string CONTEXT_TRANSFERED_BYTES("transfered_bytes"); - -// These are defined in llhttpnode.h/llhttpnode.cpp -extern const std::string CONTEXT_REQUEST; -extern const std::string CONTEXT_RESPONSE; - -static size_t headerCallback(void* data, size_t size, size_t nmemb, void* user); - -/** - * class LLURLRequestDetail - */ -class LLURLRequestDetail -{ -public: - LLURLRequestDetail(); - ~LLURLRequestDetail(); - std::string mURL; - LLCurlEasyRequest* mCurlRequest; - LLIOPipe::buffer_ptr_t mResponseBuffer; - LLChannelDescriptors mChannels; - U8* mLastRead; - U32 mBodyLimit; - S32 mByteAccumulator; - bool mIsBodyLimitSet; - LLURLRequest::SSLCertVerifyCallback mSSLVerifyCallback; -}; - -LLURLRequestDetail::LLURLRequestDetail() : - mCurlRequest(NULL), - mLastRead(NULL), - mBodyLimit(0), - mByteAccumulator(0), - mIsBodyLimitSet(false), - mSSLVerifyCallback(NULL) -{ - mCurlRequest = new LLCurlEasyRequest(); - - if(!mCurlRequest->isValid()) //failed. - { - delete mCurlRequest ; - mCurlRequest = NULL ; - } -} - -LLURLRequestDetail::~LLURLRequestDetail() -{ - delete mCurlRequest; - mLastRead = NULL; -} - -void LLURLRequest::setSSLVerifyCallback(SSLCertVerifyCallback callback, void *param) -{ - mDetail->mSSLVerifyCallback = callback; - mDetail->mCurlRequest->setSSLCtxCallback(LLURLRequest::_sslCtxCallback, (void *)this); - mDetail->mCurlRequest->setopt(CURLOPT_SSL_VERIFYPEER, true); - mDetail->mCurlRequest->setopt(CURLOPT_SSL_VERIFYHOST, 2); -} - - -// _sslCtxFunction -// Callback function called when an SSL Context is created via CURL -// used to configure the context for custom cert validation - -CURLcode LLURLRequest::_sslCtxCallback(CURL * curl, void *sslctx, void *param) -{ - LLURLRequest *req = (LLURLRequest *)param; - if(req == NULL || req->mDetail->mSSLVerifyCallback == NULL) - { - SSL_CTX_set_cert_verify_callback((SSL_CTX *)sslctx, NULL, NULL); - return CURLE_OK; - } - SSL_CTX * ctx = (SSL_CTX *) sslctx; - // disable any default verification for server certs - SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL); - // set the verification callback. - SSL_CTX_set_cert_verify_callback(ctx, req->mDetail->mSSLVerifyCallback, (void *)req); - // the calls are void - return CURLE_OK; - -} - -/** - * class LLURLRequest - */ - - -LLURLRequest::LLURLRequest(EHTTPMethod action, bool follow_redirects /* = true */) : - mAction(action), - mFollowRedirects(follow_redirects) -{ - initialize(); -} - -LLURLRequest::LLURLRequest( - EHTTPMethod action, - const std::string& url, - bool follow_redirects /* = true */) : - mAction(action), - mFollowRedirects(follow_redirects) -{ - initialize(); - setURL(url); -} - -LLURLRequest::~LLURLRequest() -{ - delete mDetail; - mDetail = NULL ; -} - -void LLURLRequest::setURL(const std::string& url) -{ - mDetail->mURL = url; - if (url.empty()) - { - LL_WARNS() << "empty URL specified" << LL_ENDL; - } -} - -const std::string& LLURLRequest::getURL() const -{ - return mDetail->mURL; -} - -void LLURLRequest::addHeader(const std::string& header, const std::string& value /* = "" */) -{ - mDetail->mCurlRequest->slist_append(header, value); -} - -void LLURLRequest::addHeaderRaw(const char* header) -{ - mDetail->mCurlRequest->slist_append(header); -} - -void LLURLRequest::setBodyLimit(U32 size) -{ - mDetail->mBodyLimit = size; - mDetail->mIsBodyLimitSet = true; -} - -void LLURLRequest::setCallback(LLURLRequestComplete* callback) -{ - mCompletionCallback = callback; - mDetail->mCurlRequest->setHeaderCallback(&headerCallback, (void*)callback); -} - -// Added to mitigate the effect of libcurl looking -// for the ALL_PROXY and http_proxy env variables -// and deciding to insert a Pragma: no-cache -// header! The only usage of this method at the -// time of this writing is in llhttpclient.cpp -// in the request() method, where this method -// is called with use_proxy = FALSE -void LLURLRequest::useProxy(bool use_proxy) -{ - static char *env_proxy; - - if (use_proxy && (env_proxy == NULL)) - { - apr_status_t status; - LLAPRPool pool; - status = apr_env_get(&env_proxy, "ALL_PROXY", pool.getAPRPool()); - if (status != APR_SUCCESS) - { - status = apr_env_get(&env_proxy, "http_proxy", pool.getAPRPool()); - } - if (status != APR_SUCCESS) - { - use_proxy = FALSE; - } - } - - - LL_DEBUGS() << "use_proxy = " << (use_proxy?'Y':'N') << ", env_proxy = " << (env_proxy ? env_proxy : "(null)") << LL_ENDL; - - if (env_proxy && use_proxy) - { - mDetail->mCurlRequest->setoptString(CURLOPT_PROXY, env_proxy); - } - else - { - mDetail->mCurlRequest->setoptString(CURLOPT_PROXY, ""); - } -} - -void LLURLRequest::useProxy(const std::string &proxy) -{ - mDetail->mCurlRequest->setoptString(CURLOPT_PROXY, proxy); -} - -void LLURLRequest::allowCookies() -{ - mDetail->mCurlRequest->setoptString(CURLOPT_COOKIEFILE, ""); -} - -//virtual -bool LLURLRequest::isValid() -{ - return mDetail->mCurlRequest && mDetail->mCurlRequest->isValid(); -} - -// virtual -LLIOPipe::EStatus LLURLRequest::handleError( - LLIOPipe::EStatus status, - LLPumpIO* pump) -{ - if(!isValid()) - { - return STATUS_EXPIRED ; - } - - if(mCompletionCallback && pump) - { - LLURLRequestComplete* complete = NULL; - complete = (LLURLRequestComplete*)mCompletionCallback.get(); - complete->httpStatus( - HTTP_INTERNAL_ERROR, - LLIOPipe::lookupStatusString(status)); - complete->responseStatus(status); - pump->respond(complete); - mCompletionCallback = NULL; - } - return status; -} - -static LLTrace::BlockTimerStatHandle FTM_PROCESS_URL_REQUEST("URL Request"); -static LLTrace::BlockTimerStatHandle FTM_PROCESS_URL_REQUEST_GET_RESULT("Get Result"); -static LLTrace::BlockTimerStatHandle FTM_URL_PERFORM("Perform"); -static LLTrace::BlockTimerStatHandle FTM_PROCESS_URL_PUMP_RESPOND("Pump Respond"); -static LLTrace::BlockTimerStatHandle FTM_URL_ADJUST_TIMEOUT("Adjust Timeout"); - -// virtual -LLIOPipe::EStatus LLURLRequest::process_impl( - const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, - bool& eos, - LLSD& context, - LLPumpIO* pump) -{ - LL_RECORD_BLOCK_TIME(FTM_PROCESS_URL_REQUEST); - PUMP_DEBUG; - //LL_INFOS() << "LLURLRequest::process_impl()" << LL_ENDL; - if (!buffer) return STATUS_ERROR; - - // we're still waiting or prcessing, check how many - // bytes we have accumulated. - const S32 MIN_ACCUMULATION = 100000; - if(pump && (mDetail->mByteAccumulator > MIN_ACCUMULATION)) - { - LL_RECORD_BLOCK_TIME(FTM_URL_ADJUST_TIMEOUT); - // This is a pretty sloppy calculation, but this - // tries to make the gross assumption that if data - // is coming in at 56kb/s, then this transfer will - // probably succeed. So, if we're accumlated - // 100,000 bytes (MIN_ACCUMULATION) then let's - // give this client another 2s to complete. - const F32 TIMEOUT_ADJUSTMENT = 2.0f; - mDetail->mByteAccumulator = 0; - pump->adjustTimeoutSeconds(TIMEOUT_ADJUSTMENT); - LL_DEBUGS() << "LLURLRequest adjustTimeoutSeconds for request: " << mDetail->mURL << LL_ENDL; - if (mState == STATE_INITIALIZED) - { - LL_INFOS() << "LLURLRequest adjustTimeoutSeconds called during upload" << LL_ENDL; - } - } - - switch(mState) - { - case STATE_INITIALIZED: - { - PUMP_DEBUG; - // We only need to wait for input if we are uploading - // something. - if(((HTTP_PUT == mAction) || (HTTP_POST == mAction)) && !eos) - { - // we're waiting to get all of the information - return STATUS_BREAK; - } - - // *FIX: bit of a hack, but it should work. The configure and - // callback method expect this information to be ready. - mDetail->mResponseBuffer = buffer; - mDetail->mChannels = channels; - if(!configure()) - { - return STATUS_ERROR; - } - mState = STATE_WAITING_FOR_RESPONSE; - - // *FIX: Maybe we should just go to the next state now... - return STATUS_BREAK; - } - case STATE_WAITING_FOR_RESPONSE: - case STATE_PROCESSING_RESPONSE: - { - PUMP_DEBUG; - LLIOPipe::EStatus status = STATUS_BREAK; - { - LL_RECORD_BLOCK_TIME(FTM_URL_PERFORM); - if(!mDetail->mCurlRequest->wait()) - { - return status ; - } - } - - bool keep_looping = true; - while(keep_looping) - { - CURLcode result; - - bool newmsg = false; - { - LL_RECORD_BLOCK_TIME(FTM_PROCESS_URL_REQUEST_GET_RESULT); - newmsg = mDetail->mCurlRequest->getResult(&result); - } - - if(!newmsg) - { - // keep processing - break; - } - - - mState = STATE_HAVE_RESPONSE; - context[CONTEXT_REQUEST][CONTEXT_TRANSFERED_BYTES] = mRequestTransferedBytes; - context[CONTEXT_RESPONSE][CONTEXT_TRANSFERED_BYTES] = mResponseTransferedBytes; - LL_DEBUGS() << this << "Setting context to " << context << LL_ENDL; - switch(result) - { - case CURLE_OK: - case CURLE_WRITE_ERROR: - // NB: The error indication means that we stopped the - // writing due the body limit being reached - if(mCompletionCallback && pump) - { - LLURLRequestComplete* complete = NULL; - complete = (LLURLRequestComplete*) - mCompletionCallback.get(); - complete->responseStatus( - result == CURLE_OK - ? STATUS_OK : STATUS_STOP); - LLPumpIO::links_t chain; - LLPumpIO::LLLinkInfo link; - link.mPipe = mCompletionCallback; - link.mChannels = LLBufferArray::makeChannelConsumer( - channels); - chain.push_back(link); - { - LL_RECORD_BLOCK_TIME(FTM_PROCESS_URL_PUMP_RESPOND); - pump->respond(chain, buffer, context); - } - mCompletionCallback = NULL; - } - break; - case CURLE_FAILED_INIT: - case CURLE_COULDNT_CONNECT: - status = STATUS_NO_CONNECTION; - keep_looping = false; - break; - default: // CURLE_URL_MALFORMAT - LL_WARNS() << "URLRequest Error: " << result - << ", " - << LLCurl::strerror(result) - << ", " - << (mDetail->mURL.empty() ? "<EMPTY URL>" : mDetail->mURL) - << LL_ENDL; - status = STATUS_ERROR; - keep_looping = false; - break; - } - } - return status; - } - case STATE_HAVE_RESPONSE: - PUMP_DEBUG; - // we already stuffed everything into channel in in the curl - // callback, so we are done. - eos = true; - context[CONTEXT_REQUEST][CONTEXT_TRANSFERED_BYTES] = mRequestTransferedBytes; - context[CONTEXT_RESPONSE][CONTEXT_TRANSFERED_BYTES] = mResponseTransferedBytes; - LL_DEBUGS() << this << "Setting context to " << context << LL_ENDL; - return STATUS_DONE; - - default: - PUMP_DEBUG; - context[CONTEXT_REQUEST][CONTEXT_TRANSFERED_BYTES] = mRequestTransferedBytes; - context[CONTEXT_RESPONSE][CONTEXT_TRANSFERED_BYTES] = mResponseTransferedBytes; - LL_DEBUGS() << this << "Setting context to " << context << LL_ENDL; - return STATUS_ERROR; - } -} - -void LLURLRequest::initialize() -{ - mState = STATE_INITIALIZED; - mDetail = new LLURLRequestDetail; - - if(!isValid()) - { - return ; - } - - mDetail->mCurlRequest->setopt(CURLOPT_NOSIGNAL, 1); - mDetail->mCurlRequest->setWriteCallback(&downCallback, (void*)this); - mDetail->mCurlRequest->setReadCallback(&upCallback, (void*)this); - mRequestTransferedBytes = 0; - mResponseTransferedBytes = 0; -} - -static LLTrace::BlockTimerStatHandle FTM_URL_REQUEST_CONFIGURE("URL Configure"); -bool LLURLRequest::configure() -{ - LL_RECORD_BLOCK_TIME(FTM_URL_REQUEST_CONFIGURE); - - bool rv = false; - S32 bytes = mDetail->mResponseBuffer->countAfter( - mDetail->mChannels.in(), - NULL); - switch(mAction) - { - case HTTP_HEAD: - mDetail->mCurlRequest->setopt(CURLOPT_HEADER, 1); - mDetail->mCurlRequest->setopt(CURLOPT_NOBODY, 1); - if (mFollowRedirects) - { - mDetail->mCurlRequest->setopt(CURLOPT_FOLLOWLOCATION, 1); - } - rv = true; - break; - case HTTP_GET: - mDetail->mCurlRequest->setopt(CURLOPT_HTTPGET, 1); - if (mFollowRedirects) - { - mDetail->mCurlRequest->setopt(CURLOPT_FOLLOWLOCATION, 1); - } - - // Set Accept-Encoding to allow response compression - mDetail->mCurlRequest->setoptString(CURLOPT_ENCODING, ""); - rv = true; - break; - - case HTTP_PUT: - // Disable the expect http 1.1 extension. POST and PUT default - // to turning this on, and I am not too sure what it means. - addHeader(HTTP_OUT_HEADER_EXPECT); - - mDetail->mCurlRequest->setopt(CURLOPT_UPLOAD, 1); - mDetail->mCurlRequest->setopt(CURLOPT_INFILESIZE, bytes); - rv = true; - break; - - case HTTP_PATCH: - // Disable the expect http 1.1 extension. POST and PUT default - // to turning this on, and I am not too sure what it means. - addHeader(HTTP_OUT_HEADER_EXPECT); - - mDetail->mCurlRequest->setopt(CURLOPT_UPLOAD, 1); - mDetail->mCurlRequest->setopt(CURLOPT_INFILESIZE, bytes); - mDetail->mCurlRequest->setoptString(CURLOPT_CUSTOMREQUEST, "PATCH"); - rv = true; - break; - - case HTTP_POST: - // Disable the expect http 1.1 extension. POST and PUT default - // to turning this on, and I am not too sure what it means. - addHeader(HTTP_OUT_HEADER_EXPECT); - - // Disable the content type http header. - // *FIX: what should it be? - addHeader(HTTP_OUT_HEADER_CONTENT_TYPE); - - // Set the handle for an http post - mDetail->mCurlRequest->setPost(NULL, bytes); - - // Set Accept-Encoding to allow response compression - mDetail->mCurlRequest->setoptString(CURLOPT_ENCODING, ""); - rv = true; - break; - - case HTTP_DELETE: - // Set the handle for an http delete - mDetail->mCurlRequest->setoptString(CURLOPT_CUSTOMREQUEST, "DELETE"); - rv = true; - break; - - case HTTP_COPY: - // Set the handle for an http copy - mDetail->mCurlRequest->setoptString(CURLOPT_CUSTOMREQUEST, "COPY"); - rv = true; - break; - - case HTTP_MOVE: - // Set the handle for an http move - mDetail->mCurlRequest->setoptString(CURLOPT_CUSTOMREQUEST, "MOVE"); - // *NOTE: should we check for the Destination header? - rv = true; - break; - - default: - LL_WARNS() << "Unhandled URLRequest action: " << mAction << LL_ENDL; - break; - } - if(rv) - { - mDetail->mCurlRequest->sendRequest(mDetail->mURL); - } - return rv; -} - -// static -size_t LLURLRequest::downCallback( - char* data, - size_t size, - size_t nmemb, - void* user) -{ - LLURLRequest* req = (LLURLRequest*)user; - if(STATE_WAITING_FOR_RESPONSE == req->mState) - { - req->mState = STATE_PROCESSING_RESPONSE; - } - U32 bytes = size * nmemb; - if (req->mDetail->mIsBodyLimitSet) - { - if (bytes > req->mDetail->mBodyLimit) - { - bytes = req->mDetail->mBodyLimit; - req->mDetail->mBodyLimit = 0; - } - else - { - req->mDetail->mBodyLimit -= bytes; - } - } - - req->mDetail->mResponseBuffer->append( - req->mDetail->mChannels.out(), - (U8*)data, - bytes); - req->mResponseTransferedBytes += bytes; - req->mDetail->mByteAccumulator += bytes; - return bytes; -} - -// static -size_t LLURLRequest::upCallback( - char* data, - size_t size, - size_t nmemb, - void* user) -{ - LLURLRequest* req = (LLURLRequest*)user; - S32 bytes = llmin( - (S32)(size * nmemb), - req->mDetail->mResponseBuffer->countAfter( - req->mDetail->mChannels.in(), - req->mDetail->mLastRead)); - req->mDetail->mLastRead = req->mDetail->mResponseBuffer->readAfter( - req->mDetail->mChannels.in(), - req->mDetail->mLastRead, - (U8*)data, - bytes); - req->mRequestTransferedBytes += bytes; - return bytes; -} - -static size_t headerCallback(void* data, size_t size, size_t nmemb, void* user) -{ - const char* header_line = (const char*)data; - size_t header_len = size * nmemb; - LLURLRequestComplete* complete = (LLURLRequestComplete*)user; - - if (!complete || !header_line) - { - return header_len; - } - - // *TODO: This should be a utility in llstring.h: isascii() - for (size_t i = 0; i < header_len; ++i) - { - if (header_line[i] < 0) - { - return header_len; - } - } - - std::string header(header_line, header_len); - - // Per HTTP spec the first header line must be the status line. - if (header.substr(0,5) == "HTTP/") - { - std::string::iterator end = header.end(); - std::string::iterator pos1 = std::find(header.begin(), end, ' '); - if (pos1 != end) ++pos1; - std::string::iterator pos2 = std::find(pos1, end, ' '); - if (pos2 != end) ++pos2; - std::string::iterator pos3 = std::find(pos2, end, '\r'); - - std::string version(header.begin(), pos1); - std::string status(pos1, pos2); - std::string reason(pos2, pos3); - - S32 status_code = atoi(status.c_str()); - if (status_code > 0) - { - complete->httpStatus(status_code, reason); - return header_len; - } - } - - std::string::iterator sep = std::find(header.begin(),header.end(),':'); - - if (sep != header.end()) - { - std::string key(header.begin(), sep); - std::string value(sep + 1, header.end()); - - key = utf8str_tolower(utf8str_trim(key)); - value = utf8str_trim(value); - - complete->header(key, value); - } - else - { - LLStringUtil::trim(header); - if (!header.empty()) - { - LL_WARNS() << "Unable to parse header: " << header << LL_ENDL; - } - } - - return header_len; -} - -static LLTrace::BlockTimerStatHandle FTM_PROCESS_URL_EXTRACTOR("URL Extractor"); -/** - * LLContextURLExtractor - */ -// virtual -LLIOPipe::EStatus LLContextURLExtractor::process_impl( - const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, - bool& eos, - LLSD& context, - LLPumpIO* pump) -{ - LL_RECORD_BLOCK_TIME(FTM_PROCESS_URL_EXTRACTOR); - PUMP_DEBUG; - // The destination host is in the context. - if(context.isUndefined() || !mRequest) - { - return STATUS_PRECONDITION_NOT_MET; - } - - // copy in to out, since this just extract the URL and does not - // actually change the data. - LLChangeChannel change(channels.in(), channels.out()); - std::for_each(buffer->beginSegment(), buffer->endSegment(), change); - - // find the context url - if(context.has(CONTEXT_DEST_URI_SD_LABEL)) - { - mRequest->setURL(context[CONTEXT_DEST_URI_SD_LABEL].asString()); - return STATUS_DONE; - } - return STATUS_ERROR; -} - - -/** - * LLURLRequestComplete - */ -LLURLRequestComplete::LLURLRequestComplete() : - mRequestStatus(LLIOPipe::STATUS_ERROR) -{ -} - -// virtual -LLURLRequestComplete::~LLURLRequestComplete() -{ -} - -//virtual -void LLURLRequestComplete::header(const std::string& header, const std::string& value) -{ -} - -//virtual -void LLURLRequestComplete::complete(const LLChannelDescriptors& channels, - const buffer_ptr_t& buffer) -{ - if(STATUS_OK == mRequestStatus) - { - response(channels, buffer); - } - else - { - noResponse(); - } -} - -//virtual -void LLURLRequestComplete::response(const LLChannelDescriptors& channels, - const buffer_ptr_t& buffer) -{ - LL_WARNS() << "LLURLRequestComplete::response default implementation called" - << LL_ENDL; -} - -//virtual -void LLURLRequestComplete::noResponse() -{ - LL_WARNS() << "LLURLRequestComplete::noResponse default implementation called" - << LL_ENDL; -} - -void LLURLRequestComplete::responseStatus(LLIOPipe::EStatus status) -{ - mRequestStatus = status; -} - -static LLTrace::BlockTimerStatHandle FTM_PROCESS_URL_COMPLETE("URL Complete"); -// virtual -LLIOPipe::EStatus LLURLRequestComplete::process_impl( - const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, - bool& eos, - LLSD& context, - LLPumpIO* pump) -{ - LL_RECORD_BLOCK_TIME(FTM_PROCESS_URL_COMPLETE); - PUMP_DEBUG; - complete(channels, buffer); - return STATUS_OK; -} diff --git a/indra/llmessage/llurlrequest.h b/indra/llmessage/llurlrequest.h deleted file mode 100644 index 88fccd4bf6..0000000000 --- a/indra/llmessage/llurlrequest.h +++ /dev/null @@ -1,357 +0,0 @@ -/** - * @file llurlrequest.h - * @author Phoenix - * @date 2005-04-21 - * @brief Declaration of url based requests on pipes. - * - * $LicenseInfo:firstyear=2005&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$ - */ - -#ifndef LL_LLURLREQUEST_H -#define LL_LLURLREQUEST_H - -/** - * This file holds the declaration of useful classes for dealing with - * url based client requests. - */ - -#include <string> -#include "lliopipe.h" -#include "llchainio.h" -#include "llerror.h" -#include <openssl/x509_vfy.h> -#include "llcurl.h" - - -/** - * External constants - */ -extern const std::string CONTEXT_DEST_URI_SD_LABEL; -extern const std::string CONTEXT_TRANSFERED_BYTES; - -class LLURLRequestDetail; - -class LLURLRequestComplete; - -/** - * @class LLURLRequest - * @brief Class to handle url based requests. - * @see LLIOPipe - * - * Currently, this class is implemented on top of curl. From the - * vantage of a programmer using this class, you do not care so much, - * but it's useful to know since in order to accomplish 'non-blocking' - * behavior, we have to use a more expensive curl interface which can - * still block if the server enters a half-accepted state. It would be - * worth the time and effort to eventually port this to a raw client - * socket. - */ -class LLURLRequest : public LLIOPipe -{ - LOG_CLASS(LLURLRequest); -public: - typedef int (* SSLCertVerifyCallback)(X509_STORE_CTX *ctx, void *param); - - /** - * @brief Constructor. - * - * @param action One of the EHTTPMethod enumerations. - */ - LLURLRequest(EHTTPMethod action, bool follow_redirects = true); - - /** - * @brief Constructor. - * - * @param action One of the EHTTPMethod enumerations. - * @param url The url of the request. It should already be encoded. - */ - LLURLRequest(EHTTPMethod action, const std::string& url, bool follow_redirects = true); - - /** - * @brief Destructor. - */ - virtual ~LLURLRequest(); - - /* @name Instance methods - */ - //@{ - /** - * @brief Set the url for the request - * - * This method assumes the url is encoded appropriately for the - * request. - * The url must be set somehow before the first call to process(), - * or the url will not be set correctly. - * - */ - void setURL(const std::string& url); - const std::string& getURL() const; - /** - * @brief Add a header to the http post. - * - * This provides a raw interface if you know what kind of request you - * will be making during construction of this instance. All - * required headers will be automatically constructed, so this is - * usually useful for encoding parameters. - */ - void addHeader(const std::string& header, const std::string& value = ""); - void addHeaderRaw(const char* header); - - /** - * @brief Check remote server certificate signed by a known root CA. - * - * Set whether request will check that remote server - * certificates are signed by a known root CA when using HTTPS. - */ - void setSSLVerifyCallback(SSLCertVerifyCallback callback, void * param); - - - /** - * @brief Return at most size bytes of body. - * - * If the body had more bytes than this limit, they will not be - * returned and the connection closed. In this case, STATUS_STOP - * will be passed to responseStatus(); - */ - void setBodyLimit(U32 size); - - /** - * @brief Set a completion callback for this URLRequest. - * - * The callback is added to this URLRequet's pump when either the - * entire buffer is known or an error like timeout or connection - * refused has happened. In the case of a complete transfer, this - * object builds a response chain such that the callback and the - * next process consumer get to read the output. - * - * This setup is a little fragile since the url request consumer - * might not just read the data - it may do a channel change, - * which invalidates the input to the callback, but it works well - * in practice. - */ - void setCallback(LLURLRequestComplete* callback); - //@} - - /* @name LLIOPipe virtual implementations - */ - - /** - * @ brief Turn off (or on) the CURLOPT_PROXY header. - */ - void useProxy(bool use_proxy); - - /** - * @ brief Set the CURLOPT_PROXY header to the given value. - */ - void useProxy(const std::string& proxy); - - /** - * @brief Turn on cookie handling for this request with CURLOPT_COOKIEFILE. - */ - void allowCookies(); - - /*virtual*/ bool isValid() ; - -public: - /** - * @brief Give this pipe a chance to handle a generated error - */ - virtual EStatus handleError(EStatus status, LLPumpIO* pump); - - -protected: - /** - * @brief Process the data in buffer - */ - virtual EStatus process_impl( - const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, - bool& eos, - LLSD& context, - LLPumpIO* pump); - //@} - -protected: - enum EState - { - STATE_INITIALIZED, - STATE_WAITING_FOR_RESPONSE, - STATE_PROCESSING_RESPONSE, - STATE_HAVE_RESPONSE, - }; - EState mState; - EHTTPMethod mAction; - bool mFollowRedirects; - LLURLRequestDetail* mDetail; - LLIOPipe::ptr_t mCompletionCallback; - S32 mRequestTransferedBytes; - S32 mResponseTransferedBytes; - - static CURLcode _sslCtxCallback(CURL * curl, void *sslctx, void *param); - -private: - /** - * @brief Initialize the object. Called during construction. - */ - void initialize(); - - /** - * @brief Handle action specific url request configuration. - * - * @return Returns true if this is configured. - */ - bool configure(); - - /** - * @brief Download callback method. - */ - static size_t downCallback( - char* data, - size_t size, - size_t nmemb, - void* user); - - /** - * @brief Upload callback method. - */ - static size_t upCallback( - char* data, - size_t size, - size_t nmemb, - void* user); - - /** - * @brief Declaration of unimplemented method to prevent copy - * construction. - */ - LLURLRequest(const LLURLRequest&); -}; - - -/** - * @class LLContextURLExtractor - * @brief This class unpacks the url out of a agent usher service so - * it can be packed into a LLURLRequest object. - * @see LLIOPipe - * - * This class assumes that the context is a map that contains an entry - * named CONTEXT_DEST_URI_SD_LABEL. - */ -class LLContextURLExtractor : public LLIOPipe -{ -public: - LLContextURLExtractor(LLURLRequest* req) : mRequest(req) {} - ~LLContextURLExtractor() {} - -protected: - /* @name LLIOPipe virtual implementations - */ - //@{ - /** - * @brief Process the data in buffer - */ - virtual EStatus process_impl( - const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, - bool& eos, - LLSD& context, - LLPumpIO* pump); - //@} - -protected: - LLURLRequest* mRequest; -}; - - -/** - * @class LLURLRequestComplete - * @brief Class which can optionally be used with an LLURLRequest to - * get notification when the url request is complete. - */ -class LLURLRequestComplete : public LLIOPipe -{ -public: - - // Called once for each header received, except status lines - virtual void header(const std::string& header, const std::string& value); - - // May be called more than once, particularly for redirects and proxy madness. - // Ex. a 200 for a connection to https through a proxy, followed by the "real" status - // a 3xx for a redirect followed by a "real" status, or more redirects. - virtual void httpStatus(S32 status, const std::string& reason) { } - - virtual void complete( - const LLChannelDescriptors& channels, - const buffer_ptr_t& buffer); - - /** - * @brief This method is called when we got a valid response. - * - * It is up to class implementers to do something useful here. - */ - virtual void response( - const LLChannelDescriptors& channels, - const buffer_ptr_t& buffer); - - /** - * @brief This method is called if there was no response. - * - * It is up to class implementers to do something useful here. - */ - virtual void noResponse(); - - /** - * @brief This method will be called by the LLURLRequest object. - * - * If this is set to STATUS_OK or STATUS_STOP, then the transfer - * is asssumed to have worked. This will lead to calling response() - * on the next call to process(). Otherwise, this object will call - * noResponse() on the next call to process. - * @param status The status of the URLRequest. - */ - void responseStatus(EStatus status); - - // constructor & destructor. - LLURLRequestComplete(); - virtual ~LLURLRequestComplete(); - -protected: - /* @name LLIOPipe virtual implementations - */ - //@{ - /** - * @brief Process the data in buffer - */ - virtual EStatus process_impl( - const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, - bool& eos, - LLSD& context, - LLPumpIO* pump); - //@} - - // value to note if we actually got the response. This value - // depends on correct usage from the LLURLRequest instance. - EStatus mRequestStatus; -}; - -#endif // LL_LLURLREQUEST_H diff --git a/indra/llmessage/message.cpp b/indra/llmessage/message.cpp index 026f71ff43..cecb2021e7 100644 --- a/indra/llmessage/message.cpp +++ b/indra/llmessage/message.cpp @@ -50,9 +50,7 @@ #include "lldir.h" #include "llerror.h" #include "llfasttimer.h" -#include "llhttpclient.h" #include "llhttpnodeadapter.h" -#include "llhttpsender.h" #include "llmd5.h" #include "llmessagebuilder.h" #include "llmessageconfig.h" @@ -77,6 +75,7 @@ #include "v3math.h" #include "v4math.h" #include "lltransfertargetvfile.h" +#include "llcorehttputil.h" // Constants //const char* MESSAGE_LOG_FILENAME = "message.log"; @@ -97,45 +96,6 @@ public: apr_pollfd_t mPollFD; }; -namespace -{ - class LLFnPtrResponder : public LLHTTPClient::Responder - { - LOG_CLASS(LLFnPtrResponder); - public: - LLFnPtrResponder(void (*callback)(void **,S32), void **callbackData, const std::string& name) : - mCallback(callback), - mCallbackData(callbackData), - mMessageName(name) - { - } - - protected: - virtual void httpFailure() - { - // don't spam when agent communication disconnected already - if (HTTP_GONE != getStatus()) - { - LL_WARNS("Messaging") << "error for message " << mMessageName - << " " << dumpResponse() << LL_ENDL; - } - // TODO: Map status in to useful error code. - if(NULL != mCallback) mCallback(mCallbackData, LL_ERR_TCP_TIMEOUT); - } - - virtual void httpSuccess() - { - if(NULL != mCallback) mCallback(mCallbackData, LL_ERR_NOERR); - } - - private: - - void (*mCallback)(void **,S32); - void **mCallbackData; - std::string mMessageName; - }; -} - class LLMessageHandlerBridge : public LLHTTPNode { virtual bool validate(const std::string& name, LLSD& context) const @@ -1129,29 +1089,6 @@ S32 LLMessageSystem::flushReliable(const LLHost &host) return send_bytes; } -LLHTTPClient::ResponderPtr LLMessageSystem::createResponder(const std::string& name) -{ - if(mSendReliable) - { - return new LLFnPtrResponder( - mReliablePacketParams.mCallback, - mReliablePacketParams.mCallbackData, - name); - } - else - { - // These messages aren't really unreliable, they just weren't - // explicitly sent as reliable, so they don't have a callback -// LL_WARNS("Messaging") << "LLMessageSystem::sendMessage: Sending unreliable " -// << mMessageBuilder->getMessageName() << " message via HTTP" -// << LL_ENDL; - return new LLFnPtrResponder( - NULL, - NULL, - name); - } -} - // This can be called from signal handlers, // so should should not use LL_INFOS(). S32 LLMessageSystem::sendMessage(const LLHost &host) @@ -1216,13 +1153,17 @@ S32 LLMessageSystem::sendMessage(const LLHost &host) if(mMessageBuilder == mLLSDMessageBuilder) { LLSD message = mLLSDMessageBuilder->getMessage(); - - const LLHTTPSender& sender = LLHTTPSender::getSender(host); - sender.send( - host, - mLLSDMessageBuilder->getMessageName(), - message, - createResponder(mLLSDMessageBuilder->getMessageName())); + + UntrustedCallback_t cb = NULL; + if ((mSendReliable) && (mReliablePacketParams.mCallback)) + { + cb = boost::bind(mReliablePacketParams.mCallback, mReliablePacketParams.mCallbackData, _1); + } + + LLCoros::instance().launch("LLMessageSystem::sendUntrustedSimulatorMessageCoro", + boost::bind(&LLMessageSystem::sendUntrustedSimulatorMessageCoro, this, + host.getUntrustedSimulatorCap(), + mLLSDMessageBuilder->getMessageName(), message, cb)); mSendReliable = FALSE; mReliablePacketParams.clear(); @@ -1410,9 +1351,16 @@ S32 LLMessageSystem::sendMessage( return 0; } - const LLHTTPSender& sender = LLHTTPSender::getSender(host); - sender.send(host, name, message, createResponder(name)); - return 1; + UntrustedCallback_t cb = NULL; + if ((mSendReliable) && (mReliablePacketParams.mCallback)) + { + cb = boost::bind(mReliablePacketParams.mCallback, mReliablePacketParams.mCallbackData, _1); + } + + LLCoros::instance().launch("LLMessageSystem::sendUntrustedSimulatorMessageCoro", + boost::bind(&LLMessageSystem::sendUntrustedSimulatorMessageCoro, this, + host.getUntrustedSimulatorCap(), name, message, cb)); + return 1; } void LLMessageSystem::logTrustedMsgFromUntrustedCircuit( const LLHost& host ) @@ -1725,7 +1673,7 @@ LLHost LLMessageSystem::findHost(const U32 circuit_code) } else { - return LLHost::invalid; + return LLHost(); } } @@ -4055,6 +4003,36 @@ const LLHost& LLMessageSystem::getSender() const return mLastSender; } +void LLMessageSystem::sendUntrustedSimulatorMessageCoro(std::string url, std::string message, LLSD body, UntrustedCallback_t callback) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("groupMembersRequest", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpOptions::ptr_t httpOpts = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions); + + + if (url.empty()) + { + LL_WARNS() << "sendUntrustedSimulatorMessageCoro called with empty capability!" << LL_ENDL; + return; + } + + LL_INFOS() << "sendUntrustedSimulatorMessageCoro: message " << message << " to cap " << url << LL_ENDL; + LLSD postData; + postData["message"] = message; + postData["body"] = body; + + LLSD result = httpAdapter->postAndSuspend(httpRequest, url, postData, httpOpts); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if ((callback) && (!callback.empty())) + callback((status) ? LL_ERR_NOERR : LL_ERR_TCP_TIMEOUT); +} + + LLHTTPRegistration<LLHTTPNodeAdapter<LLTrustedMessageService> > gHTTPRegistrationTrustedMessageWildcard("/trusted-message/<message-name>"); diff --git a/indra/llmessage/message.h b/indra/llmessage/message.h index 348b09b992..133db620e6 100644 --- a/indra/llmessage/message.h +++ b/indra/llmessage/message.h @@ -50,7 +50,6 @@ #include "lltimer.h" #include "llpacketring.h" #include "llhost.h" -#include "llcurl.h" #include "llhttpnode.h" //#include "llpacketack.h" #include "llsingleton.h" @@ -60,6 +59,7 @@ #include "llmessagesenderinterface.h" #include "llstoredmessage.h" +#include "boost/function.hpp" const U32 MESSAGE_MAX_STRINGS_LENGTH = 64; const U32 MESSAGE_NUMBER_OF_HASH_BUCKETS = 8192; @@ -489,7 +489,6 @@ public: void (*callback)(void **,S32), void ** callback_data); - LLCurl::ResponderPtr createResponder(const std::string& name); S32 sendMessage(const LLHost &host); S32 sendMessage(const U32 circuit); private: @@ -740,6 +739,9 @@ public: void receivedMessageFromTrustedSender(); private: + typedef boost::function<void(S32)> UntrustedCallback_t; + void sendUntrustedSimulatorMessageCoro(std::string url, std::string message, LLSD body, UntrustedCallback_t callback); + bool mLastMessageFromTrustedMessageService; diff --git a/indra/llmessage/tests/llhttpclientadapter_test.cpp b/indra/llmessage/tests/llhttpclientadapter_test.cpp deleted file mode 100644 index e9ce116bb3..0000000000 --- a/indra/llmessage/tests/llhttpclientadapter_test.cpp +++ /dev/null @@ -1,221 +0,0 @@ -/** - * @file llhttpclientadapter_test.cpp - * @brief Tests for LLHTTPClientAdapter - * - * $LicenseInfo:firstyear=2008&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 "llhttpclientadapter.h" - -#include "../test/lltut.h" -#include "llhttpclient.h" -#include "llcurl_stub.cpp" - -float const HTTP_REQUEST_EXPIRY_SECS = 1.0F; - -std::vector<std::string> get_urls; -std::vector< LLCurl::ResponderPtr > get_responders; -void LLHTTPClient::get(const std::string& url, LLCurl::ResponderPtr responder, const LLSD& headers, const F32 timeout, bool follow_redirects) -{ - get_urls.push_back(url); - get_responders.push_back(responder); -} - -std::vector<std::string> put_urls; -std::vector<LLSD> put_body; -std::vector<LLSD> put_headers; -std::vector<LLCurl::ResponderPtr> put_responders; - -void LLHTTPClient::put(const std::string& url, const LLSD& body, LLCurl::ResponderPtr responder, const LLSD& headers, const F32 timeout) -{ - put_urls.push_back(url); - put_responders.push_back(responder); - put_body.push_back(body); - put_headers.push_back(headers); - -} - -std::vector<std::string> delete_urls; -std::vector<LLCurl::ResponderPtr> delete_responders; - -void LLHTTPClient::del( - const std::string& url, - LLCurl::ResponderPtr responder, - const LLSD& headers, - const F32 timeout) -{ - delete_urls.push_back(url); - delete_responders.push_back(responder); -} - -namespace tut -{ - struct LLHTTPClientAdapterData - { - LLHTTPClientAdapterData() - { - get_urls.clear(); - get_responders.clear(); - put_urls.clear(); - put_responders.clear(); - put_body.clear(); - put_headers.clear(); - delete_urls.clear(); - delete_responders.clear(); - } - }; - - typedef test_group<LLHTTPClientAdapterData> factory; - typedef factory::object object; -} - -namespace -{ - tut::factory tf("LLHTTPClientAdapterData"); -} - -namespace tut -{ - // Ensure we can create the object - template<> template<> - void object::test<1>() - { - LLHTTPClientAdapter adapter; - } - - // Does the get pass the appropriate arguments to the LLHTTPClient - template<> template<> - void object::test<2>() - { - LLHTTPClientAdapter adapter; - - LLCurl::ResponderPtr responder = new LLCurl::Responder(); - - adapter.get("Made up URL", responder); - ensure_equals(get_urls.size(), 1); - ensure_equals(get_urls[0], "Made up URL"); - } - - // Ensure the responder matches the one passed to get - template<> template<> - void object::test<3>() - { - LLHTTPClientAdapter adapter; - LLCurl::ResponderPtr responder = new LLCurl::Responder(); - - adapter.get("Made up URL", responder); - - ensure_equals(get_responders.size(), 1); - ensure_equals(get_responders[0].get(), responder.get()); - } - - // Ensure the correct url is used in the put - template<> template<> - void object::test<4>() - { - LLHTTPClientAdapter adapter; - - LLCurl::ResponderPtr responder = new LLCurl::Responder(); - - LLSD body; - body["TestBody"] = "Foobar"; - - adapter.put("Made up URL", body, responder); - ensure_equals(put_urls.size(), 1); - ensure_equals(put_urls[0], "Made up URL"); - } - - // Ensure the correct responder is used by put - template<> template<> - void object::test<5>() - { - LLHTTPClientAdapter adapter; - - LLCurl::ResponderPtr responder = new LLCurl::Responder(); - - LLSD body; - body["TestBody"] = "Foobar"; - - adapter.put("Made up URL", body, responder); - - ensure_equals(put_responders.size(), 1); - ensure_equals(put_responders[0].get(), responder.get()); - } - - // Ensure the message body is passed through the put properly - template<> template<> - void object::test<6>() - { - LLHTTPClientAdapter adapter; - - LLCurl::ResponderPtr responder = new LLCurl::Responder(); - - LLSD body; - body["TestBody"] = "Foobar"; - - adapter.put("Made up URL", body, responder); - - ensure_equals(put_body.size(), 1); - ensure_equals(put_body[0]["TestBody"].asString(), "Foobar"); - } - - // Ensure that headers are passed through put properly - template<> template<> - void object::test<7>() - { - LLHTTPClientAdapter adapter; - - LLCurl::ResponderPtr responder = new LLCurl::Responder(); - - LLSD body = LLSD::emptyMap(); - body["TestBody"] = "Foobar"; - - LLSD headers = LLSD::emptyMap(); - headers["booger"] = "omg"; - - adapter.put("Made up URL", body, responder, headers); - - ensure_equals("Header count", put_headers.size(), 1); - ensure_equals( - "First header", - put_headers[0]["booger"].asString(), - "omg"); - } - - // Ensure that del() passes appropriate arguments to the LLHTTPClient - template<> template<> - void object::test<8>() - { - LLHTTPClientAdapter adapter; - - LLCurl::ResponderPtr responder = new LLCurl::Responder(); - - adapter.del("Made up URL", responder); - - ensure_equals("URL count", delete_urls.size(), 1); - ensure_equals("Received URL", delete_urls[0], "Made up URL"); - - ensure_equals("Responder count", delete_responders.size(), 1); - //ensure_equals("Responder", delete_responders[0], responder); - } -} - diff --git a/indra/llmessage/tests/llsdmessage_test.cpp b/indra/llmessage/tests/llsdmessage_test.cpp deleted file mode 100644 index 44b024a83f..0000000000 --- a/indra/llmessage/tests/llsdmessage_test.cpp +++ /dev/null @@ -1,130 +0,0 @@ -/** - * @file llsdmessage_test.cpp - * @author Nat Goodspeed - * @date 2008-12-22 - * @brief Test of llsdmessage.h - * - * $LicenseInfo:firstyear=2008&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$ - */ - -#if LL_WINDOWS -#pragma warning (disable : 4675) // "resolved by ADL" -- just as I want! -#endif - -// Precompiled header -#include "linden_common.h" -// associated header -#include "llsdmessage.h" -// STL headers -#include <iostream> -// std headers -#include <stdexcept> -#include <typeinfo> -// external library headers -// other Linden headers -#include "../test/lltut.h" -#include "../test/catch_and_store_what_in.h" -#include "llsdserialize.h" -#include "llevents.h" -#include "stringize.h" -#include "llhost.h" -#include "tests/networkio.h" -#include "tests/commtest.h" - -/***************************************************************************** -* TUT -*****************************************************************************/ -namespace tut -{ - struct llsdmessage_data: public commtest_data - { - LLEventPump& httpPump; - - llsdmessage_data(): - httpPump(pumps.obtain("LLHTTPClient")) - { - LLCurl::initClass(); - LLSDMessage::link(); - } - }; - typedef test_group<llsdmessage_data> llsdmessage_group; - typedef llsdmessage_group::object llsdmessage_object; - llsdmessage_group llsdmgr("llsdmessage"); - - template<> template<> - void llsdmessage_object::test<1>() - { - std::string threw; - // This should fail... - try - { - LLSDMessage localListener; - } - CATCH_AND_STORE_WHAT_IN(threw, LLEventPump::DupPumpName) - ensure("second LLSDMessage should throw", ! threw.empty()); - } - - template<> template<> - void llsdmessage_object::test<2>() - { - LLSD request, body; - body["data"] = "yes"; - request["payload"] = body; - request["reply"] = replyPump.getName(); - request["error"] = errorPump.getName(); - bool threw = false; - try - { - httpPump.post(request); - } - catch (const LLSDMessage::ArgError&) - { - threw = true; - } - ensure("missing URL", threw); - } - - template<> template<> - void llsdmessage_object::test<3>() - { - LLSD request, body; - body["data"] = "yes"; - request["url"] = server + "got-message"; - request["payload"] = body; - request["reply"] = replyPump.getName(); - request["error"] = errorPump.getName(); - httpPump.post(request); - ensure("got response", netio.pump()); - ensure("success response", success); - ensure_equals(result["reply"].asString(), "success"); - - body["status"] = 499; - body["reason"] = "custom error message"; - request["url"] = server + "fail"; - request["payload"] = body; - httpPump.post(request); - ensure("got response", netio.pump()); - ensure("failure response", ! success); - ensure_equals(result["status"].asInteger(), body["status"].asInteger()); - ensure_equals(result["reason"].asString(), body["reason"].asString()); - } -} // namespace tut diff --git a/indra/llmessage/tests/lltesthttpclientadapter.cpp b/indra/llmessage/tests/lltesthttpclientadapter.cpp deleted file mode 100644 index 4539e4a540..0000000000 --- a/indra/llmessage/tests/lltesthttpclientadapter.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/** - * @file - * @brief - * - * $LicenseInfo:firstyear=2008&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 "lltesthttpclientadapter.h" - -LLTestHTTPClientAdapter::LLTestHTTPClientAdapter() -{ -} - -LLTestHTTPClientAdapter::~LLTestHTTPClientAdapter() -{ -} - -void LLTestHTTPClientAdapter::get(const std::string& url, LLCurl::ResponderPtr responder) -{ - mGetUrl.push_back(url); - mGetResponder.push_back(responder); -} - -void LLTestHTTPClientAdapter::put(const std::string& url, const LLSD& body, LLCurl::ResponderPtr responder) -{ - mPutUrl.push_back(url); - mPutBody.push_back(body); - mPutResponder.push_back(responder); -} - -U32 LLTestHTTPClientAdapter::putCalls() const -{ - return mPutUrl.size(); -} - -void LLTestHTTPClientAdapter::get(const std::string& url, LLCurl::ResponderPtr responder, const LLSD& headers) -{ - mGetUrl.push_back(url); - mGetHeaders.push_back(headers); - mGetResponder.push_back(responder); -} - - diff --git a/indra/llmessage/tests/lltesthttpclientadapter.h b/indra/llmessage/tests/lltesthttpclientadapter.h deleted file mode 100644 index c29cbb3a2a..0000000000 --- a/indra/llmessage/tests/lltesthttpclientadapter.h +++ /dev/null @@ -1,57 +0,0 @@ -/** - * @file - * @brief - * - * $LicenseInfo:firstyear=2008&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$ - */ - -/* Macro Definitions */ -#ifndef LL_LLTESTHTTPCLIENTADAPTER_H -#define LL_LLTESTHTTPCLIENTADAPTER_H - - -#include "linden_common.h" -#include "llhttpclientinterface.h" - -class LLTestHTTPClientAdapter : public LLHTTPClientInterface -{ -public: - LLTestHTTPClientAdapter(); - virtual ~LLTestHTTPClientAdapter(); - virtual void get(const std::string& url, LLCurl::ResponderPtr responder); - virtual void get(const std::string& url, LLCurl::ResponderPtr responder, const LLSD& headers); - - virtual void put(const std::string& url, const LLSD& body, LLCurl::ResponderPtr responder); - U32 putCalls() const; - - std::vector<LLSD> mPutBody; - std::vector<LLSD> mGetHeaders; - std::vector<std::string> mPutUrl; - std::vector<std::string> mGetUrl; - std::vector<LLCurl::ResponderPtr> mPutResponder; - std::vector<LLCurl::ResponderPtr> mGetResponder; -}; - - - -#endif //LL_LLSIMULATORPRESENCESENDER_H - |