diff options
Diffstat (limited to 'indra/llmessage/llhttpnode.cpp')
-rw-r--r-- | indra/llmessage/llhttpnode.cpp | 449 |
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; +} |