PageRenderTime 27ms CodeModel.GetById 1ms app.highlight 22ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/test/llhttpclient_tut.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 383 lines | 285 code | 60 blank | 38 comment | 6 complexity | cb80f3bce8c21f75949c210234dec2c0 MD5 | raw file
  1/** 
  2 * @file llhttpclient_tut.cpp
  3 * @brief Testing the HTTP client classes.
  4 *
  5 * $LicenseInfo:firstyear=2006&license=viewerlgpl$
  6 * Second Life Viewer Source Code
  7 * Copyright (C) 2010, Linden Research, Inc.
  8 * 
  9 * This library is free software; you can redistribute it and/or
 10 * modify it under the terms of the GNU Lesser General Public
 11 * License as published by the Free Software Foundation;
 12 * version 2.1 of the License only.
 13 * 
 14 * This library is distributed in the hope that it will be useful,
 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 17 * Lesser General Public License for more details.
 18 * 
 19 * You should have received a copy of the GNU Lesser General Public
 20 * License along with this library; if not, write to the Free Software
 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 22 * 
 23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 24 * $/LicenseInfo$
 25 */
 26
 27/**
 28 *
 29 * These classes test the HTTP client framework.
 30 *
 31 */
 32
 33#include <tut/tut.hpp>
 34#include "linden_common.h"
 35
 36// These are too slow on Windows to actually include in the build. JC
 37#if !LL_WINDOWS
 38
 39#include "lltut.h"
 40#include "llhttpclient.h"
 41#include "llformat.h"
 42#include "llpipeutil.h"
 43#include "llproxy.h"
 44#include "llpumpio.h"
 45
 46#include "llsdhttpserver.h"
 47#include "lliohttpserver.h"
 48#include "lliosocket.h"
 49
 50namespace tut
 51{
 52	LLSD storage;
 53	
 54	class LLSDStorageNode : public LLHTTPNode
 55	{
 56	public:
 57		LLSD simpleGet() const					{ return storage; }
 58		LLSD simplePut(const LLSD& value) const	{ storage = value; return LLSD(); }
 59	};
 60
 61	class ErrorNode : public LLHTTPNode
 62	{
 63	public:
 64		void get(ResponsePtr r, const LLSD& context) const
 65			{ r->status(599, "Intentional error"); }
 66		void post(ResponsePtr r, const LLSD& context, const LLSD& input) const
 67			{ r->status(input["status"], input["reason"]); }
 68	};
 69
 70	class TimeOutNode : public LLHTTPNode
 71	{
 72	public:
 73		void get(ResponsePtr r, const LLSD& context) const
 74		{
 75            /* do nothing, the request will eventually time out */ 
 76		}
 77	};
 78
 79	LLHTTPRegistration<LLSDStorageNode> gStorageNode("/test/storage");
 80	LLHTTPRegistration<ErrorNode>		gErrorNode("/test/error");
 81	LLHTTPRegistration<TimeOutNode>		gTimeOutNode("/test/timeout");
 82
 83	struct HTTPClientTestData
 84	{
 85	public:
 86		HTTPClientTestData()
 87		{
 88			apr_pool_create(&mPool, NULL);
 89			LLCurl::initClass(false);
 90			mServerPump = new LLPumpIO(mPool);
 91			mClientPump = new LLPumpIO(mPool);
 92
 93			LLHTTPClient::setPump(*mClientPump);
 94		}
 95		
 96		~HTTPClientTestData()
 97		{
 98			delete mServerPump;
 99			delete mClientPump;
100			LLProxy::cleanupClass();
101			apr_pool_destroy(mPool);
102		}
103
104		void setupTheServer()
105		{
106			LLHTTPNode& root = LLIOHTTPServer::create(mPool, *mServerPump, 8888);
107
108			LLHTTPStandardServices::useServices();
109			LLHTTPRegistrar::buildAllServices(root);
110		}
111		
112		void runThePump(float timeout = 100.0f)
113		{
114			LLTimer timer;
115			timer.setTimerExpirySec(timeout);
116
117			while(!mSawCompleted && !mSawCompletedHeader && !timer.hasExpired())
118			{
119				if (mServerPump)
120				{
121					mServerPump->pump();
122					mServerPump->callback();
123				}
124				if (mClientPump)
125				{
126					mClientPump->pump();
127					mClientPump->callback();
128				}
129			}
130		}
131
132		void killServer()
133		{
134			delete mServerPump;
135			mServerPump = NULL;
136		}
137	
138	private:
139		apr_pool_t* mPool;
140		LLPumpIO* mServerPump;
141		LLPumpIO* mClientPump;
142
143		
144	protected:
145		void ensureStatusOK()
146		{
147			if (mSawError)
148			{
149				std::string msg =
150					llformat("error() called when not expected, status %d",
151						mStatus); 
152				fail(msg);
153			}
154		}
155	
156		void ensureStatusError()
157		{
158			if (!mSawError)
159			{
160				fail("error() wasn't called");
161			}
162		}
163		
164		LLSD getResult()
165		{
166			return mResult;
167		}
168		LLSD getHeader()
169		{
170			return mHeader;
171		}
172	
173	protected:
174		bool mSawError;
175		U32 mStatus;
176		std::string mReason;
177		bool mSawCompleted;
178		bool mSawCompletedHeader;
179		LLSD mResult;
180		LLSD mHeader;
181		bool mResultDeleted;
182
183		class Result : public LLHTTPClient::Responder
184		{
185		protected:
186			Result(HTTPClientTestData& client)
187				: mClient(client)
188			{
189			}
190		
191		public:
192			static boost::intrusive_ptr<Result> build(HTTPClientTestData& client)
193			{
194				return boost::intrusive_ptr<Result>(new Result(client));
195			}
196			
197			~Result()
198			{
199				mClient.mResultDeleted = true;
200			}
201			
202			virtual void error(U32 status, const std::string& reason)
203			{
204				mClient.mSawError = true;
205				mClient.mStatus = status;
206				mClient.mReason = reason;
207			}
208
209			virtual void result(const LLSD& content)
210			{
211				mClient.mResult = content;
212			}
213
214			virtual void completed(
215							U32 status, const std::string& reason,
216							const LLSD& content)
217			{
218				LLHTTPClient::Responder::completed(status, reason, content);
219				
220				mClient.mSawCompleted = true;
221			}
222
223			virtual void completedHeader(
224				U32 status, const std::string& reason,
225				const LLSD& content)
226			{
227				mClient.mHeader = content;
228				mClient.mSawCompletedHeader = true;
229			}
230
231		private:
232			HTTPClientTestData& mClient;
233		};
234
235		friend class Result;
236
237	protected:
238		LLHTTPClient::ResponderPtr newResult()
239		{
240			mSawError = false;
241			mStatus = 0;
242			mSawCompleted = false;
243			mSawCompletedHeader = false;
244			mResult.clear();
245			mHeader.clear();
246			mResultDeleted = false;
247			
248			return Result::build(*this);
249		}
250	};
251	
252	
253	typedef test_group<HTTPClientTestData>	HTTPClientTestGroup;
254	typedef HTTPClientTestGroup::object		HTTPClientTestObject;
255	HTTPClientTestGroup httpClientTestGroup("http_client");
256
257	template<> template<>
258	void HTTPClientTestObject::test<1>()
259	{
260		LLHTTPClient::get("http://www.google.com/", newResult());
261		runThePump();
262		ensureStatusOK();
263		ensure("result object wasn't destroyed", mResultDeleted);
264	}
265
266	template<> template<>
267	void HTTPClientTestObject::test<2>()
268	{
269		skip("error test depends on dev's local ISP not supplying \"helpful\" search page");
270		LLHTTPClient::get("http://www.invalid", newResult());
271		runThePump();
272		ensureStatusError();
273	}
274
275	template<> template<>
276		void HTTPClientTestObject::test<3>()
277	{
278		LLSD sd;
279
280		sd["list"][0]["one"] = 1;
281		sd["list"][0]["two"] = 2;
282		sd["list"][1]["three"] = 3;
283		sd["list"][1]["four"] = 4;
284		
285		setupTheServer();
286
287		LLHTTPClient::post("http://localhost:8888/web/echo", sd, newResult());
288		runThePump();
289		ensureStatusOK();
290		ensure_equals("echoed result matches", getResult(), sd);
291	}
292
293	template<> template<>
294		void HTTPClientTestObject::test<4>()
295	{
296		LLSD sd;
297
298		sd["message"] = "This is my test message.";
299
300		setupTheServer();
301		LLHTTPClient::put("http://localhost:8888/test/storage", sd, newResult());
302		runThePump();
303		ensureStatusOK();
304
305		LLHTTPClient::get("http://localhost:8888/test/storage", newResult());
306		runThePump();
307		ensureStatusOK();
308		ensure_equals("echoed result matches", getResult(), sd);
309	
310	}
311
312	template<> template<>
313		void HTTPClientTestObject::test<5>()
314	{
315		LLSD sd;
316		sd["status"] = 543;
317		sd["reason"] = "error for testing";
318
319		setupTheServer();
320
321		LLHTTPClient::post("http://localhost:8888/test/error", sd, newResult());
322		runThePump();
323		ensureStatusError();
324		ensure_contains("reason", mReason, sd["reason"]);
325	}
326
327	template<> template<>
328		void HTTPClientTestObject::test<6>()
329	{
330		setupTheServer();
331
332		LLHTTPClient::get("http://localhost:8888/test/timeout", newResult());
333		runThePump(1.0f);
334		killServer();
335		runThePump();
336		ensureStatusError();
337		ensure_equals("reason", mReason, "STATUS_ERROR");
338	}
339
340	template<> template<>
341		void HTTPClientTestObject::test<7>()
342	{
343		// Can not use the little mini server.  The blocking request
344		// won't ever let it run.  Instead get from a known LLSD
345		// source and compare results with the non-blocking get which
346		// is tested against the mini server earlier.
347		skip("secondlife.com is not reliable enough for unit tests.");
348
349
350		LLSD expected;
351
352		LLHTTPClient::get("http://secondlife.com/xmlhttp/homepage.php", newResult());
353		runThePump();
354		ensureStatusOK();
355		expected = getResult();
356
357		LLSD result;
358		result = LLHTTPClient::blockingGet("http://secondlife.com/xmlhttp/homepage.php");
359		LLSD body = result["body"];
360		ensure_equals("echoed result matches", body.size(), expected.size());
361	}
362	template<> template<>
363		void HTTPClientTestObject::test<8>()
364	{
365		// This is testing for the presence of the Header in the returned results
366		// from an HTTP::get call.
367		LLHTTPClient::get("http://www.google.com/", newResult());
368		runThePump();
369		ensureStatusOK();
370		LLSD header = getHeader();
371		ensure_equals("got a header", header.emptyMap().asBoolean(), FALSE);
372	}
373	template<> template<>
374	void HTTPClientTestObject::test<9>()
375	{
376		LLHTTPClient::head("http://www.google.com/", newResult());
377		runThePump();
378		ensureStatusOK();
379		ensure("result object wasn't destroyed", mResultDeleted);
380	}
381}
382
383#endif	// !LL_WINDOWS