Webprofile converted to coroutine.
Added JSON->LLSD converter Added corohandler for JSON datamaster
parent
a4741cecb2
commit
c437a9c4ec
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
@ -437,6 +457,58 @@ LLSD HttpCoroRawHandler::handleSuccess(LLCore::HttpResponse * response, LLCore::
|
|||
return result;
|
||||
}
|
||||
|
||||
//========================================================================
|
||||
/// 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,
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
@ -54,139 +58,6 @@
|
|||
* -> GET <redirect_url> via PostImageRedirectResponder
|
||||
*/
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// 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
|
||||
|
||||
|
|
@ -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");
|
||||
LLCoros::instance().launch("LLWebProfile::uploadImageCoro",
|
||||
boost::bind(&LLWebProfile::uploadImageCoro, _1, image, caption, add_location));
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
body << "--" << boundary << "\r\n"
|
||||
<< "Content-Disposition: form-data; name=\"AWSAccessKeyId\"\r\n\r\n"
|
||||
<< config["AWSAccessKeyId"].asString() << "\r\n";
|
||||
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;
|
||||
}
|
||||
|
||||
body << "--" << boundary << "\r\n"
|
||||
<< "Content-Disposition: form-data; name=\"acl\"\r\n\r\n"
|
||||
<< config["acl"].asString() << "\r\n";
|
||||
httpOpts->setWantHeaders(true);
|
||||
|
||||
body << "--" << boundary << "\r\n"
|
||||
<< "Content-Disposition: form-data; name=\"Content-Type\"\r\n\r\n"
|
||||
<< config["Content-Type"].asString() << "\r\n";
|
||||
// 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");
|
||||
|
||||
body << "--" << boundary << "\r\n"
|
||||
<< "Content-Disposition: form-data; name=\"policy\"\r\n\r\n"
|
||||
<< config["policy"].asString() << "\r\n";
|
||||
LL_DEBUGS("Snapshots") << "Requesting " << configUrl << LL_ENDL;
|
||||
|
||||
body << "--" << boundary << "\r\n"
|
||||
<< "Content-Disposition: form-data; name=\"signature\"\r\n\r\n"
|
||||
<< config["signature"].asString() << "\r\n";
|
||||
httpHeaders = buildDefaultHeaders();
|
||||
httpHeaders->append(HTTP_OUT_HEADER_COOKIE, getAuthCookie());
|
||||
|
||||
body << "--" << boundary << "\r\n"
|
||||
<< "Content-Disposition: form-data; name=\"success_action_redirect\"\r\n\r\n"
|
||||
<< config["success_action_redirect"].asString() << "\r\n";
|
||||
LLSD result = httpAdapter->getJsonAndYield(self, httpRequest, configUrl, httpOpts, httpHeaders);
|
||||
|
||||
body << "--" << boundary << "\r\n"
|
||||
<< "Content-Disposition: form-data; name=\"file\"; filename=\"snapshot.png\"\r\n"
|
||||
<< "Content-Type: image/png\r\n\r\n";
|
||||
LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
|
||||
LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
|
||||
|
||||
// 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];
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
body << "\r\n--" << boundary << "--\r\n";
|
||||
// Ready to build our image post body.
|
||||
|
||||
// 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);
|
||||
const LLSD &data = result["data"];
|
||||
const std::string &uploadUrl = result["url"].asStringRef();
|
||||
const std::string boundary = "----------------------------0123abcdefab";
|
||||
|
||||
// Send request, successful upload will trigger posting metadata.
|
||||
LLHTTPClient::postRaw(url, data, size, new LLWebProfileResponders::PostImageResponder(), headers);
|
||||
// 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);
|
||||
|
||||
|
||||
}
|
||||
|
||||
/*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());
|
||||
|
||||
// std::ostringstream body;
|
||||
|
||||
// *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";
|
||||
|
||||
bas << "--" << boundary << "\r\n"
|
||||
<< "Content-Disposition: form-data; name=\"AWSAccessKeyId\"\r\n\r\n"
|
||||
<< data["AWSAccessKeyId"].asString() << "\r\n";
|
||||
|
||||
bas << "--" << boundary << "\r\n"
|
||||
<< "Content-Disposition: form-data; name=\"acl\"\r\n\r\n"
|
||||
<< data["acl"].asString() << "\r\n";
|
||||
|
||||
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";
|
||||
|
||||
// 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];
|
||||
}
|
||||
|
||||
bas << "\r\n--" << boundary << "--\r\n";
|
||||
|
||||
return body;
|
||||
}
|
||||
|
||||
// static
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue