From f945415210f0e18c2c6d941fda6b7d45cb0f06f1 Mon Sep 17 00:00:00 2001 From: Don Kjer Date: Wed, 13 Mar 2013 06:26:25 +0000 Subject: Large changes to the LLCurl::Responder API, as well as pulling in some changes to common libraries from the server codebase: * Additional error checking in http handlers. * Uniform log spam for http errors. * Switch to using constants for http heads and status codes. * Fixed bugs in incorrectly checking if parsing LLSD xml resulted in an error. * Reduced spam regarding LLSD parsing errors in the default completedRaw http handler. It should not longer be necessary to short-circuit completedRaw to avoid spam. * Ported over a few bug fixes from the server code. * Switch mode http status codes to use S32 instead of U32. * Ported LLSD::asStringRef from server code; avoids copying strings all over the place. * Ported server change to LLSD::asBinary; this always returns a reference now instead of copying the entire binary blob. * Ported server pretty notation format (and pretty binary format) to llsd serialization. * The new LLCurl::Responder API no longer has two error handlers to choose from. Overriding the following methods have been deprecated: ** error - use httpFailure ** errorWithContent - use httpFailure ** result - use httpSuccess ** completed - use httpCompleted ** completedHeader - no longer necessary; call getResponseHeaders() from a completion method to obtain these headers. * In order to 'catch' a completed http request, override one of these methods: ** httpSuccess - Called for any 2xx status code. ** httpFailure - Called for any non-2xx status code. ** httpComplete - Called for all status codes. Default implementation is to call either httpSuccess or httpFailure. * It is recommended to keep these methods protected/private in order to avoid triggering of these methods without using a 'push' method (see below). * Uniform error handling should followed whenever possible by calling a variant of this during httpFailure: ** llwarns << dumpResponse() << llendl; * Be sure to include LOG_CLASS(your_class_name) in your class in order for the log entry to give more context. * In order to 'push' a result into the responder, you should no longer call error, errorWithContent, result, or completed. * Nor should you directly call httpSuccess/Failure/Completed (unless passing a message up to a parent class). * Instead, you can set the internal content of a responder and trigger a corresponding method using the following methods: ** successResult - Sets results and calls httpSuccess ** failureResult - Sets results and calls httpFailure ** completedResult - Sets results and calls httpCompleted * To obtain information about a the response from a reponder method, use the following getters: ** getStatus - HTTP status code ** getReason - Reason string ** getContent - Content (Parsed body LLSD) ** getResponseHeaders - Response Headers (LLSD map) ** getHTTPMethod - HTTP method of the request ** getURL - URL of the request * It is still possible to override completeRaw if you want to manipulate data directly out of LLPumpIO. * See indra/llmessage/llcurl.h for more information. --- indra/llmessage/llhttpconstants.cpp | 219 ++++++++++++++++++++++++++++++++++++ 1 file changed, 219 insertions(+) create mode 100644 indra/llmessage/llhttpconstants.cpp (limited to 'indra/llmessage/llhttpconstants.cpp') diff --git a/indra/llmessage/llhttpconstants.cpp b/indra/llmessage/llhttpconstants.cpp new file mode 100644 index 0000000000..2134024a14 --- /dev/null +++ b/indra/llmessage/llhttpconstants.cpp @@ -0,0 +1,219 @@ +/** + * @file llhttpconstants.cpp + * @brief Implementation of the HTTP request / response constant lookups + * + * $LicenseInfo:firstyear=2013&license=viewergpl$ + * + * Copyright (c) 2013, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "linden_common.h" +#include "llhttpconstants.h" +#include "lltimer.h" + +// for curl_getdate() (apparently parsing RFC 1123 dates is hard) +#include + +const std::string HTTP_HEADER_ACCEPT("Accept"); +const std::string HTTP_HEADER_ACCEPT_CHARSET("Accept-Charset"); +const std::string HTTP_HEADER_ACCEPT_ENCODING("Accept-Encoding"); +const std::string HTTP_HEADER_ACCEPT_LANGUAGE("Accept-Language"); +const std::string HTTP_HEADER_ACCEPT_RANGES("Accept-Ranges"); +const std::string HTTP_HEADER_AGE("Age"); +const std::string HTTP_HEADER_ALLOW("Allow"); +const std::string HTTP_HEADER_AUTHORIZATION("Authorization"); +const std::string HTTP_HEADER_CACHE_CONTROL("Cache-Control"); +const std::string HTTP_HEADER_CONNECTION("Connection"); +const std::string HTTP_HEADER_CONTENT_DESCRIPTION("Content-Description"); +const std::string HTTP_HEADER_CONTENT_ENCODING("Content-Encoding"); +const std::string HTTP_HEADER_CONTENT_ID("Content-ID"); +const std::string HTTP_HEADER_CONTENT_LANGUAGE("Content-Language"); +const std::string HTTP_HEADER_CONTENT_LENGTH("Content-Length"); +const std::string HTTP_HEADER_CONTENT_LOCATION("Content-Location"); +const std::string HTTP_HEADER_CONTENT_MD5("Content-MD5"); +const std::string HTTP_HEADER_CONTENT_RANGE("Content-Range"); +const std::string HTTP_HEADER_CONTENT_TRANSFER_ENCODING("Content-Transfer-Encoding"); +const std::string HTTP_HEADER_CONTENT_TYPE("Content-Type"); +const std::string HTTP_HEADER_COOKIE("Cookie"); +const std::string HTTP_HEADER_DATE("Date"); +const std::string HTTP_HEADER_DESTINATION("Destination"); +const std::string HTTP_HEADER_ETAG("ETag"); +const std::string HTTP_HEADER_EXPECT("Expect"); +const std::string HTTP_HEADER_EXPIRES("Expires"); +const std::string HTTP_HEADER_FROM("From"); +const std::string HTTP_HEADER_HOST("Host"); +const std::string HTTP_HEADER_IF_MATCH("If-Match"); +const std::string HTTP_HEADER_IF_MODIFIED_SINCE("If-Modified-Since"); +const std::string HTTP_HEADER_IF_NONE_MATCH("If-None-Match"); +const std::string HTTP_HEADER_IF_RANGE("If-Range"); +const std::string HTTP_HEADER_IF_UNMODIFIED_SINCE("If-Unmodified-Since"); +const std::string HTTP_HEADER_KEEP_ALIVE("Keep-Alive"); +const std::string HTTP_HEADER_LAST_MODIFIED("Last-Modified"); +const std::string HTTP_HEADER_LOCATION("Location"); +const std::string HTTP_HEADER_MAX_FORWARDS("Max-Forwards"); +const std::string HTTP_HEADER_MIME_VERSION("MIME-Version"); +const std::string HTTP_HEADER_PRAGMA("Pragma"); +const std::string HTTP_HEADER_PROXY_AUTHENTICATE("Proxy-Authenticate"); +const std::string HTTP_HEADER_PROXY_AUTHORIZATION("Proxy-Authorization"); +const std::string HTTP_HEADER_RANGE("Range"); +const std::string HTTP_HEADER_REFERER("Referer"); +const std::string HTTP_HEADER_RETRY_AFTER("Retry-After"); +const std::string HTTP_HEADER_SERVER("Server"); +const std::string HTTP_HEADER_SET_COOKIE("Set-Cookie"); +const std::string HTTP_HEADER_TE("TE"); +const std::string HTTP_HEADER_TRAILER("Trailer"); +const std::string HTTP_HEADER_TRANSFER_ENCODING("Transfer-Encoding"); +const std::string HTTP_HEADER_UPGRADE("Upgrade"); +const std::string HTTP_HEADER_USER_AGENT("User-Agent"); +const std::string HTTP_HEADER_VARY("Vary"); +const std::string HTTP_HEADER_VIA("Via"); +const std::string HTTP_HEADER_WARNING("Warning"); +const std::string HTTP_HEADER_WWW_AUTHENTICATE("WWW-Authenticate"); + + +// Sadly, our proxied headers do not follow http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html +// We need to deal with lowercase headers +const std::string HTTP_HEADER_LOWER_ACCEPT_LANGUAGE("accept-language"); +const std::string HTTP_HEADER_LOWER_CACHE_CONTROL("cache-control"); +const std::string HTTP_HEADER_LOWER_CONTENT_LENGTH("content-length"); +const std::string HTTP_HEADER_LOWER_CONTENT_TYPE("content-type"); +const std::string HTTP_HEADER_LOWER_HOST("host"); +const std::string HTTP_HEADER_LOWER_USER_AGENT("user-agent"); +const std::string HTTP_HEADER_LOWER_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_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_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& 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 + }; + 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 = (F32)date - (F32)LLTimer::getTotalSeconds(); + return true; +} + -- cgit v1.3 From beeefb45269f45ea717f58b30a0985951ae23c20 Mon Sep 17 00:00:00 2001 From: Don Kjer Date: Thu, 4 Apr 2013 21:50:45 +0000 Subject: Renaming HTTP_HEADER_* into HTTP_IN_HEADER_* and HTTP_OUT_HEADER_* to make it more clear which header strings should be used for incoming vs outgoing situations. Using constants for commonly used llhttpnode context strings. --- indra/llmessage/CMakeLists.txt | 3 - indra/llmessage/llavatarnamecache.cpp | 8 +- indra/llmessage/llcurl.cpp | 41 +- indra/llmessage/llcurl.h | 4 +- indra/llmessage/llhttpclient.cpp | 66 +-- indra/llmessage/llhttpclientadapter.cpp | 6 +- indra/llmessage/llhttpconstants.cpp | 139 ++--- indra/llmessage/llhttpconstants.h | 139 ++--- indra/llmessage/llhttpnode.cpp | 14 +- indra/llmessage/llhttpnode.h | 19 +- indra/llmessage/lliohttpserver.cpp | 28 +- indra/llmessage/lliohttpserver.h | 7 - indra/llmessage/lliosocket.cpp | 7 +- indra/llmessage/lliosocket.h | 3 + indra/llmessage/llmime.cpp | 628 --------------------- indra/llmessage/llmime.h | 292 ---------- indra/llmessage/llsdappservices.cpp | 10 +- indra/llmessage/llsdhttpserver.cpp | 2 +- indra/llmessage/llsdrpcclient.h | 4 +- indra/llmessage/lltrustedmessageservice.cpp | 6 +- indra/llmessage/lltrustedmessageservice.h | 4 + indra/llmessage/llurlrequest.cpp | 11 +- indra/llmessage/llurlrequest.h | 12 +- indra/llmessage/message.cpp | 2 +- indra/llmessage/tests/llhttpclient_test.cpp | 2 - indra/llmessage/tests/llhttpnode_stub.cpp | 112 ++++ indra/llmessage/tests/llmime_test.cpp | 446 --------------- .../tests/lltrustedmessageservice_test.cpp | 1 + indra/newview/llappearancemgr.cpp | 4 +- indra/newview/llfloaterabout.cpp | 3 +- indra/newview/llfloaterurlentry.cpp | 3 +- indra/newview/llmarketplacefunctions.cpp | 23 +- indra/newview/llmediadataclient.cpp | 4 +- indra/newview/llmeshrepository.cpp | 10 +- indra/newview/lltexturefetch.cpp | 6 +- indra/newview/lltranslate.cpp | 4 +- indra/newview/llviewerdisplayname.cpp | 2 +- indra/newview/llviewermedia.cpp | 34 +- indra/newview/llwebprofile.cpp | 11 +- indra/newview/llwebsharing.cpp | 69 ++- indra/newview/llxmlrpctransaction.cpp | 2 +- indra/test/llhttpnode_tut.cpp | 2 +- indra/test/mock_http_client.cpp | 3 +- 43 files changed, 478 insertions(+), 1718 deletions(-) delete mode 100644 indra/llmessage/llmime.cpp delete mode 100644 indra/llmessage/llmime.h create mode 100644 indra/llmessage/tests/llhttpnode_stub.cpp delete mode 100644 indra/llmessage/tests/llmime_test.cpp (limited to 'indra/llmessage/llhttpconstants.cpp') diff --git a/indra/llmessage/CMakeLists.txt b/indra/llmessage/CMakeLists.txt index 181718f465..6fa2669be6 100644 --- a/indra/llmessage/CMakeLists.txt +++ b/indra/llmessage/CMakeLists.txt @@ -59,7 +59,6 @@ set(llmessage_SOURCE_FILES llmessagetemplate.cpp llmessagetemplateparser.cpp llmessagethrottle.cpp - llmime.cpp llnamevalue.cpp llnullcipher.cpp llpacketack.cpp @@ -154,7 +153,6 @@ set(llmessage_HEADER_FILES llmessagetemplate.h llmessagetemplateparser.h llmessagethrottle.h - llmime.h llmsgvariabletype.h llnamevalue.h llnullcipher.h @@ -266,7 +264,6 @@ if (LL_TESTS) 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(llmime "" "${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/llavatarnamecache.cpp b/indra/llmessage/llavatarnamecache.cpp index 1d01b69969..e0b71acbd5 100644 --- a/indra/llmessage/llavatarnamecache.cpp +++ b/indra/llmessage/llavatarnamecache.cpp @@ -711,13 +711,9 @@ bool LLAvatarNameCache::expirationFromCacheControl(const LLSD& headers, F64 *exp // Allow the header to override the default std::string cache_control; - if (headers.has(HTTP_HEADER_CACHE_CONTROL)) + if (headers.has(HTTP_IN_HEADER_CACHE_CONTROL)) { - cache_control = headers[HTTP_HEADER_CACHE_CONTROL].asString(); - } - else if (headers.has(HTTP_HEADER_LOWER_CACHE_CONTROL)) - { - cache_control = headers[HTTP_HEADER_LOWER_CACHE_CONTROL].asString(); + cache_control = headers[HTTP_IN_HEADER_CACHE_CONTROL].asString(); } if (!cache_control.empty()) diff --git a/indra/llmessage/llcurl.cpp b/indra/llmessage/llcurl.cpp index 1269b6bc5d..68282626ae 100644 --- a/indra/llmessage/llcurl.cpp +++ b/indra/llmessage/llcurl.cpp @@ -156,9 +156,9 @@ std::string LLCurl::Responder::dumpResponse() const << "[status:" << mStatus << "] " << "[reason:" << mReason << "] "; - if (mResponseHeaders.has(HTTP_HEADER_CONTENT_TYPE)) + if (mResponseHeaders.has(HTTP_IN_HEADER_CONTENT_TYPE)) { - s << "[content-type:" << mResponseHeaders[HTTP_HEADER_CONTENT_TYPE] << "] "; + s << "[content-type:" << mResponseHeaders[HTTP_IN_HEADER_CONTENT_TYPE] << "] "; } s << "[content:" << mContent << "]"; @@ -211,34 +211,19 @@ void LLCurl::Responder::setResponseHeader(const std::string& header, const std:: mResponseHeaders[header] = value; } -const std::string& LLCurl::Responder::getResponseHeader(const std::string& header, bool check_lower) const +const std::string& LLCurl::Responder::getResponseHeader(const std::string& header) const { if (mResponseHeaders.has(header)) { return mResponseHeaders[header].asStringRef(); } - if (check_lower) - { - std::string header_lower(header); - LLStringUtil::toLower(header_lower); - if (mResponseHeaders.has(header_lower)) - { - return mResponseHeaders[header_lower].asStringRef(); - } - } static const std::string empty; return empty; } -bool LLCurl::Responder::hasResponseHeader(const std::string& header, bool check_lower) const +bool LLCurl::Responder::hasResponseHeader(const std::string& header) const { if (mResponseHeaders.has(header)) return true; - if (check_lower) - { - std::string header_lower(header); - LLStringUtil::toLower(header_lower); - return mResponseHeaders.has(header_lower); - } return false; } @@ -256,7 +241,7 @@ void LLCurl::Responder::completedRaw( { parsed=false; } - // Try to parse body as llsd, no matter what 'Content-Type' says. + // 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; @@ -271,8 +256,8 @@ void LLCurl::Responder::completedRaw( } } - // Only emit an warning if we failed to parse when 'Content-Type' == 'application/llsd+xml' - if (!parsed && (HTTP_CONTENT_LLSD_XML == getResponseHeader(HTTP_HEADER_CONTENT_TYPE))) + // 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))) { llwarns << "Failed to deserialize . " << mURL << " [status:" << mStatus << "] " << "(" << mReason << ") body: " << debug_body << llendl; @@ -643,8 +628,8 @@ void LLCurl::Easy::prepRequest(const std::string& url, if (!post) { // *TODO: Should this be set to 'Keep-Alive' ? - slist_append(HTTP_HEADER_CONNECTION, "keep-alive"); - slist_append(HTTP_HEADER_KEEP_ALIVE, "300"); + slist_append(HTTP_OUT_HEADER_CONNECTION, "keep-alive"); + slist_append(HTTP_OUT_HEADER_KEEP_ALIVE, "300"); // Accept and other headers for (std::vector::const_iterator iter = headers.begin(); iter != headers.end(); ++iter) @@ -1242,12 +1227,12 @@ bool LLCurlRequest::getByteRange(const std::string& url, if (length > 0) { std::string range = llformat("bytes=%d-%d", offset,offset+length-1); - easy->slist_append(HTTP_HEADER_RANGE, range); + easy->slist_append(HTTP_OUT_HEADER_RANGE, range); } else if (offset > 0) { std::string range = llformat("bytes=%d-", offset); - easy->slist_append(HTTP_HEADER_RANGE, range); + easy->slist_append(HTTP_OUT_HEADER_RANGE, range); } easy->setHeaders(); bool res = addEasy(easy); @@ -1274,7 +1259,7 @@ bool LLCurlRequest::post(const std::string& url, easy->setopt(CURLOPT_POSTFIELDS, (void*)NULL); easy->setopt(CURLOPT_POSTFIELDSIZE, bytes); - easy->slist_append(HTTP_HEADER_CONTENT_TYPE, HTTP_CONTENT_LLSD_XML); + easy->slist_append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_LLSD_XML); easy->setHeaders(); lldebugs << "POSTING: " << bytes << " bytes." << llendl; @@ -1302,7 +1287,7 @@ bool LLCurlRequest::post(const std::string& url, easy->setopt(CURLOPT_POSTFIELDS, (void*)NULL); easy->setopt(CURLOPT_POSTFIELDSIZE, bytes); - easy->slist_append(HTTP_HEADER_CONTENT_TYPE, HTTP_CONTENT_OCTET_STREAM); + easy->slist_append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_OCTET_STREAM); easy->setHeaders(); lldebugs << "POSTING: " << bytes << " bytes." << llendl; diff --git a/indra/llmessage/llcurl.h b/indra/llmessage/llcurl.h index c72e1e493a..8a65c783be 100644 --- a/indra/llmessage/llcurl.h +++ b/indra/llmessage/llcurl.h @@ -91,8 +91,8 @@ public: S32 getStatus() const { return mStatus; } const std::string& getReason() const { return mReason; } const LLSD& getContent() const { return mContent; } - bool hasResponseHeader(const std::string& header, bool check_lower=false) const; - const std::string& getResponseHeader(const std::string& header, bool check_lower=true) const; + 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; } diff --git a/indra/llmessage/llhttpclient.cpp b/indra/llmessage/llhttpclient.cpp index a4a1f02cd3..5830a5eca0 100755 --- a/indra/llmessage/llhttpclient.cpp +++ b/indra/llmessage/llhttpclient.cpp @@ -248,45 +248,45 @@ static void request( 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_HEADER_COOKIE)) + 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_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()); - } - } + 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_HEADER_ACCEPT)) + if(!headers.has(HTTP_OUT_HEADER_ACCEPT)) { - req->addHeader(HTTP_HEADER_ACCEPT, HTTP_CONTENT_LLSD_XML); + req->addHeader(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_LLSD_XML); } } @@ -301,21 +301,21 @@ static void request( if (method == HTTP_POST && gMessageSystem) { req->addHeader("X-SecondLife-UDP-Listen-Port", llformat("%d", - gMessageSystem->mPort)); - } + gMessageSystem->mPort)); + } if (method == HTTP_PUT || method == HTTP_POST) { - if(!headers.has(HTTP_HEADER_CONTENT_TYPE)) + 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_HEADER_CONTENT_TYPE, body_injector->contentType()); + req->addHeader(HTTP_OUT_HEADER_CONTENT_TYPE, body_injector->contentType()); } - chain.push_back(LLIOPipe::ptr_t(body_injector)); + chain.push_back(LLIOPipe::ptr_t(body_injector)); } chain.push_back(LLIOPipe::ptr_t(req)); @@ -336,7 +336,7 @@ void LLHTTPClient::getByteRange( if(offset > 0 || bytes > 0) { std::string range = llformat("bytes=%d-%d", offset, offset+bytes-1); - headers[HTTP_HEADER_RANGE] = range; + headers[HTTP_OUT_HEADER_RANGE] = range; } request(url,HTTP_GET, NULL, responder, timeout, headers); } @@ -483,19 +483,19 @@ static LLSD blocking_request( 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_HEADER_CONTENT_TYPE.c_str(), HTTP_CONTENT_LLSD_XML.c_str()).c_str()); + 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_HEADER_EXPECT.c_str()).c_str()); + headers_list = curl_slist_append(headers_list, llformat("%s:", HTTP_OUT_HEADER_EXPECT.c_str()).c_str()); } // * Do the action using curl, handle results lldebugs << "HTTP body: " << body_str << llendl; headers_list = curl_slist_append(headers_list, - llformat("%s: %s", HTTP_HEADER_ACCEPT.c_str(), HTTP_CONTENT_LLSD_XML.c_str()).c_str()); + 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 ) { @@ -617,7 +617,7 @@ void LLHTTPClient::move( const F32 timeout) { LLSD headers = hdrs; - headers[HTTP_HEADER_DESTINATION] = destination; + headers[HTTP_OUT_HEADER_DESTINATION] = destination; request(url, HTTP_MOVE, NULL, responder, timeout, headers); } diff --git a/indra/llmessage/llhttpclientadapter.cpp b/indra/llmessage/llhttpclientadapter.cpp index aaa31e36fc..b56a804f94 100644 --- a/indra/llmessage/llhttpclientadapter.cpp +++ b/indra/llmessage/llhttpclientadapter.cpp @@ -36,17 +36,17 @@ void LLHTTPClientAdapter::get(const std::string& url, LLCurl::ResponderPtr respo 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_HEADER_PRAGMA] = " "; + 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_HEADER_PRAGMA)) + if (!empty_pragma_header.has(HTTP_OUT_HEADER_PRAGMA)) { // as above - empty_pragma_header[HTTP_HEADER_PRAGMA] = " "; + empty_pragma_header[HTTP_OUT_HEADER_PRAGMA] = " "; } LLHTTPClient::get(url, responder, empty_pragma_header); } diff --git a/indra/llmessage/llhttpconstants.cpp b/indra/llmessage/llhttpconstants.cpp index 2134024a14..1995fad1e5 100644 --- a/indra/llmessage/llhttpconstants.cpp +++ b/indra/llmessage/llhttpconstants.cpp @@ -37,72 +37,76 @@ // for curl_getdate() (apparently parsing RFC 1123 dates is hard) #include -const std::string HTTP_HEADER_ACCEPT("Accept"); -const std::string HTTP_HEADER_ACCEPT_CHARSET("Accept-Charset"); -const std::string HTTP_HEADER_ACCEPT_ENCODING("Accept-Encoding"); -const std::string HTTP_HEADER_ACCEPT_LANGUAGE("Accept-Language"); -const std::string HTTP_HEADER_ACCEPT_RANGES("Accept-Ranges"); -const std::string HTTP_HEADER_AGE("Age"); -const std::string HTTP_HEADER_ALLOW("Allow"); -const std::string HTTP_HEADER_AUTHORIZATION("Authorization"); -const std::string HTTP_HEADER_CACHE_CONTROL("Cache-Control"); -const std::string HTTP_HEADER_CONNECTION("Connection"); -const std::string HTTP_HEADER_CONTENT_DESCRIPTION("Content-Description"); -const std::string HTTP_HEADER_CONTENT_ENCODING("Content-Encoding"); -const std::string HTTP_HEADER_CONTENT_ID("Content-ID"); -const std::string HTTP_HEADER_CONTENT_LANGUAGE("Content-Language"); -const std::string HTTP_HEADER_CONTENT_LENGTH("Content-Length"); -const std::string HTTP_HEADER_CONTENT_LOCATION("Content-Location"); -const std::string HTTP_HEADER_CONTENT_MD5("Content-MD5"); -const std::string HTTP_HEADER_CONTENT_RANGE("Content-Range"); -const std::string HTTP_HEADER_CONTENT_TRANSFER_ENCODING("Content-Transfer-Encoding"); -const std::string HTTP_HEADER_CONTENT_TYPE("Content-Type"); -const std::string HTTP_HEADER_COOKIE("Cookie"); -const std::string HTTP_HEADER_DATE("Date"); -const std::string HTTP_HEADER_DESTINATION("Destination"); -const std::string HTTP_HEADER_ETAG("ETag"); -const std::string HTTP_HEADER_EXPECT("Expect"); -const std::string HTTP_HEADER_EXPIRES("Expires"); -const std::string HTTP_HEADER_FROM("From"); -const std::string HTTP_HEADER_HOST("Host"); -const std::string HTTP_HEADER_IF_MATCH("If-Match"); -const std::string HTTP_HEADER_IF_MODIFIED_SINCE("If-Modified-Since"); -const std::string HTTP_HEADER_IF_NONE_MATCH("If-None-Match"); -const std::string HTTP_HEADER_IF_RANGE("If-Range"); -const std::string HTTP_HEADER_IF_UNMODIFIED_SINCE("If-Unmodified-Since"); -const std::string HTTP_HEADER_KEEP_ALIVE("Keep-Alive"); -const std::string HTTP_HEADER_LAST_MODIFIED("Last-Modified"); -const std::string HTTP_HEADER_LOCATION("Location"); -const std::string HTTP_HEADER_MAX_FORWARDS("Max-Forwards"); -const std::string HTTP_HEADER_MIME_VERSION("MIME-Version"); -const std::string HTTP_HEADER_PRAGMA("Pragma"); -const std::string HTTP_HEADER_PROXY_AUTHENTICATE("Proxy-Authenticate"); -const std::string HTTP_HEADER_PROXY_AUTHORIZATION("Proxy-Authorization"); -const std::string HTTP_HEADER_RANGE("Range"); -const std::string HTTP_HEADER_REFERER("Referer"); -const std::string HTTP_HEADER_RETRY_AFTER("Retry-After"); -const std::string HTTP_HEADER_SERVER("Server"); -const std::string HTTP_HEADER_SET_COOKIE("Set-Cookie"); -const std::string HTTP_HEADER_TE("TE"); -const std::string HTTP_HEADER_TRAILER("Trailer"); -const std::string HTTP_HEADER_TRANSFER_ENCODING("Transfer-Encoding"); -const std::string HTTP_HEADER_UPGRADE("Upgrade"); -const std::string HTTP_HEADER_USER_AGENT("User-Agent"); -const std::string HTTP_HEADER_VARY("Vary"); -const std::string HTTP_HEADER_VIA("Via"); -const std::string HTTP_HEADER_WARNING("Warning"); -const std::string HTTP_HEADER_WWW_AUTHENTICATE("WWW-Authenticate"); - - -// Sadly, our proxied headers do not follow http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html -// We need to deal with lowercase headers -const std::string HTTP_HEADER_LOWER_ACCEPT_LANGUAGE("accept-language"); -const std::string HTTP_HEADER_LOWER_CACHE_CONTROL("cache-control"); -const std::string HTTP_HEADER_LOWER_CONTENT_LENGTH("content-length"); -const std::string HTTP_HEADER_LOWER_CONTENT_TYPE("content-type"); -const std::string HTTP_HEADER_LOWER_HOST("host"); -const std::string HTTP_HEADER_LOWER_USER_AGENT("user-agent"); -const std::string HTTP_HEADER_LOWER_X_FORWARDED_FOR("x-forwarded-for"); +// 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"); @@ -121,6 +125,9 @@ 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"); diff --git a/indra/llmessage/llhttpconstants.h b/indra/llmessage/llhttpconstants.h index 34263e17c8..8cc3459654 100644 --- a/indra/llmessage/llhttpconstants.h +++ b/indra/llmessage/llhttpconstants.h @@ -132,71 +132,76 @@ bool getSecondsUntilRetryAfter(const std::string& retry_after, F32& seconds_to_w //// HTTP Headers ///// -extern const std::string HTTP_HEADER_ACCEPT; -extern const std::string HTTP_HEADER_ACCEPT_CHARSET; -extern const std::string HTTP_HEADER_ACCEPT_ENCODING; -extern const std::string HTTP_HEADER_ACCEPT_LANGUAGE; -extern const std::string HTTP_HEADER_ACCEPT_RANGES; -extern const std::string HTTP_HEADER_AGE; -extern const std::string HTTP_HEADER_ALLOW; -extern const std::string HTTP_HEADER_AUTHORIZATION; -extern const std::string HTTP_HEADER_CACHE_CONTROL; -extern const std::string HTTP_HEADER_CONNECTION; -extern const std::string HTTP_HEADER_CONTENT_DESCRIPTION; -extern const std::string HTTP_HEADER_CONTENT_ENCODING; -extern const std::string HTTP_HEADER_CONTENT_ID; -extern const std::string HTTP_HEADER_CONTENT_LANGUAGE; -extern const std::string HTTP_HEADER_CONTENT_LENGTH; -extern const std::string HTTP_HEADER_CONTENT_LOCATION; -extern const std::string HTTP_HEADER_CONTENT_MD5; -extern const std::string HTTP_HEADER_CONTENT_RANGE; -extern const std::string HTTP_HEADER_CONTENT_TRANSFER_ENCODING; -extern const std::string HTTP_HEADER_CONTENT_TYPE; -extern const std::string HTTP_HEADER_COOKIE; -extern const std::string HTTP_HEADER_DATE; -extern const std::string HTTP_HEADER_DESTINATION; -extern const std::string HTTP_HEADER_ETAG; -extern const std::string HTTP_HEADER_EXPECT; -extern const std::string HTTP_HEADER_EXPIRES; -extern const std::string HTTP_HEADER_FROM; -extern const std::string HTTP_HEADER_HOST; -extern const std::string HTTP_HEADER_IF_MATCH; -extern const std::string HTTP_HEADER_IF_MODIFIED_SINCE; -extern const std::string HTTP_HEADER_IF_NONE_MATCH; -extern const std::string HTTP_HEADER_IF_RANGE; -extern const std::string HTTP_HEADER_IF_UNMODIFIED_SINCE; -extern const std::string HTTP_HEADER_KEEP_ALIVE; -extern const std::string HTTP_HEADER_LAST_MODIFIED; -extern const std::string HTTP_HEADER_LOCATION; -extern const std::string HTTP_HEADER_MAX_FORWARDS; -extern const std::string HTTP_HEADER_MIME_VERSION; -extern const std::string HTTP_HEADER_PRAGMA; -extern const std::string HTTP_HEADER_PROXY_AUTHENTICATE; -extern const std::string HTTP_HEADER_PROXY_AUTHORIZATION; -extern const std::string HTTP_HEADER_RANGE; -extern const std::string HTTP_HEADER_REFERER; -extern const std::string HTTP_HEADER_RETRY_AFTER; -extern const std::string HTTP_HEADER_SERVER; -extern const std::string HTTP_HEADER_SET_COOKIE; -extern const std::string HTTP_HEADER_TE; -extern const std::string HTTP_HEADER_TRAILER; -extern const std::string HTTP_HEADER_TRANSFER_ENCODING; -extern const std::string HTTP_HEADER_UPGRADE; -extern const std::string HTTP_HEADER_USER_AGENT; -extern const std::string HTTP_HEADER_VARY; -extern const std::string HTTP_HEADER_VIA; -extern const std::string HTTP_HEADER_WARNING; -extern const std::string HTTP_HEADER_WWW_AUTHENTICATE; - -// Sadly, our proxied headers do not follow http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html -// We need to deal with lowercase headers -extern const std::string HTTP_HEADER_LOWER_ACCEPT_LANGUAGE; -extern const std::string HTTP_HEADER_LOWER_CACHE_CONTROL; -extern const std::string HTTP_HEADER_LOWER_CONTENT_LENGTH; -extern const std::string HTTP_HEADER_LOWER_CONTENT_TYPE; -extern const std::string HTTP_HEADER_LOWER_HOST; -extern const std::string HTTP_HEADER_LOWER_USER_AGENT; -extern const std::string HTTP_HEADER_LOWER_X_FORWARDED_FOR; +// 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 //// @@ -217,4 +222,8 @@ 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/llhttpnode.cpp b/indra/llmessage/llhttpnode.cpp index e3b42b1b16..c0c3d0bcbe 100644 --- a/indra/llmessage/llhttpnode.cpp +++ b/indra/llmessage/llhttpnode.cpp @@ -31,9 +31,14 @@ #include "llstl.h" #include "llhttpconstants.h" -#include "lliohttpserver.h" // for string constants -static const std::string CONTEXT_WILDCARD("wildcard"); +const std::string CONTEXT_HEADERS("headers"); +const std::string CONTEXT_PATH("path"); +const std::string CONTEXT_QUERY_STRING("query-string"); +const std::string CONTEXT_REQUEST("request"); +const std::string CONTEXT_RESPONSE("response"); +const std::string CONTEXT_VERB("verb"); +const std::string CONTEXT_WILDCARD("wildcard"); /** * LLHTTPNode @@ -177,9 +182,8 @@ void LLHTTPNode::options(ResponsePtr response, const LLSD& context) const LL_DEBUGS("LLHTTPNode") << "context: " << context << LL_ENDL; // default implementation constructs an url to the documentation. - // *TODO: Check for 'Host' header instead of 'host' header? std::string host( - context[CONTEXT_REQUEST][CONTEXT_HEADERS][HTTP_HEADER_LOWER_HOST].asString()); + context[CONTEXT_REQUEST][CONTEXT_HEADERS][HTTP_IN_HEADER_HOST].asString()); if(host.empty()) { response->status(HTTP_BAD_REQUEST, "Bad Request -- need Host header"); @@ -187,7 +191,7 @@ void LLHTTPNode::options(ResponsePtr response, const LLSD& context) const } std::ostringstream ostr; ostr << "http://" << host << "/web/server/api"; - ostr << context[CONTEXT_REQUEST]["path"].asString(); + ostr << context[CONTEXT_REQUEST][CONTEXT_PATH].asString(); static const std::string DOC_HEADER("X-Documentation-URL"); response->addHeader(DOC_HEADER, ostr.str()); response->status(HTTP_OK, "OK"); diff --git a/indra/llmessage/llhttpnode.h b/indra/llmessage/llhttpnode.h index 2539eec6c3..1144d88be1 100644 --- a/indra/llmessage/llhttpnode.h +++ b/indra/llmessage/llhttpnode.h @@ -31,6 +31,17 @@ #include "llrefcount.h" #include "llsd.h" +// common strings use for populating the context. basically 'request', +// 'wildcard', and 'headers'. +extern const std::string CONTEXT_HEADERS; +extern const std::string CONTEXT_PATH; +extern const std::string CONTEXT_QUERY_STRING; +extern const std::string CONTEXT_REQUEST; +extern const std::string CONTEXT_RESPONSE; +extern const std::string CONTEXT_VERB; +extern const std::string CONTEXT_WILDCARD; + + class LLChainIOFactory; /** @@ -125,7 +136,7 @@ public: virtual void methodNotAllowed(); /** - * @breif Add a name: value http header. + * @brief Add a name: value http header. * * No effort is made to ensure the response is a valid http * header. @@ -194,15 +205,15 @@ public: name, and return true if the name will construct to a valid url. For convenience, the getChild() method above will automatically insert the name in - context["request"]["wildcard"][key] if this method returns true. + context[CONTEXT_REQUEST][CONTEXT_WILDCARD][key] if this method returns true. For example, the node "agent//detail" will set - context["request"]["wildcard"]["agent_id"] eqaul to the value + context[CONTEXT_REQUEST][CONTEXT_WILDCARD]["agent_id"] eqaul to the value found during traversal. */ const LLHTTPNode* traverse(const std::string& path, LLSD& context) const; /**< find a node, if any, that can service this path - set up context["request"] information + set up context[CONTEXT_REQUEST] information */ //@} diff --git a/indra/llmessage/lliohttpserver.cpp b/indra/llmessage/lliohttpserver.cpp index 509719786c..293e74d0da 100644 --- a/indra/llmessage/lliohttpserver.cpp +++ b/indra/llmessage/lliohttpserver.cpp @@ -50,10 +50,6 @@ #include static const char HTTP_VERSION_STR[] = "HTTP/1.0"; -const std::string CONTEXT_REQUEST("request"); -const std::string CONTEXT_RESPONSE("response"); -const std::string CONTEXT_VERB("verb"); -const std::string CONTEXT_HEADERS("headers"); static LLIOHTTPServer::timing_callback_t sTimingCallback = NULL; static void* sTimingCallbackData = NULL; @@ -241,7 +237,7 @@ LLIOPipe::EStatus LLHTTPPipe::process_impl( // Log all HTTP transactions. // TODO: Add a way to log these to their own file instead of indra.log // It is just too spammy to be in indra.log. - lldebugs << verb << " " << context[CONTEXT_REQUEST]["path"].asString() + lldebugs << verb << " " << context[CONTEXT_REQUEST][CONTEXT_PATH].asString() << " " << mStatusCode << " " << mStatusMessage << " " << delta << "s" << llendl; @@ -268,7 +264,7 @@ LLIOPipe::EStatus LLHTTPPipe::process_impl( case STATE_GOOD_RESULT: { LLSD headers = mHeaders; - headers[HTTP_HEADER_CONTENT_TYPE] = HTTP_CONTENT_LLSD_XML; + headers[HTTP_OUT_HEADER_CONTENT_TYPE] = HTTP_CONTENT_LLSD_XML; context[CONTEXT_RESPONSE][CONTEXT_HEADERS] = headers; LLBufferStream ostr(channels, buffer.get()); LLSDSerialize::toXML(mResult, ostr); @@ -279,7 +275,7 @@ LLIOPipe::EStatus LLHTTPPipe::process_impl( case STATE_STATUS_RESULT: { LLSD headers = mHeaders; - headers[HTTP_HEADER_CONTENT_TYPE] = HTTP_CONTENT_TEXT_PLAIN; + headers[HTTP_OUT_HEADER_CONTENT_TYPE] = HTTP_CONTENT_TEXT_PLAIN; context[CONTEXT_RESPONSE][CONTEXT_HEADERS] = headers; context[CONTEXT_RESPONSE]["statusCode"] = mStatusCode; context[CONTEXT_RESPONSE]["statusMessage"] = mStatusMessage; @@ -300,7 +296,7 @@ LLIOPipe::EStatus LLHTTPPipe::process_impl( case STATE_EXTENDED_LLSD_RESULT: { LLSD headers = mHeaders; - headers[HTTP_HEADER_CONTENT_TYPE] = HTTP_CONTENT_LLSD_XML; + headers[HTTP_OUT_HEADER_CONTENT_TYPE] = HTTP_CONTENT_LLSD_XML; context[CONTEXT_RESPONSE][CONTEXT_HEADERS] = headers; context[CONTEXT_RESPONSE]["statusCode"] = mStatusCode; LLBufferStream ostr(channels, buffer.get()); @@ -489,7 +485,7 @@ LLIOPipe::EStatus LLHTTPResponseHeader::process_impl( S32 content_length = buffer->countAfter(channels.in(), NULL); if(0 < content_length) { - ostr << HTTP_HEADER_CONTENT_LENGTH << ": " << content_length << "\r\n"; + ostr << HTTP_OUT_HEADER_CONTENT_LENGTH << ": " << content_length << "\r\n"; } // *NOTE: This guard can go away once the LLSD static map // iterator is available. Phoenix. 2008-05-09 @@ -795,7 +791,7 @@ LLIOPipe::EStatus LLHTTPResponder::process_impl( std::string name(buf, pos_colon - buf); std::string value(pos_colon + 2); LLStringUtil::toLower(name); - if(HTTP_HEADER_LOWER_CONTENT_LENGTH == name) + if(HTTP_IN_HEADER_CONTENT_LENGTH == name) { lldebugs << "Content-Length: " << value << llendl; mContentLength = atoi(value.c_str()); @@ -870,12 +866,12 @@ LLIOPipe::EStatus LLHTTPResponder::process_impl( // HTTP headers. LLPumpIO::chain_t chain; chain.push_back(LLIOPipe::ptr_t(new LLIOFlush)); - context[CONTEXT_REQUEST]["path"] = mPath; - context[CONTEXT_REQUEST]["query-string"] = mQuery; - context[CONTEXT_REQUEST]["remote-host"] - = mBuildContext["remote-host"]; - context[CONTEXT_REQUEST]["remote-port"] - = mBuildContext["remote-port"]; + context[CONTEXT_REQUEST][CONTEXT_PATH] = mPath; + context[CONTEXT_REQUEST][CONTEXT_QUERY_STRING] = mQuery; + context[CONTEXT_REQUEST][CONTEXT_REMOTE_HOST] + = mBuildContext[CONTEXT_REMOTE_HOST]; + context[CONTEXT_REQUEST][CONTEXT_REMOTE_PORT] + = mBuildContext[CONTEXT_REMOTE_PORT]; context[CONTEXT_REQUEST][CONTEXT_HEADERS] = mHeaders; const LLChainIOFactory* protocolHandler diff --git a/indra/llmessage/lliohttpserver.h b/indra/llmessage/lliohttpserver.h index 40537e05bc..a23eafe58a 100644 --- a/indra/llmessage/lliohttpserver.h +++ b/indra/llmessage/lliohttpserver.h @@ -33,13 +33,6 @@ class LLPumpIO; -// common strings use for populating the context. bascally 'request', -// 'wildcard', and 'headers'. -extern const std::string CONTEXT_REQUEST; -extern const std::string CONTEXT_RESPONSE; -extern const std::string CONTEXT_VERB; -extern const std::string CONTEXT_HEADERS; - class LLIOHTTPServer { public: diff --git a/indra/llmessage/lliosocket.cpp b/indra/llmessage/lliosocket.cpp index 2043bae5e7..a60b840067 100644 --- a/indra/llmessage/lliosocket.cpp +++ b/indra/llmessage/lliosocket.cpp @@ -39,6 +39,9 @@ // constants // +const std::string CONTEXT_REMOTE_HOST("remote-host"); +const std::string CONTEXT_REMOTE_PORT("remote-port"); + static const S32 LL_DEFAULT_LISTEN_BACKLOG = 10; static const S32 LL_SEND_BUFFER_SIZE = 40000; static const S32 LL_RECV_BUFFER_SIZE = 40000; @@ -619,8 +622,8 @@ LLIOPipe::EStatus LLIOServerSocket::process_impl( apr_sockaddr_ip_get(&remote_host_string, remote_addr); LLSD context; - context["remote-host"] = remote_host_string; - context["remote-port"] = remote_addr->port; + context[CONTEXT_REMOTE_HOST] = remote_host_string; + context[CONTEXT_REMOTE_PORT] = remote_addr->port; LLPumpIO::chain_t chain; chain.push_back(LLIOPipe::ptr_t(new LLIOSocketReader(llsocket))); diff --git a/indra/llmessage/lliosocket.h b/indra/llmessage/lliosocket.h index be0f7dfcc6..08c21a1b97 100644 --- a/indra/llmessage/lliosocket.h +++ b/indra/llmessage/lliosocket.h @@ -42,6 +42,9 @@ #include "apr_network_io.h" #include "llchainio.h" +extern const std::string CONTEXT_REMOTE_HOST; +extern const std::string CONTEXT_REMOTE_PORT; + class LLHost; /** diff --git a/indra/llmessage/llmime.cpp b/indra/llmessage/llmime.cpp deleted file mode 100644 index 90653098db..0000000000 --- a/indra/llmessage/llmime.cpp +++ /dev/null @@ -1,628 +0,0 @@ -/** - * @file llmime.cpp - * @author Phoenix - * @date 2006-12-20 - * @brief Implementation of mime tools. - * - * $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 "llhttpconstants.h" -#include "llmime.h" - -#include - -#include "llmemorystream.h" - -/** - * Useful constants. - */ -// parser helpers -static const std::string MULTIPART("multipart"); -static const std::string BOUNDARY("boundary"); -static const std::string END_OF_CONTENT_PARAMETER("\r\n ;\t"); -static const std::string SEPARATOR_PREFIX("--"); -//static const std::string SEPARATOR_SUFFIX("\r\n"); - -/* -Content-Type: multipart/mixed; boundary="segment" -Content-Length: 24832 - ---segment -Content-Type: image/j2c -Content-Length: 23715 - - - ---segment -Content-Type: text/xml; charset=UTF-8 - - -EOF - -*/ - -/** - * LLMimeIndex - */ - -/** - * @class LLMimeIndex::Impl - * @brief Implementation details of the mime index class. - * @see LLMimeIndex - */ -class LLMimeIndex::Impl -{ -public: - Impl() : mOffset(-1), mUseCount(1) - {} - Impl(LLSD headers, S32 offset) : - mHeaders(headers), mOffset(offset), mUseCount(1) - {} -public: - LLSD mHeaders; - S32 mOffset; - S32 mUseCount; - - typedef std::vector sub_part_t; - sub_part_t mAttachments; -}; - -LLSD LLMimeIndex::headers() const -{ - return mImpl->mHeaders; -} - -S32 LLMimeIndex::offset() const -{ - return mImpl->mOffset; -} - -S32 LLMimeIndex::contentLength() const -{ - // Find the content length in the headers. - S32 length = -1; - LLSD content_length = mImpl->mHeaders[HTTP_HEADER_CONTENT_LENGTH]; - if(content_length.isDefined()) - { - length = content_length.asInteger(); - } - return length; -} - -std::string LLMimeIndex::contentType() const -{ - std::string type; - LLSD content_type = mImpl->mHeaders[HTTP_HEADER_CONTENT_TYPE]; - if(content_type.isDefined()) - { - type = content_type.asString(); - } - return type; -} - -bool LLMimeIndex::isMultipart() const -{ - bool multipart = false; - LLSD content_type = mImpl->mHeaders[HTTP_HEADER_CONTENT_TYPE]; - if(content_type.isDefined()) - { - std::string type = content_type.asString(); - int comp = type.compare(0, MULTIPART.size(), MULTIPART); - if(0 == comp) - { - multipart = true; - } - } - return multipart; -} - -S32 LLMimeIndex::subPartCount() const -{ - return mImpl->mAttachments.size(); -} - -LLMimeIndex LLMimeIndex::subPart(S32 index) const -{ - LLMimeIndex part; - if((index >= 0) && (index < (S32)mImpl->mAttachments.size())) - { - part = mImpl->mAttachments[index]; - } - return part; -} - -LLMimeIndex::LLMimeIndex() : mImpl(new LLMimeIndex::Impl) -{ -} - -LLMimeIndex::LLMimeIndex(LLSD headers, S32 content_offset) : - mImpl(new LLMimeIndex::Impl(headers, content_offset)) -{ -} - -LLMimeIndex::LLMimeIndex(const LLMimeIndex& mime) : - mImpl(mime.mImpl) -{ - ++mImpl->mUseCount; -} - -LLMimeIndex::~LLMimeIndex() -{ - if(0 == --mImpl->mUseCount) - { - delete mImpl; - } -} - -LLMimeIndex& LLMimeIndex::operator=(const LLMimeIndex& mime) -{ - // Increment use count first so that we handle self assignment - // automatically. - ++mime.mImpl->mUseCount; - if(0 == --mImpl->mUseCount) - { - delete mImpl; - } - mImpl = mime.mImpl; - return *this; -} - -bool LLMimeIndex::attachSubPart(LLMimeIndex sub_part) -{ - // *FIX: Should we check for multi-part? - if(mImpl->mAttachments.size() < S32_MAX) - { - mImpl->mAttachments.push_back(sub_part); - return true; - } - return false; -} - -/** - * LLMimeParser - */ -/** - * @class LLMimeParser::Impl - * @brief Implementation details of the mime parser class. - * @see LLMimeParser - */ -class LLMimeParser::Impl -{ -public: - // @brief Constructor. - Impl(); - - // @brief Reset this for a new parse. - void reset(); - - /** - * @brief Parse a mime entity to find the index information. - * - * This method will scan the istr until a single complete mime - * entity is read, an EOF, or limit bytes have been scanned. The - * istr will be modified by this parsing, so pass in a temporary - * stream or rewind/reset the stream after this call. - * @param istr An istream which contains a mime entity. - * @param limit The maximum number of bytes to scan. - * @param separator The multipart separator if it is known. - * @param is_subpart Set true if parsing a multipart sub part. - * @param index[out] The parsed output. - * @return Returns true if an index was parsed and no errors occurred. - */ - bool parseIndex( - std::istream& istr, - S32 limit, - const std::string& separator, - bool is_subpart, - LLMimeIndex& index); - -protected: - /** - * @brief parse the headers. - * - * At the end of a successful parse, mScanCount will be at the - * start of the content. - * @param istr The input stream. - * @param limit maximum number of bytes to process - * @param headers[out] A map of the headers found. - * @return Returns true if the parse was successful. - */ - bool parseHeaders(std::istream& istr, S32 limit, LLSD& headers); - - /** - * @brief Figure out the separator string from a content type header. - * - * @param multipart_content_type The content type value from the headers. - * @return Returns the separator string. - */ - std::string findSeparator(std::string multipart_content_type); - - /** - * @brief Scan through istr past the separator. - * - * @param istr The input stream. - * @param limit Maximum number of bytes to scan. - * @param separator The multipart separator. - */ - void scanPastSeparator( - std::istream& istr, - S32 limit, - const std::string& separator); - - /** - * @brief Scan through istr past the content of the current mime part. - * - * @param istr The input stream. - * @param limit Maximum number of bytes to scan. - * @param headers The headers for this mime part. - * @param separator The multipart separator if known. - */ - void scanPastContent( - std::istream& istr, - S32 limit, - LLSD headers, - const std::string separator); - - /** - * @brief Eat CRLF. - * - * This method has no concept of the limit, so ensure you have at - * least 2 characters left to eat before hitting the limit. This - * method will increment mScanCount as it goes. - * @param istr The input stream. - * @return Returns true if CRLF was found and consumed off of istr. - */ - bool eatCRLF(std::istream& istr); - - // @brief Returns true if parsing should continue. - bool continueParse() const { return (!mError && mContinue); } - - // @brief anonymous enumeration for parse buffer size. - enum - { - LINE_BUFFER_LENGTH = 1024 - }; - -protected: - S32 mScanCount; - bool mContinue; - bool mError; - char mBuffer[LINE_BUFFER_LENGTH]; -}; - -LLMimeParser::Impl::Impl() -{ - reset(); -} - -void LLMimeParser::Impl::reset() -{ - mScanCount = 0; - mContinue = true; - mError = false; - mBuffer[0] = '\0'; -} - -bool LLMimeParser::Impl::parseIndex( - std::istream& istr, - S32 limit, - const std::string& separator, - bool is_subpart, - LLMimeIndex& index) -{ - LLSD headers; - bool parsed_something = false; - if(parseHeaders(istr, limit, headers)) - { - parsed_something = true; - LLMimeIndex mime(headers, mScanCount); - index = mime; - if(index.isMultipart()) - { - // Figure out the separator, scan past it, and recurse. - std::string ct = headers[HTTP_HEADER_CONTENT_TYPE].asString(); - std::string sep = findSeparator(ct); - scanPastSeparator(istr, limit, sep); - while(continueParse() && parseIndex(istr, limit, sep, true, mime)) - { - index.attachSubPart(mime); - } - } - else - { - // Scan to the end of content. - scanPastContent(istr, limit, headers, separator); - if(is_subpart) - { - scanPastSeparator(istr, limit, separator); - } - } - } - if(mError) return false; - return parsed_something; -} - -bool LLMimeParser::Impl::parseHeaders( - std::istream& istr, - S32 limit, - LLSD& headers) -{ - // Headers specified in rfc-2045 will be canonicalized below. - static const S32 KNOWN_HEADER_COUNT = 6; - static const std::string KNOWN_HEADER[KNOWN_HEADER_COUNT] = - { - HTTP_HEADER_CONTENT_LENGTH, - HTTP_HEADER_CONTENT_TYPE, - HTTP_HEADER_MIME_VERSION, - HTTP_HEADER_CONTENT_TRANSFER_ENCODING, - HTTP_HEADER_CONTENT_ID, - HTTP_HEADER_CONTENT_DESCRIPTION, - }; - - while(continueParse()) - { - // Get the next line. - // We subtract 1 from the limit so that we make sure - // not to read past limit when we get() the newline. - S32 max_get = llmin((S32)LINE_BUFFER_LENGTH, limit - mScanCount - 1); - istr.getline(mBuffer, max_get, '\r'); - mScanCount += (S32)istr.gcount(); - int c = istr.get(); - if(EOF == c) - { - mContinue = false; - return false; - } - ++mScanCount; - if(c != '\n') - { - mError = true; - return false; - } - if(mScanCount >= limit) - { - mContinue = false; - } - - // Check if that's the end of headers. - if('\0' == mBuffer[0]) - { - break; - } - - // Split out the name and value. - // *NOTE: The use of strchr() here is safe since mBuffer is - // guaranteed to be NULL terminated from the call to getline() - // above. - char* colon = strchr(mBuffer, ':'); - if(!colon) - { - mError = true; - return false; - } - - // Cononicalize the name part, and store the name: value in - // the headers structure. We do this by iterating through - // 'known' headers and replacing the value found with the - // correct one. - // *NOTE: Not so efficient, but iterating through a small - // subset should not be too much of an issue. - std::string name(mBuffer, colon++ - mBuffer); - while(isspace(*colon)) ++colon; - std::string value(colon); - for(S32 ii = 0; ii < KNOWN_HEADER_COUNT; ++ii) - { - if(0 == LLStringUtil::compareInsensitive(name, KNOWN_HEADER[ii])) - { - name = KNOWN_HEADER[ii]; - break; - } - } - headers[name] = value; - } - if(headers.isUndefined()) return false; - return true; -} - -std::string LLMimeParser::Impl::findSeparator(std::string header) -{ - // 01234567890 - //Content-Type: multipart/mixed; boundary="segment" - std::string separator; - std::string::size_type pos = header.find(BOUNDARY); - if(std::string::npos == pos) return separator; - pos += BOUNDARY.size() + 1; - std::string::size_type end; - if(header[pos] == '"') - { - // the boundary is quoted, find the end from pos, and take the - // substring. - end = header.find('"', ++pos); - if(std::string::npos == end) - { - // poorly formed boundary. - mError = true; - } - } - else - { - // otherwise, it's every character until a whitespace, end of - // line, or another parameter begins. - end = header.find_first_of(END_OF_CONTENT_PARAMETER, pos); - if(std::string::npos == end) - { - // it goes to the end of the string. - end = header.size(); - } - } - if(!mError) separator = header.substr(pos, end - pos); - return separator; -} - -void LLMimeParser::Impl::scanPastSeparator( - std::istream& istr, - S32 limit, - const std::string& sep) -{ - std::ostringstream ostr; - ostr << SEPARATOR_PREFIX << sep; - std::string separator = ostr.str(); - bool found_separator = false; - while(!found_separator && continueParse()) - { - // Subtract 1 from the limit so that we make sure not to read - // past limit when we get() the newline. - S32 max_get = llmin((S32)LINE_BUFFER_LENGTH, limit - mScanCount - 1); - istr.getline(mBuffer, max_get, '\r'); - mScanCount += (S32)istr.gcount(); - if(istr.gcount() >= LINE_BUFFER_LENGTH - 1) - { - // that's way too long to be a separator, so ignore it. - continue; - } - int c = istr.get(); - if(EOF == c) - { - mContinue = false; - return; - } - ++mScanCount; - if(c != '\n') - { - mError = true; - return; - } - if(mScanCount >= limit) - { - mContinue = false; - } - if(0 == LLStringUtil::compareStrings(std::string(mBuffer), separator)) - { - found_separator = true; - } - } -} - -void LLMimeParser::Impl::scanPastContent( - std::istream& istr, - S32 limit, - LLSD headers, - const std::string separator) -{ - if(headers.has(HTTP_HEADER_CONTENT_LENGTH)) - { - S32 content_length = headers[HTTP_HEADER_CONTENT_LENGTH].asInteger(); - // Subtract 2 here for the \r\n after the content. - S32 max_skip = llmin(content_length, limit - mScanCount - 2); - istr.ignore(max_skip); - mScanCount += max_skip; - - // *NOTE: Check for hitting the limit and eof here before - // checking for the trailing EOF, because our mime parser has - // to gracefully handle incomplete mime entites. - if((mScanCount >= limit) || istr.eof()) - { - mContinue = false; - } - else if(!eatCRLF(istr)) - { - mError = true; - return; - } - } -} - -bool LLMimeParser::Impl::eatCRLF(std::istream& istr) -{ - int c = istr.get(); - ++mScanCount; - if(c != '\r') - { - return false; - } - c = istr.get(); - ++mScanCount; - if(c != '\n') - { - return false; - } - return true; -} - - -LLMimeParser::LLMimeParser() : mImpl(* new LLMimeParser::Impl) -{ -} - -LLMimeParser::~LLMimeParser() -{ - delete & mImpl; -} - -void LLMimeParser::reset() -{ - mImpl.reset(); -} - -bool LLMimeParser::parseIndex(std::istream& istr, LLMimeIndex& index) -{ - std::string separator; - return mImpl.parseIndex(istr, S32_MAX, separator, false, index); -} - -bool LLMimeParser::parseIndex( - const std::vector& buffer, - LLMimeIndex& index) -{ - LLMemoryStream mstr(&buffer[0], buffer.size()); - return parseIndex(mstr, buffer.size() + 1, index); -} - -bool LLMimeParser::parseIndex( - std::istream& istr, - S32 limit, - LLMimeIndex& index) -{ - std::string separator; - return mImpl.parseIndex(istr, limit, separator, false, index); -} - -bool LLMimeParser::parseIndex(const U8* buffer, S32 length, LLMimeIndex& index) -{ - LLMemoryStream mstr(buffer, length); - return parseIndex(mstr, length + 1, index); -} - -/* -bool LLMimeParser::verify(std::istream& isr, LLMimeIndex& index) const -{ - return false; -} - -bool LLMimeParser::verify(U8* buffer, S32 length, LLMimeIndex& index) const -{ - LLMemoryStream mstr(buffer, length); - return verify(mstr, index); -} -*/ diff --git a/indra/llmessage/llmime.h b/indra/llmessage/llmime.h deleted file mode 100644 index e6617fb503..0000000000 --- a/indra/llmessage/llmime.h +++ /dev/null @@ -1,292 +0,0 @@ -/** - * @file llmime.h - * @author Phoenix - * @date 2006-12-20 - * @brief Declaration of mime tools. - * - * $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_LLMIME_H -#define LL_LLMIME_H - -#include -#include "llsd.h" - -/** - * This file declares various tools for parsing and creating MIME - * objects as described in RFCs 2045, 2046, 2047, 2048, and 2049. - */ - -/** - * @class LLMimeIndex - * @brief Skeletal information useful for handling mime packages. - * @see LLMimeParser - * - * An instance of this class is the parsed output from a LLMimeParser - * which then allows for easy access into a data stream to find and - * get what you want out of it. - * - * This class meant as a tool to quickly find what you seek in a - * parsed mime entity. As such, it does not have useful support for - * modification of a mime entity and specializes the interface toward - * querying data from a fixed mime entity. Modifying an instance of - * LLMimeIndx does not alter a mime entity and changes to a mime - * entity itself are not propogated into an instance of a LLMimeIndex. - * - * Usage:
- * LLMimeIndex mime_index;
- * std::ifstream fstr("package.mime", ios::binary);
- * LLMimeParser parser;
- * if(parser.parseIndex(fstr, mime_index))
- * {
- * std::vector content;
- * content.resize(mime_index.contentLength());
- * fstr.seekg(mime_index.offset(), ios::beg);
- * // ...do work on fstr and content
- * }
- */ -class LLMimeIndex -{ -public: - /* @name Client interface. - */ - //@{ - /** - * @brief Get the full parsed headers for this. - * - * If there are any headers, it will be a map of header name to - * the value found on the line. The name is everything before the - * colon, and the value is the string found after the colon to the - * end of the line after trimming leading whitespace. So, for - * example: - * Content-Type: text/plain - * would become an entry in the headers of: - * headers["Content-Type"] == "text/plain" - * - * If this instance of an index was generated by the - * LLMimeParser::parseIndex() call, all header names in rfc2045 - * will be capitalized as in rfc, eg Content-Length and - * MIME-Version, not content-length and mime-version. - * @return Returns an LLSD map of header name to value. Returns - * undef if there are no headers. - */ - LLSD headers() const; - - /** - * @brief Get the content offset. - * - * @return Returns the number of bytes to the start of the data - * segment from the start of serialized mime entity. Returns -1 if - * offset is not known. - */ - S32 offset() const; - - /** - * @brief Get the length of the data segment for this mime part. - * - * @return Returns the content length in bytes. Returns -1 if - * length is not known. - */ - S32 contentLength() const; - - /** - * @brief Get the mime type associated with this node. - * - * @return Returns the mimetype. - */ - std::string contentType() const; - - /** - * @brief Helper method which simplifies parsing the return from type() - * - * @return Returns true if this is a multipart mime, and therefore - * getting subparts will succeed. - */ - bool isMultipart() const; - - /** - * @brief Get the number of atachments. - * - * @return Returns the number of sub-parts for this. - */ - S32 subPartCount() const; - - /** - * @brief Get the indicated attachment. - * - * @param index Value from 0 to (subPartCount() - 1). - * @return Returns the indicated sub-part, or an invalid mime - * index on failure. - */ - LLMimeIndex subPart(S32 index) const; - //@} - - /* @name Interface for building, testing, and helpers for typical use. - */ - //@{ - /** - * @brief Default constructor - creates a useless LLMimeIndex. - */ - LLMimeIndex(); - - /** - * @brief Full constructor. - * - * @param headers The complete headers. - * @param content_offset The number of bytes to the start of the - * data segment of this mime entity from the start of the stream - * or buffer. - */ - LLMimeIndex(LLSD headers, S32 content_offset); - - /** - * @brief Copy constructor. - * - * @param mime The other mime object. - */ - LLMimeIndex(const LLMimeIndex& mime); - - // @brief Destructor. - ~LLMimeIndex(); - - /* - * @breif Assignment operator. - * - * @param mime The other mime object. - * @return Returns this after assignment. - */ - LLMimeIndex& operator=(const LLMimeIndex& mime); - - /** - * @brief Add attachment information as a sub-part to a multipart mime. - * - * @param sub_part the part to attach. - * @return Returns true on success, false on failure. - */ - bool attachSubPart(LLMimeIndex sub_part); - //@} - -protected: - // Implementation. - class Impl; - Impl* mImpl; -}; - - -/** - * @class LLMimeParser - * @brief This class implements a MIME parser and verifier. - * - * THOROUGH_DESCRIPTION - */ -class LLMimeParser -{ -public: - // @brief Make a new mime parser. - LLMimeParser(); - - // @brief Mime parser Destructor. - ~LLMimeParser(); - - // @brief Reset internal state of this parser. - void reset(); - - - /* @name Index generation interface. - */ - //@{ - /** - * @brief Parse a stream to find the mime index information. - * - * This method will scan the istr until a single complete mime - * entity is read or EOF. The istr will be modified by this - * parsing, so pass in a temporary stream or rewind/reset the - * stream after this call. - * @param istr An istream which contains a mime entity. - * @param index[out] The parsed output. - * @return Returns true if an index was parsed and no errors occurred. - */ - bool parseIndex(std::istream& istr, LLMimeIndex& index); - - /** - * @brief Parse a vector to find the mime index information. - * - * @param buffer A vector with data to parse. - * @param index[out] The parsed output. - * @return Returns true if an index was parsed and no errors occurred. - */ - bool parseIndex(const std::vector& buffer, LLMimeIndex& index); - - /** - * @brief Parse a stream to find the mime index information. - * - * This method will scan the istr until a single complete mime - * entity is read, an EOF, or limit bytes have been scanned. The - * istr will be modified by this parsing, so pass in a temporary - * stream or rewind/reset the stream after this call. - * @param istr An istream which contains a mime entity. - * @param limit The maximum number of bytes to scan. - * @param index[out] The parsed output. - * @return Returns true if an index was parsed and no errors occurred. - */ - bool parseIndex(std::istream& istr, S32 limit, LLMimeIndex& index); - - /** - * @brief Parse a memory bufffer to find the mime index information. - * - * @param buffer The start of the buffer to parse. - * @param buffer_length The length of the buffer. - * @param index[out] The parsed output. - * @return Returns true if an index was parsed and no errors occurred. - */ - bool parseIndex(const U8* buffer, S32 buffer_length, LLMimeIndex& index); - //@} - - /** - * @brief - * - * @return - */ - //bool verify(std::istream& istr, LLMimeIndex& index) const; - - /** - * @brief - * - * @return - */ - //bool verify(U8* buffer, S32 buffer_length, LLMimeIndex& index) const; - -protected: - // Implementation. - class Impl; - Impl& mImpl; - -private: - // @brief Not implemneted to prevent copy consturction. - LLMimeParser(const LLMimeParser& parser); - - // @brief Not implemneted to prevent assignment. - LLMimeParser& operator=(const LLMimeParser& mime); -}; - -#endif // LL_LLMIME_H diff --git a/indra/llmessage/llsdappservices.cpp b/indra/llmessage/llsdappservices.cpp index 8bab91b0c0..c593027802 100644 --- a/indra/llmessage/llsdappservices.cpp +++ b/indra/llmessage/llsdappservices.cpp @@ -121,7 +121,7 @@ public: { //llinfos << "validate: " << name << ", " // << LLSDOStreamer(context) << llendl; - if((std::string("PUT") == context["request"]["verb"].asString()) && !name.empty()) + if((std::string("PUT") == context[CONTEXT_REQUEST][CONTEXT_VERB].asString()) && !name.empty()) { return true; } @@ -139,7 +139,7 @@ public: LLHTTPNode::ResponsePtr response, const LLSD& context) const { - std::string name = context["request"]["wildcard"]["option-name"]; + std::string name = context[CONTEXT_REQUEST][CONTEXT_WILDCARD]["option-name"]; LLSD options = LLApp::instance()->getOptionData( LLApp::PRIORITY_RUNTIME_OVERRIDE); response->result(options[name]); @@ -150,7 +150,7 @@ public: const LLSD& context, const LLSD& input) const { - std::string name = context["request"]["wildcard"]["option-name"]; + std::string name = context[CONTEXT_REQUEST][CONTEXT_WILDCARD]["option-name"]; LLSD options = LLApp::instance()->getOptionData( LLApp::PRIORITY_RUNTIME_OVERRIDE); options[name] = input; @@ -164,7 +164,7 @@ public: LLHTTPNode::ResponsePtr response, const LLSD& context) const { - std::string name = context["request"]["wildcard"]["option-name"]; + std::string name = context[CONTEXT_REQUEST][CONTEXT_WILDCARD]["option-name"]; LLSD options = LLApp::instance()->getOptionData( LLApp::PRIORITY_RUNTIME_OVERRIDE); options.erase(name); @@ -268,7 +268,7 @@ public: LLHTTPNode::ResponsePtr response, const LLSD& context) const { - std::string name = context["request"]["wildcard"]["option-name"]; + std::string name = context[CONTEXT_REQUEST][CONTEXT_WILDCARD]["option-name"]; response->result(LLApp::instance()->getOption(name)); } }; diff --git a/indra/llmessage/llsdhttpserver.cpp b/indra/llmessage/llsdhttpserver.cpp index 5c8fc7b2bb..8ac6b3cb12 100644 --- a/indra/llmessage/llsdhttpserver.cpp +++ b/indra/llmessage/llsdhttpserver.cpp @@ -109,7 +109,7 @@ public: virtual void get(ResponsePtr response, const LLSD& context) const { - const LLSD& remainder = context["request"]["remainder"]; + const LLSD& remainder = context[CONTEXT_REQUEST]["remainder"]; if (remainder.size() > 0) { diff --git a/indra/llmessage/llsdrpcclient.h b/indra/llmessage/llsdrpcclient.h index 02891d4f32..dfd3b7e1d0 100644 --- a/indra/llmessage/llsdrpcclient.h +++ b/indra/llmessage/llsdrpcclient.h @@ -251,7 +251,7 @@ public: LLIOPipe::ptr_t service(new Client); chain.push_back(service); LLIOPipe::ptr_t http_pipe(http); - http->addHeader(HTTP_HEADER_CONTENT_TYPE, HTTP_CONTENT_TEXT_LLSD); + http->addHeader(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_TEXT_LLSD); if(mURL.empty()) { chain.push_back(LLIOPipe::ptr_t(new LLContextURLExtractor(http))); @@ -301,7 +301,7 @@ public: LLIOPipe::ptr_t service(new Client); chain.push_back(service); LLIOPipe::ptr_t http_pipe(http); - http->addHeader(HTTP_HEADER_CONTENT_TYPE, HTTP_CONTENT_TEXT_XML); + http->addHeader(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_TEXT_XML); if(mURL.empty()) { chain.push_back(LLIOPipe::ptr_t(new LLContextURLExtractor(http))); diff --git a/indra/llmessage/lltrustedmessageservice.cpp b/indra/llmessage/lltrustedmessageservice.cpp index 8248b184e9..a67ba35879 100644 --- a/indra/llmessage/lltrustedmessageservice.cpp +++ b/indra/llmessage/lltrustedmessageservice.cpp @@ -42,9 +42,9 @@ void LLTrustedMessageService::post(LLHTTPNode::ResponsePtr response, const LLSD& context, const LLSD& input) const { - std::string name = context["request"]["wildcard"]["message-name"]; - std::string senderIP = context["request"]["remote-host"]; - std::string senderPort = context["request"]["headers"] + std::string name = context[CONTEXT_REQUEST][CONTEXT_WILDCARD]["message-name"]; + std::string senderIP = context[CONTEXT_REQUEST][CONTEXT_REMOTE_HOST]; + std::string senderPort = context[CONTEXT_REQUEST][CONTEXT_HEADERS] ["x-secondlife-udp-listen-port"]; LLSD message_data; diff --git a/indra/llmessage/lltrustedmessageservice.h b/indra/llmessage/lltrustedmessageservice.h index 688937ac2c..12a37bb535 100644 --- a/indra/llmessage/lltrustedmessageservice.h +++ b/indra/llmessage/lltrustedmessageservice.h @@ -30,6 +30,10 @@ #include "linden_common.h" #include "llhttpnode.h" +// These are defined in lliosocket.h/lliosocket.cpp +extern const std::string CONTEXT_REMOTE_HOST; +extern const std::string CONTEXT_REMOTE_PORT; + class LLSD; class LLTrustedMessageService diff --git a/indra/llmessage/llurlrequest.cpp b/indra/llmessage/llurlrequest.cpp index f354f5ab5d..49cfef2771 100644 --- a/indra/llmessage/llurlrequest.cpp +++ b/indra/llmessage/llurlrequest.cpp @@ -47,11 +47,12 @@ 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 */ @@ -479,7 +480,7 @@ bool LLURLRequest::configure() 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_HEADER_EXPECT); + addHeader(HTTP_OUT_HEADER_EXPECT); mDetail->mCurlRequest->setopt(CURLOPT_UPLOAD, 1); mDetail->mCurlRequest->setopt(CURLOPT_INFILESIZE, bytes); @@ -489,11 +490,11 @@ bool LLURLRequest::configure() 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_HEADER_EXPECT); + addHeader(HTTP_OUT_HEADER_EXPECT); // Disable the content type http header. // *FIX: what should it be? - addHeader(HTTP_HEADER_CONTENT_TYPE); + addHeader(HTTP_OUT_HEADER_CONTENT_TYPE); // Set the handle for an http post mDetail->mCurlRequest->setPost(NULL, bytes); diff --git a/indra/llmessage/llurlrequest.h b/indra/llmessage/llurlrequest.h index f334c92cc3..f3d32f5419 100644 --- a/indra/llmessage/llurlrequest.h +++ b/indra/llmessage/llurlrequest.h @@ -42,9 +42,10 @@ #include "llcurl.h" -extern const std::string CONTEXT_REQUEST; +/** + * External constants + */ extern const std::string CONTEXT_DEST_URI_SD_LABEL; -extern const std::string CONTEXT_RESPONSE; extern const std::string CONTEXT_TRANSFERED_BYTES; class LLURLRequestDetail; @@ -352,11 +353,4 @@ protected: EStatus mRequestStatus; }; - - -/** - * External constants - */ -extern const std::string CONTEXT_DEST_URI_SD_LABEL; - #endif // LL_LLURLREQUEST_H diff --git a/indra/llmessage/message.cpp b/indra/llmessage/message.cpp index 78b259c3f1..5a63da628d 100644 --- a/indra/llmessage/message.cpp +++ b/indra/llmessage/message.cpp @@ -152,7 +152,7 @@ class LLMessageHandlerBridge : public LLHTTPNode void LLMessageHandlerBridge::post(LLHTTPNode::ResponsePtr response, const LLSD& context, const LLSD& input) const { - std::string name = context["request"]["wildcard"]["message-name"]; + std::string name = context[CONTEXT_REQUEST][CONTEXT_WILDCARD]["message-name"]; char* namePtr = LLMessageStringTable::getInstance()->getString(name.c_str()); lldebugs << "Setting mLastSender " << input["sender"].asString() << llendl; diff --git a/indra/llmessage/tests/llhttpclient_test.cpp b/indra/llmessage/tests/llhttpclient_test.cpp index bdc48ce53d..43fac83c57 100644 --- a/indra/llmessage/tests/llhttpclient_test.cpp +++ b/indra/llmessage/tests/llhttpclient_test.cpp @@ -40,8 +40,6 @@ #include "llproxy.h" #include "llpumpio.h" -#include "llsdhttpserver.h" -#include "lliohttpserver.h" #include "lliosocket.h" #include "stringize.h" diff --git a/indra/llmessage/tests/llhttpnode_stub.cpp b/indra/llmessage/tests/llhttpnode_stub.cpp new file mode 100644 index 0000000000..cc2108fed5 --- /dev/null +++ b/indra/llmessage/tests/llhttpnode_stub.cpp @@ -0,0 +1,112 @@ +/** + * @file llhttpnode_stub.cpp + * @brief STUB Implementation of classes for generic HTTP/LSL/REST handling. + * + * $LicenseInfo:firstyear=2006&license=viewergpl$ + * + * Copyright (c) 2006-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "linden_common.h" +#include "llhttpnode.h" + +const std::string CONTEXT_VERB("verb"); +const std::string CONTEXT_REQUEST("request"); +const std::string CONTEXT_WILDCARD("wildcard"); +const std::string CONTEXT_PATH("path"); +const std::string CONTEXT_QUERY_STRING("query-string"); +const std::string CONTEXT_REMOTE_HOST("remote-host"); +const std::string CONTEXT_REMOTE_PORT("remote-port"); +const std::string CONTEXT_HEADERS("headers"); +const std::string CONTEXT_RESPONSE("response"); + +/** + * LLHTTPNode + */ +class LLHTTPNode::Impl +{ + // dummy +}; + +LLHTTPNode::LLHTTPNode(): impl(*new Impl) {} +LLHTTPNode::~LLHTTPNode() {} +LLSD LLHTTPNode::simpleGet() const { return LLSD(); } +LLSD LLHTTPNode::simplePut(const LLSD& input) const { return LLSD(); } +LLSD LLHTTPNode::simplePost(const LLSD& input) const { return LLSD(); } +LLSD LLHTTPNode::simpleDel(const LLSD&) const { return LLSD(); } +void LLHTTPNode::get(LLHTTPNode::ResponsePtr response, const LLSD& context) const {} +void LLHTTPNode::put(LLHTTPNode::ResponsePtr response, const LLSD& context, const LLSD& input) const {} +void LLHTTPNode::post(LLHTTPNode::ResponsePtr response, const LLSD& context, const LLSD& input) const {} +void LLHTTPNode::del(LLHTTPNode::ResponsePtr response, const LLSD& context) const {} +void LLHTTPNode::options(ResponsePtr response, const LLSD& context) const {} +LLHTTPNode* LLHTTPNode::getChild(const std::string& name, LLSD& context) const { return NULL; } +bool LLHTTPNode::handles(const LLSD& remainder, LLSD& context) const { return false; } +bool LLHTTPNode::validate(const std::string& name, LLSD& context) const { return false; } +const LLHTTPNode* LLHTTPNode::traverse(const std::string& path, LLSD& context) const { return NULL; } +void LLHTTPNode::addNode(const std::string& path, LLHTTPNode* nodeToAdd) { } +LLSD LLHTTPNode::allNodePaths() const { return LLSD(); } +const LLHTTPNode* LLHTTPNode::rootNode() const { return NULL; } +const LLHTTPNode* LLHTTPNode::findNode(const std::string& name) const { return NULL; } + +LLHTTPNode::Response::~Response(){} +void LLHTTPNode::Response::notFound(const std::string& message) +{ + status(404, message); +} +void LLHTTPNode::Response::notFound() +{ + status(404, "Not Found"); +} +void LLHTTPNode::Response::methodNotAllowed() +{ + status(405, "Method Not Allowed"); +} +void LLHTTPNode::Response::statusUnknownError(S32 code) +{ + status(code, "Unknown Error"); +} + +void LLHTTPNode::Response::status(S32 code, const std::string& message) +{ +} + +void LLHTTPNode::Response::addHeader(const std::string& name,const std::string& value) +{ + mHeaders[name] = value; +} +void LLHTTPNode::describe(Description& desc) const { } + + +const LLChainIOFactory* LLHTTPNode::getProtocolHandler() const { return NULL; } + + +LLHTTPRegistrar::NodeFactory::~NodeFactory() { } + +void LLHTTPRegistrar::registerFactory( + const std::string& path, NodeFactory& factory) {} +void LLHTTPRegistrar::buildAllServices(LLHTTPNode& root) {} + + diff --git a/indra/llmessage/tests/llmime_test.cpp b/indra/llmessage/tests/llmime_test.cpp deleted file mode 100644 index f8bf03bbcb..0000000000 --- a/indra/llmessage/tests/llmime_test.cpp +++ /dev/null @@ -1,446 +0,0 @@ -/** - * @file llmime_test.cpp - * @author Phoenix - * @date 2006-12-24 - * @brief BRIEF_DESC of llmime_test.cpp - * - * $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 "llsdserialize.h" -#include "llhttpconstants.cpp" - -#include "../llmime.h" - -#include "../test/lltut.h" - -namespace tut -{ - struct mime_index - { - }; - typedef test_group mime_index_t; - typedef mime_index_t::object mime_index_object_t; - tut::mime_index_t tut_mime_index("LLMime"); - - template<> template<> - void mime_index_object_t::test<1>() - { - LLMimeIndex mime; - ensure("no headers", mime.headers().isUndefined()); - ensure_equals("invalid offset", mime.offset(), -1); - ensure_equals("invalid content length", mime.contentLength(), -1); - ensure("no content type", mime.contentType().empty()); - ensure("not multipart", !mime.isMultipart()); - ensure_equals("no attachments", mime.subPartCount(), 0); - } - - template<> template<> - void mime_index_object_t::test<2>() - { - const S32 CONTENT_LENGTH = 6000; - const S32 CONTENT_OFFSET = 100; - const std::string CONTENT_TYPE = std::string("image/j2c"); - LLSD headers; - headers["Content-Length"] = CONTENT_LENGTH; - headers["Content-Type"] = CONTENT_TYPE; - LLMimeIndex mime(headers, CONTENT_OFFSET); - ensure("headers are map", mime.headers().isMap()); - ensure_equals("offset", mime.offset(), CONTENT_OFFSET); - ensure_equals("content length", mime.contentLength(), CONTENT_LENGTH); - ensure_equals("type is image/j2c", mime.contentType(), CONTENT_TYPE); - ensure("not multipart", !mime.isMultipart()); - ensure_equals("no attachments", mime.subPartCount(), 0); - } - - template<> template<> - void mime_index_object_t::test<3>() - { - const S32 MULTI_CONTENT_LENGTH = 8000; - const S32 MULTI_CONTENT_OFFSET = 100; - const std::string MULTI_CONTENT_TYPE = std::string("multipart/mixed"); - LLSD headers; - headers["Content-Length"] = MULTI_CONTENT_LENGTH; - headers["Content-Type"] = MULTI_CONTENT_TYPE; - LLMimeIndex mime(headers, MULTI_CONTENT_OFFSET); - llinfos << "headers: " << LLSDOStreamer(headers) - << llendl; - - - const S32 META_CONTENT_LENGTH = 700; - const S32 META_CONTENT_OFFSET = 69; - const std::string META_CONTENT_TYPE = std::string( - "text/llsd+xml"); - headers = LLSD::emptyMap(); - headers["Content-Length"] = META_CONTENT_LENGTH; - headers["Content-Type"] = META_CONTENT_TYPE; - LLMimeIndex meta(headers, META_CONTENT_OFFSET); - mime.attachSubPart(meta); - - const S32 IMAGE_CONTENT_LENGTH = 6000; - const S32 IMAGE_CONTENT_OFFSET = 200; - const std::string IMAGE_CONTENT_TYPE = std::string("image/j2c"); - headers = LLSD::emptyMap(); - headers["Content-Length"] = IMAGE_CONTENT_LENGTH; - headers["Content-Type"] = IMAGE_CONTENT_TYPE; - LLMimeIndex image(headers, IMAGE_CONTENT_OFFSET); - mime.attachSubPart(image); - - // make sure we have a valid multi-part - ensure("is multipart", mime.isMultipart()); - ensure_equals("multi offset", mime.offset(), MULTI_CONTENT_OFFSET); - ensure_equals( - "multi content length", - mime.contentLength(), - MULTI_CONTENT_LENGTH); - ensure_equals("two attachments", mime.subPartCount(), 2); - - // make sure ranged gets do the right thing with out of bounds - // sub-parts. - LLMimeIndex invalid_child(mime.subPart(-1)); - ensure("no headers", invalid_child.headers().isUndefined()); - ensure_equals("invalid offset", invalid_child.offset(), -1); - ensure_equals( - "invalid content length", invalid_child.contentLength(), -1); - ensure("no content type", invalid_child.contentType().empty()); - ensure("not multipart", !invalid_child.isMultipart()); - ensure_equals("no attachments", invalid_child.subPartCount(), 0); - - invalid_child = mime.subPart(2); - ensure("no headers", invalid_child.headers().isUndefined()); - ensure_equals("invalid offset", invalid_child.offset(), -1); - ensure_equals( - "invalid content length", invalid_child.contentLength(), -1); - ensure("no content type", invalid_child.contentType().empty()); - ensure("not multipart", !invalid_child.isMultipart()); - ensure_equals("no attachments", invalid_child.subPartCount(), 0); - } - - template<> template<> - void mime_index_object_t::test<4>() - { - const S32 MULTI_CONTENT_LENGTH = 8000; - const S32 MULTI_CONTENT_OFFSET = 100; - const std::string MULTI_CONTENT_TYPE = std::string("multipart/mixed"); - LLSD headers; - headers["Content-Length"] = MULTI_CONTENT_LENGTH; - headers["Content-Type"] = MULTI_CONTENT_TYPE; - LLMimeIndex mime(headers, MULTI_CONTENT_OFFSET); - - const S32 META_CONTENT_LENGTH = 700; - const S32 META_CONTENT_OFFSET = 69; - const std::string META_CONTENT_TYPE = std::string( - "application/llsd+xml"); - headers = LLSD::emptyMap(); - headers["Content-Length"] = META_CONTENT_LENGTH; - headers["Content-Type"] = META_CONTENT_TYPE; - LLMimeIndex meta(headers, META_CONTENT_OFFSET); - mime.attachSubPart(meta); - - const S32 IMAGE_CONTENT_LENGTH = 6000; - const S32 IMAGE_CONTENT_OFFSET = 200; - const std::string IMAGE_CONTENT_TYPE = std::string("image/j2c"); - headers = LLSD::emptyMap(); - headers["Content-Length"] = IMAGE_CONTENT_LENGTH; - headers["Content-Type"] = IMAGE_CONTENT_TYPE; - LLMimeIndex image(headers, IMAGE_CONTENT_OFFSET); - mime.attachSubPart(image); - - // check what we have - ensure("is multipart", mime.isMultipart()); - ensure_equals("multi offset", mime.offset(), MULTI_CONTENT_OFFSET); - ensure_equals( - "multi content length", - mime.contentLength(), - MULTI_CONTENT_LENGTH); - ensure_equals("two attachments", mime.subPartCount(), 2); - - LLMimeIndex actual_meta = mime.subPart(0); - ensure_equals( - "meta type", actual_meta.contentType(), META_CONTENT_TYPE); - ensure_equals( - "meta offset", actual_meta.offset(), META_CONTENT_OFFSET); - ensure_equals( - "meta content length", - actual_meta.contentLength(), - META_CONTENT_LENGTH); - - LLMimeIndex actual_image = mime.subPart(1); - ensure_equals( - "image type", actual_image.contentType(), IMAGE_CONTENT_TYPE); - ensure_equals( - "image offset", actual_image.offset(), IMAGE_CONTENT_OFFSET); - ensure_equals( - "image content length", - actual_image.contentLength(), - IMAGE_CONTENT_LENGTH); - } - -/* - template<> template<> - void mime_index_object_t::test<5>() - { - } - template<> template<> - void mime_index_object_t::test<6>() - { - } - template<> template<> - void mime_index_object_t::test<7>() - { - } - template<> template<> - void mime_index_object_t::test<8>() - { - } - template<> template<> - void mime_index_object_t::test<>() - { - } -*/ -} - - -namespace tut -{ - struct mime_parse - { - }; - typedef test_group mime_parse_t; - typedef mime_parse_t::object mime_parse_object_t; - tut::mime_parse_t tut_mime_parse("LLMimeParse"); - - template<> template<> - void mime_parse_object_t::test<1>() - { - // parse one mime object - const std::string SERIALIZED_MIME("Content-Length: 200\r\nContent-Type: text/plain\r\n\r\naaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbcccccccccc\r\n"); - std::stringstream istr; - istr.str(SERIALIZED_MIME); - LLMimeIndex mime; - LLMimeParser parser; - bool ok = parser.parseIndex(istr, mime); - ensure("Parse successful.", ok); - ensure_equals("content type", mime.contentType(), "text/plain"); - ensure_equals("content length", mime.contentLength(), 200); - ensure_equals("offset", mime.offset(), 49); - } - - template<> template<> - void mime_parse_object_t::test<2>() - { - // make sure we only parse one. - const std::string SERIALIZED_MIME("Content-Length: 200\r\nContent-Type: text/plain\r\n\r\naaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbcccccccccc\r\n\r\nContent-Length: 200\r\nContent-Type: text/plain\r\n\r\naaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbcccccccccc\r\n\r\n"); - std::stringstream istr; - istr.str(SERIALIZED_MIME); - LLMimeIndex mime; - LLMimeParser parser; - bool ok = parser.parseIndex(istr, mime); - ensure("Parse successful.", ok); - ensure("not multipart.", !mime.isMultipart()); - ensure_equals("content type", mime.contentType(), "text/plain"); - ensure_equals("content length", mime.contentLength(), 200); - ensure_equals("offset", mime.offset(), 49); - } - - template<> template<> - void mime_parse_object_t::test<3>() - { - // test multi-part and lack of content length for some of it. - /* -Content-Type: multipart/mixed; boundary="segment"rnContent-Length: 148rnrn--segmentrnContent-Type: text/plainrnrnsome datarnrn--segmentrnContent-Type: text/xml; charset=UTF-8rnContent-Length: 22rnrnrnrn - */ - const std::string SERIALIZED_MIME("Content-Type: multipart/mixed; boundary=\"segment\"\r\nContent-Length: 150\r\n\r\n--segment\r\nContent-Type: text/plain\r\n\r\nsome data\r\n\r\n--segment\r\nContent-Type: text/xml; charset=UTF-8\r\nContent-Length: 22\r\n\r\n\r\n\r\n"); - std::stringstream istr; - istr.str(SERIALIZED_MIME); - LLMimeIndex mime; - LLMimeParser parser; - bool ok = parser.parseIndex(istr, mime); - ensure("Parse successful.", ok); - ensure("is multipart.", mime.isMultipart()); - ensure_equals("sub-part count", mime.subPartCount(), 2); - ensure_equals("content length", mime.contentLength(), 150); - ensure_equals("data offset for multipart", mime.offset(), 74); - - LLMimeIndex mime_plain(mime.subPart(0)); - ensure_equals( - "first part type", - mime_plain.contentType(), - "text/plain"); - ensure_equals( - "first part content length not known.", - mime_plain.contentLength(), - -1); - ensure_equals("first part offset", mime_plain.offset(), 113); - - LLMimeIndex mime_xml(mime.subPart(1)); - ensure_equals( - "second part type", - mime_xml.contentType(), - "text/xml; charset=UTF-8"); - ensure_equals( - "second part content length", - mime_xml.contentLength(), - 22); - ensure_equals("second part offset", mime_xml.offset(), 198); - } - - template<> template<> - void mime_parse_object_t::test<4>() - { - // test multi-part, unquoted separator, and premature eof conditions - /* -Content-Type: multipart/mixed; boundary=segmentrnContent-Length: 220rnrn--segmentrnContent-Type: text/plainrnContent-Length: 55rnrnhow are you today?rnI do not know. I guess I am:n'fine'rnrn--segmentrnContent-Type: text/xml; charset=UTF-8rnContent-Length: 22rnrnrnrn */ - const std::string SERIALIZED_MIME("Content-Type: multipart/mixed; boundary=segment\r\nContent-Length: 220\r\n\r\n--segment\r\nContent-Type: text/plain\r\nContent-Length: 55\r\n\r\nhow are you today?\r\nI do not know. I guess I am:\n'fine'\r\n\r\n--segment\r\nContent-Type: text/xml; charset=UTF-8\r\nContent-Length: 22\r\n\r\n\r\n\r\n"); - std::stringstream istr; - istr.str(SERIALIZED_MIME); - LLMimeIndex mime; - LLMimeParser parser; - bool ok = parser.parseIndex(istr, mime); - ensure("Parse successful.", ok); - ensure("is multipart.", mime.isMultipart()); - ensure_equals("sub-part count", mime.subPartCount(), 2); - ensure_equals("content length", mime.contentLength(), 220); - ensure_equals("data offset for multipart", mime.offset(), 72); - - LLMimeIndex mime_plain(mime.subPart(0)); - ensure_equals( - "first part type", - mime_plain.contentType(), - "text/plain"); - ensure_equals( - "first part content length", - mime_plain.contentLength(), - 55); - ensure_equals("first part offset", mime_plain.offset(), 131); - - LLMimeIndex mime_xml(mime.subPart(1)); - ensure_equals( - "second part type", - mime_xml.contentType(), - "text/xml; charset=UTF-8"); - ensure_equals( - "second part content length", - mime_xml.contentLength(), - 22); - ensure_equals("second part offset", mime_xml.offset(), 262); - } - - template<> template<> - void mime_parse_object_t::test<5>() - { - // test multi-part with multiple params - const std::string SERIALIZED_MIME("Content-Type: multipart/mixed; boundary=segment; comment=\"testing multiple params.\"\r\nContent-Length: 220\r\n\r\n--segment\r\nContent-Type: text/plain\r\nContent-Length: 55\r\n\r\nhow are you today?\r\nI do not know. I guess I am:\n'fine'\r\n\r\n--segment\r\nContent-Type: text/xml; charset=UTF-8\r\nContent-Length: 22\r\n\r\n\r\n\r\n"); - std::stringstream istr; - istr.str(SERIALIZED_MIME); - LLMimeIndex mime; - LLMimeParser parser; - bool ok = parser.parseIndex(istr, mime); - ensure("Parse successful.", ok); - ensure("is multipart.", mime.isMultipart()); - ensure_equals("sub-part count", mime.subPartCount(), 2); - ensure_equals("content length", mime.contentLength(), 220); - - LLMimeIndex mime_plain(mime.subPart(0)); - ensure_equals( - "first part type", - mime_plain.contentType(), - "text/plain"); - ensure_equals( - "first part content length", - mime_plain.contentLength(), - 55); - - LLMimeIndex mime_xml(mime.subPart(1)); - ensure_equals( - "second part type", - mime_xml.contentType(), - "text/xml; charset=UTF-8"); - ensure_equals( - "second part content length", - mime_xml.contentLength(), - 22); - } - - template<> template<> - void mime_parse_object_t::test<6>() - { - // test multi-part with no specified boundary and eof -/* -Content-Type: multipart/relatedrnContent-Length: 220rnrn--rnContent-Type: text/plainrnContent-Length: 55rnrnhow are you today?rnI do not know. I guess I am:n'fine'rnrn--rnContent-Type: text/xml; charset=UTF-8rnContent-Length: 22rnrnrnrn -*/ - const std::string SERIALIZED_MIME("Content-Type: multipart/related\r\nContent-Length: 500\r\n\r\n--\r\nContent-Type: text/plain\r\nContent-Length: 55\r\n\r\nhow are you today?\r\nI do not know. I guess I am:\n'fine'\r\n\r\n--\r\nContent-Type: text/xml; charset=UTF-8\r\nContent-Length: 22\r\n\r\n\r\n\r\n"); - std::stringstream istr; - istr.str(SERIALIZED_MIME); - LLMimeIndex mime; - LLMimeParser parser; - bool ok = parser.parseIndex(istr, mime); - ensure("Parse successful.", ok); - ensure("is multipart.", mime.isMultipart()); - ensure_equals("sub-part count", mime.subPartCount(), 2); - ensure_equals("content length", mime.contentLength(), 500); - ensure_equals("data offset for multipart", mime.offset(), 56); - - LLMimeIndex mime_plain(mime.subPart(0)); - ensure_equals( - "first part type", - mime_plain.contentType(), - "text/plain"); - ensure_equals( - "first part content length", - mime_plain.contentLength(), - 55); - ensure_equals("first part offset", mime_plain.offset(), 108); - - LLMimeIndex mime_xml(mime.subPart(1)); - ensure_equals( - "second part type", - mime_xml.contentType(), - "text/xml; charset=UTF-8"); - ensure_equals( - "second part content length", - mime_xml.contentLength(), - 22); - ensure_equals("second part offset", mime_xml.offset(), 232); - } - -/* - template<> template<> - void mime_parse_object_t::test<>() - { - } - template<> template<> - void mime_parse_object_t::test<>() - { - } - template<> template<> - void mime_parse_object_t::test<>() - { - } - template<> template<> - void mime_parse_object_t::test<>() - { - } -*/ -} diff --git a/indra/llmessage/tests/lltrustedmessageservice_test.cpp b/indra/llmessage/tests/lltrustedmessageservice_test.cpp index b287a29841..55748ad27e 100644 --- a/indra/llmessage/tests/lltrustedmessageservice_test.cpp +++ b/indra/llmessage/tests/lltrustedmessageservice_test.cpp @@ -32,6 +32,7 @@ #include "message.h" #include "llmessageconfig.h" +#include "llhttpnode_stub.cpp" LLMessageSystem* gMessageSystem = NULL; diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index 43439bac5d..0b85f438bd 100755 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -3008,8 +3008,8 @@ public: #if 0 // *TODO: Honor server Retry-After header. // Status 503 may ask us to wait for a certain amount of time before retrying. - if (!headers.has(HTTP_HEADER_RETRY_AFTER) - || !getSecondsUntilRetryAfter(headers[HTTP_HEADER_RETRY_AFTER].asStringRef(), seconds_to_wait)) + if (!headers.has(HTTP_IN_HEADER_RETRY_AFTER) + || !getSecondsUntilRetryAfter(headers[HTTP_IN_HEADER_RETRY_AFTER].asStringRef(), seconds_to_wait)) #endif { seconds_to_wait = mDelay; diff --git a/indra/newview/llfloaterabout.cpp b/indra/newview/llfloaterabout.cpp index 63888ace11..801a24f472 100644 --- a/indra/newview/llfloaterabout.cpp +++ b/indra/newview/llfloaterabout.cpp @@ -474,8 +474,7 @@ void LLServerReleaseNotesURLFetcher::httpCompleted() LLFloaterAbout* floater_about = LLFloaterReg::getTypedInstance("sl_about"); if (floater_about) { - const bool check_lower = true; - const std::string& location = getResponseHeader(HTTP_HEADER_LOCATION, check_lower); + const std::string& location = getResponseHeader(HTTP_IN_HEADER_LOCATION); if (location.empty()) { LL_WARNS("ServerReleaseNotes") << "Missing Location header " diff --git a/indra/newview/llfloaterurlentry.cpp b/indra/newview/llfloaterurlentry.cpp index 0751c830d5..e26f1e9ea5 100644 --- a/indra/newview/llfloaterurlentry.cpp +++ b/indra/newview/llfloaterurlentry.cpp @@ -59,8 +59,7 @@ public: private: /* virtual */ void httpCompleted() { - const bool check_lower = true; - const std::string& media_type = getResponseHeader(HTTP_HEADER_CONTENT_TYPE, check_lower); + const std::string& media_type = getResponseHeader(HTTP_IN_HEADER_CONTENT_TYPE); std::string::size_type idx1 = media_type.find_first_of(";"); std::string mime_type = media_type.substr(0, idx1); diff --git a/indra/newview/llmarketplacefunctions.cpp b/indra/newview/llmarketplacefunctions.cpp index bea1d62b93..28e1df725a 100644 --- a/indra/newview/llmarketplacefunctions.cpp +++ b/indra/newview/llmarketplacefunctions.cpp @@ -167,8 +167,7 @@ namespace LLMarketplaceImport protected: /* virtual */ void httpCompleted() { - const bool check_lower = true; - const std::string& set_cookie_string = getResponseHeader(HTTP_HEADER_SET_COOKIE, check_lower); + const std::string& set_cookie_string = getResponseHeader(HTTP_IN_HEADER_SET_COOKIE); if (!set_cookie_string.empty()) { @@ -278,10 +277,11 @@ namespace LLMarketplaceImport // Make the headers for the post LLSD headers = LLSD::emptyMap(); - headers[HTTP_HEADER_ACCEPT] = "*/*"; - headers[HTTP_HEADER_COOKIE] = sMarketplaceCookie; - headers[HTTP_HEADER_CONTENT_TYPE] = HTTP_CONTENT_LLSD_XML; - headers[HTTP_HEADER_USER_AGENT] = LLViewerMedia::getCurrentUserAgent(); + headers[HTTP_OUT_HEADER_ACCEPT] = "*/*"; + headers[HTTP_OUT_HEADER_COOKIE] = sMarketplaceCookie; + // *TODO: Why are we setting Content-Type for a GET request? + headers[HTTP_OUT_HEADER_CONTENT_TYPE] = HTTP_CONTENT_LLSD_XML; + headers[HTTP_OUT_HEADER_USER_AGENT] = LLViewerMedia::getCurrentUserAgent(); if (gSavedSettings.getBOOL("InventoryOutboxLogging")) { @@ -311,12 +311,11 @@ namespace LLMarketplaceImport // Make the headers for the post LLSD headers = LLSD::emptyMap(); - headers[HTTP_HEADER_ACCEPT] = "*/*"; - headers[HTTP_HEADER_CONNECTION] = "Keep-Alive"; - headers[HTTP_HEADER_COOKIE] = sMarketplaceCookie; - // *TODO: Should this be 'application/llsd+xml'? - headers[HTTP_HEADER_CONTENT_TYPE] = HTTP_CONTENT_XML; - headers[HTTP_HEADER_USER_AGENT] = LLViewerMedia::getCurrentUserAgent(); + headers[HTTP_OUT_HEADER_ACCEPT] = "*/*"; + headers[HTTP_OUT_HEADER_CONNECTION] = "Keep-Alive"; + headers[HTTP_OUT_HEADER_COOKIE] = sMarketplaceCookie; + headers[HTTP_OUT_HEADER_CONTENT_TYPE] = HTTP_CONTENT_XML; + headers[HTTP_OUT_HEADER_USER_AGENT] = LLViewerMedia::getCurrentUserAgent(); if (gSavedSettings.getBOOL("InventoryOutboxLogging")) { diff --git a/indra/newview/llmediadataclient.cpp b/indra/newview/llmediadataclient.cpp index bc1aa087e5..691be13610 100644 --- a/indra/newview/llmediadataclient.cpp +++ b/indra/newview/llmediadataclient.cpp @@ -579,8 +579,8 @@ void LLMediaDataClient::Responder::httpFailure() F32 retry_timeout; #if 0 // *TODO: Honor server Retry-After header. - if (!hasResponseHeader(HTTP_HEADER_RETRY_AFTER) - || !getSecondsUntilRetryAfter(getResponseHeader(HTTP_HEADER_RETRY_AFTER), retry_timeout)) + if (!hasResponseHeader(HTTP_IN_HEADER_RETRY_AFTER) + || !getSecondsUntilRetryAfter(getResponseHeader(HTTP_IN_HEADER_RETRY_AFTER), retry_timeout)) #endif { retry_timeout = mRequest->getRetryTimerDelay(); diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 1469dbc346..524467a37e 100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -808,7 +808,7 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id) //reading from VFS failed for whatever reason, fetch from sim std::vector headers; - headers.push_back(HTTP_HEADER_ACCEPT + ": " + HTTP_CONTENT_OCTET_STREAM); + headers.push_back(HTTP_OUT_HEADER_ACCEPT + ": " + HTTP_CONTENT_OCTET_STREAM); std::string http_url = constructUrl(mesh_id); if (!http_url.empty()) @@ -890,7 +890,7 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id) //reading from VFS failed for whatever reason, fetch from sim std::vector headers; - headers.push_back(HTTP_HEADER_ACCEPT + ": " + HTTP_CONTENT_OCTET_STREAM); + headers.push_back(HTTP_OUT_HEADER_ACCEPT + ": " + HTTP_CONTENT_OCTET_STREAM); std::string http_url = constructUrl(mesh_id); if (!http_url.empty()) @@ -971,7 +971,7 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id) //reading from VFS failed for whatever reason, fetch from sim std::vector headers; - headers.push_back(HTTP_HEADER_ACCEPT + ": " + HTTP_CONTENT_OCTET_STREAM); + headers.push_back(HTTP_OUT_HEADER_ACCEPT + ": " + HTTP_CONTENT_OCTET_STREAM); std::string http_url = constructUrl(mesh_id); if (!http_url.empty()) @@ -1052,7 +1052,7 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, U32& c //either cache entry doesn't exist or is corrupt, request header from simulator bool retval = true ; std::vector headers; - headers.push_back(HTTP_HEADER_ACCEPT + ": " + HTTP_CONTENT_OCTET_STREAM); + headers.push_back(HTTP_OUT_HEADER_ACCEPT + ": " + HTTP_CONTENT_OCTET_STREAM); std::string http_url = constructUrl(mesh_params.getSculptID()); if (!http_url.empty()) @@ -1127,7 +1127,7 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, //reading from VFS failed for whatever reason, fetch from sim std::vector headers; - headers.push_back(HTTP_HEADER_ACCEPT + ": " + HTTP_CONTENT_OCTET_STREAM); + headers.push_back(HTTP_OUT_HEADER_ACCEPT + ": " + HTTP_CONTENT_OCTET_STREAM); std::string http_url = constructUrl(mesh_id); if (!http_url.empty()) diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 9e9efa7ebd..cc6dc64626 100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -2408,9 +2408,9 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image mHttpOptions = new LLCore::HttpOptions; mHttpHeaders = new LLCore::HttpHeaders; // *TODO: Should this be 'image/j2c' instead of 'image/x-j2c' ? - mHttpHeaders->mHeaders.push_back(HTTP_HEADER_ACCEPT + ": " + HTTP_CONTENT_IMAGE_X_J2C); + mHttpHeaders->mHeaders.push_back(HTTP_OUT_HEADER_ACCEPT + ": " + HTTP_CONTENT_IMAGE_X_J2C); mHttpMetricsHeaders = new LLCore::HttpHeaders; - mHttpMetricsHeaders->mHeaders.push_back(HTTP_HEADER_CONTENT_TYPE + ": " + HTTP_CONTENT_LLSD_XML); + mHttpMetricsHeaders->mHeaders.push_back(HTTP_OUT_HEADER_CONTENT_TYPE + ": " + HTTP_CONTENT_LLSD_XML); mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicyDefault(); } @@ -4043,7 +4043,7 @@ void LLTextureFetchDebugger::init() { mHttpHeaders = new LLCore::HttpHeaders; // *TODO: Should this be 'image/j2c' instead of 'image/x-j2c' ? - mHttpHeaders->mHeaders.push_back(HTTP_HEADER_ACCEPT + ": " + HTTP_CONTENT_IMAGE_X_J2C); + mHttpHeaders->mHeaders.push_back(HTTP_OUT_HEADER_ACCEPT + ": " + HTTP_CONTENT_IMAGE_X_J2C); } } diff --git a/indra/newview/lltranslate.cpp b/indra/newview/lltranslate.cpp index b9840fa3e4..ae934d9f5a 100755 --- a/indra/newview/lltranslate.cpp +++ b/indra/newview/lltranslate.cpp @@ -394,8 +394,8 @@ void LLTranslate::sendRequest(const std::string& url, LLHTTPClient::ResponderPtr LLVersionInfo::getPatch(), LLVersionInfo::getBuild()); - sHeader.insert(HTTP_HEADER_ACCEPT, HTTP_CONTENT_TEXT_PLAIN); - sHeader.insert(HTTP_HEADER_USER_AGENT, user_agent); + sHeader.insert(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_TEXT_PLAIN); + sHeader.insert(HTTP_OUT_HEADER_USER_AGENT, user_agent); } LLHTTPClient::get(url, responder, sHeader, REQUEST_TIMEOUT); diff --git a/indra/newview/llviewerdisplayname.cpp b/indra/newview/llviewerdisplayname.cpp index 5d94756da3..3f836efdd3 100644 --- a/indra/newview/llviewerdisplayname.cpp +++ b/indra/newview/llviewerdisplayname.cpp @@ -86,7 +86,7 @@ void LLViewerDisplayName::set(const std::string& display_name, const set_name_sl // People API can return localized error messages. Indicate our // language preference via header. LLSD headers; - headers[HTTP_HEADER_ACCEPT_LANGUAGE] = LLUI::getLanguage(); + headers[HTTP_OUT_HEADER_ACCEPT_LANGUAGE] = LLUI::getLanguage(); // People API requires both the old and new value to change a variable. // Our display name will be in cache before the viewer's UI is available diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index 8c29a03176..2cec808f19 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -185,8 +185,7 @@ private: llwarns << dumpResponse() << " [headers:" << getResponseHeaders() << "]" << llendl; } - const bool check_lower = true; - const std::string& media_type = getResponseHeader(HTTP_HEADER_CONTENT_TYPE, check_lower); + const std::string& media_type = getResponseHeader(HTTP_IN_HEADER_CONTENT_TYPE); std::string::size_type idx1 = media_type.find_first_of(";"); std::string mime_type = media_type.substr(0, idx1); @@ -289,8 +288,7 @@ public: // We don't care about the content of the response, only the Set-Cookie header. LL_DEBUGS("MediaAuth") << dumpResponse() << " [headers:" << getResponseHeaders() << "]" << LL_ENDL; - const bool check_lower = true; - const std::string& cookie = getResponseHeader(HTTP_HEADER_SET_COOKIE, check_lower); + const std::string& cookie = getResponseHeader(HTTP_IN_HEADER_SET_COOKIE); // *TODO: What about bad status codes? Does this destroy previous cookies? LLViewerMedia::openIDCookieResponse(cookie); @@ -321,12 +319,10 @@ public: LLSD stripped_content = getResponseHeaders(); // *TODO: Check that this works. - stripped_content.erase(HTTP_HEADER_SET_COOKIE); - stripped_content.erase("set-cookie"); + stripped_content.erase(HTTP_IN_HEADER_SET_COOKIE); LL_WARNS("MediaAuth") << stripped_content << LL_ENDL; - const bool check_lower = true; - const std::string& cookie = getResponseHeader(HTTP_HEADER_SET_COOKIE, check_lower); + const std::string& cookie = getResponseHeader(HTTP_IN_HEADER_SET_COOKIE); LL_DEBUGS("MediaAuth") << "cookie = " << cookie << LL_ENDL; // *TODO: What about bad status codes? Does this destroy previous cookies? @@ -1383,12 +1379,12 @@ void LLViewerMedia::removeCookie(const std::string &name, const std::string &dom LLSD LLViewerMedia::getHeaders() { LLSD headers = LLSD::emptyMap(); - headers[HTTP_HEADER_ACCEPT] = "*/*"; + headers[HTTP_OUT_HEADER_ACCEPT] = "*/*"; // *TODO: Should this be 'application/llsd+xml' ? // *TODO: Should this even be set at all? This header is only not overridden in 'GET' methods. - headers[HTTP_HEADER_CONTENT_TYPE] = HTTP_CONTENT_XML; - headers[HTTP_HEADER_COOKIE] = sOpenIDCookie; - headers[HTTP_HEADER_USER_AGENT] = getCurrentUserAgent(); + headers[HTTP_OUT_HEADER_CONTENT_TYPE] = HTTP_CONTENT_XML; + headers[HTTP_OUT_HEADER_COOKIE] = sOpenIDCookie; + headers[HTTP_OUT_HEADER_USER_AGENT] = getCurrentUserAgent(); return headers; } @@ -1429,9 +1425,9 @@ void LLViewerMedia::setOpenIDCookie() // Do a web profile get so we can store the cookie LLSD headers = LLSD::emptyMap(); - headers[HTTP_HEADER_ACCEPT] = "*/*"; - headers[HTTP_HEADER_COOKIE] = sOpenIDCookie; - headers[HTTP_HEADER_USER_AGENT] = getCurrentUserAgent(); + headers[HTTP_OUT_HEADER_ACCEPT] = "*/*"; + headers[HTTP_OUT_HEADER_COOKIE] = sOpenIDCookie; + headers[HTTP_OUT_HEADER_USER_AGENT] = getCurrentUserAgent(); std::string profile_url = getProfileURL(""); LLURL raw_profile_url( profile_url.c_str() ); @@ -1461,9 +1457,9 @@ void LLViewerMedia::openIDSetup(const std::string &openid_url, const std::string LLSD headers = LLSD::emptyMap(); // Keep LLHTTPClient from adding an "Accept: application/llsd+xml" header - headers[HTTP_HEADER_ACCEPT] = "*/*"; + headers[HTTP_OUT_HEADER_ACCEPT] = "*/*"; // and use the expected content-type for a post, instead of the LLHTTPClient::postRaw() default of "application/octet-stream" - headers[HTTP_HEADER_CONTENT_TYPE] = "application/x-www-form-urlencoded"; + headers[HTTP_OUT_HEADER_CONTENT_TYPE] = "application/x-www-form-urlencoded"; // postRaw() takes ownership of the buffer and releases it later, so we need to allocate a new buffer here. size_t size = openid_token.size(); @@ -2631,9 +2627,9 @@ void LLViewerMediaImpl::navigateInternal() // Accept: application/llsd+xml // which is really not what we want. LLSD headers = LLSD::emptyMap(); - headers[HTTP_HEADER_ACCEPT] = "*/*"; + headers[HTTP_OUT_HEADER_ACCEPT] = "*/*"; // Allow cookies in the response, to prevent a redirect loop when accessing join.secondlife.com - headers[HTTP_HEADER_COOKIE] = ""; + headers[HTTP_OUT_HEADER_COOKIE] = ""; LLHTTPClient::getHeaderOnly( mMediaURL, new LLMimeDiscoveryResponder(this), headers, 10.0f); } else if("data" == scheme || "file" == scheme || "about" == scheme) diff --git a/indra/newview/llwebprofile.cpp b/indra/newview/llwebprofile.cpp index ee78ba20cb..567138e160 100644 --- a/indra/newview/llwebprofile.cpp +++ b/indra/newview/llwebprofile.cpp @@ -167,9 +167,8 @@ public: if (getStatus() == HTTP_SEE_OTHER) { LLSD headers = LLViewerMedia::getHeaders(); - headers[HTTP_HEADER_COOKIE] = LLWebProfile::getAuthCookie(); - const bool check_lower=true; - const std::string& redir_url = getResponseHeader(HTTP_HEADER_LOCATION, check_lower); + headers[HTTP_OUT_HEADER_COOKIE] = LLWebProfile::getAuthCookie(); + const std::string& redir_url = getResponseHeader(HTTP_IN_HEADER_LOCATION); if (redir_url.empty()) { llwarns << "Received empty redirection URL " << dumpResponse() << llendl; @@ -207,7 +206,7 @@ void LLWebProfile::uploadImage(LLPointer image, const std::str LL_DEBUGS("Snapshots") << "Requesting " << config_url << llendl; LLSD headers = LLViewerMedia::getHeaders(); - headers[HTTP_HEADER_COOKIE] = getAuthCookie(); + headers[HTTP_OUT_HEADER_COOKIE] = getAuthCookie(); LLHTTPClient::get(config_url, new LLWebProfileResponders::ConfigResponder(image), headers); } @@ -231,8 +230,8 @@ void LLWebProfile::post(LLPointer image, const LLSD& config, c const std::string boundary = "----------------------------0123abcdefab"; LLSD headers = LLViewerMedia::getHeaders(); - headers[HTTP_HEADER_COOKIE] = getAuthCookie(); - headers[HTTP_HEADER_CONTENT_TYPE] = "multipart/form-data; boundary=" + boundary; + headers[HTTP_OUT_HEADER_COOKIE] = getAuthCookie(); + headers[HTTP_OUT_HEADER_CONTENT_TYPE] = "multipart/form-data; boundary=" + boundary; std::ostringstream body; diff --git a/indra/newview/llwebsharing.cpp b/indra/newview/llwebsharing.cpp index ba536ac582..7036162014 100644 --- a/indra/newview/llwebsharing.cpp +++ b/indra/newview/llwebsharing.cpp @@ -55,25 +55,47 @@ public: const LLIOPipe::buffer_ptr_t& buffer) { LLBufferStream istr(channels, buffer.get()); + // *TODO: LLSD notation is not actually JSON. LLPointer parser = new LLSDNotationParser(); - if (parser->parse(istr, mContent, LLSDSerialize::SIZE_UNLIMITED) == LLSDParser::PARSE_FAILURE) + std::string debug_body("(empty)"); + bool parsed=true; + if (EOF == istr.peek()) { - if (HTTP_CONTENT_JSON == getResponseHeader(HTTP_HEADER_CONTENT_TYPE)) + parsed=false; + } + // Try to parse body as llsd, no matter what 'content-type' says. + else if (parser->parse(istr, mContent, LLSDSerialize::SIZE_UNLIMITED) == LLSDParser::PARSE_FAILURE) + { + parsed=false; + char body[1025]; + body[1024] = '\0'; + istr.seekg(0, std::ios::beg); + istr.get(body,1024); + if (strlen(body) > 0) { - mStatus = HTTP_INTERNAL_ERROR; - mReason = "Failed to deserialize LLSD from JSON response."; - char body[1025]; - body[1024] = '\0'; - istr.seekg(0, std::ios::beg); - istr.get(body,1024); - if (strlen(body) > 0) - { - mContent["body"] = body; - } + mContent = body; + debug_body = body; } } + // Only emit a warning if we failed to parse when 'content-type' == 'application/json' + if (!parsed && (HTTP_CONTENT_JSON == getResponseHeader(HTTP_IN_HEADER_CONTENT_TYPE))) + { + llwarns << "Failed to deserialize LLSD from JSON response. " << getURL() + << " [status:" << mStatus << "] " + << "(" << mReason << ") body: " << debug_body << llendl; + } + + if (!parsed) + { + // *TODO: This isn't necessarily the server's fault. Using a 5xx code + // isn't really appropriate here. + // Also, this hides the actual status returned by the server.... + mStatus = HTTP_INTERNAL_ERROR; + mReason = "Failed to deserialize LLSD from JSON response."; + } + httpCompleted(); } }; @@ -132,11 +154,10 @@ private: virtual void httpSuccess() { - const bool check_lower=true; - if (hasResponseHeader(HTTP_HEADER_SET_COOKIE, check_lower)) + if (hasResponseHeader(HTTP_IN_HEADER_SET_COOKIE)) { // OpenID request succeeded and returned a session cookie. - LLWebSharing::instance().receiveSessionCookie(getResponseHeader(HTTP_HEADER_SET_COOKIE, check_lower)); + LLWebSharing::instance().receiveSessionCookie(getResponseHeader(HTTP_IN_HEADER_SET_COOKIE)); } } }; @@ -299,7 +320,7 @@ void LLWebSharing::sendConfigRequest() LL_DEBUGS("WebSharing") << "Requesting Snapshot Sharing config data from: " << config_url << LL_ENDL; LLSD headers = LLSD::emptyMap(); - headers[HTTP_HEADER_ACCEPT] = HTTP_CONTENT_JSON; + headers[HTTP_OUT_HEADER_ACCEPT] = HTTP_CONTENT_JSON; LLHTTPClient::get(config_url, new LLWebSharingConfigResponder(), headers); } @@ -310,8 +331,8 @@ void LLWebSharing::sendOpenIDAuthRequest() LL_DEBUGS("WebSharing") << "Starting OpenID Auth: " << auth_url << LL_ENDL; LLSD headers = LLSD::emptyMap(); - headers[HTTP_HEADER_COOKIE] = mOpenIDCookie; - headers[HTTP_HEADER_ACCEPT] = "*/*"; + headers[HTTP_OUT_HEADER_COOKIE] = mOpenIDCookie; + headers[HTTP_OUT_HEADER_ACCEPT] = "*/*"; // Send request, successful login will trigger fetching a security token. LLHTTPClient::get(auth_url, new LLWebSharingOpenIDAuthResponder(), headers); @@ -337,10 +358,10 @@ void LLWebSharing::sendSecurityTokenRequest() LL_DEBUGS("WebSharing") << "Fetching security token from: " << token_url << LL_ENDL; LLSD headers = LLSD::emptyMap(); - headers[HTTP_HEADER_COOKIE] = mSessionCookie; + headers[HTTP_OUT_HEADER_COOKIE] = mSessionCookie; - headers[HTTP_HEADER_ACCEPT] = HTTP_CONTENT_JSON; - headers[HTTP_HEADER_CONTENT_TYPE] = HTTP_CONTENT_JSON; + headers[HTTP_OUT_HEADER_ACCEPT] = HTTP_CONTENT_JSON; + headers[HTTP_OUT_HEADER_CONTENT_TYPE] = HTTP_CONTENT_JSON; std::ostringstream body; body << "{ \"gadgets\": [{ \"url\":\"" @@ -366,10 +387,10 @@ void LLWebSharing::sendUploadRequest() static const std::string BOUNDARY("------------abcdef012345xyZ"); LLSD headers = LLSD::emptyMap(); - headers[HTTP_HEADER_COOKIE] = mSessionCookie; + headers[HTTP_OUT_HEADER_COOKIE] = mSessionCookie; - headers[HTTP_HEADER_ACCEPT] = HTTP_CONTENT_JSON; - headers[HTTP_HEADER_CONTENT_TYPE] = "multipart/form-data; boundary=" + BOUNDARY; + headers[HTTP_OUT_HEADER_ACCEPT] = HTTP_CONTENT_JSON; + headers[HTTP_OUT_HEADER_CONTENT_TYPE] = "multipart/form-data; boundary=" + BOUNDARY; std::ostringstream body; body << "--" << BOUNDARY << "\r\n" diff --git a/indra/newview/llxmlrpctransaction.cpp b/indra/newview/llxmlrpctransaction.cpp index 604e08161e..7c5f8be1b5 100644 --- a/indra/newview/llxmlrpctransaction.cpp +++ b/indra/newview/llxmlrpctransaction.cpp @@ -331,7 +331,7 @@ void LLXMLRPCTransaction::Impl::init(XMLRPC_REQUEST request, bool useGzip) This might help with bug #503 */ mCurlRequest->setopt(CURLOPT_DNS_CACHE_TIMEOUT, -1); - mCurlRequest->slist_append(HTTP_HEADER_CONTENT_TYPE, HTTP_CONTENT_TEXT_XML); + mCurlRequest->slist_append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_TEXT_XML); if (useGzip) { diff --git a/indra/test/llhttpnode_tut.cpp b/indra/test/llhttpnode_tut.cpp index d580263103..c528a34129 100644 --- a/indra/test/llhttpnode_tut.cpp +++ b/indra/test/llhttpnode_tut.cpp @@ -44,7 +44,7 @@ namespace tut std::ostringstream pathOutput; bool addSlash = false; - LLSD& remainder = mContext["request"]["remainder"]; + LLSD& remainder = mContext[CONTEXT_REQUEST]["remainder"]; for (LLSD::array_const_iterator i = remainder.beginArray(); i != remainder.endArray(); ++i) diff --git a/indra/test/mock_http_client.cpp b/indra/test/mock_http_client.cpp index d7ef407d52..e72902bfc2 100644 --- a/indra/test/mock_http_client.cpp +++ b/indra/test/mock_http_client.cpp @@ -25,8 +25,7 @@ */ #include "linden_common.h" -#include "llsdhttpserver.h" -#include "lliohttpserver.h" +#include "llhttpnode.h" namespace tut { -- cgit v1.3 From 24b9657597be6d0b7af472d5deda4b4828ead606 Mon Sep 17 00:00:00 2001 From: "Brad Payne (Vir Linden)" Date: Mon, 15 Apr 2013 20:35:26 -0400 Subject: SH-4061 WIP - improved retry policy test, turned up and fixed a bug in getSecondsUntilRetryAfter --- indra/llmessage/llhttpconstants.cpp | 36 +++++++------ indra/newview/tests/llhttpretrypolicy_test.cpp | 70 +++++++++++++------------- 2 files changed, 53 insertions(+), 53 deletions(-) mode change 100644 => 100755 indra/llmessage/llhttpconstants.cpp (limited to 'indra/llmessage/llhttpconstants.cpp') diff --git a/indra/llmessage/llhttpconstants.cpp b/indra/llmessage/llhttpconstants.cpp old mode 100644 new mode 100755 index 1995fad1e5..016f1f1970 --- a/indra/llmessage/llhttpconstants.cpp +++ b/indra/llmessage/llhttpconstants.cpp @@ -2,31 +2,28 @@ * @file llhttpconstants.cpp * @brief Implementation of the HTTP request / response constant lookups * - * $LicenseInfo:firstyear=2013&license=viewergpl$ + * $LicenseInfo:firstyear=2013&license=viewerlgpl$ * * Copyright (c) 2013, Linden Research, Inc. * * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * Copyright (C) 2013, Linden Research, Inc. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * 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. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * 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. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * 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$ */ @@ -220,7 +217,8 @@ bool getSecondsUntilRetryAfter(const std::string& retry_after, F32& seconds_to_w time_t date = curl_getdate(retry_after.c_str(), NULL ); if (-1 == date) return false; - seconds_to_wait = (F32)date - (F32)LLTimer::getTotalSeconds(); + seconds_to_wait = (F64)date - LLTimer::getTotalSeconds(); + return true; } diff --git a/indra/newview/tests/llhttpretrypolicy_test.cpp b/indra/newview/tests/llhttpretrypolicy_test.cpp index 42bb9abe90..6fa3a57364 100755 --- a/indra/newview/tests/llhttpretrypolicy_test.cpp +++ b/indra/newview/tests/llhttpretrypolicy_test.cpp @@ -216,21 +216,23 @@ void RetryPolicyTestObject::test<6>() time_t nowseconds; time(&nowseconds); - std::string str3 = LLDate((F64)nowseconds).asRFC1123(); + std::string str3 = LLDate((F64)(nowseconds+44)).asRFC1123(); seconds_to_wait = F32_MAX; success = getSecondsUntilRetryAfter(str3, seconds_to_wait); + std::cerr << " str3 [" << str3 << "]" << std::endl; ensure("parse 3", success); - ensure_approximately_equals("parse 3", seconds_to_wait, 0.0F, 6); + ensure_approximately_equals("parse 3", seconds_to_wait, 44.0F, 2); } // Test retry-after field in both llmessage and CoreHttp headers. template<> template<> void RetryPolicyTestObject::test<7>() { + std::cerr << "7 starts" << std::endl; + LLSD sd_headers; time_t nowseconds; time(&nowseconds); - sd_headers[HTTP_IN_HEADER_RETRY_AFTER] = LLDate((F64)nowseconds).asRFC1123(); LLAdaptiveRetryPolicy policy(17.0,644.0,3.0,5); F32 seconds_to_wait; bool should_retry; @@ -245,40 +247,40 @@ void RetryPolicyTestObject::test<7>() ensure_approximately_equals("header 1", seconds_to_wait, 17.0F, 6); // retry header should override, give delay of 0 + std::string date_string = LLDate((F64)(nowseconds+7)).asRFC1123(); + sd_headers[HTTP_IN_HEADER_RETRY_AFTER] = date_string; policy.onFailure(503,sd_headers); should_retry = policy.shouldRetry(seconds_to_wait); ensure("header 2", should_retry); - ensure_approximately_equals("header 2", seconds_to_wait, 0.0F, 6); - - // retry header in LLCore::HttpHeaders - { - LLCore::HttpResponse *response = new LLCore::HttpResponse(); - LLCore::HttpHeaders *headers = new LLCore::HttpHeaders(); - response->setStatus(503); - response->setHeaders(headers); - headers->append(HTTP_IN_HEADER_RETRY_AFTER, std::string("600")); - policy.onFailure(response); - should_retry = policy.shouldRetry(seconds_to_wait); - ensure("header 3",should_retry); - ensure_approximately_equals("header 3", seconds_to_wait, 600.0F, 6); - response->release(); - } - - // retry header in LLCore::HttpHeaders - { - LLCore::HttpResponse *response = new LLCore::HttpResponse(); - LLCore::HttpHeaders *headers = new LLCore::HttpHeaders(); - response->setStatus(503); - response->setHeaders(headers); - LLSD sd_headers; - time(&nowseconds); - headers->append(HTTP_IN_HEADER_RETRY_AFTER,LLDate((F64)nowseconds).asRFC1123()); - policy.onFailure(response); - should_retry = policy.shouldRetry(seconds_to_wait); - ensure("header 4",should_retry); - ensure_approximately_equals("header 4", seconds_to_wait, 0.0F, 6); - response->release(); - } + ensure_approximately_equals("header 2", seconds_to_wait, 7.0F, 2); + + LLCore::HttpResponse *response; + LLCore::HttpHeaders *headers; + + response = new LLCore::HttpResponse(); + headers = new LLCore::HttpHeaders(); + response->setStatus(503); + response->setHeaders(headers); + headers->append(HTTP_IN_HEADER_RETRY_AFTER, std::string("600")); + policy.onFailure(response); + should_retry = policy.shouldRetry(seconds_to_wait); + ensure("header 3",should_retry); + ensure_approximately_equals("header 3", seconds_to_wait, 600.0F, 6); + response->release(); + + response = new LLCore::HttpResponse(); + headers = new LLCore::HttpHeaders(); + response->setStatus(503); + response->setHeaders(headers); + time(&nowseconds); + date_string = LLDate((F64)(nowseconds+77)).asRFC1123(); + std::cerr << "date_string [" << date_string << "]" << std::endl; + headers->append(HTTP_IN_HEADER_RETRY_AFTER,date_string); + policy.onFailure(response); + should_retry = policy.shouldRetry(seconds_to_wait); + ensure("header 4",should_retry); + ensure_approximately_equals("header 4", seconds_to_wait, 77.0F, 2); + response->release(); // Timeout should be clamped at max. policy.onFailure(500,LLSD()); -- cgit v1.3 From a85fa3b10a406218cabfecc0d592e816f8dfdb53 Mon Sep 17 00:00:00 2001 From: Don Kjer Date: Thu, 11 Jul 2013 15:15:04 -0700 Subject: Adding support for COPY methods to httpclient. Implementing viewer-side use of AISv3 COPY library folder operation. (SH-4304) --- indra/llmessage/llhttpclient.cpp | 13 + indra/llmessage/llhttpclient.h | 18 +- indra/llmessage/llhttpconstants.cpp | 6 +- indra/llmessage/llhttpconstants.h | 1 + indra/llmessage/llurlrequest.cpp | 10 +- indra/newview/llaisapi.cpp | 588 ++++++++++++++++++++++++++++-------- indra/newview/llaisapi.h | 55 +++- indra/newview/llappearancemgr.cpp | 76 ++++- indra/newview/llinventorymodel.cpp | 24 +- indra/newview/llviewerinventory.cpp | 62 ++-- indra/newview/llviewerinventory.h | 4 +- indra/newview/llviewerregion.cpp | 19 +- indra/newview/llviewerregion.h | 1 + 13 files changed, 689 insertions(+), 188 deletions(-) (limited to 'indra/llmessage/llhttpconstants.cpp') diff --git a/indra/llmessage/llhttpclient.cpp b/indra/llmessage/llhttpclient.cpp index 53cef54559..70c890a8de 100755 --- a/indra/llmessage/llhttpclient.cpp +++ b/indra/llmessage/llhttpclient.cpp @@ -631,6 +631,19 @@ void LLHTTPClient::move( 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) { diff --git a/indra/llmessage/llhttpclient.h b/indra/llmessage/llhttpclient.h index 4e7495495f..5ace2d74c9 100755 --- a/indra/llmessage/llhttpclient.h +++ b/indra/llmessage/llhttpclient.h @@ -119,7 +119,7 @@ public: 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 * @@ -136,6 +136,22 @@ public: 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); + //@} /** diff --git a/indra/llmessage/llhttpconstants.cpp b/indra/llmessage/llhttpconstants.cpp index 016f1f1970..01f4a080b0 100755 --- a/indra/llmessage/llhttpconstants.cpp +++ b/indra/llmessage/llhttpconstants.cpp @@ -133,6 +133,8 @@ 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) { @@ -145,7 +147,9 @@ const std::string& httpMethodAsVerb(EHTTPMethod method) HTTP_VERB_POST, HTTP_VERB_DELETE, HTTP_VERB_MOVE, - HTTP_VERB_OPTIONS + HTTP_VERB_OPTIONS, + HTTP_VERB_PATCH, + HTTP_VERB_COPY }; if(((S32)method <=0) || ((S32)method >= HTTP_METHOD_COUNT)) { diff --git a/indra/llmessage/llhttpconstants.h b/indra/llmessage/llhttpconstants.h index aa947af414..4aa3cc6394 100755 --- a/indra/llmessage/llhttpconstants.h +++ b/indra/llmessage/llhttpconstants.h @@ -112,6 +112,7 @@ enum EHTTPMethod HTTP_MOVE, // Caller will need to set 'Destination' header HTTP_OPTIONS, HTTP_PATCH, + HTTP_COPY, HTTP_METHOD_COUNT }; diff --git a/indra/llmessage/llurlrequest.cpp b/indra/llmessage/llurlrequest.cpp index cadff49cb8..7bf930aeb0 100755 --- a/indra/llmessage/llurlrequest.cpp +++ b/indra/llmessage/llurlrequest.cpp @@ -516,13 +516,19 @@ bool LLURLRequest::configure() break; case HTTP_DELETE: - // Set the handle for an http post + // 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 post + // Set the handle for an http move mDetail->mCurlRequest->setoptString(CURLOPT_CUSTOMREQUEST, "MOVE"); // *NOTE: should we check for the Destination header? rv = true; diff --git a/indra/newview/llaisapi.cpp b/indra/newview/llaisapi.cpp index 6d5f1951f9..cb8700865a 100755 --- a/indra/newview/llaisapi.cpp +++ b/indra/newview/llaisapi.cpp @@ -40,14 +40,25 @@ // AISCommand - base class for retry-able HTTP requests using the AISv3 cap. AISCommand::AISCommand(LLPointer callback): + mCommandFunc(NULL), mCallback(callback) { mRetryPolicy = new LLAdaptiveRetryPolicy(1.0, 32.0, 2.0, 10); } -void AISCommand::run_command() +bool AISCommand::run_command() { - mCommandFunc(); + if (NULL == mCommandFunc) + { + // This may happen if a command failed to initiate itself. + LL_WARNS("Inventory") << "AIS command attempted with null command function" << LL_ENDL; + return false; + } + else + { + mCommandFunc(); + return true; + } } void AISCommand::setCommandFunc(command_func_type command_func) @@ -80,9 +91,9 @@ void AISCommand::httpSuccess() if (mCallback) { - LLUUID item_id; // will default to null if parse fails. - getResponseUUID(content,item_id); - mCallback->fire(item_id); + LLUUID id; // will default to null if parse fails. + getResponseUUID(content,id); + mCallback->fire(id); } } @@ -96,12 +107,12 @@ void AISCommand::httpFailure() if (!content.isMap()) { LL_DEBUGS("Inventory") << "Malformed response contents " << content - << " status " << status << " reason " << reason << llendl; + << " status " << status << " reason " << reason << LL_ENDL; } else { LL_DEBUGS("Inventory") << "failed with content: " << ll_pretty_print_sd(content) - << " status " << status << " reason " << reason << llendl; + << " status " << status << " reason " << reason << LL_ENDL; } mRetryPolicy->onFailure(status, headers); F32 seconds_to_wait; @@ -113,12 +124,23 @@ void AISCommand::httpFailure() { // Command func holds a reference to self, need to release it // after a success or final failure. + // *TODO: Notify user? This seems bad. setCommandFunc(no_op); } } //static -bool AISCommand::getCap(std::string& cap) +bool AISCommand::isAPIAvailable() +{ + if (gAgent.getRegion()) + { + return gAgent.getRegion()->isCapabilityAvailable("InventoryAPIv3"); + } + return false; +} + +//static +bool AISCommand::getInvCap(std::string& cap) { if (gAgent.getRegion()) { @@ -131,18 +153,39 @@ bool AISCommand::getCap(std::string& cap) return false; } +//static +bool AISCommand::getLibCap(std::string& cap) +{ + if (gAgent.getRegion()) + { + cap = gAgent.getRegion()->getCapability("LibraryAPIv3"); + } + if (!cap.empty()) + { + return true; + } + return false; +} + +//static +void AISCommand::getCapabilityNames(LLSD& capabilityNames) +{ + capabilityNames.append("InventoryAPIv3"); + capabilityNames.append("LibraryAPIv3"); +} + RemoveItemCommand::RemoveItemCommand(const LLUUID& item_id, LLPointer callback): AISCommand(callback) { std::string cap; - if (!getCap(cap)) + if (!getInvCap(cap)) { llwarns << "No cap found" << llendl; return; } std::string url = cap + std::string("/item/") + item_id.asString(); - LL_DEBUGS("Inventory") << "url: " << url << llendl; + LL_DEBUGS("Inventory") << "url: " << url << LL_ENDL; LLHTTPClient::ResponderPtr responder = this; LLSD headers; F32 timeout = HTTP_REQUEST_EXPIRY_SECS; @@ -155,13 +198,13 @@ RemoveCategoryCommand::RemoveCategoryCommand(const LLUUID& item_id, AISCommand(callback) { std::string cap; - if (!getCap(cap)) + if (!getInvCap(cap)) { llwarns << "No cap found" << llendl; return; } std::string url = cap + std::string("/category/") + item_id.asString(); - LL_DEBUGS("Inventory") << "url: " << url << llendl; + LL_DEBUGS("Inventory") << "url: " << url << LL_ENDL; LLHTTPClient::ResponderPtr responder = this; LLSD headers; F32 timeout = HTTP_REQUEST_EXPIRY_SECS; @@ -174,13 +217,13 @@ PurgeDescendentsCommand::PurgeDescendentsCommand(const LLUUID& item_id, AISCommand(callback) { std::string cap; - if (!getCap(cap)) + if (!getInvCap(cap)) { llwarns << "No cap found" << llendl; return; } std::string url = cap + std::string("/category/") + item_id.asString() + "/children"; - LL_DEBUGS("Inventory") << "url: " << url << llendl; + LL_DEBUGS("Inventory") << "url: " << url << LL_ENDL; LLCurl::ResponderPtr responder = this; LLSD headers; F32 timeout = HTTP_REQUEST_EXPIRY_SECS; @@ -195,14 +238,14 @@ UpdateItemCommand::UpdateItemCommand(const LLUUID& item_id, AISCommand(callback) { std::string cap; - if (!getCap(cap)) + if (!getInvCap(cap)) { llwarns << "No cap found" << llendl; return; } std::string url = cap + std::string("/item/") + item_id.asString(); - LL_DEBUGS("Inventory") << "url: " << url << llendl; - LL_DEBUGS("Inventory") << "request: " << ll_pretty_print_sd(mUpdates) << llendl; + LL_DEBUGS("Inventory") << "url: " << url << LL_ENDL; + LL_DEBUGS("Inventory") << "request: " << ll_pretty_print_sd(mUpdates) << LL_ENDL; LLCurl::ResponderPtr responder = this; LLSD headers; headers["Content-Type"] = "application/llsd+xml"; @@ -218,13 +261,13 @@ UpdateCategoryCommand::UpdateCategoryCommand(const LLUUID& item_id, AISCommand(callback) { std::string cap; - if (!getCap(cap)) + if (!getInvCap(cap)) { llwarns << "No cap found" << llendl; return; } std::string url = cap + std::string("/category/") + item_id.asString(); - LL_DEBUGS("Inventory") << "url: " << url << llendl; + LL_DEBUGS("Inventory") << "url: " << url << LL_ENDL; LLCurl::ResponderPtr responder = this; LLSD headers; headers["Content-Type"] = "application/llsd+xml"; @@ -238,7 +281,7 @@ SlamFolderCommand::SlamFolderCommand(const LLUUID& folder_id, const LLSD& conten AISCommand(callback) { std::string cap; - if (!getCap(cap)) + if (!getInvCap(cap)) { llwarns << "No cap found" << llendl; return; @@ -255,146 +298,335 @@ SlamFolderCommand::SlamFolderCommand(const LLUUID& folder_id, const LLSD& conten setCommandFunc(cmd); } +CopyLibraryCategoryCommand::CopyLibraryCategoryCommand(const LLUUID& source_id, + const LLUUID& dest_id, + LLPointer callback): + AISCommand(callback) +{ + std::string cap; + if (!getLibCap(cap)) + { + llwarns << "No cap found" << llendl; + return; + } + LL_DEBUGS("Inventory") << "Copying library category: " << source_id << " => " << dest_id << LL_ENDL; + LLUUID tid; + tid.generate(); + std::string url = cap + std::string("/category/") + source_id.asString() + "?tid=" + tid.asString(); + llinfos << url << llendl; + LLCurl::ResponderPtr responder = this; + LLSD headers; + F32 timeout = HTTP_REQUEST_EXPIRY_SECS; + command_func_type cmd = boost::bind(&LLHTTPClient::copy, url, dest_id.asString(), responder, headers, timeout); + setCommandFunc(cmd); +} + +bool CopyLibraryCategoryCommand::getResponseUUID(const LLSD& content, LLUUID& id) +{ + if (content.has("category_id")) + { + id = content["category_id"]; + return true; + } + return false; +} + AISUpdate::AISUpdate(const LLSD& update) { parseUpdate(update); } +void AISUpdate::clearParseResults() +{ + mCatDescendentDeltas.clear(); + mCatDescendentsKnown.clear(); + mCatVersionsUpdated.clear(); + mItemsCreated.clear(); + mItemsUpdated.clear(); + mCategoriesCreated.clear(); + mCategoriesUpdated.clear(); + mObjectsDeletedIds.clear(); + mItemIds.clear(); + mCategoryIds.clear(); +} + void AISUpdate::parseUpdate(const LLSD& update) { - // parse _categories_removed -> mObjectsDeleted - uuid_vec_t cat_ids; + clearParseResults(); + parseMeta(update); + parseContent(update); +} + +void AISUpdate::parseMeta(const LLSD& update) +{ + // parse _categories_removed -> mObjectsDeletedIds + uuid_list_t cat_ids; parseUUIDArray(update,"_categories_removed",cat_ids); - for (uuid_vec_t::const_iterator it = cat_ids.begin(); + for (uuid_list_t::const_iterator it = cat_ids.begin(); it != cat_ids.end(); ++it) { LLViewerInventoryCategory *cat = gInventory.getCategory(*it); - mCatDeltas[cat->getParentUUID()]--; - mObjectsDeleted.insert(*it); + mCatDescendentDeltas[cat->getParentUUID()]--; + mObjectsDeletedIds.insert(*it); } - // parse _categories_items_removed -> mObjectsDeleted - uuid_vec_t item_ids; + // parse _categories_items_removed -> mObjectsDeletedIds + uuid_list_t item_ids; parseUUIDArray(update,"_category_items_removed",item_ids); - for (uuid_vec_t::const_iterator it = item_ids.begin(); + parseUUIDArray(update,"_removed_items",item_ids); + for (uuid_list_t::const_iterator it = item_ids.begin(); it != item_ids.end(); ++it) { LLViewerInventoryItem *item = gInventory.getItem(*it); - mCatDeltas[item->getParentUUID()]--; - mObjectsDeleted.insert(*it); + mCatDescendentDeltas[item->getParentUUID()]--; + mObjectsDeletedIds.insert(*it); } - // parse _broken_links_removed -> mObjectsDeleted - uuid_vec_t broken_link_ids; + // parse _broken_links_removed -> mObjectsDeletedIds + uuid_list_t broken_link_ids; parseUUIDArray(update,"_broken_links_removed",broken_link_ids); - for (uuid_vec_t::const_iterator it = broken_link_ids.begin(); + for (uuid_list_t::const_iterator it = broken_link_ids.begin(); it != broken_link_ids.end(); ++it) { LLViewerInventoryItem *item = gInventory.getItem(*it); - mCatDeltas[item->getParentUUID()]--; - mObjectsDeleted.insert(*it); + mCatDescendentDeltas[item->getParentUUID()]--; + mObjectsDeletedIds.insert(*it); } // parse _created_items - parseUUIDArray(update,"_created_items",mItemsCreatedIds); + parseUUIDArray(update,"_created_items",mItemIds); + + // parse _created_categories + parseUUIDArray(update,"_created_categories",mCategoryIds); - if (update.has("_embedded")) + // Parse updated category versions. + const std::string& ucv = "_updated_category_versions"; + if (update.has(ucv)) { - const LLSD& embedded = update["_embedded"]; - for(LLSD::map_const_iterator it = embedded.beginMap(), - end = embedded.endMap(); - it != end; ++it) + for(LLSD::map_const_iterator it = update[ucv].beginMap(), + end = update[ucv].endMap(); + it != end; ++it) { - const std::string& field = (*it).first; - - // parse created links - if (field == "link") - { - const LLSD& links = embedded["link"]; - parseCreatedLinks(links); - } + const LLUUID id((*it).first); + S32 version = (*it).second.asInteger(); + mCatVersionsUpdated[id] = version; } } +} - // Parse item update at the top level. - if (update.has("item_id")) +void AISUpdate::parseContent(const LLSD& update) +{ + if (update.has("linked_id")) { - LLUUID item_id = update["item_id"].asUUID(); - LLPointer new_item(new LLViewerInventoryItem); - LLViewerInventoryItem *curr_item = gInventory.getItem(item_id); - if (curr_item) + parseLink(update); + } + else if (update.has("item_id")) + { + parseItem(update); + } + + if (update.has("category_id")) + { + parseCategory(update); + } + else + { + if (update.has("_embedded")) { - // Default to current values where not provided. - new_item->copyViewerItem(curr_item); + parseEmbedded(update["_embedded"]); } - BOOL rv = new_item->unpackMessage(update); - if (rv) + } +} + +void AISUpdate::parseItem(const LLSD& item_map) +{ + LLUUID item_id = item_map["item_id"].asUUID(); + LLPointer new_item(new LLViewerInventoryItem); + LLViewerInventoryItem *curr_item = gInventory.getItem(item_id); + if (curr_item) + { + // Default to current values where not provided. + new_item->copyViewerItem(curr_item); + } + BOOL rv = new_item->unpackMessage(item_map); + if (rv) + { + if (curr_item) { mItemsUpdated[item_id] = new_item; // This statement is here to cause a new entry with 0 // delta to be created if it does not already exist; // otherwise has no effect. - mCatDeltas[new_item->getParentUUID()]; + mCatDescendentDeltas[new_item->getParentUUID()]; } else { - llerrs << "unpack failed" << llendl; + mItemsCreated[item_id] = new_item; + mCatDescendentDeltas[new_item->getParentUUID()]++; } } + else + { + // *TODO: Wow, harsh. Should we just complain and get out? + llerrs << "unpack failed" << llendl; + } +} - // Parse updated category versions. - const std::string& ucv = "_updated_category_versions"; - if (update.has(ucv)) +void AISUpdate::parseLink(const LLSD& link_map) +{ + LLUUID item_id = link_map["item_id"].asUUID(); + LLPointer new_link(new LLViewerInventoryItem); + LLViewerInventoryItem *curr_link = gInventory.getItem(item_id); + if (curr_link) { - for(LLSD::map_const_iterator it = update[ucv].beginMap(), - end = update[ucv].endMap(); - it != end; ++it) + // Default to current values where not provided. + new_link->copyViewerItem(curr_link); + } + BOOL rv = new_link->unpackMessage(link_map); + if (rv) + { + const LLUUID& parent_id = new_link->getParentUUID(); + if (curr_link) { - const LLUUID id((*it).first); - S32 version = (*it).second.asInteger(); - mCatVersions[id] = version; + mItemsUpdated[item_id] = new_link; + // This statement is here to cause a new entry with 0 + // delta to be created if it does not already exist; + // otherwise has no effect. + mCatDescendentDeltas[parent_id]; + } + else + { + LLPermissions default_perms; + default_perms.init(gAgent.getID(),gAgent.getID(),LLUUID::null,LLUUID::null); + default_perms.initMasks(PERM_NONE,PERM_NONE,PERM_NONE,PERM_NONE,PERM_NONE); + new_link->setPermissions(default_perms); + LLSaleInfo default_sale_info; + new_link->setSaleInfo(default_sale_info); + //LL_DEBUGS("Inventory") << "creating link from llsd: " << ll_pretty_print_sd(link_map) << LL_ENDL; + mItemsCreated[item_id] = new_link; + mCatDescendentDeltas[parent_id]++; } } + else + { + // *TODO: Wow, harsh. Should we just complain and get out? + llerrs << "unpack failed" << llendl; + } } -void AISUpdate::parseUUIDArray(const LLSD& content, const std::string& name, uuid_vec_t& ids) + +void AISUpdate::parseCategory(const LLSD& category_map) { - ids.clear(); - if (content.has(name)) + LLUUID category_id = category_map["category_id"].asUUID(); + + // Check descendent count first, as it may be needed + // to populate newly created categories + if (category_map.has("_embedded")) { - for(LLSD::array_const_iterator it = content[name].beginArray(), - end = content[name].endArray(); - it != end; ++it) + parseDescendentCount(category_id, category_map["_embedded"]); + } + + LLPointer new_cat(new LLViewerInventoryCategory(category_id)); + LLViewerInventoryCategory *curr_cat = gInventory.getCategory(category_id); + if (curr_cat) + { + // Default to current values where not provided. + new_cat->copyViewerCategory(curr_cat); + } + BOOL rv = new_cat->unpackMessage(category_map); + // *NOTE: unpackMessage does not unpack version or descendent count. + //if (category_map.has("version")) + //{ + // mCatVersionsUpdated[category_id] = category_map["version"].asInteger(); + //} + if (rv) + { + if (curr_cat) + { + mCategoriesUpdated[category_id] = new_cat; + // This statement is here to cause a new entry with 0 + // delta to be created if it does not already exist; + // otherwise has no effect. + mCatDescendentDeltas[new_cat->getParentUUID()]; + } + else { - ids.push_back((*it).asUUID()); + // Set version/descendents for newly created categories. + if (category_map.has("version")) + { + S32 version = category_map["version"].asInteger(); + LL_DEBUGS("Inventory") << "Setting version to " << version + << " for new category " << category_id << LL_ENDL; + new_cat->setVersion(version); + } + uuid_int_map_t::const_iterator lookup_it = mCatDescendentsKnown.find(category_id); + if (mCatDescendentsKnown.end() != lookup_it) + { + S32 descendent_count = lookup_it->second; + LL_DEBUGS("Inventory") << "Setting descendents count to " << descendent_count + << " for new category " << category_id << LL_ENDL; + new_cat->setDescendentCount(descendent_count); + } + mCategoriesCreated[category_id] = new_cat; + mCatDescendentDeltas[new_cat->getParentUUID()]++; } } + else + { + // *TODO: Wow, harsh. Should we just complain and get out? + llerrs << "unpack failed" << llendl; + } + + // Check for more embedded content. + if (category_map.has("_embedded")) + { + parseEmbedded(category_map["_embedded"]); + } } -void AISUpdate::parseLink(const LLUUID& link_id, const LLSD& link_map) +void AISUpdate::parseDescendentCount(const LLUUID& category_id, const LLSD& embedded) { - LLPointer new_link(new LLViewerInventoryItem); - BOOL rv = new_link->unpackMessage(link_map); - if (rv) + // We can only determine true descendent count if this contains all descendent types. + if (embedded.has("category") && + embedded.has("link") && + embedded.has("item")) { - LLPermissions default_perms; - default_perms.init(gAgent.getID(),gAgent.getID(),LLUUID::null,LLUUID::null); - default_perms.initMasks(PERM_NONE,PERM_NONE,PERM_NONE,PERM_NONE,PERM_NONE); - new_link->setPermissions(default_perms); - LLSaleInfo default_sale_info; - new_link->setSaleInfo(default_sale_info); - //LL_DEBUGS("Inventory") << "creating link from llsd: " << ll_pretty_print_sd(link_map) << llendl; - mItemsCreated[link_id] = new_link; - const LLUUID& parent_id = new_link->getParentUUID(); - mCatDeltas[parent_id]++; + mCatDescendentsKnown[category_id] = embedded["category"].size(); + mCatDescendentsKnown[category_id] += embedded["link"].size(); + mCatDescendentsKnown[category_id] += embedded["item"].size(); } - else +} + +void AISUpdate::parseEmbedded(const LLSD& embedded) +{ + if (embedded.has("link")) + { + parseEmbeddedLinks(embedded["link"]); + } + if (embedded.has("item")) + { + parseEmbeddedItems(embedded["item"]); + } + if (embedded.has("category")) { - llwarns << "failed to parse" << llendl; + parseEmbeddedCategories(embedded["category"]); + } +} + +void AISUpdate::parseUUIDArray(const LLSD& content, const std::string& name, uuid_list_t& ids) +{ + if (content.has(name)) + { + for(LLSD::array_const_iterator it = content[name].beginArray(), + end = content[name].endArray(); + it != end; ++it) + { + ids.insert((*it).asUUID()); + } } } -void AISUpdate::parseCreatedLinks(const LLSD& links) +void AISUpdate::parseEmbeddedLinks(const LLSD& links) { for(LLSD::map_const_iterator linkit = links.beginMap(), linkend = links.endMap(); @@ -402,47 +634,134 @@ void AISUpdate::parseCreatedLinks(const LLSD& links) { const LLUUID link_id((*linkit).first); const LLSD& link_map = (*linkit).second; - uuid_vec_t::const_iterator pos = - std::find(mItemsCreatedIds.begin(), - mItemsCreatedIds.end(),link_id); - if (pos != mItemsCreatedIds.end()) + if (mItemIds.end() == mItemIds.find(link_id)) + { + LL_DEBUGS("Inventory") << "Ignoring link not in items list " << link_id << LL_ENDL; + } + else + { + parseLink(link_map); + } + } +} + +void AISUpdate::parseEmbeddedItems(const LLSD& items) +{ + // Special case: this may be a single item (_embedded in a link) + if (items.has("item_id")) + { + if (mItemIds.end() != mItemIds.find(items["item_id"].asUUID())) + { + parseContent(items); + } + return; + } + + for(LLSD::map_const_iterator itemit = items.beginMap(), + itemend = items.endMap(); + itemit != itemend; ++itemit) + { + const LLUUID item_id((*itemit).first); + const LLSD& item_map = (*itemit).second; + if (mItemIds.end() == mItemIds.find(item_id)) + { + LL_DEBUGS("Inventory") << "Ignoring item not in items list " << item_id << LL_ENDL; + } + else + { + parseItem(item_map); + } + } +} + +void AISUpdate::parseEmbeddedCategories(const LLSD& categories) +{ + for(LLSD::map_const_iterator categoryit = categories.beginMap(), + categoryend = categories.endMap(); + categoryit != categoryend; ++categoryit) + { + const LLUUID category_id((*categoryit).first); + const LLSD& category_map = (*categoryit).second; + if (mCategoryIds.end() == mCategoryIds.find(category_id)) { - parseLink(link_id,link_map); + LL_DEBUGS("Inventory") << "Ignoring category not in categories list " << category_id << LL_ENDL; } else { - LL_DEBUGS("Inventory") << "Ignoring link not in created items list " << link_id << llendl; + parseCategory(category_map); } } } void AISUpdate::doUpdate() { - // Do descendent/version accounting. - // Can remove this if/when we use the version info directly. - for (std::map::const_iterator catit = mCatDeltas.begin(); - catit != mCatDeltas.end(); ++catit) + // Do version/descendent accounting. + for (std::map::const_iterator catit = mCatDescendentDeltas.begin(); + catit != mCatDescendentDeltas.end(); ++catit) { const LLUUID cat_id(catit->first); - S32 delta = catit->second; - LLInventoryModel::LLCategoryUpdate up(cat_id, delta); - gInventory.accountForUpdate(up); + // Don't account for update if we just created this category. + if (mCategoriesCreated.find(cat_id) != mCategoriesCreated.end()) + { + LL_DEBUGS("Inventory") << "Skipping version increment for new category " << cat_id << LL_ENDL; + continue; + } + + // Don't account for update unless AIS told us it updated that category. + if (mCatVersionsUpdated.find(cat_id) == mCatVersionsUpdated.end()) + { + LL_DEBUGS("Inventory") << "Skipping version increment for non-updated category " << cat_id << LL_ENDL; + continue; + } + + // If we have a known descendent count, set that now. + LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id); + if (cat) + { + S32 descendent_delta = catit->second; + S32 old_count = cat->getDescendentCount(); + LL_DEBUGS("Inventory") << "Updating descendent count for " << cat_id + << " with delta " << descendent_delta << " from " + << old_count << " to " << (old_count+descendent_delta) << LL_ENDL; + LLInventoryModel::LLCategoryUpdate up(cat_id, descendent_delta); + gInventory.accountForUpdate(up); + } + else + { + LL_DEBUGS("Inventory") << "Skipping version accounting for unknown category " << cat_id << LL_ENDL; + } } - - // TODO - how can we use this version info? Need to be sure all - // changes are going through AIS first, or at least through - // something with a reliable responder. - for (uuid_int_map_t::iterator ucv_it = mCatVersions.begin(); - ucv_it != mCatVersions.end(); ++ucv_it) + + // CREATE CATEGORIES + for (deferred_category_map_t::const_iterator create_it = mCategoriesCreated.begin(); + create_it != mCategoriesCreated.end(); ++create_it) { - const LLUUID id = ucv_it->first; - S32 version = ucv_it->second; - LLViewerInventoryCategory *cat = gInventory.getCategory(id); - if (cat->getVersion() != version) + LLUUID category_id(create_it->first); + LLPointer new_category = create_it->second; + + gInventory.updateCategory(new_category); + LL_DEBUGS("Inventory") << "created category " << category_id << LL_ENDL; + } + + // UPDATE CATEGORIES + for (deferred_category_map_t::const_iterator update_it = mCategoriesUpdated.begin(); + update_it != mCategoriesUpdated.end(); ++update_it) + { + LLUUID category_id(update_it->first); + LLPointer new_category = update_it->second; + // Since this is a copy of the category *before* the accounting update, above, + // we need to transfer back the updated version/descendent count. + LLViewerInventoryCategory* curr_cat = gInventory.getCategory(new_category->getUUID()); + if (NULL == curr_cat) { - llwarns << "Possible version mismatch for category " << cat->getName() - << ", viewer version " << cat->getVersion() - << " server version " << version << llendl; + LL_WARNS("Inventory") << "Failed to update unknown category " << new_category->getUUID() << LL_ENDL; + } + else + { + new_category->setVersion(curr_cat->getVersion()); + new_category->setDescendentCount(curr_cat->getDescendentCount()); + gInventory.updateCategory(new_category); + LL_DEBUGS("Inventory") << "updated category " << category_id << LL_ENDL; } } @@ -456,7 +775,7 @@ void AISUpdate::doUpdate() // FIXME risky function since it calls updateServer() in some // cases. Maybe break out the update/create cases, in which // case this is create. - LL_DEBUGS("Inventory") << "created item " << item_id << llendl; + LL_DEBUGS("Inventory") << "created item " << item_id << LL_ENDL; gInventory.updateItem(new_item); } @@ -469,19 +788,36 @@ void AISUpdate::doUpdate() // FIXME risky function since it calls updateServer() in some // cases. Maybe break out the update/create cases, in which // case this is update. - LL_DEBUGS("Inventory") << "updated item " << item_id << llendl; - //LL_DEBUGS("Inventory") << ll_pretty_print_sd(new_item->asLLSD()) << llendl; + LL_DEBUGS("Inventory") << "updated item " << item_id << LL_ENDL; + //LL_DEBUGS("Inventory") << ll_pretty_print_sd(new_item->asLLSD()) << LL_ENDL; gInventory.updateItem(new_item); } // DELETE OBJECTS - for (std::set::const_iterator del_it = mObjectsDeleted.begin(); - del_it != mObjectsDeleted.end(); ++del_it) + for (std::set::const_iterator del_it = mObjectsDeletedIds.begin(); + del_it != mObjectsDeletedIds.end(); ++del_it) { - LL_DEBUGS("Inventory") << "deleted item " << *del_it << llendl; + LL_DEBUGS("Inventory") << "deleted item " << *del_it << LL_ENDL; gInventory.onObjectDeletedFromServer(*del_it, false, false, false); } + // TODO - how can we use this version info? Need to be sure all + // changes are going through AIS first, or at least through + // something with a reliable responder. + for (uuid_int_map_t::iterator ucv_it = mCatVersionsUpdated.begin(); + ucv_it != mCatVersionsUpdated.end(); ++ucv_it) + { + const LLUUID id = ucv_it->first; + S32 version = ucv_it->second; + LLViewerInventoryCategory *cat = gInventory.getCategory(id); + if (cat->getVersion() != version) + { + llwarns << "Possible version mismatch for category " << cat->getName() + << ", viewer version " << cat->getVersion() + << " server version " << version << llendl; + } + } + gInventory.notifyObservers(); } diff --git a/indra/newview/llaisapi.h b/indra/newview/llaisapi.h index 1f9555f004..f4e219e9e6 100755 --- a/indra/newview/llaisapi.h +++ b/indra/newview/llaisapi.h @@ -31,7 +31,6 @@ #include #include #include -#include #include "llcurl.h" #include "llhttpclient.h" #include "llhttpretrypolicy.h" @@ -46,7 +45,7 @@ public: virtual ~AISCommand() {} - void run_command(); + bool run_command(); void setCommandFunc(command_func_type command_func); @@ -54,13 +53,17 @@ public: // LLInventoryCallback::fire(). May or may not need to bother, // since most LLInventoryCallbacks do their work in the // destructor. - virtual bool getResponseUUID(const LLSD& content, LLUUID& id); /* virtual */ void httpSuccess(); + /* virtual */ void httpFailure(); - /*virtual*/ void httpFailure(); + static bool isAPIAvailable(); + static bool getInvCap(std::string& cap); + static bool getLibCap(std::string& cap); + static void getCapabilityNames(LLSD& capabilityNames); - static bool getCap(std::string& cap); +protected: + virtual bool getResponseUUID(const LLSD& content, LLUUID& id); private: command_func_type mCommandFunc; @@ -118,26 +121,52 @@ private: LLSD mContents; }; +class CopyLibraryCategoryCommand: public AISCommand +{ +public: + CopyLibraryCategoryCommand(const LLUUID& source_id, const LLUUID& dest_id, LLPointer callback); + +protected: + /* virtual */ bool getResponseUUID(const LLSD& content, LLUUID& id); +}; + class AISUpdate { public: AISUpdate(const LLSD& update); void parseUpdate(const LLSD& update); - void parseUUIDArray(const LLSD& content, const std::string& name, uuid_vec_t& ids); - void parseLink(const LLUUID& link_id, const LLSD& link_map); - void parseCreatedLinks(const LLSD& links); + void parseMeta(const LLSD& update); + void parseContent(const LLSD& update); + void parseUUIDArray(const LLSD& content, const std::string& name, uuid_list_t& ids); + void parseLink(const LLSD& link_map); + void parseItem(const LLSD& link_map); + void parseCategory(const LLSD& link_map); + void parseDescendentCount(const LLUUID& category_id, const LLSD& embedded); + void parseEmbedded(const LLSD& embedded); + void parseEmbeddedLinks(const LLSD& links); + void parseEmbeddedItems(const LLSD& links); + void parseEmbeddedCategories(const LLSD& links); void doUpdate(); private: + void clearParseResults(); + typedef std::map uuid_int_map_t; - uuid_int_map_t mCatDeltas; - uuid_int_map_t mCatVersions; + uuid_int_map_t mCatDescendentDeltas; + uuid_int_map_t mCatDescendentsKnown; + uuid_int_map_t mCatVersionsUpdated; typedef std::map > deferred_item_map_t; deferred_item_map_t mItemsCreated; deferred_item_map_t mItemsUpdated; - - std::set mObjectsDeleted; - uuid_vec_t mItemsCreatedIds; + typedef std::map > deferred_category_map_t; + deferred_category_map_t mCategoriesCreated; + deferred_category_map_t mCategoriesUpdated; + + // These keep track of uuid's mentioned in meta values. + // Useful for filtering out which content we are interested in. + uuid_list_t mObjectsDeletedIds; + uuid_list_t mItemIds; + uuid_list_t mCategoryIds; }; #endif diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index a0f8cbe911..fc07932860 100755 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -435,6 +435,50 @@ private: S32 LLCallAfterInventoryCopyMgr::sInstanceCount = 0; +class LLWearCategoryAfterCopy: public LLInventoryCallback +{ +public: + LLWearCategoryAfterCopy(bool append): + mAppend(append) + {} + + // virtual + void fire(const LLUUID& id) + { + // Wear the inventory category. + LLInventoryCategory* cat = gInventory.getCategory(id); + LLAppearanceMgr::instance().wearInventoryCategoryOnAvatar(cat, mAppend); + } + +private: + bool mAppend; +}; + +class LLTrackPhaseWrapper : public LLInventoryCallback +{ +public: + LLTrackPhaseWrapper(const std::string& phase_name, LLPointer cb = NULL): + mTrackingPhase(phase_name), + mCB(cb) + { + selfStartPhase(mTrackingPhase); + } + + // virtual + void fire(const LLUUID& id) + { + selfStopPhase(mTrackingPhase); + if (mCB) + { + mCB->fire(id); + } + } + +protected: + std::string mTrackingPhase; + LLPointer mCB; +}; + LLUpdateAppearanceOnDestroy::LLUpdateAppearanceOnDestroy(bool enforce_item_restrictions, bool enforce_ordering, nullary_func_t post_update_func @@ -2244,10 +2288,31 @@ void LLAppearanceMgr::wearInventoryCategory(LLInventoryCategory* category, bool LL_INFOS("Avatar") << self_av_string() << "wearInventoryCategory( " << category->getName() << " )" << LL_ENDL; - selfStartPhase("wear_inventory_category_fetch"); - callAfterCategoryFetch(category->getUUID(),boost::bind(&LLAppearanceMgr::wearCategoryFinal, - &LLAppearanceMgr::instance(), - category->getUUID(), copy, append)); + // If we are copying from library, attempt to use AIS to copy the category. + bool ais_ran=false; + if (copy && AISCommand::isAPIAvailable()) + { + LLUUID parent_id; + parent_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_CLOTHING); + if (parent_id.isNull()) + { + parent_id = gInventory.getRootFolderID(); + } + + LLPointer copy_cb = new LLWearCategoryAfterCopy(append); + LLPointer track_cb = new LLTrackPhaseWrapper( + std::string("wear_inventory_category_callback"), copy_cb); + LLPointer cmd_ptr = new CopyLibraryCategoryCommand(category->getUUID(), parent_id, track_cb); + ais_ran=cmd_ptr->run_command(); + } + + if (!ais_ran) + { + selfStartPhase("wear_inventory_category_fetch"); + callAfterCategoryFetch(category->getUUID(),boost::bind(&LLAppearanceMgr::wearCategoryFinal, + &LLAppearanceMgr::instance(), + category->getUUID(), copy, append)); + } } S32 LLAppearanceMgr::getActiveCopyOperations() const @@ -3544,8 +3609,7 @@ void LLAppearanceMgr::makeNewOutfitLinks(const std::string& new_folder_name, boo // First, make a folder in the My Outfits directory. const LLUUID parent_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); - std::string cap; - if (AISCommand::getCap(cap)) + if (AISCommand::isAPIAvailable()) { // cap-based category creation was buggy until recently. use // existence of AIS as an indicator the fix is present. Does diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index e1fd2e02fa..0d99bea3fc 100755 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -1718,7 +1718,6 @@ void LLInventoryModel::accountForUpdate(const LLCategoryUpdate& update) const LLViewerInventoryCategory* cat = getCategory(update.mCategoryID); if(cat) { - bool accounted = false; S32 version = cat->getVersion(); if(version != LLViewerInventoryCategory::VERSION_UNKNOWN) { @@ -1733,22 +1732,27 @@ void LLInventoryModel::accountForUpdate(const LLCategoryUpdate& update) const } if(descendents_server == descendents_actual) { - accounted = true; descendents_actual += update.mDescendentDelta; cat->setDescendentCount(descendents_actual); cat->setVersion(++version); - lldebugs << "accounted: '" << cat->getName() << "' " + LL_DEBUGS("Inventory") << "accounted: '" << cat->getName() << "' " << version << " with " << descendents_actual - << " descendents." << llendl; + << " descendents." << LL_ENDL; + } + else + { + // Error condition, this means that the category did not register that + // it got new descendents (perhaps because it is still being loaded) + // which means its descendent count will be wrong. + llwarns << "Accounting failed for '" << cat->getName() << "' version:" + << version << " due to mismatched descendent count: server == " + << descendents_server << ", viewer == " << descendents_actual << llendl; } } - if(!accounted) + else { - // Error condition, this means that the category did not register that - // it got new descendents (perhaps because it is still being loaded) - // which means its descendent count will be wrong. - llwarns << "Accounting failed for '" << cat->getName() << "' version:" - << version << llendl; + llwarns << "Accounting failed for '" << cat->getName() << "' version: unknown (" + << version << ")" << llendl; } } else diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index c934dd991b..5beae8ec24 100755 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -430,7 +430,7 @@ void LLViewerInventoryItem::fetchFromServer(void) const } // virtual -BOOL LLViewerInventoryItem::unpackMessage(LLSD item) +BOOL LLViewerInventoryItem::unpackMessage(const LLSD& item) { BOOL rv = LLInventoryItem::fromLLSD(item); @@ -866,6 +866,21 @@ void LLViewerInventoryCategory::localizeName() LLLocalizedInventoryItemsDictionary::getInstance()->localizeInventoryObjectName(mName); } +// virtual +BOOL LLViewerInventoryCategory::unpackMessage(const LLSD& category) +{ + BOOL rv = LLInventoryCategory::fromLLSD(category); + localizeName(); + return rv; +} + +// virtual +void LLViewerInventoryCategory::unpackMessage(LLMessageSystem* msg, const char* block, S32 block_num) +{ + LLInventoryCategory::unpackMessage(msg, block, block_num); + localizeName(); +} + ///---------------------------------------------------------------------------- /// Local function definitions ///---------------------------------------------------------------------------- @@ -1159,24 +1174,22 @@ void update_inventory_item( const LLSD& updates, LLPointer cb) { - LLPointer obj = gInventory.getItem(item_id); - LL_DEBUGS("Inventory") << "item_id: [" << item_id << "] name " << (obj ? obj->getName() : "(NOT FOUND)") << llendl; - if(obj) + bool ais_ran = false; + if (AISCommand::isAPIAvailable()) { - LLPointer new_item(new LLViewerInventoryItem); - new_item->copyViewerItem(obj); - new_item->fromLLSD(updates,false); - - std::string cap; - if (AISCommand::getCap(cap)) - { - LLSD new_llsd; - new_item->asLLSD(new_llsd); - LLPointer cmd_ptr = new UpdateItemCommand(item_id, new_llsd, cb); - cmd_ptr->run_command(); - } - else // no cap + LLPointer cmd_ptr = new UpdateItemCommand(item_id, updates, cb); + ais_ran = cmd_ptr->run_command(); + } + if (!ais_ran) + { + LLPointer obj = gInventory.getItem(item_id); + LL_DEBUGS("Inventory") << "item_id: [" << item_id << "] name " << (obj ? obj->getName() : "(NOT FOUND)") << llendl; + if(obj) { + LLPointer new_item(new LLViewerInventoryItem); + new_item->copyViewerItem(obj); + new_item->fromLLSD(updates,false); + LLMessageSystem* msg = gMessageSystem; msg->newMessageFast(_PREHASH_UpdateInventoryItem); msg->nextBlockFast(_PREHASH_AgentData); @@ -1216,9 +1229,8 @@ void update_inventory_category( LLPointer new_cat = new LLViewerInventoryCategory(obj); new_cat->fromLLSD(updates); - //std::string cap; // FIXME - restore this once the back-end work has been done. - if (0) // if (AISCommand::getCap(cap)) + if (0) // if (AISCommand::isAPIAvailable()) { LLSD new_llsd = new_cat->asLLSD(); LLPointer cmd_ptr = new UpdateCategoryCommand(cat_id, new_llsd, cb); @@ -1254,8 +1266,7 @@ void remove_inventory_item( LL_DEBUGS("Inventory") << "item_id: [" << item_id << "] name " << (obj ? obj->getName() : "(NOT FOUND)") << llendl; if(obj) { - std::string cap; - if (AISCommand::getCap(cap)) + if (AISCommand::isAPIAvailable()) { LLPointer cmd_ptr = new RemoveItemCommand(item_id, cb); cmd_ptr->run_command(); @@ -1325,8 +1336,7 @@ void remove_inventory_category( LLNotificationsUtil::add("CannotRemoveProtectedCategories"); return; } - std::string cap; - if (AISCommand::getCap(cap)) + if (AISCommand::isAPIAvailable()) { LLPointer cmd_ptr = new RemoveCategoryCommand(cat_id, cb); cmd_ptr->run_command(); @@ -1429,8 +1439,7 @@ void purge_descendents_of(const LLUUID& id, LLPointer cb) } else { - std::string cap; - if (AISCommand::getCap(cap)) + if (AISCommand::isAPIAvailable()) { LLPointer cmd_ptr = new PurgeDescendentsCommand(id, cb); cmd_ptr->run_command(); @@ -1564,8 +1573,7 @@ void slam_inventory_folder(const LLUUID& folder_id, const LLSD& contents, LLPointer cb) { - std::string cap; - if (AISCommand::getCap(cap)) + if (AISCommand::isAPIAvailable()) { LL_DEBUGS("Avatar") << "using AISv3 to slam folder, id " << folder_id << " new contents: " << ll_pretty_print_sd(contents) << llendl; diff --git a/indra/newview/llviewerinventory.h b/indra/newview/llviewerinventory.h index de1f3daa1e..0d4ffaa575 100755 --- a/indra/newview/llviewerinventory.h +++ b/indra/newview/llviewerinventory.h @@ -124,7 +124,7 @@ public: virtual void packMessage(LLMessageSystem* msg) const; virtual BOOL unpackMessage(LLMessageSystem* msg, const char* block, S32 block_num = 0); - virtual BOOL unpackMessage(LLSD item); + virtual BOOL unpackMessage(const LLSD& item); virtual BOOL importFile(LLFILE* fp); virtual BOOL importLegacyStream(std::istream& input_stream); @@ -224,6 +224,8 @@ public: bool importFileLocal(LLFILE* fp); void determineFolderType(); void changeType(LLFolderType::EType new_folder_type); + virtual void unpackMessage(LLMessageSystem* msg, const char* block, S32 block_num = 0); + virtual BOOL unpackMessage(const LLSD& category); private: friend class LLInventoryModel; diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index dca1a5b542..ad046accd0 100755 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -30,6 +30,7 @@ // linden libraries #include "indra_constants.h" +#include "llaisapi.h" #include "llavatarnamecache.h" // name lookup cap url #include "llfloaterreg.h" #include "llmath.h" @@ -1645,7 +1646,7 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames) capabilityNames.append("FetchInventory2"); capabilityNames.append("FetchInventoryDescendents2"); capabilityNames.append("IncrementCOFVersion"); - capabilityNames.append("InventoryAPIv3"); + AISCommand::getCapabilityNames(capabilityNames); } capabilityNames.append("GetDisplayNames"); @@ -1893,6 +1894,22 @@ std::string LLViewerRegion::getCapability(const std::string& name) const return iter->second; } +bool LLViewerRegion::isCapabilityAvailable(const std::string& name) const +{ + if (!capabilitiesReceived() && (name!=std::string("Seed")) && (name!=std::string("ObjectMedia"))) + { + llwarns << "isCapabilityAvailable called before caps received for " << name << llendl; + } + + CapabilityMap::const_iterator iter = mImpl->mCapabilities.find(name); + if(iter == mImpl->mCapabilities.end()) + { + return false; + } + + return true; +} + bool LLViewerRegion::capabilitiesReceived() const { return mCapabilitiesReceived; diff --git a/indra/newview/llviewerregion.h b/indra/newview/llviewerregion.h index 5ac2a83aaf..4fb1b7402c 100755 --- a/indra/newview/llviewerregion.h +++ b/indra/newview/llviewerregion.h @@ -244,6 +244,7 @@ public: S32 getNumSeedCapRetries(); void setCapability(const std::string& name, const std::string& url); void setCapabilityDebug(const std::string& name, const std::string& url); + bool isCapabilityAvailable(const std::string& name) const; // implements LLCapabilityProvider virtual std::string getCapability(const std::string& name) const; -- cgit v1.3