PageRenderTime 48ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/indra/test/llhttpnode_tut.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 427 lines | 307 code | 85 blank | 35 comment | 18 complexity | e4c9dfa5bf95d655a0401600167faaaf MD5 | raw file
Possible License(s): LGPL-2.1
  1. /**
  2. * @file lliohttpnode_tut.cpp
  3. * @date May 2006
  4. * @brief HTTP server unit tests
  5. *
  6. * $LicenseInfo:firstyear=2006&license=viewerlgpl$
  7. * Second Life Viewer Source Code
  8. * Copyright (C) 2010, Linden Research, Inc.
  9. *
  10. * This library is free software; you can redistribute it and/or
  11. * modify it under the terms of the GNU Lesser General Public
  12. * License as published by the Free Software Foundation;
  13. * version 2.1 of the License only.
  14. *
  15. * This library is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  18. * Lesser General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU Lesser General Public
  21. * License along with this library; if not, write to the Free Software
  22. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  23. *
  24. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  25. * $/LicenseInfo$
  26. */
  27. #include "linden_common.h"
  28. #include "lltut.h"
  29. #include "llhttpnode.h"
  30. #include "llsdhttpserver.h"
  31. namespace tut
  32. {
  33. struct HTTPNodeTestData
  34. {
  35. LLHTTPNode mRoot;
  36. LLSD mContext;
  37. const LLSD& context() { return mContext; }
  38. std::string remainderPath()
  39. {
  40. std::ostringstream pathOutput;
  41. bool addSlash = false;
  42. LLSD& remainder = mContext["request"]["remainder"];
  43. for (LLSD::array_const_iterator i = remainder.beginArray();
  44. i != remainder.endArray();
  45. ++i)
  46. {
  47. if (addSlash) { pathOutput << '/'; }
  48. pathOutput << i->asString();
  49. addSlash = true;
  50. }
  51. return pathOutput.str();
  52. }
  53. void ensureRootTraversal(const std::string& path,
  54. const LLHTTPNode* expectedNode,
  55. const char* expectedRemainder)
  56. {
  57. mContext.clear();
  58. const LLHTTPNode* actualNode = mRoot.traverse(path, mContext);
  59. ensure_equals("traverse " + path + " node",
  60. actualNode, expectedNode);
  61. ensure_equals("traverse " + path + " remainder",
  62. remainderPath(), expectedRemainder);
  63. }
  64. class Response : public LLHTTPNode::Response
  65. {
  66. public:
  67. static LLPointer<Response> create() {return new Response();}
  68. LLSD mResult;
  69. void result(const LLSD& result) { mResult = result; }
  70. void status(S32 code, const std::string& message) { }
  71. void extendedResult(S32 code, const std::string& message, const LLSD& headers) { }
  72. private:
  73. Response() {;} // Must be accessed through LLPointer.
  74. };
  75. typedef LLPointer<Response> ResponsePtr;
  76. LLSD get(const std::string& path)
  77. {
  78. mContext.clear();
  79. const LLHTTPNode* node = mRoot.traverse(path, mContext);
  80. ensure(path + " found", node != NULL);
  81. ResponsePtr response = Response::create();
  82. node->get(LLHTTPNode::ResponsePtr(response), mContext);
  83. return response->mResult;
  84. }
  85. LLSD post(const std::string& path, const LLSD& input)
  86. {
  87. mContext.clear();
  88. const LLHTTPNode* node = mRoot.traverse(path, mContext);
  89. ensure(path + " found", node != NULL);
  90. ResponsePtr response = Response::create();
  91. node->post(LLHTTPNode::ResponsePtr(response), mContext, input);
  92. return response->mResult;
  93. }
  94. void ensureMemberString(const std::string& name,
  95. const LLSD& actualMap, const std::string& member,
  96. const std::string& expectedValue)
  97. {
  98. ensure_equals(name + " " + member,
  99. actualMap[member].asString(), expectedValue);
  100. }
  101. void ensureInArray(const LLSD& actualArray,
  102. const std::string& expectedValue)
  103. {
  104. LLSD::array_const_iterator i = actualArray.beginArray();
  105. LLSD::array_const_iterator end = actualArray.endArray();
  106. for (; i != end; ++i)
  107. {
  108. std::string path = i->asString();
  109. if (path == expectedValue)
  110. {
  111. return;
  112. }
  113. }
  114. fail("didn't find " + expectedValue);
  115. }
  116. };
  117. typedef test_group<HTTPNodeTestData> HTTPNodeTestGroup;
  118. typedef HTTPNodeTestGroup::object HTTPNodeTestObject;
  119. HTTPNodeTestGroup httpNodeTestGroup("http node");
  120. template<> template<>
  121. void HTTPNodeTestObject::test<1>()
  122. {
  123. // traversal of the lone node
  124. ensureRootTraversal("", &mRoot, "");
  125. ensureRootTraversal("/", &mRoot, "");
  126. ensureRootTraversal("foo", NULL, "foo");
  127. ensureRootTraversal("foo/bar", NULL, "foo/bar");
  128. ensure_equals("root of root", mRoot.rootNode(), &mRoot);
  129. }
  130. template<> template<>
  131. void HTTPNodeTestObject::test<2>()
  132. {
  133. // simple traversal of a single node
  134. LLHTTPNode* helloNode = new LLHTTPNode;
  135. mRoot.addNode("hello", helloNode);
  136. ensureRootTraversal("hello", helloNode, "");
  137. ensureRootTraversal("/hello", helloNode, "");
  138. ensureRootTraversal("hello/", helloNode, "");
  139. ensureRootTraversal("/hello/", helloNode, "");
  140. ensureRootTraversal("hello/there", NULL, "there");
  141. ensure_equals("root of hello", helloNode->rootNode(), &mRoot);
  142. }
  143. template<> template<>
  144. void HTTPNodeTestObject::test<3>()
  145. {
  146. // traversal of mutli-branched tree
  147. LLHTTPNode* greekNode = new LLHTTPNode;
  148. LLHTTPNode* alphaNode = new LLHTTPNode;
  149. LLHTTPNode* betaNode = new LLHTTPNode;
  150. LLHTTPNode* gammaNode = new LLHTTPNode;
  151. greekNode->addNode("alpha", alphaNode);
  152. greekNode->addNode("beta", betaNode);
  153. greekNode->addNode("gamma", gammaNode);
  154. mRoot.addNode("greek", greekNode);
  155. LLHTTPNode* hebrewNode = new LLHTTPNode;
  156. LLHTTPNode* alephNode = new LLHTTPNode;
  157. hebrewNode->addNode("aleph", alephNode);
  158. mRoot.addNode("hebrew", hebrewNode);
  159. ensureRootTraversal("greek/alpha", alphaNode, "");
  160. ensureRootTraversal("greek/beta", betaNode, "");
  161. ensureRootTraversal("greek/delta", NULL, "delta");
  162. ensureRootTraversal("greek/gamma", gammaNode, "");
  163. ensureRootTraversal("hebrew/aleph", alephNode, "");
  164. ensure_equals("root of greek", greekNode->rootNode(), &mRoot);
  165. ensure_equals("root of alpha", alphaNode->rootNode(), &mRoot);
  166. ensure_equals("root of beta", betaNode->rootNode(), &mRoot);
  167. ensure_equals("root of gamma", gammaNode->rootNode(), &mRoot);
  168. ensure_equals("root of hebrew", hebrewNode->rootNode(), &mRoot);
  169. ensure_equals("root of aleph", alephNode->rootNode(), &mRoot);
  170. }
  171. template<> template<>
  172. void HTTPNodeTestObject::test<4>()
  173. {
  174. // automatic creation of parent nodes and not overriding existing nodes
  175. LLHTTPNode* alphaNode = new LLHTTPNode;
  176. LLHTTPNode* betaNode = new LLHTTPNode;
  177. LLHTTPNode* gammaNode = new LLHTTPNode;
  178. LLHTTPNode* gamma2Node = new LLHTTPNode;
  179. mRoot.addNode("greek/alpha", alphaNode);
  180. mRoot.addNode("greek/beta", betaNode);
  181. mRoot.addNode("greek/gamma", gammaNode);
  182. mRoot.addNode("greek/gamma", gamma2Node);
  183. LLHTTPNode* alephNode = new LLHTTPNode;
  184. mRoot.addNode("hebrew/aleph", alephNode);
  185. ensureRootTraversal("greek/alpha", alphaNode, "");
  186. ensureRootTraversal("greek/beta", betaNode, "");
  187. ensureRootTraversal("greek/delta", NULL, "delta");
  188. ensureRootTraversal("greek/gamma", gammaNode, "");
  189. ensureRootTraversal("hebrew/aleph", alephNode, "");
  190. ensure_equals("root of alpha", alphaNode->rootNode(), &mRoot);
  191. ensure_equals("root of beta", betaNode->rootNode(), &mRoot);
  192. ensure_equals("root of gamma", gammaNode->rootNode(), &mRoot);
  193. ensure_equals("root of aleph", alephNode->rootNode(), &mRoot);
  194. }
  195. class IntegerNode : public LLHTTPNode
  196. {
  197. public:
  198. virtual void get(ResponsePtr response, const LLSD& context) const
  199. {
  200. int n = context["extra"]["value"];
  201. LLSD info;
  202. info["value"] = n;
  203. info["positive"] = n > 0;
  204. info["zero"] = n == 0;
  205. info["negative"] = n < 0;
  206. response->result(info);
  207. }
  208. virtual bool validate(const std::string& name, LLSD& context) const
  209. {
  210. int n;
  211. std::istringstream i_stream(name);
  212. i_stream >> n;
  213. if (i_stream.fail() || i_stream.get() != EOF)
  214. {
  215. return false;
  216. }
  217. context["extra"]["value"] = n;
  218. return true;
  219. }
  220. };
  221. class SquareNode : public LLHTTPNode
  222. {
  223. public:
  224. virtual void get(ResponsePtr response, const LLSD& context) const
  225. {
  226. int n = context["extra"]["value"];
  227. response->result(n*n);
  228. }
  229. };
  230. template<> template<>
  231. void HTTPNodeTestObject::test<5>()
  232. {
  233. // wildcard nodes
  234. LLHTTPNode* miscNode = new LLHTTPNode;
  235. LLHTTPNode* iNode = new IntegerNode;
  236. LLHTTPNode* sqNode = new SquareNode;
  237. mRoot.addNode("test/misc", miscNode);
  238. mRoot.addNode("test/<int>", iNode);
  239. mRoot.addNode("test/<int>/square", sqNode);
  240. ensureRootTraversal("test/42", iNode, "");
  241. ensure_equals("stored integer",
  242. context()["extra"]["value"].asInteger(), 42);
  243. ensureRootTraversal("test/bob", NULL, "bob");
  244. ensure("nothing stored",
  245. context()["extra"]["value"].isUndefined());
  246. ensureRootTraversal("test/3/square", sqNode, "");
  247. ResponsePtr response = Response::create();
  248. sqNode->get(LLHTTPNode::ResponsePtr(response), context());
  249. ensure_equals("square result", response->mResult.asInteger(), 9);
  250. }
  251. class AlphaNode : public LLHTTPNode
  252. {
  253. public:
  254. virtual bool handles(const LLSD& remainder, LLSD& context) const
  255. {
  256. LLSD::array_const_iterator i = remainder.beginArray();
  257. LLSD::array_const_iterator end = remainder.endArray();
  258. for (; i != end; ++i)
  259. {
  260. std::string s = i->asString();
  261. if (s.empty() || s[0] != 'a')
  262. {
  263. return false;
  264. }
  265. }
  266. return true;
  267. }
  268. };
  269. template<> template<>
  270. void HTTPNodeTestObject::test<6>()
  271. {
  272. // nodes that handle remainders
  273. LLHTTPNode* miscNode = new LLHTTPNode;
  274. LLHTTPNode* aNode = new AlphaNode;
  275. LLHTTPNode* zNode = new LLHTTPNode;
  276. mRoot.addNode("test/misc", miscNode);
  277. mRoot.addNode("test/alpha", aNode);
  278. mRoot.addNode("test/alpha/zebra", zNode);
  279. ensureRootTraversal("test/alpha", aNode, "");
  280. ensureRootTraversal("test/alpha/abe", aNode, "abe");
  281. ensureRootTraversal("test/alpha/abe/amy", aNode, "abe/amy");
  282. ensureRootTraversal("test/alpha/abe/bea", NULL, "abe/bea");
  283. ensureRootTraversal("test/alpha/bob", NULL, "bob");
  284. ensureRootTraversal("test/alpha/zebra", zNode, "");
  285. }
  286. template<> template<>
  287. void HTTPNodeTestObject::test<7>()
  288. {
  289. // test auto registration
  290. LLHTTPStandardServices::useServices();
  291. LLHTTPRegistrar::buildAllServices(mRoot);
  292. {
  293. LLSD result = get("web/hello");
  294. ensure_equals("hello result", result.asString(), "hello");
  295. }
  296. {
  297. LLSD stuff = 3.14159;
  298. LLSD result = post("web/echo", stuff);
  299. ensure_equals("echo result", result, stuff);
  300. }
  301. }
  302. template<> template<>
  303. void HTTPNodeTestObject::test<8>()
  304. {
  305. // test introspection
  306. LLHTTPRegistrar::buildAllServices(mRoot);
  307. mRoot.addNode("test/misc", new LLHTTPNode);
  308. mRoot.addNode("test/<int>", new IntegerNode);
  309. mRoot.addNode("test/<int>/square", new SquareNode);
  310. const LLSD result = get("web/server/api");
  311. ensure("result is array", result.isArray());
  312. ensure("result size", result.size() >= 2);
  313. ensureInArray(result, "web/echo");
  314. ensureInArray(result, "web/hello");
  315. ensureInArray(result, "test/misc");
  316. ensureInArray(result, "test/<int>");
  317. ensureInArray(result, "test/<int>/square");
  318. }
  319. template<> template<>
  320. void HTTPNodeTestObject::test<9>()
  321. {
  322. // test introspection details
  323. LLHTTPRegistrar::buildAllServices(mRoot);
  324. const LLSD helloDetails = get("web/server/api/web/hello");
  325. ensure_contains("hello description",
  326. helloDetails["description"].asString(), "hello");
  327. ensure_equals("method name", helloDetails["api"][0].asString(), std::string("GET"));
  328. ensureMemberString("hello", helloDetails, "output", "\"hello\"");
  329. ensure_contains("hello __file__",
  330. helloDetails["__file__"].asString(), "llsdhttpserver.cpp");
  331. ensure("hello line", helloDetails["__line__"].isInteger());
  332. const LLSD echoDetails = get("web/server/api/web/echo");
  333. ensure_contains("echo description",
  334. echoDetails["description"].asString(), "echo");
  335. ensure_equals("method name", echoDetails["api"][0].asString(), std::string("POST"));
  336. ensureMemberString("echo", echoDetails, "input", "<any>");
  337. ensureMemberString("echo", echoDetails, "output", "<the input>");
  338. ensure_contains("echo __file__",
  339. echoDetails["__file__"].asString(), "llsdhttpserver.cpp");
  340. ensure("echo", echoDetails["__line__"].isInteger());
  341. }
  342. }