summaryrefslogtreecommitdiff
path: root/indra/llmessage/llhttpnode.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llmessage/llhttpnode.cpp')
-rw-r--r--indra/llmessage/llhttpnode.cpp449
1 files changed, 449 insertions, 0 deletions
diff --git a/indra/llmessage/llhttpnode.cpp b/indra/llmessage/llhttpnode.cpp
new file mode 100644
index 0000000000..e7d441b22c
--- /dev/null
+++ b/indra/llmessage/llhttpnode.cpp
@@ -0,0 +1,449 @@
+/**
+ * @file llhttpnode.cpp
+ * @brief Implementation of classes for generic HTTP/LSL/REST handling.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llhttpnode.h"
+
+#include "boost/tokenizer.hpp"
+
+#include "llstl.h"
+
+static const std::string CONTEXT_REQUEST("request");
+static const std::string CONTEXT_WILDCARD("wildcard");
+
+/**
+ * LLHTTPNode
+ */
+
+class LLHTTPNode::Impl
+{
+public:
+ typedef std::map<std::string, LLHTTPNode*> ChildMap;
+
+ ChildMap mNamedChildren;
+ LLHTTPNode* mWildcardChild;
+ std::string mWildcardName;
+ std::string mWildcardKey;
+ LLHTTPNode* mParentNode;
+
+ Impl() : mWildcardChild(NULL), mParentNode(NULL) { }
+
+ LLHTTPNode* findNamedChild(const std::string& name) const;
+};
+
+
+LLHTTPNode* LLHTTPNode::Impl::findNamedChild(const std::string& name) const
+{
+ LLHTTPNode* child = get_ptr_in_map(mNamedChildren, name);
+
+ if (!child && ((name[0] == '*') || (name == mWildcardName)))
+ {
+ child = mWildcardChild;
+ }
+
+ return child;
+}
+
+
+LLHTTPNode::LLHTTPNode()
+ : impl(* new Impl)
+{
+}
+
+// virtual
+LLHTTPNode::~LLHTTPNode()
+{
+ std::for_each(impl.mNamedChildren.begin(), impl.mNamedChildren.end(),
+ DeletePairedPointer());
+
+ delete impl.mWildcardChild;
+
+ delete &impl;
+}
+
+
+namespace {
+ class NotImplemented
+ {
+ };
+}
+
+// virtual
+LLSD LLHTTPNode::get() const
+{
+ throw NotImplemented();
+}
+
+// virtual
+LLSD LLHTTPNode::put(const LLSD& input) const
+{
+ throw NotImplemented();
+}
+
+// virtual
+LLSD LLHTTPNode::post(const LLSD& input) const
+{
+ throw NotImplemented();
+}
+
+
+// virtual
+void LLHTTPNode::get(LLHTTPNode::ResponsePtr response, const LLSD& context) const
+{
+ try
+ {
+ response->result(get());
+ }
+ catch (NotImplemented)
+ {
+ response->methodNotAllowed();
+ }
+}
+
+// virtual
+void LLHTTPNode::put(LLHTTPNode::ResponsePtr response, const LLSD& context, const LLSD& input) const
+{
+ try
+ {
+ response->result(put(input));
+ }
+ catch (NotImplemented)
+ {
+ response->methodNotAllowed();
+ }
+}
+
+// virtual
+void LLHTTPNode::post(LLHTTPNode::ResponsePtr response, const LLSD& context, const LLSD& input) const
+{
+ try
+ {
+ response->result(post(input));
+ }
+ catch (NotImplemented)
+ {
+ response->methodNotAllowed();
+ }
+}
+
+// virtual
+void LLHTTPNode::del(LLHTTPNode::ResponsePtr response, const LLSD& context) const
+{
+ try
+ {
+ response->result(del(context));
+ }
+ catch (NotImplemented)
+ {
+ response->methodNotAllowed();
+ }
+
+}
+
+// virtual
+LLSD LLHTTPNode::del() const
+{
+ throw NotImplemented();
+}
+
+// virtual
+LLSD LLHTTPNode::del(const LLSD&) const
+{
+ return del();
+}
+
+
+// virtual
+LLHTTPNode* LLHTTPNode::getChild(const std::string& name, LLSD& context) const
+{
+ LLHTTPNode* namedChild = get_ptr_in_map(impl.mNamedChildren, name);
+ if (namedChild)
+ {
+ return namedChild;
+ }
+
+ if (impl.mWildcardChild
+ && impl.mWildcardChild->validate(name, context))
+ {
+ context[CONTEXT_REQUEST][CONTEXT_WILDCARD][impl.mWildcardKey] = name;
+ return impl.mWildcardChild;
+ }
+
+ return NULL;
+}
+
+
+// virtual
+bool LLHTTPNode::handles(const LLSD& remainder, LLSD& context) const
+{
+ return remainder.size() == 0;
+}
+
+// virtual
+bool LLHTTPNode::validate(const std::string& name, LLSD& context) const
+{
+ return false;
+}
+
+const LLHTTPNode* LLHTTPNode::traverse(
+ const std::string& path, LLSD& context) const
+{
+ typedef boost::tokenizer< boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep("/", "", boost::drop_empty_tokens);
+ tokenizer tokens(path, sep);
+ tokenizer::iterator iter = tokens.begin();
+ tokenizer::iterator end = tokens.end();
+
+ const LLHTTPNode* node = this;
+ for(; iter != end; ++iter)
+ {
+ LLHTTPNode* child = node->getChild(*iter, context);
+ if(!child)
+ {
+ lldebugs << "LLHTTPNode::traverse: Couldn't find '" << *iter << "'" << llendl;
+ break;
+ }
+ lldebugs << "LLHTTPNode::traverse: Found '" << *iter << "'" << llendl;
+
+ node = child;
+ }
+
+ LLSD& remainder = context[CONTEXT_REQUEST]["remainder"];
+ for(; iter != end; ++iter)
+ {
+ remainder.append(*iter);
+ }
+
+ return node->handles(remainder, context) ? node : NULL;
+}
+
+
+
+void LLHTTPNode::addNode(const std::string& path, LLHTTPNode* nodeToAdd)
+{
+ typedef boost::tokenizer< boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep("/", "", boost::drop_empty_tokens);
+ tokenizer tokens(path, sep);
+ tokenizer::iterator iter = tokens.begin();
+ tokenizer::iterator end = tokens.end();
+
+ LLHTTPNode* node = this;
+ for(; iter != end; ++iter)
+ {
+ LLHTTPNode* child = node->impl.findNamedChild(*iter);
+ if (!child) { break; }
+ node = child;
+ }
+
+ if (iter == end)
+ {
+ llwarns << "LLHTTPNode::addNode: already a node that handles "
+ << path << llendl;
+ return;
+ }
+
+ while (true)
+ {
+ std::string pathPart = *iter;
+
+ ++iter;
+ bool lastOne = iter == end;
+
+ LLHTTPNode* nextNode = lastOne ? nodeToAdd : new LLHTTPNode();
+
+ switch (pathPart[0])
+ {
+ case '<':
+ // *NOTE: This should really validate that it is of
+ // the proper form: <wildcardkey> so that the substr()
+ // generates the correct key name.
+ node->impl.mWildcardChild = nextNode;
+ node->impl.mWildcardName = pathPart;
+ if(node->impl.mWildcardKey.empty())
+ {
+ node->impl.mWildcardKey = pathPart.substr(
+ 1,
+ pathPart.size() - 2);
+ }
+ break;
+ case '*':
+ node->impl.mWildcardChild = nextNode;
+ if(node->impl.mWildcardName.empty())
+ {
+ node->impl.mWildcardName = pathPart;
+ }
+ break;
+
+ default:
+ node->impl.mNamedChildren[pathPart] = nextNode;
+ }
+ nextNode->impl.mParentNode = node;
+
+ if (lastOne) break;
+ node = nextNode;
+ }
+}
+
+static void append_node_paths(LLSD& result,
+ const std::string& name, const LLHTTPNode* node)
+{
+ result.append(name);
+
+ LLSD paths = node->allNodePaths();
+ LLSD::array_const_iterator i = paths.beginArray();
+ LLSD::array_const_iterator end = paths.endArray();
+
+ for (; i != end; ++i)
+ {
+ result.append(name + "/" + (*i).asString());
+ }
+}
+
+LLSD LLHTTPNode::allNodePaths() const
+{
+ LLSD result;
+
+ Impl::ChildMap::const_iterator i = impl.mNamedChildren.begin();
+ Impl::ChildMap::const_iterator end = impl.mNamedChildren.end();
+ for (; i != end; ++i)
+ {
+ append_node_paths(result, i->first, i->second);
+ }
+
+ if (impl.mWildcardChild)
+ {
+ append_node_paths(result, impl.mWildcardName, impl.mWildcardChild);
+ }
+
+ return result;
+}
+
+
+const LLHTTPNode* LLHTTPNode::rootNode() const
+{
+ const LLHTTPNode* node = this;
+
+ while (true)
+ {
+ const LLHTTPNode* next = node->impl.mParentNode;
+ if (!next)
+ {
+ return node;
+ }
+ node = next;
+ }
+}
+
+
+const LLHTTPNode* LLHTTPNode::findNode(const std::string& name) const
+{
+ return impl.findNamedChild(name);
+}
+
+LLHTTPNode::Response::~Response()
+{
+}
+
+void LLHTTPNode::Response::status(S32 code)
+{
+ status(code, "Unknown Error");
+}
+
+void LLHTTPNode::Response::notFound(const std::string& message)
+{
+ status(404, message);
+}
+
+void LLHTTPNode::Response::notFound()
+{
+ status(404, "Not Found");
+}
+
+void LLHTTPNode::Response::methodNotAllowed()
+{
+ status(405, "Method Not Allowed");
+}
+
+void LLHTTPNode::describe(Description& desc) const
+{
+ desc.shortInfo("unknown service (missing describe() method)");
+}
+
+
+const LLChainIOFactory* LLHTTPNode::getProtocolHandler() const
+{
+ return NULL;
+}
+
+
+
+namespace
+{
+ typedef std::map<std::string, LLHTTPRegistrar::NodeFactory*> FactoryMap;
+
+ FactoryMap& factoryMap()
+ {
+ static FactoryMap theMap;
+ return theMap;
+ }
+}
+
+LLHTTPRegistrar::NodeFactory::~NodeFactory() { }
+
+void LLHTTPRegistrar::registerFactory(
+ const std::string& path, NodeFactory& factory)
+{
+ factoryMap()[path] = &factory;
+}
+
+void LLHTTPRegistrar::buildAllServices(LLHTTPNode& root)
+{
+ const FactoryMap& map = factoryMap();
+
+ FactoryMap::const_iterator i = map.begin();
+ FactoryMap::const_iterator end = map.end();
+ for (; i != end; ++i)
+ {
+ llinfos << "LLHTTPRegistrar::buildAllServices adding node for path "
+ << i->first << llendl;
+
+ root.addNode(i->first, i->second->build());
+ }
+}
+
+LLPointer<LLSimpleResponse> LLSimpleResponse::create()
+{
+ return new LLSimpleResponse();
+}
+
+LLSimpleResponse::~LLSimpleResponse()
+{
+}
+
+void LLSimpleResponse::result(const LLSD& result)
+{
+ status(200, "OK");
+}
+
+void LLSimpleResponse::status(S32 code, const std::string& message)
+{
+ mCode = code;
+ mMessage = message;
+}
+
+void LLSimpleResponse::print(std::ostream& out) const
+{
+ out << mCode << " " << mMessage;
+}
+
+
+std::ostream& operator<<(std::ostream& out, const LLSimpleResponse& resp)
+{
+ resp.print(out);
+ return out;
+}