/** * @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 #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 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 > tokenizer; boost::char_separator 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 > tokenizer; boost::char_separator 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: 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 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::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; }