diff options
| author | Rider Linden <rider@lindenlab.com> | 2015-05-20 17:37:27 -0700 | 
|---|---|---|
| committer | Rider Linden <rider@lindenlab.com> | 2015-05-20 17:37:27 -0700 | 
| commit | c437a9c4ec865c38366c8057010d24311888ecb1 (patch) | |
| tree | d505cbcd4d1455b309689d170b24485a3ff94087 | |
| parent | a4741cecb2112f418c1d98ca63a261e707a856c3 (diff) | |
Webprofile converted to coroutine.
Added JSON->LLSD converter
Added corohandler for JSON data
| -rwxr-xr-x | indra/llcommon/CMakeLists.txt | 5 | ||||
| -rw-r--r-- | indra/llcommon/llsdjson.cpp | 78 | ||||
| -rw-r--r-- | indra/llcommon/llsdjson.h | 59 | ||||
| -rwxr-xr-x | indra/llcorehttp/httpheaders.cpp | 18 | ||||
| -rwxr-xr-x | indra/llcorehttp/httpheaders.h | 6 | ||||
| -rwxr-xr-x | indra/llmessage/CMakeLists.txt | 4 | ||||
| -rw-r--r-- | indra/llmessage/llcorehttputil.cpp | 82 | ||||
| -rw-r--r-- | indra/llmessage/llcorehttputil.h | 12 | ||||
| -rwxr-xr-x | indra/newview/llwebprofile.cpp | 372 | ||||
| -rwxr-xr-x | indra/newview/llwebprofile.h | 12 | 
10 files changed, 449 insertions, 199 deletions
| diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 1459b9ada2..9086691375 100755 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -8,6 +8,7 @@ include(LLCommon)  include(Linking)  include(Boost)  include(LLSharedLibs) +include(JsonCpp)  include(GoogleBreakpad)  include(GooglePerfTools)  include(Copy3rdPartyLibs) @@ -17,6 +18,7 @@ include(URIPARSER)  include_directories(      ${EXPAT_INCLUDE_DIRS}      ${LLCOMMON_INCLUDE_DIRS} +    ${JSONCPP_INCLUDE_DIR}      ${ZLIB_INCLUDE_DIRS}      ${BREAKPAD_INCLUDE_DIRECTORIES}      ${URIPARSER_INCLUDE_DIRS} @@ -85,6 +87,7 @@ set(llcommon_SOURCE_FILES      llrefcount.cpp      llrun.cpp      llsd.cpp +	llsdjson.cpp      llsdparam.cpp      llsdserialize.cpp      llsdserialize_xml.cpp @@ -193,6 +196,7 @@ set(llcommon_HEADER_FILES      llrefcount.h      llsafehandle.h      llsd.h +	llsdjson.h      llsdparam.h      llsdserialize.h      llsdserialize_xml.h @@ -260,6 +264,7 @@ target_link_libraries(      ${APRUTIL_LIBRARIES}      ${APR_LIBRARIES}      ${EXPAT_LIBRARIES} +    ${JSONCPP_LIBRARIES}      ${ZLIB_LIBRARIES}      ${WINDOWS_LIBRARIES}      ${BOOST_PROGRAM_OPTIONS_LIBRARY} diff --git a/indra/llcommon/llsdjson.cpp b/indra/llcommon/llsdjson.cpp new file mode 100644 index 0000000000..2afdba388a --- /dev/null +++ b/indra/llcommon/llsdjson.cpp @@ -0,0 +1,78 @@ +/**  + * @file llsdjson.cpp + * @brief LLSD flexible data system + * + * $LicenseInfo:firstyear=2015&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2015, Linden Research, Inc. + *  + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + *  + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + *  + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + *  + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +// Must turn on conditional declarations in header file so definitions end up +// with proper linkage. +#define LLSD_DEBUG_INFO +#include "linden_common.h" + +#include "llsdjson.h" + +#include "llerror.h" +#include "../llmath/llmath.h" + +//========================================================================= +LLSD LlsdFromJson(const Json::Value &val) +{ +    LLSD result; + +    switch (val.type()) +    { +    default: +    case Json::nullValue: +        break; +    case Json::intValue: +        result = LLSD(static_cast<LLSD::Integer>(val.asInt())); +        break; +    case Json::uintValue: +        result = LLSD(static_cast<LLSD::Integer>(val.asUInt())); +        break; +    case Json::realValue: +        result = LLSD(static_cast<LLSD::Real>(val.asDouble())); +        break; +    case Json::stringValue: +        result = LLSD(static_cast<LLSD::String>(val.asString())); +        break; +    case Json::booleanValue: +        result = LLSD(static_cast<LLSD::Boolean>(val.asBool())); +        break; +    case Json::arrayValue: +        result = LLSD::emptyArray(); +        for (Json::ValueConstIterator it = val.begin(); it != val.end(); ++it) +        { +            result.append(LlsdFromJson((*it))); +        } +        break; +    case Json::objectValue: +        result = LLSD::emptyMap(); +        for (Json::ValueConstIterator it = val.begin(); it != val.end(); ++it) +        { +            result[it.memberName()] = LlsdFromJson((*it)); +        } +        break; +    } +    return result; +} diff --git a/indra/llcommon/llsdjson.h b/indra/llcommon/llsdjson.h new file mode 100644 index 0000000000..cdf9fed500 --- /dev/null +++ b/indra/llcommon/llsdjson.h @@ -0,0 +1,59 @@ +/**  + * @file llsdjson.cpp + * @brief LLSD flexible data system + * + * $LicenseInfo:firstyear=2015&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2015, Linden Research, Inc. + *  + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + *  + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + *  + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + *  + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLSDJSON_H +#define LL_LLSDJSON_H + +#include <map> +#include <string> +#include <vector> + +#include "stdtypes.h" + +#include "llsd.h" +#include "value.h" + +/// Convert a parsed JSON structure into LLSD maintaining member names and  +/// array indexes. +/// JSON/JavaScript types are converted as follows: +///  +/// JSON Type     | LLSD Type +/// --------------+-------------- +///  null         |  undefined +///  integer      |  LLSD::Integer +///  unsigned     |  LLSD::Integer +///  real/numeric |  LLSD::Real +///  string       |  LLSD::String +///  boolean      |  LLSD::Boolean +///  array        |  LLSD::Array +///  object       |  LLSD::Map +///   +/// For maps and arrays child entries will be converted and added to the structure. +/// Order is preserved for an array but not for objects. +LLSD LlsdFromJson(const Json::Value &val); + + +#endif // LL_LLSDJSON_H diff --git a/indra/llcorehttp/httpheaders.cpp b/indra/llcorehttp/httpheaders.cpp index 73c92c8f10..e03b1b080d 100755 --- a/indra/llcorehttp/httpheaders.cpp +++ b/indra/llcorehttp/httpheaders.cpp @@ -118,6 +118,24 @@ const std::string * HttpHeaders::find(const std::string &name) const  	return NULL;  } +void HttpHeaders::remove(const char *name) +{ +    remove(std::string(name)); +} + +void HttpHeaders::remove(const std::string &name) +{ +    iterator iend(end()); +    for (iterator iter(begin()); iend != iter; ++iter) +    { +        if ((*iter).first == name) +        { +            mHeaders.erase(iter); +            return; +        } +    } +} +  // Standard Iterators  HttpHeaders::iterator HttpHeaders::begin() diff --git a/indra/llcorehttp/httpheaders.h b/indra/llcorehttp/httpheaders.h index 940f92183c..51bd76a01d 100755 --- a/indra/llcorehttp/httpheaders.h +++ b/indra/llcorehttp/httpheaders.h @@ -146,13 +146,17 @@ public:  	//					a pointer to a std::string in the container.  	//					Pointer is valid only for the lifetime of  	//					the container or until container is modifed. -	  	const std::string * find(const std::string &name) const;  	const std::string * find(const char * name) const  	{  		return find(std::string(name));  	} +    // Remove the header from the list if found. +    //  +    void remove(const std::string &name); +    void remove(const char *name); +  	// Count of headers currently in the list.  	size_type size() const  		{ diff --git a/indra/llmessage/CMakeLists.txt b/indra/llmessage/CMakeLists.txt index f6ca6a3634..51b8ed6c62 100755 --- a/indra/llmessage/CMakeLists.txt +++ b/indra/llmessage/CMakeLists.txt @@ -14,6 +14,7 @@ include(LLAddBuildTest)  include(Python)  include(Tut)  include(Python) +include(JsonCpp)  include_directories (${CMAKE_CURRENT_SOURCE_DIR}) @@ -23,6 +24,7 @@ include_directories(      ${LLMATH_INCLUDE_DIRS}      ${LLMESSAGE_INCLUDE_DIRS}      ${LLVFS_INCLUDE_DIRS} +    ${JSONCPP_INCLUDE_DIR}      )  set(llmessage_SOURCE_FILES @@ -229,6 +231,7 @@ target_link_libraries(    ${LLVFS_LIBRARIES}    ${LLMATH_LIBRARIES}    ${CARES_LIBRARIES} +  ${JSONCPP_LIBRARIES}    ${OPENSSL_LIBRARIES}    ${CRYPTO_LIBRARIES}    ${XMLRPCEPI_LIBRARIES} @@ -254,6 +257,7 @@ if (LL_TESTS)      ${LLCOMMON_LIBRARIES}      ${LLMESSAGE_LIBRARIES}      ${LLCOREHTTP_LIBRARIES} +    ${JSONCPP_LIBRARIES}      ${BOOST_CONTEXT_LIBRARY}      ${BOOST_COROUTINE_LIBRARY}      ${GOOGLEMOCK_LIBRARIES} diff --git a/indra/llmessage/llcorehttputil.cpp b/indra/llmessage/llcorehttputil.cpp index cf34029dfe..05d2e84f88 100644 --- a/indra/llmessage/llcorehttputil.cpp +++ b/indra/llmessage/llcorehttputil.cpp @@ -32,7 +32,10 @@  #include <iterator>  #include "llcorehttputil.h"  #include "llhttpconstants.h" +#include "llsd.h" +#include "llsdjson.h"  #include "llsdserialize.h" +#include "reader.h"   using namespace LLCore; @@ -252,6 +255,23 @@ void HttpCoroHandler::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRespons      }      buildStatusEntry(response, status, result); + +#if 0 +    // commenting out, but keeping since this can be useful for debugging +    if (!status) +    { +        LLSD &httpStatus = result[HttpCoroutineAdapter::HTTP_RESULTS]; + +        LLCore::BufferArray *body = response->getBody(); +        LLCore::BufferArrayStream bas(body); +        LLSD::Binary bodyData; +        bodyData.reserve(response->getBodySize()); +        bas >> std::noskipws; +        bodyData.assign(std::istream_iterator<U8>(bas), std::istream_iterator<U8>()); +        httpStatus["error_body"] = bodyData; +    } +#endif +      mReplyPump.post(result);  } @@ -438,6 +458,58 @@ LLSD HttpCoroRawHandler::handleSuccess(LLCore::HttpResponse * response, LLCore::  }  //======================================================================== +/// The HttpCoroJSONHandler is a specialization of the LLCore::HttpHandler for  +/// interacting with coroutines.  +///  +/// In addition to the normal "http_results" the returned LLSD will contain  +/// JSON entries will be converted into an LLSD map.  All results are considered  +/// strings +///                       +class HttpCoroJSONHandler : public HttpCoroHandler +{ +public: +    HttpCoroJSONHandler(LLEventStream &reply); + +    virtual LLSD handleSuccess(LLCore::HttpResponse * response, LLCore::HttpStatus &status); +}; + +//------------------------------------------------------------------------- +HttpCoroJSONHandler::HttpCoroJSONHandler(LLEventStream &reply) : +    HttpCoroHandler(reply) +{ +} + +LLSD HttpCoroJSONHandler::handleSuccess(LLCore::HttpResponse * response, LLCore::HttpStatus &status) +{ +    LLSD result = LLSD::emptyMap(); + +    BufferArray * body(response->getBody()); +    if (!body || !body->size()) +    { +        return result; +    } + +    LLCore::BufferArrayStream bas(body); +    Json::Value jsonRoot; + +    try +    { +        bas >> jsonRoot; +    } +    catch (std::runtime_error e) +    {   // deserialization failed.  Record the reason and pass back an empty map for markup. +        status = LLCore::HttpStatus(499, std::string(e.what())); +        return result; +    } + +    // Convert the JSON structure to LLSD +    result = LlsdFromJson(jsonRoot); + +    return result; +} + + +//========================================================================  HttpRequestPumper::HttpRequestPumper(const LLCore::HttpRequest::ptr_t &request) :      mHttpRequest(request)  { @@ -614,6 +686,16 @@ LLSD HttpCoroutineAdapter::getRawAndYield(LLCoros::self & self, LLCore::HttpRequ      return getAndYield_(self, request, url, options, headers, httpHandler);  } +LLSD HttpCoroutineAdapter::getJsonAndYield(LLCoros::self & self, LLCore::HttpRequest::ptr_t request, +    const std::string & url, LLCore::HttpOptions::ptr_t options, LLCore::HttpHeaders::ptr_t headers) +{ +    LLEventStream  replyPump(mAdapterName + "Reply", true); +    HttpCoroHandler::ptr_t httpHandler = HttpCoroHandler::ptr_t(new HttpCoroJSONHandler(replyPump)); + +    return getAndYield_(self, request, url, options, headers, httpHandler); +} + +  LLSD HttpCoroutineAdapter::getAndYield_(LLCoros::self & self, LLCore::HttpRequest::ptr_t &request,      const std::string & url,      LLCore::HttpOptions::ptr_t &options, LLCore::HttpHeaders::ptr_t &headers,  diff --git a/indra/llmessage/llcorehttputil.h b/indra/llmessage/llcorehttputil.h index 35e5b0aa2d..d6219318f9 100644 --- a/indra/llmessage/llcorehttputil.h +++ b/indra/llmessage/llcorehttputil.h @@ -373,6 +373,18 @@ public:              headers);      } +    LLSD getJsonAndYield(LLCoros::self & self, LLCore::HttpRequest::ptr_t request, +        const std::string & url, +        LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions(), false), +        LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders(), false)); +    LLSD getJsonndYield(LLCoros::self & self, LLCore::HttpRequest::ptr_t &request, +        const std::string & url, LLCore::HttpHeaders::ptr_t &headers) +    { +        return getJsonAndYield(self, request, url, +            LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions(), false), +            headers); +    } +      /// Execute a DELETE transaction on the supplied URL and yield execution of       /// the coroutine until a result is available. diff --git a/indra/newview/llwebprofile.cpp b/indra/newview/llwebprofile.cpp index ddb7f7bfce..3d371e629f 100755 --- a/indra/newview/llwebprofile.cpp +++ b/indra/newview/llwebprofile.cpp @@ -34,10 +34,14 @@  #include "llimagepng.h"  #include "llplugincookiestore.h" +#include "llsdserialize.h" +  // newview  #include "llpanelprofile.h" // for getProfileURL(). FIXME: move the method to LLAvatarActions  #include "llviewermedia.h" // FIXME: don't use LLViewerMedia internals +#include "llcorehttputil.h" +  // third-party  #include "reader.h" // JSON @@ -55,139 +59,6 @@   */  /////////////////////////////////////////////////////////////////////////////// -// LLWebProfileResponders::ConfigResponder - -class LLWebProfileResponders::ConfigResponder : public LLHTTPClient::Responder -{ -	LOG_CLASS(LLWebProfileResponders::ConfigResponder); - -public: -	ConfigResponder(LLPointer<LLImageFormatted> imagep) -	:	mImagep(imagep) -	{ -	} - -	// *TODO: Check for 'application/json' content type, and parse json at the base class. -	/*virtual*/ void completedRaw( -		const LLChannelDescriptors& channels, -		const LLIOPipe::buffer_ptr_t& buffer) -	{ -		LLBufferStream istr(channels, buffer.get()); -		std::stringstream strstrm; -		strstrm << istr.rdbuf(); -		const std::string body = strstrm.str(); - -		if (getStatus() != HTTP_OK) -		{ -			LL_WARNS() << "Failed to get upload config " << dumpResponse() << LL_ENDL; -			LLWebProfile::reportImageUploadStatus(false); -			return; -		} - -		Json::Value root; -		Json::Reader reader; -		if (!reader.parse(body, root)) -		{ -			LL_WARNS() << "Failed to parse upload config: " << reader.getFormatedErrorMessages() << LL_ENDL; -			LLWebProfile::reportImageUploadStatus(false); -			return; -		} - -		// *TODO: 404 = not supported by the grid -		// *TODO: increase timeout or handle 499 Expired - -		// Convert config to LLSD. -		const Json::Value data = root["data"]; -		const std::string upload_url = root["url"].asString(); -		LLSD config; -		config["acl"]						= data["acl"].asString(); -		config["AWSAccessKeyId"]			= data["AWSAccessKeyId"].asString(); -		config["Content-Type"]				= data["Content-Type"].asString(); -		config["key"]						= data["key"].asString(); -		config["policy"]					= data["policy"].asString(); -		config["success_action_redirect"]	= data["success_action_redirect"].asString(); -		config["signature"]					= data["signature"].asString(); -		config["add_loc"]					= data.get("add_loc", "0").asString(); -		config["caption"]					= data.get("caption", "").asString(); - -		// Do the actual image upload using the configuration. -		LL_DEBUGS("Snapshots") << "Got upload config, POSTing image to " << upload_url << ", config=[" << config << "]" << LL_ENDL; -		LLWebProfile::post(mImagep, config, upload_url); -	} - -private: -	LLPointer<LLImageFormatted> mImagep; -}; - -/////////////////////////////////////////////////////////////////////////////// -// LLWebProfilePostImageRedirectResponder -class LLWebProfileResponders::PostImageRedirectResponder : public LLHTTPClient::Responder -{ -	LOG_CLASS(LLWebProfileResponders::PostImageRedirectResponder); - -public: -	/*virtual*/ void completedRaw( -		const LLChannelDescriptors& channels, -		const LLIOPipe::buffer_ptr_t& buffer) -	{ -		if (getStatus() != HTTP_OK) -		{ -			LL_WARNS() << "Failed to upload image " << dumpResponse() << LL_ENDL; -			LLWebProfile::reportImageUploadStatus(false); -			return; -		} - -		LLBufferStream istr(channels, buffer.get()); -		std::stringstream strstrm; -		strstrm << istr.rdbuf(); -		const std::string body = strstrm.str(); -		LL_INFOS() << "Image uploaded." << LL_ENDL; -		LL_DEBUGS("Snapshots") << "Uploading image succeeded. Response: [" << body << "]" << LL_ENDL; -		LLWebProfile::reportImageUploadStatus(true); -	} -}; - - -/////////////////////////////////////////////////////////////////////////////// -// LLWebProfileResponders::PostImageResponder -class LLWebProfileResponders::PostImageResponder : public LLHTTPClient::Responder -{ -	LOG_CLASS(LLWebProfileResponders::PostImageResponder); - -public: -	/*virtual*/ void completedRaw(const LLChannelDescriptors& channels, -								  const LLIOPipe::buffer_ptr_t& buffer) -	{ -		// Viewer seems to fail to follow a 303 redirect on POST request -		// (URLRequest Error: 65, Send failed since rewinding of the data stream failed). -		// Handle it manually. -		if (getStatus() == HTTP_SEE_OTHER) -		{ -			LLSD headers = LLViewerMedia::getHeaders(); -			headers[HTTP_OUT_HEADER_COOKIE] = LLWebProfile::getAuthCookie(); -			const std::string& redir_url = getResponseHeader(HTTP_IN_HEADER_LOCATION); -			if (redir_url.empty()) -			{ -				LL_WARNS() << "Received empty redirection URL " << dumpResponse() << LL_ENDL; -				LL_DEBUGS("Snapshots") << "[headers:" << getResponseHeaders() << "]" << LL_ENDL; -				LLWebProfile::reportImageUploadStatus(false); -			} -			else -			{ -				LL_DEBUGS("Snapshots") << "Got redirection URL: " << redir_url << LL_ENDL; -				LLHTTPClient::get(redir_url, new LLWebProfileResponders::PostImageRedirectResponder, headers); -			} -		} -		else -		{ -			LL_WARNS() << "Unexpected POST response " << dumpResponse() << LL_ENDL; -			LL_DEBUGS("Snapshots") << "[headers:" << getResponseHeaders() << "]" << LL_ENDL; -			LLWebProfile::reportImageUploadStatus(false); -		} -	} -}; - -///////////////////////////////////////////////////////////////////////////////  // LLWebProfile  std::string LLWebProfile::sAuthCookie; @@ -196,15 +67,9 @@ LLWebProfile::status_callback_t LLWebProfile::mStatusCallback;  // static  void LLWebProfile::uploadImage(LLPointer<LLImageFormatted> image, const std::string& caption, bool add_location)  { -	// Get upload configuration data. -	std::string config_url(getProfileURL(LLStringUtil::null) + "snapshots/s3_upload_config"); -	config_url += "?caption=" + LLURI::escape(caption); -	config_url += "&add_loc=" + std::string(add_location ? "1" : "0"); - -	LL_DEBUGS("Snapshots") << "Requesting " << config_url << LL_ENDL; -	LLSD headers = LLViewerMedia::getHeaders(); -	headers[HTTP_OUT_HEADER_COOKIE] = getAuthCookie(); -	LLHTTPClient::get(config_url, new LLWebProfileResponders::ConfigResponder(image), headers); +    LLCoros::instance().launch("LLWebProfile::uploadImageCoro", +        boost::bind(&LLWebProfile::uploadImageCoro, _1, image, caption, add_location)); +  }  // static @@ -214,74 +79,193 @@ void LLWebProfile::setAuthCookie(const std::string& cookie)  	sAuthCookie = cookie;  } -// static -void LLWebProfile::post(LLPointer<LLImageFormatted> image, const LLSD& config, const std::string& url) + +/*static*/ +LLCore::HttpHeaders::ptr_t LLWebProfile::buildDefaultHeaders()  { -	if (dynamic_cast<LLImagePNG*>(image.get()) == 0) -	{ -		LL_WARNS() << "Image to upload is not a PNG" << LL_ENDL; -		llassert(dynamic_cast<LLImagePNG*>(image.get()) != 0); -		return; -	} +    LLCore::HttpHeaders::ptr_t httpHeaders(new LLCore::HttpHeaders); +    LLSD headers = LLViewerMedia::getHeaders(); -	const std::string boundary = "----------------------------0123abcdefab"; +    for (LLSD::map_iterator it = headers.beginMap(); it != headers.endMap(); ++it) +    { +        httpHeaders->append((*it).first, (*it).second.asStringRef()); +    } -	LLSD headers = LLViewerMedia::getHeaders(); -	headers[HTTP_OUT_HEADER_COOKIE] = getAuthCookie(); -	headers[HTTP_OUT_HEADER_CONTENT_TYPE] = "multipart/form-data; boundary=" + boundary; +    return httpHeaders; +} -	std::ostringstream body; -	// *NOTE: The order seems to matter. -	body	<< "--" << boundary << "\r\n" -			<< "Content-Disposition: form-data; name=\"key\"\r\n\r\n" -			<< config["key"].asString() << "\r\n"; +/*static*/ +void LLWebProfile::uploadImageCoro(LLCoros::self& self, LLPointer<LLImageFormatted> image, std::string caption, bool addLocation) +{ +    LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); +    LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t +        httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("genericPostCoro", httpPolicy)); +    LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); +    LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); +    LLCore::HttpHeaders::ptr_t httpHeaders; + +    if (dynamic_cast<LLImagePNG*>(image.get()) == 0) +    { +        LL_WARNS() << "Image to upload is not a PNG" << LL_ENDL; +        llassert(dynamic_cast<LLImagePNG*>(image.get()) != 0); +        return; +    } + +    httpOpts->setWantHeaders(true); + +    // Get upload configuration data. +    std::string configUrl(getProfileURL(std::string()) + "snapshots/s3_upload_config"); +    configUrl += "?caption=" + LLURI::escape(caption); +    configUrl += "&add_loc=" + std::string(addLocation ? "1" : "0"); + +    LL_DEBUGS("Snapshots") << "Requesting " << configUrl << LL_ENDL; + +    httpHeaders = buildDefaultHeaders(); +    httpHeaders->append(HTTP_OUT_HEADER_COOKIE, getAuthCookie()); + +    LLSD result = httpAdapter->getJsonAndYield(self, httpRequest, configUrl, httpOpts, httpHeaders); + +    LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; +    LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + +    if (!status) +    { +        std::ostringstream ostm; +        LLSDSerialize::toPrettyXML(httpResults, ostm); +        LL_WARNS("Snapshots") << "Failed to get image upload config" << LL_ENDL; +        LL_WARNS("Snapshots") << ostm.str() << LL_ENDL; +        LLWebProfile::reportImageUploadStatus(false); +        return; +    } + +    // Ready to build our image post body. + +    const LLSD &data = result["data"]; +    const std::string &uploadUrl = result["url"].asStringRef(); +    const std::string boundary = "----------------------------0123abcdefab"; + +    // a new set of headers. +    httpHeaders = buildDefaultHeaders(); +    httpHeaders->append(HTTP_OUT_HEADER_COOKIE, getAuthCookie()); +    httpHeaders->remove(HTTP_OUT_HEADER_CONTENT_TYPE); +    httpHeaders->append(HTTP_OUT_HEADER_CONTENT_TYPE, "multipart/form-data; boundary=" + boundary); +     +    LLCore::BufferArray::ptr_t body = LLWebProfile::buildPostData(data, image, boundary); + +    result = httpAdapter->postAndYield(self, httpRequest, uploadUrl, body, httpOpts, httpHeaders); + +    { +        std::ostringstream ostm; +        LLSDSerialize::toPrettyXML(result, ostm); +        LL_WARNS("Snapshots") << ostm.str() << LL_ENDL; +    } +    body.reset(); +    httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; +    status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + +    if (!status && (status != LLCore::HttpStatus(HTTP_SEE_OTHER))) +    { +        LL_WARNS("Snapshots") << "Failed to upload image data." << LL_ENDL; +        LLWebProfile::reportImageUploadStatus(false); +        return; +    } + +    LLSD resultHeaders = httpResults[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_HEADERS]; + +    httpHeaders = buildDefaultHeaders(); +    httpHeaders->append(HTTP_OUT_HEADER_COOKIE, getAuthCookie()); + +    const std::string& redirUrl = resultHeaders[HTTP_IN_HEADER_LOCATION].asStringRef(); + +    if (redirUrl.empty()) +    { +        LL_WARNS("Snapshots") << "Received empty redirection URL in post image." << LL_ENDL; +        LLWebProfile::reportImageUploadStatus(false); +    } + +    LL_DEBUGS("Snapshots") << "Got redirection URL: " << redirUrl << LL_ENDL; + +    result = httpAdapter->getRawAndYield(self, httpRequest, redirUrl, httpOpts, httpHeaders); + +    httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; +    status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + +    if (status != LLCore::HttpStatus(HTTP_OK)) +    { +        LL_WARNS("Snapshots") << "Failed to upload image." << LL_ENDL; +        LLWebProfile::reportImageUploadStatus(false); +        return; +    } + +    LLSD raw = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_RAW]; +//    const LLSD::Binary &rawBin = raw.asBinary(); +//    std::istringstream rawresult(rawBin.begin(), rawBin.end()); + +//    LLBufferStream istr(channels, buffer.get()); +//     std::stringstream strstrm; +//     strstrm << istr.rdbuf(); +//     const std::string body = strstrm.str(); +    LL_INFOS("Snapshots") << "Image uploaded." << LL_ENDL; +    LL_DEBUGS("Snapshots") << "Uploading image succeeded. Response: [" << raw.asString() << "]" << LL_ENDL; +    LLWebProfile::reportImageUploadStatus(true); -	body	<< "--" << boundary << "\r\n" -			<< "Content-Disposition: form-data; name=\"AWSAccessKeyId\"\r\n\r\n" -			<< config["AWSAccessKeyId"].asString() << "\r\n"; -	body	<< "--" << boundary << "\r\n" -			<< "Content-Disposition: form-data; name=\"acl\"\r\n\r\n" -			<< config["acl"].asString() << "\r\n"; +} -	body	<< "--" << boundary << "\r\n" -			<< "Content-Disposition: form-data; name=\"Content-Type\"\r\n\r\n" -			<< config["Content-Type"].asString() << "\r\n"; +/*static*/ +LLCore::BufferArray::ptr_t LLWebProfile::buildPostData(const LLSD &data, LLPointer<LLImageFormatted> &image, const std::string &boundary) +{ +    LLCore::BufferArray::ptr_t body(new LLCore::BufferArray); +    LLCore::BufferArrayStream bas(body.get()); -	body	<< "--" << boundary << "\r\n" -			<< "Content-Disposition: form-data; name=\"policy\"\r\n\r\n" -			<< config["policy"].asString() << "\r\n"; +    //    std::ostringstream body; -	body	<< "--" << boundary << "\r\n" -			<< "Content-Disposition: form-data; name=\"signature\"\r\n\r\n" -			<< config["signature"].asString() << "\r\n"; +    // *NOTE: The order seems to matter. +    bas << "--" << boundary << "\r\n" +        << "Content-Disposition: form-data; name=\"key\"\r\n\r\n" +        << data["key"].asString() << "\r\n"; -	body	<< "--" << boundary << "\r\n" -			<< "Content-Disposition: form-data; name=\"success_action_redirect\"\r\n\r\n" -			<< config["success_action_redirect"].asString() << "\r\n"; +    bas << "--" << boundary << "\r\n" +        << "Content-Disposition: form-data; name=\"AWSAccessKeyId\"\r\n\r\n" +        << data["AWSAccessKeyId"].asString() << "\r\n"; -	body	<< "--" << boundary << "\r\n" -			<< "Content-Disposition: form-data; name=\"file\"; filename=\"snapshot.png\"\r\n" -			<< "Content-Type: image/png\r\n\r\n"; +    bas << "--" << boundary << "\r\n" +        << "Content-Disposition: form-data; name=\"acl\"\r\n\r\n" +        << data["acl"].asString() << "\r\n"; -	// Insert the image data. -	// *FIX: Treating this as a string will probably screw it up ... -	U8* image_data = image->getData(); -	for (S32 i = 0; i < image->getDataSize(); ++i) -	{ -		body << image_data[i]; -	} +    bas << "--" << boundary << "\r\n" +        << "Content-Disposition: form-data; name=\"Content-Type\"\r\n\r\n" +        << data["Content-Type"].asString() << "\r\n"; + +    bas << "--" << boundary << "\r\n" +        << "Content-Disposition: form-data; name=\"policy\"\r\n\r\n" +        << data["policy"].asString() << "\r\n"; + +    bas << "--" << boundary << "\r\n" +        << "Content-Disposition: form-data; name=\"signature\"\r\n\r\n" +        << data["signature"].asString() << "\r\n"; + +    bas << "--" << boundary << "\r\n" +        << "Content-Disposition: form-data; name=\"success_action_redirect\"\r\n\r\n" +        << data["success_action_redirect"].asString() << "\r\n"; + +    bas << "--" << boundary << "\r\n" +        << "Content-Disposition: form-data; name=\"file\"; filename=\"snapshot.png\"\r\n" +        << "Content-Type: image/png\r\n\r\n"; -	body <<	"\r\n--" << boundary << "--\r\n"; +    // Insert the image data. +    //char *datap = (char *)(image->getData()); +    //bas.write(datap, image->getDataSize()); +    U8* image_data = image->getData(); +    for (S32 i = 0; i < image->getDataSize(); ++i) +    { +        bas << image_data[i]; +    } -	// postRaw() takes ownership of the buffer and releases it later. -	size_t size = body.str().size(); -	U8 *data = new U8[size]; -	memcpy(data, body.str().data(), size); +    bas << "\r\n--" << boundary << "--\r\n"; -	// Send request, successful upload will trigger posting metadata. -	LLHTTPClient::postRaw(url, data, size, new LLWebProfileResponders::PostImageResponder(), headers); +    return body;  }  // static diff --git a/indra/newview/llwebprofile.h b/indra/newview/llwebprofile.h index 10279bffac..604ef7aff7 100755 --- a/indra/newview/llwebprofile.h +++ b/indra/newview/llwebprofile.h @@ -28,6 +28,10 @@  #define LL_LLWEBPROFILE_H  #include "llimage.h" +#include "lleventcoro.h" +#include "llcoros.h" +#include "httpheaders.h" +#include "bufferarray.h"  namespace LLWebProfileResponders  { @@ -54,11 +58,11 @@ public:  	static void setImageUploadResultCallback(status_callback_t cb) { mStatusCallback = cb; }  private: -	friend class LLWebProfileResponders::ConfigResponder; -	friend class LLWebProfileResponders::PostImageResponder; -	friend class LLWebProfileResponders::PostImageRedirectResponder; +    static LLCore::HttpHeaders::ptr_t buildDefaultHeaders(); + +    static void uploadImageCoro(LLCoros::self& self, LLPointer<LLImageFormatted> image, std::string caption, bool add_location); +    static LLCore::BufferArray::ptr_t buildPostData(const LLSD &data, LLPointer<LLImageFormatted> &image, const std::string &boundary); -	static void post(LLPointer<LLImageFormatted> image, const LLSD& config, const std::string& url);  	static void reportImageUploadStatus(bool ok);  	static std::string getAuthCookie(); | 
