Renamed AICurlInterface::Responder to AICurlInterface::ResponderBase, but without the virtual 'event' methods. Derived from that: Responder and ReponderWithCompleted, where the first defines result = 0, ErrorWithContent and error, and the latter completedRaw and completed. Added HttpClient::IgnoreBody, derived from Responder and implementing 'result' doing nothing; HttpClient::Ignore is now derived from IgnoreBody and defines the still pure virtual getHTTPTimeoutPolicy. Added ResponderBase::decode_body, which is now the sole place where the code makes the decision wether some response data might be LLSD or not based on the http status result. Before it just tried to decode everything as LLSD, which seems a bit nonsense. ResponderWithCompleted::completed no longer does anything, since classes derived from ResponderWithCompleted are expected to override it, or never call it by overriding completedRaw. Entry point is now ResponderBase::finished = 0, instead of completedRaw, where ResponderWithCompleted implements finished by called completedRaw, but Responder doesn't: that directly calls result/errorWithContent/error. Or, for the hack ResponderAdapter, the entry points are pubResult/pubErrorWithContent. Those are now the ONLY public methods, so more confusion. mFinished is now set in all cases. As a result of all that, it is no longer possible to accidently pass a responder to ResponderAdapter that would break because it expects completed() and completedRaw() to be called. Added LLBufferArray::writeChannelTo. Fixed bug for BlockingResponder::body (returned reference to temporary). LLSDMessage::ResponderAdapter now allows a "timeoutpolicy" name to be passed (not doing so results in the default timings), so that the timeout policy of the used responder is retained. Fixed llfasttimerview.cpp to test LLSDSerialize::fromXML() to return a positive value instead of non-zero, because it may return -1 when the parsing fails (three places). Removed LLHTTPClient::Responder as base class from LLFloaterRegionDebugConsole completely: it isn't a responder! Several other responder classes were simplified a bit in order to compile again with the above changes.
188 lines
6.8 KiB
C++
188 lines
6.8 KiB
C++
/**
|
|
* @file llsdmessage.cpp
|
|
* @author Nat Goodspeed
|
|
* @date 2008-10-31
|
|
* @brief Implementation for llsdmessage.
|
|
*
|
|
* $LicenseInfo:firstyear=2008&license=viewerlgpl$
|
|
* Second Life Viewer Source Code
|
|
* Copyright (C) 2010, Linden Research, Inc.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation;
|
|
* version 2.1 of the License only.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
|
* $/LicenseInfo$
|
|
*/
|
|
|
|
#if LL_WINDOWS
|
|
#pragma warning (disable : 4675) // "resolved by ADL" -- just as I want!
|
|
#endif
|
|
|
|
// Precompiled header
|
|
#include "linden_common.h"
|
|
// associated header
|
|
#include "llsdmessage.h"
|
|
// STL headers
|
|
// std headers
|
|
// external library headers
|
|
// other Linden headers
|
|
#include "llevents.h"
|
|
#include "llsdserialize.h"
|
|
#include "llhttpclient.h"
|
|
#include "llmessageconfig.h"
|
|
#include "llhost.h"
|
|
#include "message.h"
|
|
#include "llsdutil.h"
|
|
#include "aihttptimeoutpolicy.h"
|
|
|
|
// Declare a static LLSDMessage instance to ensure that we have a listener as
|
|
// soon as someone tries to post on our canonical LLEventPump name.
|
|
static LLSDMessage httpListener;
|
|
|
|
LLSDMessage::LLSDMessage():
|
|
// Instantiating our own local LLEventPump with a string name the
|
|
// constructor is NOT allowed to tweak is a way of ensuring Singleton
|
|
// semantics: attempting to instantiate a second LLSDMessage object would
|
|
// throw LLEventPump::DupPumpName.
|
|
mEventPump("LLHTTPClient")
|
|
{
|
|
mEventPump.listen("self", boost::bind(&LLSDMessage::httpListener, this, _1));
|
|
}
|
|
|
|
bool LLSDMessage::httpListener(const LLSD& request)
|
|
{
|
|
llassert(false); // This function is never called. --Aleric
|
|
|
|
// Extract what we want from the request object. We do it all up front
|
|
// partly to document what we expect.
|
|
LLSD::String url(request["url"]);
|
|
LLSD payload(request["payload"]);
|
|
LLSD::String reply(request["reply"]);
|
|
LLSD::String error(request["error"]);
|
|
LLSD::String timeoutpolicy(request["timeoutpolicy"]);
|
|
// If the LLSD doesn't even have a "url" key, we doubt it was intended for
|
|
// this listener.
|
|
if (url.empty())
|
|
{
|
|
std::ostringstream out;
|
|
out << "request event without 'url' key to '" << mEventPump.getName() << "'";
|
|
throw ArgError(out.str());
|
|
}
|
|
LLSDMessage::EventResponder* responder =
|
|
new LLSDMessage::EventResponder(LLEventPumps::instance(), request, url, "POST", reply, error);
|
|
responder->setTimeoutPolicy(timeoutpolicy);
|
|
LLHTTPClient::post4(url, payload, responder);
|
|
return false;
|
|
}
|
|
|
|
LLSDMessage::EventResponder::EventResponder(LLEventPumps& pumps, LLSD const& request, std::string const& target,
|
|
std::string const& message, std::string const& replyPump, std::string const& errorPump) :
|
|
mPumps(pumps), mReqID(request), mTarget(target), mMessage(message), mReplyPump(replyPump), mErrorPump(errorPump),
|
|
mHTTPTimeoutPolicy(AIHTTPTimeoutPolicy::getTimeoutPolicyByName(std::string()))
|
|
{
|
|
}
|
|
|
|
void LLSDMessage::EventResponder::setTimeoutPolicy(std::string const& name)
|
|
{
|
|
mHTTPTimeoutPolicy = AIHTTPTimeoutPolicy::getTimeoutPolicyByName(name);
|
|
}
|
|
|
|
void LLSDMessage::EventResponder::result(const LLSD& data)
|
|
{
|
|
// If our caller passed an empty replyPump name, they're not
|
|
// listening: this is a fire-and-forget message. Don't bother posting
|
|
// to the pump whose name is "".
|
|
if (! mReplyPump.empty())
|
|
{
|
|
LLSD response(data);
|
|
mReqID.stamp(response);
|
|
mPumps.obtain(mReplyPump).post(response);
|
|
}
|
|
else // default success handling
|
|
{
|
|
LL_INFOS("LLSDMessage::EventResponder")
|
|
<< "'" << mMessage << "' to '" << mTarget << "' succeeded"
|
|
<< LL_ENDL;
|
|
}
|
|
}
|
|
|
|
void LLSDMessage::EventResponder::errorWithContent(U32 status, const std::string& reason, const LLSD& content)
|
|
{
|
|
// If our caller passed an empty errorPump name, they're not
|
|
// listening: "default error handling is acceptable." Only post to an
|
|
// explicit pump name.
|
|
if (! mErrorPump.empty())
|
|
{
|
|
LLSD info(mReqID.makeResponse());
|
|
info["target"] = mTarget;
|
|
info["message"] = mMessage;
|
|
info["status"] = LLSD::Integer(status);
|
|
info["reason"] = reason;
|
|
info["content"] = content;
|
|
mPumps.obtain(mErrorPump).post(info);
|
|
}
|
|
else // default error handling
|
|
{
|
|
// convention seems to be to use llinfos, but that seems a bit casual?
|
|
LL_WARNS("LLSDMessage::EventResponder")
|
|
<< "'" << mMessage << "' to '" << mTarget
|
|
<< "' failed with code " << status << ": " << reason << '\n'
|
|
<< ll_pretty_print_sd(content)
|
|
<< LL_ENDL;
|
|
}
|
|
}
|
|
|
|
LLSDMessage::ResponderAdapter::ResponderAdapter(LLHTTPClient::Responder* responder,
|
|
const std::string& name):
|
|
mResponder(responder),
|
|
mReplyPump(name + ".reply", true), // tweak name for uniqueness
|
|
mErrorPump(name + ".error", true)
|
|
{
|
|
mReplyPump.listen("self", boost::bind(&ResponderAdapter::listener, this, _1, true));
|
|
mErrorPump.listen("self", boost::bind(&ResponderAdapter::listener, this, _1, false));
|
|
}
|
|
|
|
std::string LLSDMessage::ResponderAdapter::getTimeoutPolicyName(void) const
|
|
{
|
|
return mResponder->getHTTPTimeoutPolicy().name();
|
|
}
|
|
|
|
bool LLSDMessage::ResponderAdapter::listener(const LLSD& payload, bool success)
|
|
{
|
|
AICurlInterface::Responder* responder = dynamic_cast<AICurlInterface::Responder*>(mResponder.get());
|
|
// If this assertion fails then ResponderAdapter has been used for a ResponderWithCompleted derived class,
|
|
// which is not allowed because ResponderAdapter can only work for classes derived from Responder that
|
|
// implement result() and errorWithContent (or just error).
|
|
llassert_always(responder);
|
|
if (success)
|
|
{
|
|
responder->pubResult(payload);
|
|
}
|
|
else
|
|
{
|
|
responder->pubErrorWithContent(payload["status"].asInteger(), payload["reason"], payload["content"]);
|
|
}
|
|
|
|
/*---------------- MUST BE LAST STATEMENT BEFORE RETURN ----------------*/
|
|
delete this;
|
|
// Destruction of mResponder will usually implicitly free its referent as well
|
|
/*------------------------- NOTHING AFTER THIS -------------------------*/
|
|
return false;
|
|
}
|
|
|
|
void LLSDMessage::link()
|
|
{
|
|
}
|