PageRenderTime 130ms CodeModel.GetById 91ms app.highlight 34ms RepoModel.GetById 1ms app.codeStats 1ms

/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
  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
 28#include "linden_common.h"
 29#include "lltut.h"
 30#include "llhttpnode.h"
 31#include "llsdhttpserver.h"
 32
 33namespace tut
 34{
 35	struct HTTPNodeTestData
 36	{
 37		LLHTTPNode mRoot;
 38		LLSD mContext;
 39	
 40		const LLSD& context() { return mContext; }
 41		
 42		std::string remainderPath()
 43		{
 44			std::ostringstream pathOutput;
 45			bool addSlash = false;
 46			
 47			LLSD& remainder = mContext["request"]["remainder"];
 48			for (LLSD::array_const_iterator i = remainder.beginArray();
 49				i != remainder.endArray();
 50				++i)
 51			{
 52				if (addSlash) { pathOutput << '/'; }
 53				pathOutput << i->asString();
 54				addSlash = true;
 55			}
 56			
 57			return pathOutput.str();
 58		}
 59		
 60		void ensureRootTraversal(const std::string& path,
 61			const LLHTTPNode* expectedNode,
 62			const char* expectedRemainder)
 63		{
 64			mContext.clear();
 65			
 66			const LLHTTPNode* actualNode = mRoot.traverse(path, mContext);
 67			
 68			ensure_equals("traverse " + path + " node",
 69				actualNode, expectedNode);
 70			ensure_equals("traverse " + path + " remainder",
 71				remainderPath(), expectedRemainder);
 72		}
 73
 74		class Response : public LLHTTPNode::Response
 75		{
 76		public:
 77			static LLPointer<Response> create() {return new Response();}
 78
 79			LLSD mResult;
 80
 81			void result(const LLSD& result) { mResult = result; }
 82			void status(S32 code, const std::string& message) { }
 83			void extendedResult(S32 code, const std::string& message, const LLSD& headers) { }
 84			
 85		private:
 86			Response() {;} // Must be accessed through LLPointer.
 87		};
 88
 89		typedef LLPointer<Response> ResponsePtr;
 90
 91		LLSD get(const std::string& path)
 92		{
 93			mContext.clear();
 94			const LLHTTPNode* node = mRoot.traverse(path, mContext);
 95			ensure(path + " found", node != NULL);
 96
 97			ResponsePtr response = Response::create();
 98			node->get(LLHTTPNode::ResponsePtr(response), mContext);
 99			return response->mResult;
100		}
101		
102		LLSD post(const std::string& path, const LLSD& input)
103		{
104			mContext.clear();
105			const LLHTTPNode* node = mRoot.traverse(path, mContext);
106			ensure(path + " found", node != NULL);
107
108			ResponsePtr response = Response::create();
109			node->post(LLHTTPNode::ResponsePtr(response), mContext, input);
110			return response->mResult;
111		}
112		
113		void ensureMemberString(const std::string& name,
114			const LLSD& actualMap, const std::string& member,
115			const std::string& expectedValue)
116		{
117			ensure_equals(name + " " + member,
118				actualMap[member].asString(), expectedValue);
119		}
120		
121		
122		void ensureInArray(const LLSD& actualArray,
123			const std::string& expectedValue)
124		{
125			LLSD::array_const_iterator i = actualArray.beginArray();
126			LLSD::array_const_iterator end = actualArray.endArray();
127		
128			for (; i != end; ++i)
129			{
130				std::string path = i->asString();
131
132				if (path == expectedValue)
133				{
134					return;
135				}
136			}
137			
138			fail("didn't find " + expectedValue);
139		}
140			
141	};
142	
143	typedef test_group<HTTPNodeTestData>	HTTPNodeTestGroup;
144	typedef HTTPNodeTestGroup::object		HTTPNodeTestObject;
145	HTTPNodeTestGroup httpNodeTestGroup("http node");
146
147	template<> template<>
148	void HTTPNodeTestObject::test<1>()
149	{
150		// traversal of the lone node
151		
152		ensureRootTraversal("", &mRoot, "");
153		ensureRootTraversal("/", &mRoot, "");
154		ensureRootTraversal("foo", NULL, "foo");
155		ensureRootTraversal("foo/bar", NULL, "foo/bar");
156		
157		ensure_equals("root of root", mRoot.rootNode(), &mRoot);
158	}
159	
160	template<> template<>
161	void HTTPNodeTestObject::test<2>()
162	{
163		// simple traversal of a single node
164		
165		LLHTTPNode* helloNode = new LLHTTPNode;
166		mRoot.addNode("hello", helloNode);
167
168		ensureRootTraversal("hello", helloNode, "");
169		ensureRootTraversal("/hello", helloNode, "");
170		ensureRootTraversal("hello/", helloNode, "");
171		ensureRootTraversal("/hello/", helloNode, "");
172
173		ensureRootTraversal("hello/there", NULL, "there");
174		
175		ensure_equals("root of hello", helloNode->rootNode(), &mRoot);
176	}
177
178	template<> template<>
179	void HTTPNodeTestObject::test<3>()
180	{
181		// traversal of mutli-branched tree
182		
183		LLHTTPNode* greekNode = new LLHTTPNode;
184		LLHTTPNode* alphaNode = new LLHTTPNode;
185		LLHTTPNode* betaNode = new LLHTTPNode;
186		LLHTTPNode* gammaNode = new LLHTTPNode;
187		
188		greekNode->addNode("alpha", alphaNode);
189		greekNode->addNode("beta", betaNode);
190		greekNode->addNode("gamma", gammaNode);
191		mRoot.addNode("greek", greekNode);
192		
193		LLHTTPNode* hebrewNode = new LLHTTPNode;
194		LLHTTPNode* alephNode = new LLHTTPNode;
195		
196		hebrewNode->addNode("aleph", alephNode);
197		mRoot.addNode("hebrew", hebrewNode);
198
199		ensureRootTraversal("greek/alpha", alphaNode, "");
200		ensureRootTraversal("greek/beta", betaNode, "");
201		ensureRootTraversal("greek/delta", NULL, "delta");
202		ensureRootTraversal("greek/gamma", gammaNode, "");
203		ensureRootTraversal("hebrew/aleph", alephNode, "");
204
205		ensure_equals("root of greek", greekNode->rootNode(), &mRoot);
206		ensure_equals("root of alpha", alphaNode->rootNode(), &mRoot);
207		ensure_equals("root of beta", betaNode->rootNode(), &mRoot);
208		ensure_equals("root of gamma", gammaNode->rootNode(), &mRoot);
209		ensure_equals("root of hebrew", hebrewNode->rootNode(), &mRoot);
210		ensure_equals("root of aleph", alephNode->rootNode(), &mRoot);
211	}
212
213	template<> template<>
214	void HTTPNodeTestObject::test<4>()
215	{
216		// automatic creation of parent nodes and not overriding existing nodes
217		
218		LLHTTPNode* alphaNode = new LLHTTPNode;
219		LLHTTPNode* betaNode = new LLHTTPNode;
220		LLHTTPNode* gammaNode = new LLHTTPNode;
221		LLHTTPNode* gamma2Node = new LLHTTPNode;
222		
223		mRoot.addNode("greek/alpha", alphaNode);
224		mRoot.addNode("greek/beta", betaNode);
225		
226		mRoot.addNode("greek/gamma", gammaNode);
227		mRoot.addNode("greek/gamma", gamma2Node);
228		
229		LLHTTPNode* alephNode = new LLHTTPNode;
230		
231		mRoot.addNode("hebrew/aleph", alephNode);
232
233		ensureRootTraversal("greek/alpha", alphaNode, "");
234		ensureRootTraversal("greek/beta", betaNode, "");
235		ensureRootTraversal("greek/delta", NULL, "delta");
236		ensureRootTraversal("greek/gamma", gammaNode, "");
237		ensureRootTraversal("hebrew/aleph", alephNode, "");
238
239		ensure_equals("root of alpha", alphaNode->rootNode(), &mRoot);
240		ensure_equals("root of beta", betaNode->rootNode(), &mRoot);
241		ensure_equals("root of gamma", gammaNode->rootNode(), &mRoot);
242		ensure_equals("root of aleph", alephNode->rootNode(), &mRoot);
243	}
244	
245	class IntegerNode : public LLHTTPNode
246	{
247	public:
248		virtual void get(ResponsePtr response, const LLSD& context) const
249		{
250			int n = context["extra"]["value"];
251			
252			LLSD info;
253			info["value"] = n;
254			info["positive"] = n > 0;
255			info["zero"] = n == 0;
256			info["negative"] = n < 0;
257		
258			response->result(info);
259		}
260		
261		virtual bool validate(const std::string& name, LLSD& context) const
262		{
263			int n;
264			std::istringstream i_stream(name);
265			i_stream >> n;
266
267			if (i_stream.fail()  ||  i_stream.get() != EOF)
268			{
269				return false;
270			}
271
272			context["extra"]["value"] = n;
273			return true;
274		}
275	};
276	
277	class SquareNode : public LLHTTPNode
278	{
279	public:
280		virtual void get(ResponsePtr response, const LLSD& context) const
281		{
282			int n = context["extra"]["value"];		
283			response->result(n*n);
284		}
285	};
286	
287	template<> template<>
288	void HTTPNodeTestObject::test<5>()
289	{
290		// wildcard nodes
291		
292		LLHTTPNode* miscNode = new LLHTTPNode;
293		LLHTTPNode* iNode = new IntegerNode;
294		LLHTTPNode* sqNode = new SquareNode;
295		
296		mRoot.addNode("test/misc", miscNode);
297		mRoot.addNode("test/<int>", iNode);
298		mRoot.addNode("test/<int>/square", sqNode);
299		
300		ensureRootTraversal("test/42", iNode, "");
301		ensure_equals("stored integer",
302			context()["extra"]["value"].asInteger(), 42);
303		
304		ensureRootTraversal("test/bob", NULL, "bob");
305		ensure("nothing stored",
306			context()["extra"]["value"].isUndefined());
307			
308		ensureRootTraversal("test/3/square", sqNode, "");
309		ResponsePtr response = Response::create();
310		sqNode->get(LLHTTPNode::ResponsePtr(response), context());
311		ensure_equals("square result", response->mResult.asInteger(), 9);
312	}
313	
314	class AlphaNode : public LLHTTPNode
315	{
316	public:
317		virtual bool handles(const LLSD& remainder, LLSD& context) const
318		{
319			LLSD::array_const_iterator i = remainder.beginArray();
320			LLSD::array_const_iterator end = remainder.endArray();
321			
322			for (; i != end; ++i)
323			{
324				std::string s = i->asString();
325				if (s.empty() || s[0] != 'a')
326				{
327					return false;
328				}
329			}
330			
331			return true;
332		}
333	};
334	
335	template<> template<>
336	void HTTPNodeTestObject::test<6>()
337	{
338		// nodes that handle remainders
339		
340		LLHTTPNode* miscNode = new LLHTTPNode;
341		LLHTTPNode* aNode = new AlphaNode;
342		LLHTTPNode* zNode = new LLHTTPNode;
343		
344		mRoot.addNode("test/misc", miscNode);
345		mRoot.addNode("test/alpha", aNode);
346		mRoot.addNode("test/alpha/zebra", zNode);
347		
348		ensureRootTraversal("test/alpha", aNode, "");
349		ensureRootTraversal("test/alpha/abe", aNode, "abe");
350		ensureRootTraversal("test/alpha/abe/amy", aNode, "abe/amy");
351		ensureRootTraversal("test/alpha/abe/bea", NULL, "abe/bea");
352		ensureRootTraversal("test/alpha/bob", NULL, "bob");
353		ensureRootTraversal("test/alpha/zebra", zNode, "");
354	}
355	
356	template<> template<>
357	void HTTPNodeTestObject::test<7>()
358	{
359		// test auto registration
360		
361		LLHTTPStandardServices::useServices();
362		LLHTTPRegistrar::buildAllServices(mRoot);
363		
364		{
365			LLSD result = get("web/hello");
366			ensure_equals("hello result", result.asString(), "hello");
367		}
368		{
369			LLSD stuff = 3.14159;
370			LLSD result = post("web/echo", stuff);
371			ensure_equals("echo result", result, stuff);
372		}
373	}
374
375	template<> template<>
376	void HTTPNodeTestObject::test<8>()
377	{
378		// test introspection
379		
380		LLHTTPRegistrar::buildAllServices(mRoot);
381		
382		mRoot.addNode("test/misc", new LLHTTPNode);
383		mRoot.addNode("test/<int>", new IntegerNode);
384		mRoot.addNode("test/<int>/square", new SquareNode);
385
386		const LLSD result = get("web/server/api");
387		
388		ensure("result is array", result.isArray());
389		ensure("result size", result.size() >= 2);
390		
391		ensureInArray(result, "web/echo");
392		ensureInArray(result, "web/hello");
393		ensureInArray(result, "test/misc");
394		ensureInArray(result, "test/<int>");
395		ensureInArray(result, "test/<int>/square");
396	}
397	
398	template<> template<>
399	void HTTPNodeTestObject::test<9>()
400	{
401		// test introspection details
402
403		LLHTTPRegistrar::buildAllServices(mRoot);
404
405		const LLSD helloDetails = get("web/server/api/web/hello");
406		
407		ensure_contains("hello description",
408			helloDetails["description"].asString(), "hello");
409		ensure_equals("method name", helloDetails["api"][0].asString(), std::string("GET"));
410		ensureMemberString("hello", helloDetails, "output", "\"hello\"");
411		ensure_contains("hello __file__",
412			helloDetails["__file__"].asString(), "llsdhttpserver.cpp");
413		ensure("hello line", helloDetails["__line__"].isInteger());
414
415
416		const LLSD echoDetails = get("web/server/api/web/echo");
417		
418		ensure_contains("echo description",
419			echoDetails["description"].asString(), "echo");
420		ensure_equals("method name", echoDetails["api"][0].asString(), std::string("POST"));
421		ensureMemberString("echo", echoDetails, "input", "<any>");
422		ensureMemberString("echo", echoDetails, "output", "<the input>");
423		ensure_contains("echo __file__",
424			echoDetails["__file__"].asString(), "llsdhttpserver.cpp");
425		ensure("echo", echoDetails["__line__"].isInteger());
426	}
427}