summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--indra/llcommon/lleventdispatcher.cpp69
-rw-r--r--indra/llcommon/lleventdispatcher.h36
2 files changed, 85 insertions, 20 deletions
diff --git a/indra/llcommon/lleventdispatcher.cpp b/indra/llcommon/lleventdispatcher.cpp
index 7abdc8f57a..8e20d7184a 100644
--- a/indra/llcommon/lleventdispatcher.cpp
+++ b/indra/llcommon/lleventdispatcher.cpp
@@ -742,6 +742,8 @@ std::ostream& operator<<(std::ostream& out, const LLEventDispatcher& self)
/*****************************************************************************
* LLDispatchListener
*****************************************************************************/
+std::string LLDispatchListener::mReplyKey{ "reply" };
+
/*==========================================================================*|
TODO:
* When process() finds name.isArray(), construct response array from
@@ -752,33 +754,27 @@ std::ostream& operator<<(std::ostream& out, const LLEventDispatcher& self)
note, caller can't care about order
* Possible future transactional behavior: look up all names before calling any
|*==========================================================================*/
-std::string LLDispatchListener::mReplyKey{ "reply" };
-
bool LLDispatchListener::process(const LLSD& event) const
{
- LLSD result;
- try
- {
- result = (*this)(event);
- }
- catch (const DispatchError& err)
- {
- // If the incoming event contains a "reply" key, we'll respond to the
- // invoker with an error message (below). But if not -- silently
- // ignoring an invocation request would be confusing at best. Escalate.
- if (! event.has(mReplyKey))
- {
- throw;
- }
-
- // reply with a map containing an "error" key explaining the problem
- reply(llsd::map("error", err.what()), event);
+ // Collecting errors is only meaningful with a reply key. Without one, if
+ // an error occurs, let the exception propagate.
+ auto returned = call("", event, (! event.has(mReplyKey)));
+ std::string& error{ returned.first };
+ LLSD& result{ returned.second };
+
+ if (! error.empty())
+ {
+ // Here there was an error and the incoming event has mReplyKey --
+ // else DispatchError would already have propagated out of the call()
+ // above. Reply with a map containing an "error" key explaining the
+ // problem.
+ reply(llsd::map("error", error), event);
return false;
}
// We seem to have gotten a valid result. But we don't know whether the
// registered callable is void or non-void. If it's void,
- // LLEventDispatcher will return isUndefined(). Otherwise, try to send it
+ // LLEventDispatcher returned isUndefined(). Otherwise, try to send it
// back to our invoker.
if (result.isDefined())
{
@@ -792,6 +788,39 @@ bool LLDispatchListener::process(const LLSD& event) const
return false;
}
+// Pass empty name to call LLEventDispatcher::operator()(const LLSD&),
+// non-empty name to call operator()(const std::string&, const LLSD&).
+// Returns (empty string, return value) on successful call.
+// Returns (error message, undefined) if error and 'exception' is false.
+// Throws DispatchError if error and 'exception' is true.
+std::pair<std::string, LLSD> LLDispatchListener::call(const std::string& name, const LLSD& event,
+ bool exception) const
+{
+ try
+ {
+ if (name.empty())
+ {
+ // unless this throws, return (empty string, real return value)
+ return { {}, (*this)(event) };
+ }
+ else
+ {
+ // unless this throws, return (empty string, real return value)
+ return { {}, (*this)(name, event) };
+ }
+ }
+ catch (const DispatchError& err)
+ {
+ if (exception)
+ {
+ // Caller asked for an exception on error. Oblige.
+ throw;
+ }
+ // Caller does NOT want an exception: return (error message, undefined)
+ return { err.what(), LLSD() };
+ }
+}
+
void LLDispatchListener::reply(const LLSD& reply, const LLSD& request) const
{
// Call sendReply() unconditionally: sendReply() itself tests whether the
diff --git a/indra/llcommon/lleventdispatcher.h b/indra/llcommon/lleventdispatcher.h
index 494fc6a366..a348a6660f 100644
--- a/indra/llcommon/lleventdispatcher.h
+++ b/indra/llcommon/lleventdispatcher.h
@@ -42,6 +42,7 @@
#include <string>
#include <typeinfo>
#include <type_traits>
+#include <utility> // std::pair
#include "llevents.h"
#include "llptrto.h"
#include "llsdutil.h"
@@ -522,6 +523,33 @@ LLEventDispatcher::make_invoker(Method f, const InstanceGetter& getter)
* that contains (or derives from) LLDispatchListener need only specify the
* LLEventPump name and dispatch key, and add() its methods. Incoming events
* will automatically be dispatched.
+ *
+ * If the incoming event contains a "reply" key specifying the LLSD::String
+ * name of an LLEventPump to which to respond, LLDispatchListener will attempt
+ * to send a response to that LLEventPump.
+ *
+ * If some error occurs (e.g. nonexistent callable name, wrong params) and
+ * "reply" is present, LLDispatchListener will send a response map to the
+ * specified LLEventPump containing an "error" key whose value is the relevant
+ * error message. If "reply" is not present, the DispatchError exception will
+ * propagate.
+ *
+ * If LLDispatchListener successfully calls the target callable, but no
+ * "reply" key is present, any value returned by that callable is discarded.
+ * If a "reply" key is present, but the target callable is void -- or it
+ * returns LLSD::isUndefined() -- no response is sent. If a void callable
+ * wants to send a response, it must do so explicitly.
+ *
+ * If the target callable returns a type convertible to LLSD (and, if it
+ * directly returns LLSD, the return value isDefined()), and if a "reply" key
+ * is present in the incoming event, LLDispatchListener will post the returned
+ * value to the "reply" LLEventPump. If the returned value is an LLSD map, it
+ * will merge the echoed "reqid" key into the map and send that. Otherwise, it
+ * will send an LLSD map containing "reqid" and a "data" key whose value is
+ * the value returned by the target callable.
+ *
+ * (It is inadvisable for a target callable to return an LLSD map containing
+ * keys "reqid" or "error", as that will confuse the invoker.)
*/
// Instead of containing an LLEventStream, LLDispatchListener derives from it.
// This allows an LLEventPumps::PumpFactory to return a pointer to an
@@ -532,6 +560,7 @@ class LL_COMMON_API LLDispatchListener:
public LLEventStream
{
public:
+ /// LLEventPump name, dispatch key [, arguments key (see LLEventDispatcher)]
template <typename... ARGS>
LLDispatchListener(const std::string& pumpname, const std::string& key,
ARGS&&... args);
@@ -539,6 +568,13 @@ public:
private:
bool process(const LLSD& event) const;
+ // Pass empty name to call LLEventDispatcher::operator()(const LLSD&),
+ // non-empty name to call operator()(const std::string&, const LLSD&).
+ // Returns (empty string, return value) on successful call.
+ // Returns (error message, undefined) if error and 'exception' is false.
+ // Throws DispatchError if error and 'exception' is true.
+ std::pair<std::string, LLSD> call(const std::string& name, const LLSD& event,
+ bool exception) const;
void reply(const LLSD& reply, const LLSD& request) const;
LLTempBoundListener mBoundListener;