summaryrefslogtreecommitdiff
path: root/indra/llcommon/lleventdispatcher.cpp
diff options
context:
space:
mode:
authorNat Goodspeed <nat@lindenlab.com>2023-01-13 00:10:23 -0500
committerNat Goodspeed <nat@lindenlab.com>2023-07-13 12:49:08 -0400
commitd637229beeb3b7fa2bc7adb850ce9337b119037c (patch)
tree4d55981d96ea87a5c1af24723d623ea5ca627367 /indra/llcommon/lleventdispatcher.cpp
parent27f19826b6ef9b4af5db8613c44988b5c973618b (diff)
DRTVWR-558: Introduce LLDispatchListener batched requests.
Now the value of the incoming event's dispatch key may be an LLSD::String (as before), a map or an array, as documented in the augmented Doxygen class comments. LLDispatchListener will attempt multiple calls before sending a reply. (cherry picked from commit 7671b37285c6cdf1afcddb0019311a822c8a4dc5)
Diffstat (limited to 'indra/llcommon/lleventdispatcher.cpp')
-rw-r--r--indra/llcommon/lleventdispatcher.cpp183
1 files changed, 141 insertions, 42 deletions
diff --git a/indra/llcommon/lleventdispatcher.cpp b/indra/llcommon/lleventdispatcher.cpp
index 8e20d7184a..d10cf16b88 100644
--- a/indra/llcommon/lleventdispatcher.cpp
+++ b/indra/llcommon/lleventdispatcher.cpp
@@ -744,32 +744,44 @@ std::ostream& operator<<(std::ostream& out, const LLEventDispatcher& self)
*****************************************************************************/
std::string LLDispatchListener::mReplyKey{ "reply" };
-/*==========================================================================*|
- TODO:
-* When process() finds name.isArray(), construct response array from
- dispatching each call -- args must also be (array of args structures)
- (could also construct response map, IF array contains unique names)
-* When process() finds name.isMap(), construct response map from dispatching
- each call -- value of each key is its args struct -- argskey ignored --
- note, caller can't care about order
-* Possible future transactional behavior: look up all names before calling any
-|*==========================================================================*/
bool LLDispatchListener::process(const LLSD& event) const
{
- // 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 };
+ // Decide what to do based on the incoming value of the specified dispatch
+ // key.
+ LLSD name{ event[getDispatchKey()] };
+ if (name.isMap())
+ {
+ call_map(name, event);
+ }
+ else if (name.isArray())
+ {
+ call_array(name, event);
+ }
+ else
+ {
+ call_one(name, event);
+ }
+ return false;
+}
- if (! error.empty())
+void LLDispatchListener::call_one(const LLSD& name, const LLSD& event) const
+{
+ LLSD result;
+ try
{
- // 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;
+ result = (*this)(event);
+ }
+ catch (const DispatchError& err)
+ {
+ if (! event.has(mReplyKey))
+ {
+ // Without a reply key, let the exception propagate.
+ throw;
+ }
+
+ // Here there was an error and the incoming event has mReplyKey. Reply
+ // with a map containing an "error" key explaining the problem.
+ return reply(llsd::map("error", err.what()), event);
}
// We seem to have gotten a valid result. But we don't know whether the
@@ -785,40 +797,127 @@ bool LLDispatchListener::process(const LLSD& event) const
}
reply(result, event);
}
- 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
+void LLDispatchListener::call_map(const LLSD& reqmap, const LLSD& event) const
{
- try
+ // LLSD map containing returned values
+ LLSD result;
+ // collect any error messages here
+ std::ostringstream errors;
+ const char* delim = "";
+
+ for (const auto& pair : llsd::inMap(reqmap))
+ {
+ const LLSD::String& name{ pair.first };
+ const LLSD& args{ pair.second };
+ try
+ {
+ // With this form, capture return value even if undefined:
+ // presence of the key in the response map can be used to detect
+ // which request keys succeeded.
+ result[name] = (*this)(name, args);
+ }
+ catch (const DispatchError& err)
+ {
+ // collect message in 'errors', with hint as to which request map
+ // entry failed
+ errors << delim << err.what() << " (" << getDispatchKey()
+ << '[' << name << "])";
+ delim = "\n";
+ }
+ }
+
+ // so, were there any errors?
+ std::string error = errors.str();
+ if (! error.empty())
{
- if (name.empty())
+ if (! event.has(mReplyKey))
{
- // unless this throws, return (empty string, real return value)
- return { {}, (*this)(event) };
+ // can't send reply, throw
+ sCallFail<DispatchError>(error);
}
else
{
- // unless this throws, return (empty string, real return value)
- return { {}, (*this)(name, event) };
+ // reply key present
+ result["error"] = error;
}
}
- catch (const DispatchError& err)
+
+ reply(result, event);
+}
+
+void LLDispatchListener::call_array(const LLSD& reqarray, const LLSD& event) const
+{
+ // LLSD array containing returned values
+ LLSD results;
+ // arguments array, if present -- const because, if it's shorter than
+ // reqarray, we don't want to grow it
+ const LLSD argsarray{ event[getArgsKey()] };
+ // error message, if any
+ std::string error;
+
+ // classic index loop because we need the index
+ for (size_t i = 0, size = reqarray.size(); i < size; ++i)
+ {
+ const auto& reqentry{ reqarray[i] };
+ std::string name;
+ LLSD args;
+ if (reqentry.isString())
+ {
+ name = reqentry.asString();
+ args = argsarray[i];
+ }
+ else if (reqentry.isArray() && reqentry.size() == 2 && reqentry[0].isString())
+ {
+ name = reqentry[0].asString();
+ args = reqentry[1];
+ }
+ else
+ {
+ // reqentry isn't in either of the documented forms
+ error = stringize(*this, ": ", getDispatchKey(), '[', i, "] ",
+ reqentry, " unsupported");
+ break;
+ }
+
+ // reqentry is one of the valid forms, got name and args
+ try
+ {
+ // With this form, capture return value even if undefined
+ results.append((*this)(name, args));
+ }
+ catch (const DispatchError& err)
+ {
+ // append hint as to which requentry produced the error
+ error = stringize(err.what(), " (", getDispatchKey(), '[', i, ']');
+ break;
+ }
+ }
+
+ LLSD result;
+ // was there an error?
+ if (! error.empty())
{
- if (exception)
+ if (! event.has(mReplyKey))
{
- // Caller asked for an exception on error. Oblige.
- throw;
+ // can't send reply, throw
+ sCallFail<DispatchError>(error);
+ }
+ else
+ {
+ // reply key present
+ result["error"] = error;
}
- // Caller does NOT want an exception: return (error message, undefined)
- return { err.what(), LLSD() };
}
+
+ // wrap the results array as response map "data" key, as promised
+ if (results.isDefined())
+ {
+ result["data"] = results;
+ }
+
+ reply(result, event);
}
void LLDispatchListener::reply(const LLSD& reply, const LLSD& request) const